11. 基于JDK、Cglib实现AOP切面
11. 基于JDK、Cglib实现AOP切面
工程结构
lqf-spring-step-11
├── main
│ ├── java
│ │ └── lqf
│ │ └── springframework
│ │ ├── aop
│ │ │ ├── AdvisedSupport.java
│ │ │ ├── ClassFilter.java
│ │ │ ├── MethodMatcher.java
│ │ │ ├── Pointcut.java
│ │ │ ├── TargetSource.java
│ │ │ ├── aspectj
│ │ │ │ └── AspectJExpressionPointcut.java
│ │ │ └── framework
│ │ │ ├── AopProxy.java
│ │ │ ├── Cglib2AopProxy.java
│ │ │ ├── JdkDynamicAopProxy.java
│ │ │ └── ReflectiveMethodInvocation.java
│ │ ├── beans
│ │ │ ├── BeansException.java
│ │ │ ├── PropertyValue.java
│ │ │ ├── PropertyValues.java
│ │ │ └── factory
│ │ │ ├── Aware.java
│ │ │ ├── BeanClassLoaderAware.java
│ │ │ ├── BeanFactory.java
│ │ │ ├── BeanFactoryAware.java
│ │ │ ├── BeanNameAware.java
│ │ │ ├── ConfigurableListableBeanFactory.java
│ │ │ ├── DisposableBean.java
│ │ │ ├── FactoryBean.java
│ │ │ ├── HierarchicalBeanFactory.java
│ │ │ ├── InitializingBean.java
│ │ │ ├── ListableBeanFactory.java
│ │ │ ├── config
│ │ │ │ ├── AutowireCapableBeanFactory.java
│ │ │ │ ├── BeanDefinition.java
│ │ │ │ ├── BeanFactoryPostProcessor.java
│ │ │ │ ├── BeanPostProcessor.java
│ │ │ │ ├── BeanReference.java
│ │ │ │ ├── ConfigurableBeanFactory.java
│ │ │ │ └── SingletonBeanRegistry.java
│ │ │ ├── support
│ │ │ │ ├── AbstractAutowireCapableBeanFactory.java
│ │ │ │ ├── AbstractBeanDefinitionReader.java
│ │ │ │ ├── AbstractBeanFactory.java
│ │ │ │ ├── BeanDefinitionReader.java
│ │ │ │ ├── BeanDefinitionRegistry.java
│ │ │ │ ├── CglibSubclassingInstantiationStrategy.java
│ │ │ │ ├── DefaultListableBeanFactory.java
│ │ │ │ ├── DefaultSingletonBeanRegistry.java
│ │ │ │ ├── DisposableBeanAdapter.java
│ │ │ │ ├── FactoryBeanRegistrySupport.java
│ │ │ │ ├── InstantiationStrategy.java
│ │ │ │ └── SimpleInstantiationStrategy.java
│ │ │ └── xml
│ │ │ └── XmlBeanDefinitionReader.java
│ │ ├── context
│ │ │ ├── ApplicationContext.java
│ │ │ ├── ApplicationContextAware.java
│ │ │ ├── ApplicationEvent.java
│ │ │ ├── ApplicationEventPublisher.java
│ │ │ ├── ApplicationListener.java
│ │ │ ├── ConfigurableApplicationContext.java
│ │ │ ├── event
│ │ │ │ ├── AbstractApplicationEventMulticaster.java
│ │ │ │ ├── ApplicationContextEvent.java
│ │ │ │ ├── ApplicationEventMulticaster.java
│ │ │ │ ├── ContextClosedEvent.java
│ │ │ │ ├── ContextRefreshedEvent.java
│ │ │ │ └── SimpleApplicationEventMulticaster.java
│ │ │ └── support
│ │ │ ├── AbstractApplicationContext.java
│ │ │ ├── AbstractRefreshableApplicationContext.java
│ │ │ ├── AbstractXmlApplicationContext.java
│ │ │ ├── ApplicationContextAwareProcessor.java
│ │ │ └── ClassPathXmlApplicationContext.java
│ │ ├── core
│ │ │ └── io
│ │ │ ├── ClassPathResource.java
│ │ │ ├── DefaultResourceLoader.java
│ │ │ ├── FileSystemResource.java
│ │ │ ├── Resource.java
│ │ │ ├── ResourceLoader.java
│ │ │ └── UrlResource.java
│ │ └── util
│ │ └── ClassUtils.java
│ └── resources
└── test
├── java
│ └── lqf
│ └── springframework
│ ├── SpringTest.java
│ ├── event
│ │ ├── ContextClosedEventListener.java
│ │ ├── ContextRefreshedEventListener.java
│ │ ├── CustomEvent.java
│ │ └── CustomEventListener.java
│ └── service
│ ├── IUserService.java
│ ├── UserService.java
│ └── UserServiceInterceptor.java
└── resources
├── important.properties
├── spring.xml
└── springPostProcessor.xml
AOP 切点表达式和使用以及基于 JDK 和 CGLIB 的动态代理类关系,如图

