1. AOP 概念
AOP(Aspect Oriented Programming),即面向切面编程。
- 连接点(JoinPoint)
程序执行的某个特定位置:如类开始初始化前、类初始化后、类某个方法调用前、调用后、方法抛出异常后。一个类或一段程序代码拥有一些具有边界性质的特定点,这些点中的特定点就称为“连接点”。Spring仅支持方法的连接点,即仅能在方法调用前、方法调用后、方法抛出异常时以及方法调用前后这些程序执行点织入增强。 - 切点(Pointcut)
每个类具有多个连接点,如果一个类拥有15个方法,那么这些方法都是连接点,连接点相当于数据库中的记录,而切点相当于查询条件。切点和连接点不是一对一的关系,一个切点可以匹配多个连接点。Spring AOP的规则解析引擎负责切点所设定的查询条件,找到对应的连接点。其实确切地说,不能称之为查询连接点,因为连接点是方法执行前、执行后等包括方位信息的具体程序执行点,而切点只定位到某个方法上,所以如果希望定位到具体连接点上,还需要提供方位信息。 - 增强(Advice)
增强是织入到目标类连接点上的一段程序代码,在Spring中,增强除用于描述一段程序代码外,还拥有另一个和连接点相关的信息,这便是执行点的方位。结合执行点方位信息和切点信息,我们就可以找到特定的连接点。 - 通知器(Advisor)
当我们完成切面增强设计(Advice)和切入点的设计(Pointcut),需要一个对象把他们结合起来,Advisor 就是起到这个作用,通过Advisor ,可以确定在哪个Pointcut 使用哪个Advice。所以一个Advisor包含一个Advice 和 一个Pointcut 信息。
2. 一些疑问
- Spring AOP 增强的代理类 在什么时候创建 ?
- Spring AOP 怎样为一个类 和 方法 匹配 增强的 ?
- Spring AOP 是如何 协调 前置通知 后置通知 异常通知 返回通知的?
- 切面类 可以被 AOP增强么?
3. 注册 AnnotationAwareAspectJAutoProxyCreator
AnnotationAwareAspectJAutoProxyCreator是用来处理 当前项目当中 所有 AspectJ 注解的切面类。以及所有的 Spring Advisor ,同时 也是一个 BeanPostProcessor 。所以 想了解Spring AOP 如何实现,就要先分析 AnnotationAwareAspectJAutoProxyCreator,那么他从何而来,是如何注册到容器内的。
注册AnnotationAwareAspectJAutoProxyCreator方式:
- 使用
EnableAspectJAutoProxy
注解,改注解 又被@Import
注解注释,向容器中注册了AspectJAutoProxyRegistrar,最后由 AspectJAutoProxyRegistrar 向容器中注册 AnnotationAwareAspectJAutoProxyCreator。
class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(
AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
//向容器注册 AnnotationAwareAspectJAutoProxyCreator
AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
AnnotationAttributes enableAspectJAutoProxy =
AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
//对proxyTargetClass 属性的支持,切换JDK代理 和 CGLIB 代理
if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
}
//是否对 AopContext.currentPoxy支持。
if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
}
}
}
- 使用
@EnableAutoConfiguration
注解。改注解会向容器注册Spring.factories文件中声明的类,其中AopAutoConfiguration是对Spring AOP自动配置的支持。
@Configuration
@ConditionalOnClass({ EnableAspectJAutoProxy.class, Aspect.class, Advice.class })
@ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true)
public class AopAutoConfiguration {
@Configuration
最终还是通过 EnableAspectJAutoProxy 注册AnnotationAwareAspectJAutoProxyCreator
@EnableAspectJAutoProxy(proxyTargetClass = false)
判断条件 ,可以在 application.properties 中 配置。
@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false", matchIfMissing = true)
public static class JdkDynamicAutoProxyConfiguration {
}
@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true)
@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true", matchIfMissing = false)
public static class CglibAutoProxyConfiguration {
}
}
- xml 标签
Spring 采用 自定义标签 <aop:aspectj-autoproxy /> 注册AnnotationAwareAspectJAutoProxyCreator
4. AnnotationAwareAspectJAutoProxyCreator的初始化
AnnotationAwareAspectJAutoProxyCreator初始化 主要初始一些 组件对象。
组件包括:
- AspectJAdvisorFactory
根据AspectJ 注释的切面类 创建 Spring AOP Advisors 的工厂接口。Advisor增强器的创建 都是由AspectJAdvisorFactory完成。 - BeanFactoryAspectJAdvisorsBuilder
从项目中 查找所有的切面类,然后使用 AspecJAdvisorFactory 创建 Advisors。 - advisorRetrievalHelper
对Xml 配置Advisor 的支持。从容器中检索 所有的 Advisor 类型。
5 . 创建代理类过程
由于 AnnotationAwareAspectJAutoProxyCreator 是一个 BeanPostProcessor 实现类,Spring 会在对一个对象的初始化前后执行 BeanPostProcssor 的接口方法。Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;就是 创建增强代理对象的方法。该方法被AnnotationAwareAspectJAutoProxyCreator的父类AbstractAutoProxyCreator实现。
- postProcessAfterInitialization
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
//bean 就是需要 被增强的类。
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
//防止 bean 多次被增强。
if (!this.earlyProxyReferences.contains(cacheKey)) {
//如果有必要 就进行封装,有没有必要主要取决于 bean 是否需要被增强。
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
- wrapIfNecessary
对给定bean 就行封装。
/**
*如果有必要 封装 bean
*/
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
//如果已经处理过
if (beanName != null && this.targetSourcedBeans.contains(beanName)) {
return bean;
}
//如果不需要增强 就直接返回
if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
return bean;
}
//如果是一个 组件类,组件类包括Advice,Pointcut,Advisor,AopInfrastructureBean实现类,和 Aspect注解注释的类。所以 切面类 自己不会被AOP 增强。
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
// 为bean 获取和 匹配合适的 增强器
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
//如果 没有 匹配到增强器 那么就不用创建增强代理类。
if (specificInterceptors != DO_NOT_PROXY) {
this.advisedBeans.put(cacheKey, Boolean.TRUE);
//创建 增强代理类
Object proxy = createProxy(
bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
}
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
5.1 获取增强器
-
getAdvicesAndAdvisorsForBean(为bean获取和匹配合适的增强器)
getAdvicesAndAdvisorsForBean ---> findEligibleAdvisors:为bean获取和匹配增强器分为两步:
5.1.1 获取到所有的增强器
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
//获取所有的增强器
List<Advisor> candidateAdvisors = findCandidateAdvisors();
//匹配合适的增强器
List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
extendAdvisors(eligibleAdvisors);
if (!eligibleAdvisors.isEmpty()) {
eligibleAdvisors = sortAdvisors(eligibleAdvisors);
}
return eligibleAdvisors;
}
- findCandidateAdvisors 获取到所有的增强器
调用AnnotationAwareAspectjAutoProxyCreator 的 findCandidateAdvisors@Override protected List<Advisor> findCandidateAdvisors() { // 对xml 配置 Advisor 的支持. List<Advisor> advisors = super.findCandidateAdvisors(); // 搜索容器中所有 @Aspecj 注解申明的 切面类,构建增强器 advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors()); return advisors; }
- aspectJAdvisorsBuilder.buildAspectJAdvisors()
代码比较多,就不粘出来了,大致逻辑就是
- 获取容器中已经注册的BeanNames
- 遍历 所有已经注册的Bean,查找 @AspectJ
- 通过AspectJAdvisorFactory获取Advisor。怎么获取的 就不解释了。
5.1.2 匹配合适的增强器
匹配合适的增强器,Spring 使用了 AopUtils.canApply。
匹配逻辑就是:
遍历 所有增强器,采用增强器的 Poincut 对一个对象的所有方法进行匹配,一旦有方法匹配到,就返回true.
对方法的匹配需要遍历 所有方法 一一就行匹配。
5.2 创建 代理类
通过 ProxyFactory 创建动态代理类,创建之前 先配置ProxyFactory,设置目标对象,设置是否对目标对象代理(会影响采用JDK代理或者Cglib代理),设置增强器集合,等。
配置ProxyFactory完成之后就是获取代理类了,
调用ProxyFactory.getProxy:
public Object getProxy(ClassLoader classLoader) {
//先获取 AopProxy,AopProxy有JDK实现和 Cglib实现两种。
return createAopProxy().getProxy(classLoader);
}
判断采取JDK代理 或Cglib代理。
判断条件:
- Optimize: 是否采用了 激进的优化策略,该优化仅支持 Cglib代理
- ProxyTargetClass: 代理目标类,代理目标类 就是采用 子类继承的方式创建代理,所以也是Cglib代理,可以通过。
- hasNoUserSuppliedProxyInterfaces:判断是否是实现了接口,如果没有必须采用Cglib代理。
所以如果我们想在项目中 采用Cglib代理的话 application.properties中配置:
spring.aop.proxy-target-class=false,或者使用注解配置proxyTargetClass = true .
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
//判断采用什么代理类型。
//Optimize 是否采用了 激进的优化策略,该优化仅支持 Cglib代理
//ProxyTargetClass 代理目标类,代理目标类 就是采用 子类继承的方式创建代理,所以也是Cglib代理,可以通过
// 判断是否是实现了接口,如果没有必须采用Cglib代理。
if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
Class<?> targetClass = config.getTargetClass();
if (targetClass == null) {
throw new AopConfigException("TargetSource cannot determine target class: " +
"Either an interface or a target is required for proxy creation.");
}
如果目标对象是接口 采用 JDK代理。
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
return new ObjenesisCglibAopProxy(config);
}
else {
return new JdkDynamicAopProxy(config);
}
}
- 拿JdkDynamicAopProxy来说。
获取代理类:
JDK动态代理的关键是 InvocationHandler,JdkDynamicAopProxy实现了InvocationHandler接口。
@Override
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);
//获取代理类,其中InvocationHanler 是 this,就是JdkDynamicAopProxy。
return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}
6.增强方法的执行
增强方法的执行 是AOP的核心所在,理解Spring Aop 是如何 协调 各种通知 的执行,是理解的关键。
通知 分为前置 后置 异常 返回 环绕通知,在一个方法中每种通知的执行时机不同,协调他们之间执行顺序很重要。
但是Spring AOP 采用了很聪明的方法让各种各样的通知准确有序的工作。
- JdkDynamicAopProxy.invoker
由于invoker 方法很大,我删除了大部分的代码,保留几处关键代码:
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//省略了 对 hashCode equals 等方法的处理....
//exposeProxy属性的支持。
if (this.advised.exposeProxy) {
// Make invocation available if necessary.
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
// 获取目标对象类
target = targetSource.getTarget();
if (target != null) {
targetClass = target.getClass();
}
// 获取拦截器链 这里this.advised 就是AnnotationAwareAspectJAutoProxyCreator
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
//如果 没有拦截器链 则直接执行。
if (chain.isEmpty()) {
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
}
else {
//开始执行拦截器链
invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
retVal = invocation.proceed();
}
//返回执行结果。
return retVal;
}
6.1 创建拦截器链
拦截器链InterceptorAndDynamicMethodMatcher 类 和 MethodInterceptor 类型的集合。
InterceptorAndDynamicMethodMatcher 封装了 MethodInterceptor 和 MethodMather ,作用就是在执行时进行再次匹配。
创建拦截器的过程就是 对所有 适合 目标类的 Advisor进行再一次筛选。对匹配的Advisor封装成 MethodInterceptor。通过MethodInterceptor 统一 增强方法的调用。
6.2 执行拦截器链
使用ReflectiveMethodInvocation 对连接器链进行封装。通过process 方法触发拦截器开始执行。
public Object proceed() throws Throwable {
// 判断拦截器链 是否执行结束,如果执行结束执行目标方法。
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
return invokeJoinpoint();
}
//获取下一个 需要执行的 拦截器
Object interceptorOrInterceptionAdvice =
this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
//如果需要执行时 进行匹配
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
InterceptorAndDynamicMethodMatcher dm =
(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
//如果匹配,并不清楚 为什么还要在这里 进行再次匹配。
if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments))
//注意这里 将ReflectiveMethodInvocation 自己当参数 ,传入调用。
return dm.interceptor.invoke(this);
}
else {
//递归的调用
return proceed();
}
}
else {
//如果 MethodInterceptor
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}
查看代码并没有发现任何的关于协调前置,后置各种通知的代码。其实所有的协调工作都是由MethodInterceptor 自己维护。
- 前置MethodInterceptor(MethodBeforeAdviceInterceptor)的invoke
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
//激活 前置增强方法
this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis() );
//继续调用下一个 拦截器。
return mi.proceed();
}
-
后置MethodInterceptor(AspectJAfterAdvice)
@Override public Object invoke(MethodInvocation mi) throws Throwable { try { //继续调用一下拦截器。 return mi.proceed(); } finally { //在finally 里面激活 后置增强方法 invokeAdviceMethod(getJoinPointMatch(), null, null); } }
通过对比前置 和 后置 拦截器,可以发现 具体协调各种通知 的 逻辑 实际是利用了 递归的思想。
后置拦截器为例: 先执行 递归方法 proceed()。最后从递归方法返回的时候 最后调用 后置方法。
7. 模拟拦截器调用
为了更好理解 拦截器调用,自己实现了一个简单的拦截器链调用过程。
7.1 准备工作
-
接口
//方法调用 interface MethodInvocation{ Object process() throws Throwable; } //方法拦截器 interface MethodInterceptor{ Object invoke(MethodInvocation mi) throws Throwable; }
-
实现
//后置增强方法的 拦截器 class AfterMethodInterceptor implements MethodInterceptor{ String identification; public AfterMethodInterceptor(String identification){ this.identification = identification; } @Override public Object invoke(MethodInvocation mi) throws Throwable { try { return mi.process(); }finally { System.out.println("执行后置通知"+identification); } } } //前置 的 方法拦截器 class BeforMethodInterceptor implements MethodInterceptor{ String identification; public BeforMethodInterceptor(String identification){ this.identification = identification; } @Override public Object invoke(MethodInvocation mi) throws Throwable { System.out.println("执行前置通知"+identification); return mi.process(); } } // 默认的 方法调用实现 class DefaultMethodInvacation implements MethodInvocation { List<MethodInterceptor> chian; Object target; //目标对象 Method method; //目标方法 Object[] args; //方法参数 int currentChianIndex; //记录拦截器链当前执行位置 public DefaultMethodInvacation(List<MethodInterceptor> chian,Object target,Method method,Object[] args){ this.chian = chian; this.method = method; this.target = target; this.args = args; } @Override public Object process() throws Throwable{ Object value ; //如果 拦截器 执行完毕 执行 目标方法 if(currentChianIndex == chian.size()){ value = method.invoke(target, args); return value; } //获取下一个 拦截器 MethodInterceptor methodInterceptor = chian.get(currentChianIndex++); return methodInterceptor.invoke(this); } } //目标对象 class TargetObj{ //目标方法 public void targetMethod(){ System.out.println("-----目标方法执行----"); } }
7.2 测试代码
@Test
public void aopchain() throws Throwable {
List<MethodInterceptor> chain = Lists.newArrayList();
//拦截器链,穿插这 放入 前置 和 后置 拦截器 。
chain.add(new AfterMethodInterceptor("1"));
chain.add(new BeforMethodInterceptor("1"));
chain.add(new AfterMethodInterceptor("2"));
chain.add(new BeforMethodInterceptor("2"));
chain.add(new AfterMethodInterceptor("3"));
chain.add(new BeforMethodInterceptor("3"));
//目标对象
TargetObj targetObj = new TargetObj();
//目标方法
Method method = TargetObj.class.getMethod("targetMethod");
DefaultMethodInvacation defaultMethodInvacation = new DefaultMethodInvacation(chain, targetObj, method, null);
//执行拦截器链
defaultMethodInvacation.process();
}
7.3 执行结果
虽然 前置 和 后置 都是 无序的 存放在 拦截器链中,但是 前置 和 后置 都在各自的位置执行。
执行前置通知1
执行前置通知2
执行前置通知3
-----目标方法执行----
执行后置通知3
执行后置通知2
执行后置通知1
网友评论