在Spring中AOP是咱们使用的很是频繁的一个特性。经过AOP咱们能够补足一些面向对象编程中不足或难以实现的部分。html
首先在学习源码以前咱们须要了解关于AOP的相关概念如切点切面等,以及如何使用AOP,这里能够看我以前的文章:Spring系列之AOP的原理及手动实现java
对于Java这种面向对象语言来讲任何功能的实现都是依赖于对象,AOP也不例外。spring
首先咱们先来准备好在配置文件中配置好AOP相关的属性。express
spring.xml编程
<bean id="aspect" class="cn.javass.spring.chapter6.aop.HelloWorldAspect"/>
<bean id="beforeAdvice" class="cn.javass.spring.chapter6.aop.BeforeAdviceImpl"/>
<aop:aspectj-autoproxy/>
<aop:config>
<aop:pointcut id="pointcutA" expression="execution(* cn.javass..*.sayAfterReturning(..))"></aop:pointcut>
<aop:advisor id="advisor" pointcut="execution(* cn.javass..*.sayAdvisorBefore(..))"
advice-ref="beforeAdvice"/>
<aop:aspect id="aspects" ref="aspect">
<aop:before pointcut="execution(* cn.javass..*.sayBefore(..)) and args(param)" method="beforeAdvice(java.lang.String)" arg-names="param"/>
<aop:after-returning pointcut-ref="pointcutA" method="afterReturningAdvice" arg-names="retVal" returning="retVal"/>
<aop:after-throwing pointcut="execution(* cn.javass..*.sayAfterThrowing(..))" method="afterThrowingAdvice" arg-names="exception" throwing="exception"/>
<aop:after pointcut="execution(* cn.javass..*.sayAfterFinally(..))" method="afterFinallyAdvice"/>
<aop:around pointcut="execution(* cn.javass..*.sayAround(..))" method="aroundAdvice"/>
</aop:aspect>
</aop:config>
复制代码
在上面的配置中建立了几种不一样的advice。这些配置在spring启动时会被相应的建立为对象。bash
在前面IOC文章中咱们有提到在Spring中经过spi的机制来肯定解析配置文件中不一样标签的解析类。app
在aop包中找到spring.handlers文件:函数
http\://www.springframework.org/schema/aop=org.springframework.aop.config.AopNamespaceHandler
复制代码
能够肯定的是处理aop相关标签的就是AopNamespaceHandler
这个类。post
public void init() {
this.registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());
this.registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
this.registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());
this.registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
}
复制代码
在AopNamespaceHandler
中除了构造函数就只有上面的init方法,上面代码就是注册解析不一样标签的解析器的BeanDefinition。咱们须要关注的是aspectj-autoproxy
标签的解析器类AspectJAutoProxyBeanDefinitionParser
。学习
aspectj-autoproxy
标签是aop功能的开关,不论是经过注解仍是配置文件方式都须要显示的注明该标签。经过aop命名空间的aop:aspectj-autoproxy/声明自动为spring容器中那些配置@aspectJ切面的bean建立代理,织入切面。
跟踪AspectJAutoProxyBeanDefinitionParser
的parse方法最终会进入到AopConfigUtils#registerOrEscalateApcAsRequired
中。
private static BeanDefinition registerOrEscalateApcAsRequired(Class<?> cls, BeanDefinitionRegistry registry, Object source) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
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) {
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);
registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
return beanDefinition;
}
复制代码
这里的常量AUTO_PROXY_CREATOR_BEAN_NAME = "org.springframework.aop.config.internalAutoProxyCreator";
首先要明白的是internalAutoProxyCreator并非spring中的一个实际的类,AUTO_PROXY_CREATOR_BEAN_NAME是一个用于建立aop类的Beandefinition的名字。
在上面的代码逻辑中若是AUTO_PROXY_CREATOR_BEAN_NAME表示的Beandefinition已经存在则判断新须要注册的类其优先级和已经存在的类定义进行比较,若是新须要注册的优先级较高则进行替换。
若是不存在已经注册的Beandefinition则将其进行注册。被注册的Beandefinition表示的类为AspectJAwareAdvisorAutoProxyCreator
。
在前面IOC文章中分析过了在解析完配置文件后须要建立的对象都会将其BeanDefinition注册到IOC容器中,因此咱们能够将断点设置在配置文件解析完成以后就能够看到须要建立那些对象了。
如上图helloWorldService
就是须要被加强的类。
public interface IHelloWorldService {
void sayHello();
void sayBefore(String param);
boolean sayAfterReturning();
void sayAfterThrowing();
boolean sayAfterFinally();
void sayAround(String param);
void sayAdvisorBefore(String param);
}
复制代码
而aspect,beforeAdvice,pointcutA,advisor
都是咱们在配置文件中配置过的,是切点,切面和处理方法的实现类。org.springframework.aop.config.internalAutoProxyCreator
是上面分析过的用于建立aop代理的实现类。
然后面的以org.springframework.aop.aspectj.AspectJPointcutAdvisor
开头的几个类实际上就是包含了切点和通知的一个切面的实现类,也就是它来决定哪些类须要被加强。
若是看过前面手写aop文章的同窗应该知道当时咱们分析aop加强时机时有说过aop的加强功能其实是依赖于动态代理实现的。而动态代理若是要对一个对象进行加强那么首先须要持有该对象才行。
因此咱们在对对象进行加强的前提是该对象已经被建立完成以后。并且咱们要清楚的是一个类对象被加强后咱们全部须要使用该对象的地方都应该使用该对象,这样就肯定了类加强的时机必定是在类对象建立以后而且在完成注入以前。
前面有说过建立代理对象其实是经过AspectJAwareAdvisorAutoProxyCreator
来完成,先来了解一下该类,查看该类的继承体系。
能够看到实际上该类自己仍是一个BeanPostProcessor,那么能够确定的是咱们只要找到执行BeanPostProcessor的地方而且是在实例化后执行的地方便可。通过调试后定位到AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsAfterInitialization
方法。
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) throws BeansException {
Object result = existingBean;
for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
result = beanProcessor.postProcessAfterInitialization(result, beanName);
if (result == null) {
return result;
}
}
return result;
}
复制代码
这里是对后置处理器进行遍历,对于aop咱们须要关注的是AspectJAwareAdvisorAutoProxyCreator
这一个处理器。
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
//more code
// Create proxy if we have advice.
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;
}
复制代码
这里去掉了前面一些代码,getAdvicesAndAdvisorsForBean方法是用来获取和当前对象匹配的切面。这里获取相匹配的切面类是经过AbstractAdvisorAutoProxyCreator
来实现。
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
List<Advisor> candidateAdvisors = findCandidateAdvisors();
List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
extendAdvisors(eligibleAdvisors);
if (!eligibleAdvisors.isEmpty()) {
//根据在配置文件中配置的order属性或者注解@order()进行从小到大的排序
//order的值越小其优先级越高
eligibleAdvisors = sortAdvisors(eligibleAdvisors);
}
return eligibleAdvisors;
}
复制代码
首先获取到全部的切面类,而后经过AopUtils.findAdvisorsThatCanApply
方法来肯定哪些类可以匹配。
public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
if (candidateAdvisors.isEmpty()) {
return candidateAdvisors;
}
List<Advisor> eligibleAdvisors = new LinkedList<Advisor>();
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;
}
public static boolean canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions) {
if (advisor instanceof IntroductionAdvisor) {
return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass);
}
else if (advisor instanceof PointcutAdvisor) {
PointcutAdvisor pca = (PointcutAdvisor) advisor;
return canApply(pca.getPointcut(), targetClass, hasIntroductions);
}
else {
// It doesn't have a pointcut so we assume it applies. return true; } } 复制代码
实现逻辑很简单,遍历全部的advisor调用canApply肯定是否匹配。
切面是有切入点和通知组成,切入点用来肯定哪些对象须要被加强,而通知决定如何进行加强。因此很明显这里肯定类对象是否匹配是由切入点(pointCut)决定的。
咱们先来看一下切入点是什么。
public interface Pointcut {
//类过滤器 用于肯定类对象是否匹配 只有当类对象匹配后才进行方法的匹配
ClassFilter getClassFilter();
//方法匹配器 用于肯定具体哪一些方法须要被加强。
MethodMatcher getMethodMatcher();
//生成一个pointcut对象实例
Pointcut TRUE = TruePointcut.INSTANCE;
}
复制代码
上面咱们能够看到实际上就是经过ClassFilter和MethodMatcher相互配合来实现的,具体的实现过程会由于实现方式大同小异。其中实现方式包括好比正则匹配,AspectJ匹配等,在咱们以前的手写系列中就是经过正则来进行匹配的,这里匹配的实现不深刻探讨。
经过上面的逻辑即可以肯定好加强该类会用到哪些advisor。
当肯定好须要用到的advisor和其顺序后就开始进行建立代理对象了。建立代理对象的方法由前面提到的wrapIfNecessary
来调用createProxy
方法实现。
protected Object createProxy(Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.copyFrom(this);
if (!proxyFactory.isProxyTargetClass()) {
if (shouldProxyTargetClass(beanClass, beanName)) {
proxyFactory.setProxyTargetClass(true);
}
else {
evaluateProxyInterfaces(beanClass, proxyFactory);
}
}
Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
for (Advisor advisor : advisors) {
proxyFactory.addAdvisor(advisor);
}
proxyFactory.setTargetSource(targetSource);
customizeProxyFactory(proxyFactory);
proxyFactory.setFrozen(this.freezeProxy);
if (advisorsPreFiltered()) {
proxyFactory.setPreFiltered(true);
}
return proxyFactory.getProxy(getProxyClassLoader());
}
复制代码
建立实际上市代理经过代理工厂类(ProxyFactory)实现的。
在建立代理对象时须要肯定使用JDK代理仍是cglib代理,前面有提到过若是在配置文件中配置了proxy-target-class="true"
的话那么就只会使用cglib进行代理。可是若是没有配置的话则须要经过实际状况来决定是JDK代理仍是cglib。
而除了proxy-target-class
外,咱们实际上还能够配置一个属性optimize
,该属性默认值为false,若是咱们将其置为true那么就表示容许spring对代理生成策略进行优化,意思就是若是该类有接口,就代理接口(使用JDK代理);若是没有接口,就代理类(使用CGLIB代理)。而不是像若是只配置proxyTargetClass=true时强制代理类,而不去考虑代理接口的方式。
综上在spring中使用代理方式的策略以下:
optimize
和proxy-target-class
而且该类实现了接口,那么使用JDK动态代理。optimize
和proxy-target-class
而且该类没有实现接口,那么使用cglib动态代理。optimize
和proxy-target-class
而且该类实现了接口,那么使用JDK动态代理。optimize
和proxy-target-class
而且该类没有实现接口,那么使用cglib动态代理。实现代码以下:
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()) {
return new JdkDynamicAopProxy(config);
}
return new ObjenesisCglibAopProxy(config);
}
else {
return new JdkDynamicAopProxy(config);
}
}
复制代码
如今已经取到了建立代理对象的策略和目标对象,就能够直接建立代理对象了。若是对这放面有兴趣的能够自行搜索。
建立好代理对象以后使用代理对象替代以前建立好的对象,那么在使用的时候就会调用加强后的方法完成功能。
在实际开发过程当中,可能会存在一个方法被多个advisor加强,可能有的在方法执行前加强有的在方法执行后进行加强。那么在spring中如何肯定每个加强方法的调用时机保证不会出问题的呢?
在手写aop系列中有讲过这个问题,当时咱们是经过责任链模式来解决这个问题。实际上spring中就是经过责任链模式来解决该问题的。
在JdkDynamicAopProxy
的invoke
方法中,会经过getInterceptorsAndDynamicInterceptionAdvice方法来获取加强当前调用方法的全部advisor的chain,可是须要注意的是这个chain并非根据实际应该的执行顺序排列的。仅仅只是全部会被执行的加强方法的集合。
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
复制代码
获取的chain会被封装成一个ReflectiveMethodInvocation
,实际执行过程经过该类的proceed()
方法完成。
invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
retVal = invocation.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;
if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
return dm.interceptor.invoke(this);
}else {
return proceed();
}
}else {
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}
复制代码
责任链模式中存在着一个index的变量,经过这个索引来决定链中的对象的执行。若是当前对象暂时没法执行则会经过当前的对象来调用下一个执行的对象。而对于不一样类型的对象经过多态来进行不一样的处理。好比若是是一个before类型的advice。
MethodBeforeAdviceInterceptor#invoke
public Object invoke(MethodInvocation mi) throws Throwable {
this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis() );
return mi.proceed();
}
复制代码
对于before类型的advice会直接调用,而后继续调用下一个对象执行。
对于after类型的advice:
AspectJAfterAdvice#invoke
public Object invoke(MethodInvocation mi) throws Throwable {
try {
return mi.proceed();
}
finally {
invokeAdviceMethod(getJoinPointMatch(), null, null);
}
}
复制代码
代码意思很明显了,after类型的advice则是当前不进行加强操做,而是先调用下一个处理对象。而后因为这些对象是相似递归那样嵌套调用的,因此只须要将处理逻辑置后那么只须要等嵌套中心的代码执行完成,后面的代码仍是会执行的。
基本逻辑以下图。