在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則是當前不進行加強操做,而是先調用下一個處理對象。而後因爲這些對象是相似遞歸那樣嵌套調用的,因此只須要將處理邏輯置後那麼只須要等嵌套中心的代碼執行完成,後面的代碼仍是會執行的。
基本邏輯以下圖。