咱们在业务开发中,使用得最多的是面向对象编程(OOP),由于它的代码逻辑直观,从上往下就能查看完整的执行链路。java
在这个基础上延伸,出现了面向切面编程(AOP),将能够重复性的横切逻辑抽取到统一的模块中。spring
例如日志打印、安全监测,若是按照 OOP 的思想,在每一个方法的先后都要加上重复的代码,以后要修改的话,更改的地方就会太多,致使很差维护。因此出现了 AOP 编程, AOP 所关注的方向是横向的,不一样于 OOP 的纵向。express
因此接下来一块儿来学习 AOP 是如何使用以及 Spring 容器里面的处理逻辑~编程
建立用于拦截的 bean
public class TestAopBean {缓存
private String testStr = "testStr"; public void testAop() { // 被拦截的方法,简单打印 System.out.println("I am the true aop bean"); }
}
建立 Advisor
@Aspectpublic class AspectJTest {
public class AspectJTest {安全
@Pointcut("execution(* *.testAop(..))") public void test() { } @Before("test()") public void beforeTest() { System.out.println("before Test"); } @After("test()") public void afterTest() { System.out.println("after Test"); } @Around("test()") public Object aroundTest(ProceedingJoinPoint joinPoint) { System.out.println("around Before"); Object o = null; try { // 调用切面的方法 o = joinPoint.proceed(); } catch (Throwable e) { e.printStackTrace(); } System.out.println("around After"); return o; }
}
首先类打上了 @Aspect 注解,让 Spring 认识到这个是一个切面 bean,在方法打上 @Pointcut("execution( .testAop(..))"),表示这是一个切点方法,execution() 内部的表达式指明被拦截的方法,Before 、After、Around 分别表示在被拦截方法的前、后已经环绕执行。框架
建立配置文件 aop.xml
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd"> <!--开启 AOP 功能--> <aop:aspectj-autoproxy /> <bean id="aopTestBean" class="aop.TestAopBean"/> <bean class="aop.AspectJTest" /></beans>
<beans xmlns="http://www.springframework.org/schema/beans"ide
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd"> <!--开启 AOP 功能--> <aop:aspectj-autoproxy /> <bean id="aopTestBean" class="aop.TestAopBean"/> <bean class="aop.AspectJTest" />
</beans>
测试 Demo
public class AopTestBootstrap {函数
public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("aop/aop.xml"); TestAopBean bean = (TestAopBean) context.getBean("aopTestBean"); bean.testAop(); // 输出内容 看输出顺序,了解到加强方法的执行顺序 : // Around proceed 以前 -> Before -> Around proceed 以后 -> After //around Before //before Test //I am the true aop bean //around After //after Test }
}
根据上面的启动例子,发如今本身写的核心业务方法 testAop() 上,明明只是简单打印了 I am the true aop bean,但执行结果输出了其它内容,说明这个类被加强了,在不修改核心业务方法上,咱们对它进行了扩展。证实了 AOP 可使辅助功能独立于核心业务以外,方便了程序的扩展和解耦。工具
使用起来很方便,接下来一块儿来看看 Spring 是如何实现 AOP 功能的吧~
动态 AOP 自定义标签
以前在介绍自定义标签时,提到了 AOP 的实现也借助了自定义注解,根据自定义标签的思想:每一个自定义的标签,都有对应的解析器,而后借助强大的开发工具 IDEA 定位功能,找到解析器注册的地方:
640?wx_fmt=png
按住 ctrl
,定位标签对应的 xsd
文件
根据命名文件,在 META-INF
目录下找到了 spring.handlers
文件
在处理器文件中发现了处理器 AopNamespaceHandler
public class AopNamespaceHandler extends NamespaceHandlerSupport {
@Override public void init() { // In 2.0 XSD as well as in 2.1 XSD. registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser()); // 注释 8.1 自定义注解,注册解析器,元素名是 aspectj-autoproxy registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser()); registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator()); // Only in 2.0 XSD: moved to context namespace as of 2.1 registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser()); }
}
处理器继承自 NamespaceHandlerSupport,在加载过程当中,将会执行 init 初始化方法,在这里,会注册 aspectj-autoproxy 类型的解析器 AspectJAutoProxyBeanDefinitionParser
如何注册自定义解析器以前也了解过了,因此接下来直接来看看,遇到 aspectj-autoproxy 类型的 bean,程序是如何解析的。
注册 AnnotationAwareAspectJAutoProxyCreator
来看下解析时,它的入口方法以下:
public BeanDefinition parse(Element element, ParserContext parserContext) {
// aop 注解的解析入口,注册 AnnotationAwareAspectJAutoProxyCreator AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element); // 对注解中子类的处理 extendBeanDefinition(element, parserContext); return null;
}
入口方法一如既往的简洁,交代了要作的事情,而后具体复杂逻辑再交给工具类或者子类继续实现,因此接下来要看的是如何注册 AnnotationAwareAspectJAutoProxyCreator。
public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary( ParserContext parserContext, Element sourceElement) {
// 经过工具类,注册或升级 AspectJAnnotationAutoProxyCreator BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary( parserContext.getRegistry(), parserContext.extractSource(sourceElement)); // 处理 proxy-target-class 以及 expose-proxy 属性 useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement); // 注册组件并通知,让监听器进行处理 registerComponentIfNecessary(beanDefinition, parserContext);
}
能够看到这个方法内部有三个处理逻辑,因此咱们来一个一个去分析了解:
注册或者升级 AnnotationAwareAspectJAutoProxyCreator
对于 AOP 的实现,基本上都是靠 AnnotationAwareAspectJAutoProxyCreator 去完成,它能够根据 @Point 注解定义的切点来自动代理相匹配的 bean。
因为 Spring 替咱们作了不少工做,因此开发 AOP 业务时才能够这么简单,连配置也简化了许多,因此来看下 Spring 是如何使用自定义配置来帮助咱们自动注册 AnnotationAwareAspectJAutoProxyCreator。
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary( BeanDefinitionRegistry registry, @Nullable Object source) {
// 实际注册的 bean 类型是 AnnotationAwareAspectJAutoProxyCreator return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
}
private static BeanDefinition registerOrEscalateApcAsRequired(
Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) { Assert.notNull(registry, "BeanDefinitionRegistry must not be null"); if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) { // 若是在 registry 已经存在自动代理建立器,而且传入的代理器类型与注册的不一致,根据优先级判断是否须要修改 BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME); if (!cls.getName().equals(apcDefinition.getBeanClassName())) { // 根据优先级选择使用哪个 int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName()); int requiredPriority = findPriorityForClass(cls); if (currentPriority < requiredPriority) { // 传进来的参数优先级更大,修改注册的 beanName,使用传进来的代理建立器 apcDefinition.setBeanClassName(cls.getName()); } } // 由于已经存在代理器,不须要以后的默认设置,直接返回 return null; } RootBeanDefinition beanDefinition = new RootBeanDefinition(cls); beanDefinition.setSource(source); // 默认的是最小优先级 beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE); beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); // 自动代理建立器的注册名字永远是 org.springframework.aop.config.internalAutoProxyCreator registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition); return beanDefinition;
}
这个步骤中,实现了自动注册 AnnotationAwareAspectJAutoProxyCreator 类,同时能看到涉及到优先级的概念和注册名一直都是 AUTO_PROXY_CREATOR_BEAN_NAME。
处理 proxy-target-class 以及 expose-proxy 属性
private static void useClassProxyingIfNecessary(BeanDefinitionRegistry registry, @Nullable Element sourceElement) {
if (sourceElement != null) { // 这方法做用挺简单的,就是解析下面两个属性,若是是 true,将它们加入代理注册器的属性列表中 // definition.getPropertyValues().add("proxyTargetClass", Boolean.TRUE) boolean proxyTargetClass = Boolean.parseBoolean(sourceElement.getAttribute(PROXY_TARGET_CLASS_ATTRIBUTE)); if (proxyTargetClass) { // 处理 proxy-target-class 属性 // 与代码生成方式有关,在以后步骤中决定使用 jdk 动态代理 或 cglib AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry); } boolean exposeProxy = Boolean.parseBoolean(sourceElement.getAttribute(EXPOSE_PROXY_ATTRIBUTE)); if (exposeProxy) { // 处理 expose-proxy 属性 // 扩展加强,有时候目标对象内部的自我调用没法实施切面中的加强,经过这个属性能够同时对两个方法进行加强 AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry); } }
}
关于 AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry); 方法,它是一个属性设置的过程,若是解析到的属性为 true,将它们加入代理注册器的属性列表中,这里不细说下去。
将这两个属性分开熟悉:
proxy-target-class
Spring AOP 部分使用 JDK 动态代理 (Proxy + InvocationHandler),或者 CGLIB (Code Generation LIB)来为目标对象建立代理。书中提到,推荐使用的是 JDK 动态代理。
若是被代理的目标对象实现了至少一个接口,则会使用 JDK 动态代理。全部该目标类型实现的接口都将被代理。
若该目标对象没有实现任何接口,则建立一个 CGLIB 代理。若是但愿代理目标对象的全部方法,而不仅是实现自接口的方法,能够经过该属性 proxy-target-class 开启强制使用 CGLIB 代理。
可是强制开启 CGLIB 会有如下两个问题:
若是考虑好上面两个方面,那就能够经过如下两个地方来强制开启 CGLIB 代理:
<!-- one --><aop:config proxy-target-class="true">...</aop:config><!-- two --><aop:aspectj-autoproxy proxy-target-class="true"/>
<aop:config proxy-target-class="true">...</aop:config>
<!-- two -->
<aop:aspectj-autoproxy proxy-target-class="true"/>
其中有关 CGLIB 代理,这位老哥讲得很透彻,建议你们能够去了解一下~ Cglib及其基本使用
expose-proxy
有时候目标对象内部的自我调用将没法实施切面中的加强。
例如两个方法都加上了事务注解 @Transactional 可是事务类型不同:
public interface TestService {
void a(); void b();
}
public class TestServiceImpl implements TestService {
@Override @Transactional(propagation = Propagation.REQUIRED) public void a() { this.b(); } @Override @Transactional(propagation = Propagation.REQUIRES_NEW) public void b() { System.out.println("Hello world"); }
}
此处的 this 指向了目标对象, this.b() 方法将不会执行 b 事务的切面,即不会执行事务加强。
为了解决这个问题,使 a() 和 b() 方法同时加强,能够经过 expose-proxy 来实现:
<!-- one --><aop:config expose-proxy="true">...</aop:config><!-- two --><aop:aspectj-autoproxy expose-proxy="true"/>
<aop:config expose-proxy="true">...</aop:config>
<!-- two -->
<aop:aspectj-autoproxy expose-proxy="true"/>
注册组件并通知
emmmm,这个方法内部逻辑如名字同样清晰,因此不细说啦。
建立 AOP 代理
前面主要围绕着自动代理器 AnnotationAwareAspectJAutoProxyCreator 的注册流程来说解,接下来看自动代理器作了什么来完成 AOP 的操做。
下面是 AnnotationAwareAspectJAutoProxyCreator 的继承体系:
640?wx_fmt=png
在图片右上角,发现它实现了 BeanPostProcessor 接口,以前文章提到过,它是一个后处理器,能够在 bean 实例化先后进行扩展。查看了实现了该接口的两个方法,postProcessBeforeInitialization 没有作处理,直接返回该对象。
实际进行处理的是 postProcessAfterInitialization 方法,在 bean 实例化以后的处理,在这一步中进行里代理加强,因此来看下这个方法:
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
if (bean != null) { // 组装 key Object cacheKey = getCacheKey(bean.getClass(), beanName); if (this.earlyProxyReferences.remove(cacheKey) != bean) { // 若是适合被代理,则须要封装指定的 bean return wrapIfNecessary(bean, beanName, cacheKey); } } return bean;
}
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
// 若是已经处理过 if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) { return bean; } // 不需加强 if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) { return bean; } // 给定的 bean 类是否表明一个基础设施类,基础设置类不该代理 || 配置了指定 bean 不须要代理 if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) { this.advisedBeans.put(cacheKey, Boolean.FALSE); return 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;
}
来提取一下核心流程:
获取加强方法或者加强器
咱们刚才写的 @Before 、@After 之类的,就是加强方法,AOP 处理时,要先找出这些加强方法。
根据获取的加强进行代理
找到加强方法后,须要对这些加强方法进行加强代理,实际上这个 bean 已经不彻底是原来的类型了,会变成代理后的类型。
获取加强方法或者加强器
入口方法在这里:
protected Object[] getAdvicesAndAdvisorsForBean(
Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) { // 寻找符合的切面 List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName); if (advisors.isEmpty()) { return DO_NOT_PROXY; } return advisors.toArray();
}
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
// 从 beanFactory 中获取声明为 AspectJ 注解的类,对并这些类进行加强器的提取 // 委派给子类实现 org.springframework.aop.aspectj.autoproxy.AspectJAwareAdvisorAutoProxyCreator.extendAdvisors List<Advisor> candidateAdvisors = findCandidateAdvisors(); // 寻找匹配的加强器 List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName); extendAdvisors(eligibleAdvisors); if (!eligibleAdvisors.isEmpty()) { eligibleAdvisors = sortAdvisors(eligibleAdvisors); } return eligibleAdvisors;
}
对于指定 bean 的加强方法的获取包含这两个步骤,获取全部的加强以及寻找全部加强中适用于 bean 的加强并应用。对应于 findCandidateAdvisors 和 findAdvisorsThatCanApply 这两个方法。若是没找到对应的加强器,那就返回 DO_NOT_PROXY ,表示不须要进行加强。
因为逻辑太多,因此接下来贴的代码不会太多,主要来了解它的大体流程,有须要的能够跟着源码工程的注释跟踪完整的流程~:
寻找对应的加强器 findCandidateAdvisors
protected List<Advisor> findCandidateAdvisors() {
List<Advisor> advisors = super.findCandidateAdvisors(); if (this.aspectJAdvisorsBuilder != null) { // 注释 8.3 实际调用的是 org.springframework.aop.aspectj.annotation.BeanFactoryAspectJAdvisorsBuilder.buildAspectJAdvisors advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors()); } return advisors;
}
实际来看,关键是这个方法 this.aspectJAdvisorsBuilder.buildAspectJAdvisors() 这个方法看起来简单,可是实际处理的逻辑不少,代码深度也不少,因此为了不太多代码,我罗列了主要流程,和关键的处理方法作了什么
主要流程以下:
获取全部 beanName,会将以前在 beanFactory 中注册的 bean 都提取出来。
遍历前一步骤提取出来的 bean 列表,找出打上 @AspectJ 注解的类,进行进一步处理
继续对前一步提取的 @AspectJ 注解的类进行加强器的提取
将提取结果加入缓存中
能够查询代码中的注释,从 [注释 8.3] 到 [注释 8.8 根据切点信息生成加强器] 都是这个方法的处理逻辑
※※在这个流程的最后一步中,会将识别到的切点信息(PointCut)和加强方法(Advice)进行封装,具体是由 Advisor 的实现类 InstantiationModelAwarePointcutAdvisorImpl 进行统一封装。
public InstantiationModelAwarePointcutAdvisorImpl(AspectJExpressionPointcut declaredPointcut, Method aspectJAdviceMethod, AspectJAdvisorFactory aspectJAdvisorFactory, MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {
// 简单赋值 this.declaredPointcut = declaredPointcut; ... if (aspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) { Pointcut preInstantiationPointcut = Pointcuts.union(aspectInstanceFactory.getAspectMetadata().getPerClausePointcut(), this.declaredPointcut); this.pointcut = new PerTargetInstantiationModelPointcut( this.declaredPointcut, preInstantiationPointcut, aspectInstanceFactory); this.lazy = true; } else { // A singleton aspect. this.pointcut = this.declaredPointcut; this.lazy = false; // 初始化加强器 this.instantiatedAdvice = instantiateAdvice(this.declaredPointcut); }
}
封装体前半部分逻辑只是简单赋值。关键是这个方法 instantiateAdvice(this.declaredPointcut),在这一步中,对不一样的加强(Before/After/Around)实现的逻辑是不同的。在 ReflectiveAspectJAdvisorFactory#getAdvice 方法中区别实现了根据不一样的注解类型封装不一样的加强器。
public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut expressionPointcut, MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {
... // 注释 8.7 根据不一样的注解类型封装不一样的加强器 switch (aspectJAnnotation.getAnnotationType()) { case AtPointcut: } return null; case AtAround: springAdvice = new AspectJAroundAdvice( candidateAdviceMethod, expressionPointcut, aspectInstanceFactory); break; case AtBefore: springAdvice = new AspectJMethodBeforeAdvice( candidateAdviceMethod, expressionPointcut, aspectInstanceFactory); break; case AtAfter: springAdvice = new AspectJAfterAdvice( candidateAdviceMethod, expressionPointcut, aspectInstanceFactory); break; case AtAfterReturning: springAdvice = new AspectJAfterReturningAdvice( candidateAdviceMethod, expressionPointcut, aspectInstanceFactory); AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation.getAnnotation(); if (StringUtils.hasText(afterReturningAnnotation.returning())) { springAdvice.setReturningName(afterReturningAnnotation.returning()); } break; case AtAfterThrowing: springAdvice = new AspectJAfterThrowingAdvice( candidateAdviceMethod, expressionPointcut, aspectInstanceFactory); AfterThrowing afterThrowingAnnotation = (AfterThrowing) aspectJAnnotation.getAnnotation(); if (StringUtils.hasText(afterThrowingAnnotation.throwing())) { springAdvice.setThrowingName(afterThrowingAnnotation.throwing()); } break; default: }
}
最后切点方法经过解析和封装成 Advisor,提取到的结果加入到缓存中。细心的你可能会发现除了普通的加强器外,还有另外两种加强器:同步实例化加强器和引介加强器。因为用的比较少,因此我看到源码中这两个分支处理没有深刻去学习,感兴趣的同窗请继续深刻学习这两种加强器~
获取匹配的加强器 findAdvisorsThatCanApply
在前面流程中,已经完成了全部加强器的解析,可是对于前面解析到的加强器,并不必定都适用于当前处理的 bean,因此还须要经过一个方法来挑选出合适的加强器。
protected List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> beanClass, String beanName) {
ProxyCreationContext.setCurrentProxiedBeanName(beanName); try { // 在这一步中进行过滤加强器 return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass); } finally { ProxyCreationContext.setCurrentProxiedBeanName(null); }
}
能够看到,具体实现过滤操做的是工具类方法 AopUtils.findAdvisorsThatCanApply:
public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
if (candidateAdvisors.isEmpty()) { return candidateAdvisors; } List<Advisor> eligibleAdvisors = new ArrayList<>(); // 遍历全部加强器 for (Advisor candidate : candidateAdvisors) { // 首先处理引介加强 if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) { eligibleAdvisors.add(candidate); } } boolean hasIntroductions = !eligibleAdvisors.isEmpty(); for (Advisor candidate : candidateAdvisors) { // 前面处理过了,跳过 if (candidate instanceof IntroductionAdvisor) { // already processed continue; } // 处理普通加强器类型 if (canApply(candidate, clazz, hasIntroductions)) { eligibleAdvisors.add(candidate); } } return eligibleAdvisors;
}
具体判断逻辑在 canApply() 方法中,若是判断符合条件的,加入到 eligibleAdvisors 中,最后返回对于这个 bean 适合的加强器列表。
建立代理
经过前面的流程,获取到了全部对应 bean 的加强器后,能够开始代理的建立。
protected Object createProxy(Class<?> beanClass, @Nullable String beanName, @Nullable Object[] specificInterceptors, TargetSource targetSource) {
ProxyFactory proxyFactory = new ProxyFactory(); // 拷贝,获取当前类中的相关属性 proxyFactory.copyFrom(this); // 决定对于给定 bean 是否应该使用 targetClass 而不是他的接口代理 if (!proxyFactory.isProxyTargetClass()) { // 检查 proxyTargetClass 设置以及 preserveTargetClass 属性 if (shouldProxyTargetClass(beanClass, beanName)) { proxyFactory.setProxyTargetClass(true); } else { // 添加代理接口 evaluateProxyInterfaces(beanClass, proxyFactory); } } // 这一步中,主要将拦截器封装为加强器 Advisor[] advisors = buildAdvisors(beanName, specificInterceptors); proxyFactory.addAdvisors(advisors); proxyFactory.setTargetSource(targetSource); // 定制代理 customizeProxyFactory(proxyFactory); // 用来控制代理工厂被配置以后,是否含容许修改通知 // 缺省值为 false,不容许修改代理的配置 proxyFactory.setFrozen(this.freezeProxy); if (advisorsPreFiltered()) { proxyFactory.setPreFiltered(true); } // 生成代理,委托给了 ProxyFactory 去处理。 return proxyFactory.getProxy(getProxyClassLoader());
}
对于代理类的建立和处理, Spring 委托给了 ProxyFactory 去处理,在上面贴出的函数主要是对 ProxyFactory 的初始化操做,进而对真正的建立代理作准备,主要流程以下:
获取当前类的属性
添加代理接口
封装 Advisor 并加入到 ProxyFactory
设置要代理的类
为子类提供定制的函数 customizeProxyFactory,子类经过该方法对 ProxyFactory 进行进一步的封装
进行获取代理操做
比较关键的是第三个步骤和第六个步骤,其中在第三个步骤中,进行的是拦截器包装,详细代码流程请查 [注释 8.9 为给定的bean建立AOP代理] 和 [注释 8.10 包装拦截器,封装成 Advisor]。
接着,完成了全部加强器的封装过程,到了解析的最后一步,进行代理的建立和获取。
public Object getProxy(@Nullable ClassLoader classLoader) { return createAopProxy().getProxy(classLoader);}
return createAopProxy().getProxy(classLoader);
}
建立代理 createAopProxy()
定位到建立代理的代码:
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
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."); } if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) { return new JdkDynamicAopProxy(config); } return new ObjenesisCglibAopProxy(config); } else { return new JdkDynamicAopProxy(config); }
}
从上面代码中能看出,根据了几个关键属性,判断建立的是哪一种类型的 AopProxy,一种是 JDK 动态代理,另外一种是 CGLIB 动态代理。
前面提到过的 proxy-target-class 属性和 targetClass 属性,在这里判断了应该建立哪个代理。
获取代理 getProxy()
640?wx_fmt=png
观察图片以及前面分析,能够知道有两种代理方式:[JDK 动态代理] 和 [CGLIB 动态代理]
同时先说下动态代理的含义:抽象类在编译期间是未肯定具体实现子类,在运行时才生成最终对象。
JDK 动态代理
JDK 代理是默认推荐的代理方式,使用的是 Proxy + InvocationHandler。
能够经过如下方式实现:定义一个接口、实现类,和一个处理器继承于 InvocationHandler,而后重载处理器中的 invoke 方法,对代理对象进行加强。
JdkDynamicAopProxy.java
public Object getProxy(@Nullable ClassLoader classLoader) {
// 注释 8.11 JDK 动态代理 if (logger.isTraceEnabled()) { logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource()); } Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true); findDefinedEqualsAndHashCodeMethods(proxiedInterfaces); return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}
获取代理的核心步骤在 Proxy.newProxyInstance(classLoader, proxiedInterfaces, this),第三个参数是 JdkDynamicAopProxy 自己,并且它实现了 InvocationHandler 接口,重载了 invoke 方法。
org.springframework.aop.framework.JdkDynamicAopProxy#invoke
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 注释 8.12 jdk 动态代理重载的 invoke 方法 MethodInvocation invocation; Object oldProxy = null; boolean setProxyContext = false; TargetSource targetSource = this.advised.targetSource; Object target = null; try { Object retVal; if (this.advised.exposeProxy) { // Make invocation available if necessary. oldProxy = AopContext.setCurrentProxy(proxy); setProxyContext = true; } target = targetSource.getTarget(); Class<?> targetClass = (target != null ? target.getClass() : null); // Get the interception chain for this method. // 获取此方法的拦截链 List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass); // 检查咱们是否有任何切面逻辑。若是咱们不这样作,咱们能够回退直接反射调用目标,并避免建立 MethodInvocation。 if (chain.isEmpty()) { Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args); retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse); } else { // 将拦截器封装在 ReflectiveMethodInvocation,便于使用 proceed 执行拦截器 invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain); // 执行拦截器链 retVal = invocation.proceed(); } ... return retVal; } finally { if (target != null && !targetSource.isStatic()) { targetSource.releaseTarget(target); } if (setProxyContext) { AopContext.setCurrentProxy(oldProxy); } }
}
建立 JDK 代理过程当中,主要的工做时建立了一个拦截器链,并使用 ReflectiveMethodInvocation 类进行封装,封装以后,逐一调用它的 proceed 方法, 用来实如今目标方法的前置加强和后置加强。
org.springframework.aop.framework.ReflectiveMethodInvocation#proceed
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; Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass()); if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) { return dm.interceptor.invoke(this); } else { // 匹配失败,跳过拦截器,直接返回 return proceed(); } } else { return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this); }
}
具体代码和注释请定位到该方法查看。关于 JDK 动态代理,深刻学习的话也能够单独拎出来,因此推荐看这篇资料 小豹子带你看源码:JDK 动态代理,进行了和学习
CGLIB 动态代理
CGLIB[Code Generation LIB] 是一个强大的高性能的代码生成包。它普遍应用于许多 AOP 框架。
再次推荐参考资料一,这位老哥将 CGLIB 代理, 详细介绍了 CGLIB 在什么场景使用,以及被它加强后代码处理顺序,Cglib及其基本使用。
但愿看完这篇文章,能过了解到 CGLIB 代码生成包具体是如何对类进行加强。
代理加强结果
经过前面一系列步骤,解析标签、属性、加强方法,到最后获取 CGLIB 代理,经过代理建立 bean
来看下最后被代理的 bean 内部:
640?wx_fmt=png
从图中能够看到,最终建立的是被修饰后的 bean,内部很明显是 CGGLIB 代理生成的代码,咱们在不修改业务代码的状况下,实现了方法加强。
静态 AOP
既然有动态代理,那么也会有静态代理。
使用静态 AOP 的时候,须要用到 LTW (Load-Time Weaving 加载时织入),指的是在虚拟机载入字节码文件时动态织入 AspectJ 切面。
AOP 的静态代理主要是在虚拟机启动时经过改变目标对象字节码的方式来完成对目标对象的加强,它与动态代理相比具备更高的效率,由于在动态代理调用的过程当中,还须要一个动态建立代理类并代理目标对象的步骤,而静态代理则是在启动时便完成了字节码增减,当系统再次调用目标类时,与调动正常的类并没有区别,因此在效率上会相对高些。
关于静态 AOP 的使用和学习,能够参考这篇文章:从代理机制到Spring AOP
总结
动态 AOP 使用起来很简单,对于如何实现,总结起来就两点:
动态解析 AOP
标签
建立 AOP
代理
但在 Spring 底层实现逻辑倒是复杂到不行,从 Spring 框架中能够看到这是良好的代码设计思路,顶层入口尽可能简单,使用者很容易就能掌握该功能,复杂实现逻辑都被隐藏了。
写这一篇 AOP 学习总结,花了将近一周,先看了一遍书籍, 下班后花了一晚,将大体流程理了一遍,次日晚上走读代码,发现有些地方还存在疑惑,例如 JDK 和 cglib 动态代理是怎么回事,翻阅查询资料,弄懂后又过了一天。
将代码注释加上,分析动态代理每个步骤作的事情,结合以前学的后处理器 BeanPostProcessor 知识和自定义标签解析知识一块儿又梳理一遍。零零散散,终于整理完成。
在静态 AOP 知识点,按照个人理解,越往系统底层深刻,它的执行效率越高,因此减小了动态建立代理类和代理目标对象的步骤,静态代理的速度会获得提高。同时因为接近底层后,代码编写的复杂度一样会增长,因此我在权衡高频率使用场景(动态代理),本次学习没有详细去了解,留下这个坑,之后有机会再填吧~