他做每一件小事都像救命稻草一样紧紧抓住,等到有一天我一看,嚯,好家伙,他抱住的已经是可以让我仰望的参天大树了 <士兵突击>
有一天:领导建议我们给删除操作方法添加操作日志。
为了完成这个意愿,AOP为此做了多少工作呢??
这一切的一切,都要从代理说起
1.静态代理:
重点:编译期把内容织入代理对象
2.动态代理:通过动态生成新的类字节码,来达到动态代理的目标
重点:生成了新的字节码,动态代理
具体可阅读我的 静态代理与JDK动态代理与CGLIB动态代理
再来重温这句话 领导建议给删除操作方法添加操作日志
这里面包含的点:
谁提的 给谁提的 提的什么建议
至此引出了三个接口出来:
Advisor Pointcut Advise
当然这里面还有几个隐藏的角色,AOP体系也有对应的定义:
TargetSource
: 目标对象 Joinpoint
:连接点,当领导提到建议具体到某个方法上时,Joinpoint就是那个方法的信息封装体。在 Joinpoint
中我们可以获取到目标方法的相关信息 Advised
:当一个人被建议成功后,他就可以看做一个Advised 他们之间的关系:
这几个概念就组成了AOP体系的整体概念框架。
此时:我们达成一个共识,
有了规范的指导,剩下的就是实现规范,Spring提供了哪些东西呢?
以我们最熟悉的切面编程为例,事务,来看看AOP体系的运作过程.
切面,事务本质都是提建议。例如下面,我给 cn.wqd.aop
包下的方法提了日志记录的建议,事务是给方法提了事务控制的建议
@Aspect @Component public class WebLogAcpect { private Logger logger = LoggerFactory.getLogger(WebLogAcpect.class); //定义切入点,切入点为com.example.aop下的所有函数 @Pointcut("execution(public * cn.wqd.aop..*.*(..))") public void webLog(){} @Before("webLog()") public void doBefore(JoinPoint joinPoint) throws Throwable { System.out.println("这里是AOP前置方法"); } } 复制代码
Service public class TransactionalService { @Autowired DataSource dataSource; @Autowired UserDao userDao; @Transactional public String save(){ System.out.println("被事务方法"); User user = new User("被事务方法",1); Map<Object, Object> map = TransactionSynchronizationManager.getResourceMap(); System.out.println(userDao.getClass().getName());// userDao.save(user); return "save"; } 复制代码
如何使用AOP是我们最熟悉的,但是他们又和上面的角色有啥关系呢?别急往下看
提了建议,Spring如何知道是否有提建议者。
为了把应用中的那些建议者找出来,Spring首先提供了一个 BeanFactoryAdvisorRetrievalHelper
,建议者检索工具:目的是识别出那些 实现了Advisor接口的
的建议者,并把他们注册成一个Bean,并缓存起来。
public class BeanFactoryAdvisorRetrievalHelper { public List<Advisor> findAdvisorBeans() { (1) advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors( this.beanFactory, Advisor.class, true, false); (2) this.cachedAdvisorBeanNames = advisorNames; (3) advisors.add(this.beanFactory.getBean(name, Advisor.class)); } } 复制代码
检索逻辑:找到所有实现了 Advisor
的Beandefiniton;缓存他们的beanName;调用Bean创建逻辑,创建一个Bean出来。
直接实现 Advisor
接口,显示的表明自己就是一个建议者。
但是平时我们开发并不是用这种直接实现 Advisor
的方式。 @Aspect
注解,事务才是我们常用的。那他们到底是不是建议者呢?
@Aspect
切面建议者
把 @Aspect
注解的类的建议读取出来,Spring为此提供了一个建议者构建工具 BeanFactoryAspectJAdvisorsBuilder
Aspect建议者构建器。
从其名字直译:他是一个专门构建Aspect建议者的Bean的工具
private final AspectJAdvisorFactory advisorFactory; public BeanFactoryAspectJAdvisorsBuilder(ListableBeanFactory beanFactory) { this(beanFactory, new ReflectiveAspectJAdvisorFactory(beanFactory)); } 复制代码
通过其构造方法,我们发现他默认会创建一个 ReflectiveAspectJAdvisorFactory
,从其名字可以看出,他是通过反射的机制来工作的。 BeanFactoryAspectJAdvisorsBuilder
的大部分工作其实就是由 ReflectiveAspectJAdvisorFactory
完成的。
public List<Advisor> buildAspectJAdvisors() { synchronized (this) { (1) String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors( this.beanFactory, Object.class, true, false); (2) for (String beanName : beanNames) { (3) if (this.advisorFactory.isAspect(beanType)) { MetadataAwareAspectInstanceFactory factory = new PrototypeAspectInstanceFactory(this.beanFactory, beanName); (4) advisors.addAll(this.advisorFactory.getAdvisors(factory)); } (5) this.aspectBeanNames = aspectNames; return advisors; } } } 复制代码
构建过程分析:
isAspect
类: 此步 (AnnotationUtils.findAnnotation(clazz, Aspect.class) != null);
查看其是不是被 Aspect
标注 Aspect
类中把所有的建议者提取出来,注册成Bean spring 很多工具类都注重缓存的使用
) 建议者识别提取过程最重要的就在第4步中,我们再深入看看这一步到底干了什么。
这一步是由 ReflectiveAspectJAdvisorFactory
完成的
从 Aspect
类中识别建议者并注册Bean
public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) { for (Method method : getAdvisorMethods(aspectClass)【1】) { Advisor advisor = 【2】getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName); if (advisor != null) { advisors.add(advisor); } } // Find introduction fields. for (Field field : aspectClass.getDeclaredFields()【3】) { Advisor advisor = 【4】getDeclareParentsAdvisor(field); if (advisor != null) { advisors.add(advisor); } } return advisors; } 复制代码
流程:
Aspect
注解的类中,所有的可能是建议者的方法: getAdvisorMethods
获取的是不被 Pointcut
注解注释的方法,这些都可能是建议者方法,(包含父类,接口方法) getAdvisor
尝试解析这些方法是不是建议者,通过查看其是否被以下注解标注来判断。 private static final Class<?>[] ASPECTJ_ANNOTATION_CLASSES = new Class<?>[] { Pointcut.class, Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class}; 复制代码
是的话,解析注解上的切点信息[列如:上文例子中 @Before("webLog()")
注解里的weblog()所代表的切点] . 连同本方法信息包装成一个 InstantiationModelAwarePointcutAdvisorImpl
建议者。
此时
AspectJMethodBeforeAdvice
建议 webLog()
是 切点
InstantiationModelAwarePointcutAdvisorImpl
就是建议者 @DeclareParents
注解的属性:这里也可以配置建议者。不同于 Before
之类的注解标注的方法的是对存在方法的增强。 此种增强叫做引介增强, 一个Java类,没有实现A接口,在不修改Java类的情况下,使其具备A接口的功能
.这就是引介增强 4.为配置的引介增强创建一个建议者:DeclareParentsAdvisor
此时@Aspect注解的类里配置的建议都被解析出来,并创建了建议者
我们再来看看事务建议者是如何被找到的呢?
启动事务注解 EnableTransactionManagement
会注册一个 ProxyTransactionManagementConfiguration
配置类
public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration { @Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME) @Role(BeanDefinition.ROLE_INFRASTRUCTURE) public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor() { BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor(); advisor.setTransactionAttributeSource(transactionAttributeSource()); advisor.setAdvice(transactionInterceptor()); advisor.setOrder(this.enableTx.<Integer>getNumber("order")); return advisor; } .... } 复制代码
此配置类,直接注册一个建议者: BeanFactoryTransactionAttributeSourceAdvisor
此建议者:以 TransactionInterceptor
为建议,以 TransactionAttributeSourcePointcut
为切点。
我们的 @Transactional
注解在目标方法扮演的角色是标记,谁的标记?切点的标记,
我们回顾下切点的意义:表示对哪些进行增强,具体表现在其 matches
方法上,来匹配范围
public boolean matches(Method method, Class<?> targetClass) { if (TransactionalProxy.class.isAssignableFrom(targetClass)) { return false; } TransactionAttributeSource tas = getTransactionAttributeSource(); return (tas == null || tas.getTransactionAttribute(method, targetClass) != null); } 复制代码
TransactionAttributeSourcePointcut
切点的 TransactionAttributeSource
的getTransactionAttribute方法,会计算目标方法是否符合被增强的条件,其中决定性的是否被 @Transactional
标记
到此两种建议者的获取过程,我们已经知晓。有了建议者,接下来就是给目标方法创建创建代理,采纳这些建议。这里涉及到两个问题:
这一块涉及到Bean的生命周期。
代理的创建时机肯定是有了目标对象后,才会创建代理啊。目标对象都没有,建议者又是建议的谁呢?
在Bean创建的生命周期最后一步,即初始化完成后,会走一遍 BeanPostProcessor.postProcessAfterInitialization
方法,再执行一次扩展。此时Bean已成型。
protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) { ...... ...... if (mbd == null || !mbd.isSynthetic()) { wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName); } return wrappedBean; } 复制代码
其中BeanPostProcessor的抽象类 AbstractAdvisorAutoProxyCreator
会根据是否存在给当前目标对象的建议者,来决定是否创建代理对象。
这块意思很清晰,一个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; } if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) { this.advisedBeans.put(cacheKey, Boolean.FALSE); return bean; } 【1】是否有适合当前Bean的建议者。 Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null); if (specificInterceptors != DO_NOT_PROXY) { this.advisedBeans.put(cacheKey, Boolean.TRUE); 【2】创建代理对象 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; } 复制代码
getAdvicesAndAdvisorsForBean:
有了建议者,有了目标对象,创建代理也就顺理成章。但创建所涉及的功能组件却没有那么简单。
InvocationHandler
,并提供 getProxy
方法,通过反射 Proxy
创建代理。 getProxy
通过Enhancer+ DynamicAdvisedInterceptor
创建代理对象
这两种增强都会有一个 AdvisedSupport
属性保存建议者。
private final AdvisedSupport advised; 复制代码
这两个是底层封装,Spring创建代理并没有直接调用他们,而是封装了三个更高层次的组件
这三个组件都是 AdvisedSupport
的子类。
这三个组件都可以用作创建代理对象,他们上没有本质的区别,都是 ProxyCreatorSupport
的子类。创建代理对象的逻辑调用也在 ProxyCreatorSupport
中。
将 多个建议者
告诉 ProxyCreatorSupport
,ProxyCreatorSupport会调用策略工厂 AopProxyFactory
选择 JdkDynamicAopProxy
或者 CglibAopProxy
创建代理对象。
protected Object createProxy( Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) { 【1】ProxyFactory组件 ProxyFactory proxyFactory = new ProxyFactory(); proxyFactory.copyFrom(this); 【2】设置建议者 Advisor[] advisors = buildAdvisors(beanName, specificInterceptors); proxyFactory.addAdvisors(advisors); 【3】创建代理对象 return proxyFactory.getProxy(getProxyClassLoader()); } 复制代码
可以看出,简简单单的JDK,CGLB代理。Spring 融入此功能时做了多么复杂的设计。
以JDK动态代理为例,执行invoke方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { [1] List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass); [2] invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain); [3] // Proceed to the joinpoint through the interceptor chain. retVal = invocation.proceed(); } 复制代码
AdvisedSupport
获建议者链,此过程通过 AdvisorChainFactory
组件完成,将适用于当前目标对象的建议者取出来,创建一个执行建议链。
代理类其实也是实现了 Advied
接口,但是内存生成的那个类,并不会展示Advied接口,但是通过instance of 查看可以查看。
AOP之所以复杂,就在于Spring在上层建筑上所做的东西比较多。其本质就是JDK动态代理(Proxy+InvocationHandler)与CGLB(Enhancer + MethodInterceptor(CallBack))
搞清楚SpringAOP组件的角色,以及与底层基础的连接点。理解AOP就不难了。
万层高楼平地起,抓住根 与 主干,就能窥探其奥秘
反过来讲,当我们设计架构时,应该列出我们的核心,围绕核心做上层设计。
第一遍刷