概述
AOP(Aspect Oriented Programming) 面向切面编程,实质上是对我们的对象进行增强,并且提供良好的管理机制。
对于对象增强,可以有以下几种方法
- 装饰器模式,比如 JDK 中的 I/O 流
1
| InputStream inputStream = new LineNumberInputStream(new BufferedInputStream(new FileInputStream("")));
|
静态代理模式
适配器模式
动态代理,包括 Proxy 和 CGLIB 等方法进行动态代理
SpringAOP 是一套 AOP 的解决方案,他比传统的对象增强更容易管理和扩展。
在对象增强上,它的规则是:基于动态代理来实现。默认地,如果使用接口方法的,用 JDK 提供的动态代理实现,如果没有接口,使用 CGLIB 实现。SpringAOP 基于 IOC 容器,动态代理之后,会把原对象替换成动态代理的对象。
概念
Advisor 是 AOP 的一个概念,他是 保存 AOP 配置 的一个单位
注解配置 Spring AOP
SpringAOP 和 AspectJ 没多大关系,而仅仅是使用了 AspectJ 中的概念,包括使用的注解也是直接来自于 AspectJ 的包(有点迷)。
依赖
1 2 3 4 5
| <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.11</version> </dependency>
|
或者在 SpringBoot
1 2 3 4
| <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
|
注解
注解开启,在 Main 类上面标上这个注解
在 配置文件 Bean 上 打上 @Aspect
配置 PointCut
1 2 3 4 5
| @Aspect public class SystemArchitecture { @Pointcut("execution(* transfer(..))") private void anyOldTransfer() {} }
|
一些配置规则
1 2 3 4 5 6 7 8 9 10
| @Pointcut("execution(* transfer(..))")
@Pointcut("within(com.javadoop.springaoplearning.service..*)")
@Pointcut("execution( .*(..)) @annotation(com.javadoop.annotation.Subscribe)")
@Pointcut("bean(*Service)")
|
一些实践中的配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| @Aspect public class SystemArchitecture {
@Pointcut("within(com.javadoop.web..*)") public void inWebLayer() {}
@Pointcut("within(com.javadoop.service..*)") public void inServiceLayer() {}
@Pointcut("within(com.javadoop.dao..*)") public void inDataAccessLayer() {}
@Pointcut("execution(* com.javadoop..service.*.*(..))") public void businessService() {}
@Pointcut("execution(* com.javadoop.dao.*.*(..))") public void dataAccessOperation() {} }
|
配置 Advice
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
| @Aspect public class AdviceExample {
@Before("com.javadoop.aop.SystemArchitecture.dataAccessOperation()") public void doAccessCheck() { }
@Before("com.javadoop.springaoplearning.aop_spring_2_aspectj.SystemArchitecture.businessService()") public void logArgs(JoinPoint joinPoint) { System.out.println("方法执行前,打印入参:" + Arrays.toString(joinPoint.getArgs())); }
@AfterReturning("com.javadoop.aop.SystemArchitecture.dataAccessOperation()") public void doAccessCheck() { }
@AfterReturning( pointcut="com.javadoop.aop.SystemArchitecture.dataAccessOperation()", returning="retVal") public void doAccessCheck(Object retVal) { }
@AfterThrowing("com.javadoop.aop.SystemArchitecture.dataAccessOperation()") public void doRecoveryActions() { }
@AfterThrowing( pointcut="com.javadoop.aop.SystemArchitecture.dataAccessOperation()", throwing="ex") public void doRecoveryActions(DataAccessException ex) { }
@After("com.javadoop.aop.SystemArchitecture.dataAccessOperation()") public void doReleaseLock() { }
@Around("com.javadoop.aop.SystemArchitecture.businessService()") public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable { Object retVal = pjp.proceed(); return retVal; }
}
|
advisor 会由 Spring 给我们生成。
原理解析
在 Spring 中,Bean 初始化结束后,会对每一个 bean 调用一次实现 BeanPostProcessor 接口的 Bean postProcessAfterInitialization() 方法。
当我们通过注解配置 AOP (XML 差不多),Spring 会 给我们注册一个 AnnotationAwareAspectJAutoProxyCreator 的 Bean,这个 Bean 就实现了 BeanPostProcessor 接口 的 postProcessAfterInitialization() 方法。
这个方法传入 原始 Bean,返回处理过的 Bean ,我们在这里就可以对 这个 Bean 偷梁换柱,换成 Proxy 对象。实现我们代理的目的。
在 ProxyCreater 中实现了这个方法:
1 2 3 4 5 6 7 8 9 10
| @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (bean != null) { Object cacheKey = getCacheKey(bean.getClass(), beanName); if (!this.earlyProxyReferences.contains(cacheKey)) { return wrapIfNecessary(bean, beanName, cacheKey); } } return bean; }
|
这个方法干了这些事情:
- 把这个 bean 匹配的 AOP 增强配置打包成 advisor
保存这个 Bean 的接口方法
根据接口方法和配置决定使用 JDK Proxy 代理生成器(JdkDynamicAopProxy) 还是 CGLIB 代理生成器(ObjenesisCglibAopProxy) 来生成代理对象,一般情况下:
如果被代理的目标类实现了一个或多个自定义的接口,那么就会使用 JDK 动态代理
如果没有实现任何接口,会使用 CGLIB 实现代理。
生成代理对象后,把这个代理对象替换掉原来的 Bean
简单讲一下创建 JDK Proxy 代理,CGLIB 类似,但是操纵比较复杂。
Proxy 使用方法见 Java 动态代理机制 (一) JDK Proxy 详解
JdkDynamicAopProxy -> getProxy
1 2 3 4 5 6 7 8 9 10 11
| public Object getProxy(ClassLoader classLoader) { if (logger.isDebugEnabled()) { logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource()); } Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true); findDefinedEqualsAndHashCodeMethods(proxiedInterfaces); return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this); }
|
当我们调用 被 Proxy 代理的类的时候,都会调用到 JdkDynamicAopProxy 的 invoke 方法,invoke 方法 在我们原方法的周围做一些增强(从 advisor 中获取我们写好的增强函数)。剩下的就交给 JDK 来处理了。
参考资料
Spring AOP 源码解析
Spring AOP 使用介绍,从前世到今生
What is the difference between Advisor and Aspect in AOP?