这是一篇纯实例的博客,网络上关于AspectJ的实例博客非常少,很多的博客都是在讲解AOP思想的一些概念以及从AspectJ语言本身出发去讲解AspectJ,但小编觉得作为急需在项目中使用,换句话说对于初学者来说,没有必要从AspectJ语言本身去深入了解它,如果对AOP思想不明白的,请参考我的博客 一起来谈谈 Spring AOP!
本文将从AspectJ在Spring中的应用入手讲解。
首先明白一点,AOP思想的实现框架或者说库有很多,最知名的无非就是AspectJ、Cglib、JDK动态代理三种了,归根到底它们都是使用了代理这种设计模式,区别在于下面表格:
名称 | 代理类型 | 基本原理 | 特性 |
---|---|---|---|
AspectJ | 静态代理 |
代理类在编译期间就生成,但是在生成时将相应的切面 织入
到代理类中 |
|
Cglib | 动态代理 | 代理类在运行时动态生成,底层使用字节码处理框架ASM,来转换字节码并生成新的类 | 弥补JDK动态代理只能代理接口的不足,cglib可以动态代理类 |
JDK动态代理 | 动态代理 | 代理类在运行时动态生成,源码级别会调用一个Native方法 | 只能代理接口 |
关于cglib代理,可以阅读小编的另外一篇博客cglib代理的使用
applicationContext.xml
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
@Aspect @Component public class ExecutionAspect { @Before("execution(* wokao666.club.myapp.aspectJ.*.before*(..))") public void doBefore(JoinPoint joinPoint) throws Throwable { System.err.println("这是一个前置通知,在方法调用之前被执行!!!"); } @After("execution(* wokao666.club.myapp.aspectJ.*.after*(..))") public void doAfter(JoinPoint joinPoint) throws Throwable { System.err.println("这是一个后置通知,在方法调用之后被执行!!!"); } @Around("execution(* wokao666.club.myapp.aspectJ.*.around*(..))") public void doAround(ProceedingJoinPoint joinPoint) throws Throwable { System.err.println("这是一个环绕通知,在方法调用前后都会执行!!!"); System.err.println("执行前"); joinPoint.proceed(); System.err.println("执行后"); } }
说明下上面的切入点表达式, execution
表示连接点类型,第一个 *
表示拦截的方法但返回值类型, *
表示任意类型返回值;第二部分 wokao666.club.myapp.aspectJ.*.before*(..)
表示以 wokao666.club.myapp.aspectJ
包下的任意类的所有以 before
开头的方法。
Main.java
public class Main { private static ClassPathXmlApplicationContext ctx = null; public static void main(String[] args) { ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); TestMethod test = (TestMethod) ctx.getBean("bean"); test.before("before"); System.err.println("====================================================="); test.after("after"); System.err.println("====================================================="); test.around("around"); }
@Component("bean") public class TestMethod { public void before(String name) { System.err.println("the param Name is " + name); } public void after(String name) { System.err.println("the param Name is " + name); } public void around(String name) { System.err.println("the param Name is " + name); } }
pom.xml
<dependency> <groupId>aspectj</groupId> <artifactId>aspectjrt</artifactId> <version>1.5.4</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.1</version> </dependency>
执行结果:
这是一个前置通知,在方法调用之前被执行!!! the param Name is before ===================================================== the param Name is after 这是一个后置通知,在方法调用之后被执行!!! ===================================================== 这是一个环绕通知,在方法调用前后都会执行!!! 执行前 the param Name is around 执行后
有时候我们会使用自定义注解来标识我们的业务方法,下面将讲解AspectJ拦截基于注解的实现,跟生面的差别在于切入点表达式的区别而已。
RpcService
/** * 远程服务注解,主要用于拦截日志、错误码等方面处理 */ @Documented @Retention(RetentionPolicy.RUNTIME) @Target(value = { ElementType.METHOD, ElementType.TYPE }) public @interface RpcService { }
@Component("bean") public class TestMethod { @RpcService public void around(String name) { System.err.println("the param Name is " + name); } }
@Aspect @Component public class AnnotationAspect { @Around("@annotation(wokao666.club.myapp.annotation.RpcService)") public void doAround(ProceedingJoinPoint joinPoint) throws Throwable { System.err.println("这是一个环绕通知,在方法调用前后都会执行!!!"); System.err.println("执行前"); joinPoint.proceed(); System.err.println("执行后"); } }
public class Main { private static ClassPathXmlApplicationContext ctx = null; public static void main(String[] args) { ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); TestMethod test = (TestMethod) ctx.getBean("bean"); test.around("around"); } }