- 整个类关系图就是 AOP 实现核心逻辑的地方,上面部分是关于方法的匹配实现,下面从 AopProxy 开始是关于方法的代理操作。
- AspectJExpressionPointcut 的核心功能主要依赖于 aspectj 组件并处理 Pointcut、ClassFilter、MethodMatcher
接口实现,专门用于处理类和方法的匹配过滤操作。 - AopProxy 是代理的抽象对象,它的实现主要是基于 JDK 的代理和 Cglib 代理。在前面章节关于对象的实例化
CglibSubclassingInstantiationStrategy,我们也使用过 Cglib 提供的功能。
设计
在把 AOP 整个切面设计融合到 Spring 前,我们需要解决两个问题,包括:如何给符合规则的方法做代理,
以及做完代理方法的案例后,把类的职责拆分出来。
而这两个功能点的实现,都是以切面的思想进行设计和开发。如果不是很清楚 AOP 是啥,你可以把切面理解为用刀切韭菜,一根一根切总是有点慢,
那么用手(代理)把韭菜捏成一把,用菜刀或者斧头这样不同的拦截操作来处理。而程序中其实也是一样,只不过韭菜变成了方法,菜刀变成了拦截方法。
整体设计结构如下图:

- 就像在使用 Spring 的 AOP 一样,只处理一些需要被拦截的方法。在拦截方法后,执行你对方法的扩展操作。
- 那么我们就需要先来实现一个可以代理方法的 Proxy,其实代理方法主要是使用到方法拦截器类处理方法的调用
MethodInterceptor#invoke,而不是直接使用 invoke 方法中的入参 Method method 进行method.invoke(targetObj, args)
这块是整个使用时的差异。 - 除了以上的核心功能实现,还需要使用到
org.aspectj.weaver.tools.PointcutParser
处理拦截表达式execution(* lqf.springframework.service.IUserService.*(..)),有了方法代理和处理拦截,我们就可以完成设计出一个
AOP 的雏形了。
实现
切点表达式
- 定义接口
- 源码1: lqf.springframework.aop.Pointcut
- 切入点接口,定义用于获取 ClassFilter、MethodMatcher 的两个类,这两个接口获取都是切点表达式提供的内容。
- 源码2: lqf.springframework.aop.MethodMatcher
+
方法匹配,找到表达式范围内匹配下的目标类和方法。在上文的案例中有所体现:methodMatcher.matches(method, targetObj.getClass())
- 源码1: lqf.springframework.aop.Pointcut
- 实现切点表达式类
- 源码: lqf.springframework.aop.aspectj.AspectJExpressionPointcut
- AspectJExpressionPointcut 类是 AspectJ 表达式的实现类,主要是用于处理切点表达式的解析和匹配操作。
- 在 AspectJExpressionPointcut 类中,主要是使用到了 AspectJ 提供的 PointcutParser 处理切点表达式,然后再通过
PointcutExpression#matches进行匹配操作。
- 源码: lqf.springframework.aop.aspectj.AspectJExpressionPointcut
包装切面通知信息
源码: lqf.springframework.aop.AdvisedSupport
- AdvisedSupport,主要是用于把代理、拦截、匹配的各项属性包装到一个类中,方便在 Proxy 实现类进行使用。
- TargetSource,是一个目标对象,在目标对象类中提供 Object 入参属性,以及获取目标类 TargetClass 信息。
- MethodInterceptor,是一个具体拦截方法实现类,由用户自己实现 MethodInterceptor#invoke 方法,做具体的处理。
- MethodMatcher,是一个匹配方法的操作,这个对象由 AspectJExpressionPointcut 提供服务。
代理抽象实现(JDK&Cglib)
定义接口 源码1: lqf.springframework.aop.framework.AopProxy
- 定义一个标准接口,用于获取代理类。因为具体实现代理的方式可以有 JDK 方式,也可以是 Cglib 方式,所以定义接口会更加方便管理实现类。
源码2: lqf.springframework.aop.framework.JdkDynamicAopProxy
- 基于 JDK 实现的代理类,需要实现接口 AopProxy、InvocationHandler,这样就可以把代理对象 getProxy 和反射调用方法 invoke
分开处理了。 - getProxy 方法中的是代理一个对象的操作,需要提供入参 ClassLoader、AdvisedSupport、和当前这个类 this,因为这个类提供了
invoke 方法。 - invoke 方法中主要处理匹配的方法后,使用用户自己提供的方法拦截实现,做反射调用 methodInterceptor.invoke 。
- 这里还有一个 ReflectiveMethodInvocation,其他它就是一个入参的包装信息,提供了入参对象:目标对象、方法、入参。
源码3: lqf.springframework.aop.framework.Cglib2AopProxy
- 基于 Cglib 使用 Enhancer 代理的类可以在运行期间为接口使用底层 ASM 字节码增强技术处理对象的代理对象生成,因此被代理类不需要实现任何接口。
- 关于扩展进去的用户拦截方法,主要是在 Enhancer#setCallback 中处理,用户自己的新增的拦截处理。这里可以看到
DynamicAdvisedInterceptor#intercept 匹配方法后做了相应的反射操作。
总结
-
从本文对 Proxy#newProxyInstance、MethodInterceptor#invoke,的使用验证切面核心原理以及再把功能拆解到 Spring
框架实现中,可以看到一个貌似复杂的技术其实核心内容往往没有太多,但因为需要为了满足后续更多的扩展就需要进行职责解耦和包装,通过这样设计模式的使用,以此让调用方能更加简化,自身也可以不断按需扩展。 -
AOP 的功能实现目前还没有与 Spring 结合,只是对切面技术的一个具体实现,可以先学习到如何处理代理对象、过滤方法、拦截方法,以及使用
Cglib 和 JDK 代理的区别,其实这与的技术不只是在 Spring 框架中有所体现,在其他各类需要减少人工硬编码的场景下,都会用到。