三、基于注解方式@AspectJ实现AOP
PS:其实springboot此配置是默认开启的,所以根本可以不用管了,在Springboot中使用过注解配置方式的人会问是否需要在程序主类中增加@EnableAspectJAutoProxy来启用,实际并不需要。看下面关于AOP的默认配置属性,其中spring.aop.auto属性默认是开启的,也就是说只要引入了AOP依赖后,其实默认已经增加了@EnableAspectJAutoProxy。
截图看看Boot的一些默认配置:
不过后面会有点奇怪的问题,springboot中,不管这个项是否设置为true或者false,都不会跟以前spring项目中,如果没有设置为true,当代理类没有继承接口,启动项目的时候会报错。而springboot项目中,会自动转换成使用CGLIB进行动态代理,其中原理是怎么实现,就没有去看底层代码了,估计底层进行了改造吧!
切面:
/** * @author fangshixiang@vipkid.com.cn * @description * @date 2018-10-30 11:32 */ @Component //这个组件一定得加入到容器才行 @Aspect public class SpringLogAspect { //定义一个切入点:指定哪些方法可以被切入(如果是别的类需要使用 请用该方法的全类名) @Pointcut("execution(* com.fsx.run.service..*.*(..))") public void pointCut() { } @Before("pointCut()") public void doBefore(JoinPoint joinPoint) { System.out.println("AOP Before Advice..."); } @After("pointCut()") public void doAfter(JoinPoint joinPoint) { System.out.println("AOP After Advice..."); } @AfterReturning(pointcut = "pointCut()", returning = "returnVal") public void afterReturn(JoinPoint joinPoint, Object returnVal) { System.out.println("AOP AfterReturning Advice:" + returnVal); } @AfterThrowing(pointcut = "pointCut()", throwing = "error") public void afterThrowing(JoinPoint joinPoint, Throwable error) { System.out.println("AOP AfterThrowing Advice..." + error); System.out.println("AfterThrowing..."); } // 环绕通知:此处有一个坑,当AfterReturning和Around共存时,AfterReturning是获取不到返回值的 //@Around("pointCut()") //public void around(ProceedingJoinPoint pjp) { // System.out.println("AOP Aronud before..."); // try { // pjp.proceed(); // } catch (Throwable e) { // e.printStackTrace(); // } // System.out.println("AOP Aronud after..."); //} }
idea很智能:如果切中了,会有这个小图标的
controller层这么测试:
@Autowired AService aService; @Autowired BService bService; @Override public Object testDemo(String str) { aService.sayHelloA(); bService.sayHelloB(); return str + "succuss~"; }
访问,控制台打印结果如下:
AOP Before Advice... hello A AOP After Advice... AOP AfterReturning Advice:AServiceImpl AOP Before Advice... hello B AOP After Advice... AOP AfterReturning Advice:BServiceImpl
此处有一个坑,当AfterReturning和Around共存时,AfterReturning是获取不到返回值的。当然,如果你的方法本来就没有返回值,那肯定也是null咯
关于@Pointcut切点表达式的书写,请参见:
小家Spring】Spring AOP中@Pointcut切入点表达式最全面使用介绍
神一样的AspectJ --> AOP的领跑者
AspectJ 可以成为是AOP的鼻祖,规范的制定者。】
因此本文先进行一个简单案例的演示,然后引出AOP中一些晦涩难懂的抽象概念,放心,通过本篇博客,我们将会非常轻松地理解并掌握它们。编写一个HelloWord的类,然后利用AspectJ技术切入该类的执行过程。
Hello类:
public class HelloWord { public void sayHello(){ System.out.println("hello world !"); } public static void main(String args[]){ HelloWord helloWord =new HelloWord(); helloWord.sayHello(); } }
编写AspectJ类,注意关键字为aspect(MyAspectJDemo.aj,其中aj为AspectJ的后缀),含义与class相同,即定义一个AspectJ的类(注意:后缀名是.aj)
public aspect MyAspectJDemo { /** * 定义切点,日志记录切点 */ pointcut recordLog():call(* HelloWord.sayHello(..)); /** * 定义切点,权限验证(实际开发中日志和权限一般会放在不同的切面中,这里仅为方便演示) */ pointcut authCheck():call(* HelloWord.sayHello(..)); /** * 定义前置通知! */ before():authCheck(){ System.out.println("sayHello方法执行前验证权限"); } /** * 定义后置通知 */ after():recordLog(){ System.out.println("sayHello方法执行后记录日志"); } }
这样直接运行HelloWorld的main,是不会有AOP效果的。因为我们还需要配置:改变编译器重新编译后再运行,具体参考:
AspectJ——简介以及在IntelliJ IDEA下的配置
配置好后运行结果如下:
AspectJ是一个java实现的AOP框架,它能够对java代码进行AOP编译(一般在编译期进行),让java代码具有AspectJ的AOP功能(当然需要特殊的编译器),可以这样说AspectJ是目前实现AOP框架中最成熟,功能最丰富的语言,更幸运的是,AspectJ与java程序完全兼容,几乎是无缝关联,因此对于有java编程基础的工程师,上手和使用都非常容易。