Spring裡的aop實現方式和原始碼分析 java中代理,靜態代理,動態代理以及spring aop代理方式,實現原理統一彙總
使用"橫切"技術,AOP把軟體系統分為兩個部分:核心關注點和橫切關注點。業務處理的主要流程是核心關注點,與之關係不大的部分是橫切關注點。橫切關注點的一個特點是,他們經常發生在核心關注點的多處,而各處基本相似,比如許可權認證、日誌、事務。AOP的作用在於分離系統中的各種關注點,將核心關注點和橫切關注點分離開來。
AOP核心概念
1、橫切關注點
對哪些方法進行攔截,攔截後怎麼處理,這些關注點稱之為橫切關注點
2、切面(aspect)
類是對物體特徵的抽象,切面就是對橫切關注點的抽象
3、連線點(joinpoint)
被攔截到的點,因為Spring只支援方法型別的連線點,所以在Spring中連線點指的就是被攔截到的方法,實際上連線點還可以是欄位或者構造器
4、切入點(pointcut)
對連線點進行攔截的定義
5、通知(advice)
所謂通知指的就是指攔截到連線點之後要執行的程式碼,通知分為前置、後置、異常、最終、環繞通知五類
6、目標物件
代理的目標物件
7、織入(weave)
將切面應用到目標物件並導致代理物件建立的過程
8、引入(introduction)
在不修改程式碼的前提下,引入可以在執行期為類動態地新增一些方法或欄位
使用ProxyFactoryBean實現AOP
Spring自己的AOP實現在於ProxyFactoryBean。
介面
package com.shoudongdaili;public interface IPerson { void say(); }
實現類
package com.shoudongdaili; public class Person3 implements IPerson { private String name3; private int age3; public String getName3() { return name3; } public void setName3(String name3) { this.name3 = name3; }public int getAge3() { return age3; } public void setAge3(int age3) { this.age3 = age3; } @Override public String toString() { return "Person3 [name3=" + name3 + ", age3=" + age3 + "]"; } @Override public void say() { System.out.println(toString()); } }
通知類
package com.shoudongdaili; import java.lang.reflect.Method; import org.aopalliance.intercept.MethodInterceptor; import org.springframework.aop.MethodBeforeAdvice; import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator; public class MyAdvice3 implements MethodBeforeAdvice { @Override public void before(Method method, Object[] args, Object target) throws Throwable { //arg0 是 目標類的方法 arg1是目標類的入引數 arg2是目標類例項 發生異常則拋給Throwable System.out.println("before my advice3..."); } }
bean.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:p="http://www.springframework.org/schema/p" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- 代理前原物件 --> <bean id="person3" class="com.shoudongdaili.Person3"></bean>
<!-- 通知類 --> <bean id="myAdvice3" class="com.shoudongdaili.MyAdvice3"></bean>
<!-- 代理物件 --> <bean id="proxyPerson3" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="proxyInterfaces" value="com.shoudongdaili.IPerson"></property> <property name="target" ref="person3"></property> <property name="interceptorNames"> <list> <value>myAdvice3</value> </list> </property> </bean> </beans>
測試類
package com.shoudongdaili; import org.springframework.context.support.ClassPathXmlApplicationContext; public class BeanTest3 { public static void main(String[] args) { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("zidongdaili/shoudongdaili-bean.xml"); // IPerson的實現類有Person3和proxyPerson3代理類這兩個,注意這裡是使用proxyPerson3 IPerson person3 = (IPerson) context.getBean("proxyPerson3"); person3.say(); } }
原始碼解讀
然後我們就要原始碼分析下這一過程,先看下是如何產生代理物件的,在ProxyFactoryBean的getObject方法中:
public class ProxyFactoryBean extends ProxyCreatorSupport implements FactoryBean<Object>, BeanClassLoaderAware, BeanFactoryAware { @Override public Object getObject() throws BeansException { //重點一 initializeAdvisorChain(); if (isSingleton()) { //重點二 return getSingletonInstance(); } else { if (this.targetName == null) { logger.warn("Using non-singleton proxies with singleton targets is often undesirable. " + "Enable prototype proxies by setting the 'targetName' property."); } return newPrototypeInstance(); } } }
重點1:就是根據我們配置的interceptorNames來獲取對應的bean,並卻轉化成Advisor。
this.advisorChainInitialized:標示是否已進行過初始化,若以初始化則不再進行初始化。然後就是將interceptorNames轉化成Advisor。根據interceptorNames所包含的字串到容器中進行查詢,如果含有*則,則表示進行一定的匹配,符合的都會納入。
如下:
private synchronized void initializeAdvisorChain() throws AopConfigException, BeansException { if (this.advisorChainInitialized) { return; } if (!ObjectUtils.isEmpty(this.interceptorNames)) { if (this.beanFactory == null) { throw new IllegalStateException("No BeanFactory available anymore (probably due to serialization) " + "- cannot resolve interceptor names " + Arrays.asList(this.interceptorNames)); } // Globals can't be last unless we specified a targetSource using the property... if (this.interceptorNames[this.interceptorNames.length - 1].endsWith(GLOBAL_SUFFIX) && this.targetName == null && this.targetSource == EMPTY_TARGET_SOURCE) { throw new AopConfigException("Target required after globals"); } // Materialize interceptor chain from bean names. for (String name : this.interceptorNames) { if (logger.isTraceEnabled()) { logger.trace("Configuring advisor or advice '" + name + "'"); } if (name.endsWith(GLOBAL_SUFFIX)) { if (!(this.beanFactory instanceof ListableBeanFactory)) { throw new AopConfigException( "Can only use global advisors or interceptors with a ListableBeanFactory"); } addGlobalAdvisor((ListableBeanFactory) this.beanFactory, name.substring(0, name.length() - GLOBAL_SUFFIX.length())); } else { // If we get here, we need to add a named interceptor. // We must check if it's a singleton or prototype. Object advice; if (this.singleton || this.beanFactory.isSingleton(name)) { // Add the real Advisor/Advice to the chain. advice = this.beanFactory.getBean(name); } else { // It's a prototype Advice or Advisor: replace with a prototype. // Avoid unnecessary creation of prototype bean just for advisor chain initialization. advice = new PrototypePlaceholderAdvisor(name); } addAdvisorOnChainCreation(advice, name); } } } this.advisorChainInitialized = true; }
這中間頁經過了Advice到Advisor的轉換,如下:
private void addAdvisorOnChainCreation(Object next, String name) { // We need to convert to an Advisor if necessary so that our source reference // matches what we find from superclass interceptors. Advisor advisor = namedBeanToAdvisor(next); if (logger.isTraceEnabled()) { logger.trace("Adding advisor with name '" + name + "'"); } addAdvisor(advisor); }
private Advisor namedBeanToAdvisor(Object next) { try { return this.advisorAdapterRegistry.wrap(next); } catch (UnknownAdviceTypeException ex) { // We expected this to be an Advisor or Advice, // but it wasn't. This is a configuration error. throw new AopConfigException("Unknown advisor type " + next.getClass() + "; Can only include Advisor or Advice type beans in interceptorNames chain except for last entry," + "which may also be target or TargetSource", ex); } }
public Advisor wrap(Object adviceObject) throws UnknownAdviceTypeException { if (adviceObject instanceof Advisor) { return (Advisor) adviceObject; } if (!(adviceObject instanceof Advice)) { throw new UnknownAdviceTypeException(adviceObject); } Advice advice = (Advice) adviceObject; if (advice instanceof MethodInterceptor) { // So well-known it doesn't even need an adapter. return new DefaultPointcutAdvisor(advice); } for (AdvisorAdapter adapter : this.adapters) { // Check that it is supported. if (adapter.supportsAdvice(advice)) { return new DefaultPointcutAdvisor(advice); } } throw new UnknownAdviceTypeException(advice); }
public class DefaultPointcutAdvisor extends AbstractGenericPointcutAdvisor implements Serializable { public DefaultPointcutAdvisor(Advice advice) { this(Pointcut.TRUE, advice); } }
這個包裹過程已經見過很多遍了,採用了介面卡的模式。
之後又是和其他的AOP方式接軌了,設定一些列要實現的介面和引數,使用DefaultAopProxyFactory先創建出AopProxy,要麼是JdkDynamicAopProxy,要麼是CglibAopProxy,然後就可以呼叫AopProxy的getProxy方法來獲取代理物件。
具體JdkDynamicAopProxy和CglibAopProxy的區別聯絡,參閱java中代理,靜態代理,動態代理以及spring aop代理方式,實現原理統一彙總
這種方式實現的AOP還是比較麻煩的,同時配置一個ProxyFactoryBean僅能實現對一個目標物件的攔截,要想攔截多個目標物件,需要配置多個ProxyFactoryBean。所以大部分還是使用Spring引進的aspectj的AOP方式來進行AOP程式設計。
使用DefaultAdvisorAutoProxyCreator實現自動代理完成AOP
介面
package com.zidongdaili; public interface IPerson4 { void sayhi(); }
實現類
package com.zidongdaili; public class Person4 implements IPerson4 { private String name4; private int age4; public String getName4() { return name4; } public void setName4(String name4) { this.name4 = name4; } public int getAge4() { return age4; } public void setAge4(int age4) { this.age4 = age4; } @Override public String toString() { return "Person4 [name4=" + name4 + ", age4=" + age4 + "]"; } @Override public void sayhi() { System.out.println(toString()); } }
通知類
package com.zidongdaili; import java.lang.reflect.Method; import org.springframework.aop.MethodBeforeAdvice; public class MyAdvice4 implements MethodBeforeAdvice { @Override public void before(Method method, Object[] args, Object target) throws Throwable { //arg0 是 目標類的方法 arg1是目標類的入引數 arg2是目標類例項 發生異常則拋給Throwable System.out.println("before my advice4..."); } }
bean.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:p="http://www.springframework.org/schema/p" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <bean id="person4" class="com.zidongdaili.Person4"></bean> <bean id="myAdvice4" class="com.zidongdaili.MyAdvice4"></bean> <bean class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> <!-- id="advisor" 可以不寫 --> <property name="pattern"> <value>.*say.+</value> <!-- 業務實現方法名匹配 --> </property> <property name="advice"> <ref bean="myAdvice4" /> </property> </bean>
<!-- 自動代理 --> <bean id="autoProxyCreator" class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"> </bean> </beans>
測試類
package com.zidongdaili; import org.springframework.context.support.ClassPathXmlApplicationContext; public class BeanTest4 { public static void main(String[] args) { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("zidongdaili/zidongdaili-bean.xml"); IPerson4 person4 = (IPerson4) context.getBean("person4"); person4.sayhi(); } }
使用BeanNameAutoProxyCreator實現自動代理完成AOP
BeanNameAutoProxyCreator是自動代理建立器的三種(BeanNameAutoProxyCreator,DefaultAdvisorAutoProxyCreator,AbstractAdvisorAutoProxyCreator)之一.它是根據攔截器和設定的Bean的名稱表示式做匹配來建立代理.下面是個例子
1.主要依賴(略)
2.宣告一個環繞通知(攔截器)
public class MyMethodInterceptor implements MethodInterceptor { @Override public Object invoke(MethodInvocation invocation) throws Throwable { System.out.println(getClass()+"呼叫方法前"); Object ret=invocation.proceed(); System.out.println(getClass()+"呼叫方法後"); return ret; } }
3.要建立代理的目標類與介面
public interface UserService { void print(); } public class UserServiceImpl implements UserService { public void print(){ System.out.println(getClass()+"#print"); } }
4.配置
@Configuration public class AppConfig { //要建立代理的目標Bean @Bean public UserService userService(){ return new UserServiceImpl(); } //建立Advice或Advisor @Bean public Advice myMethodInterceptor(){ return new MyMethodInterceptor(); } //使用BeanNameAutoProxyCreator來建立代理 @Bean public BeanNameAutoProxyCreator beanNameAutoProxyCreator(){ BeanNameAutoProxyCreator beanNameAutoProxyCreator=new BeanNameAutoProxyCreator(); //設定要建立代理的那些Bean的名字 beanNameAutoProxyCreator.setBeanNames("userSer*"); //設定攔截鏈名字(這些攔截器是有先後順序的) beanNameAutoProxyCreator.setInterceptorNames("myMethodInterceptor"); return beanNameAutoProxyCreator; } }
5.測試
public class Main { public static void main(String[] args) { ApplicationContext applicationContext=new AnnotationConfigApplicationContext(AppConfig.class); UserService userService= applicationContext.getBean(UserService.class); userService.print(); } }
原始碼分析
BeanNameAutoProxyCreator是一個BeanPostProcessor.它在Bean例項化隨後,呼叫回撥org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#postProcessAfterInitialization進行後期處理來完成代理的建立.
其中AbstractAutoProxyCreator是BeanNameAutoProxyCreator的超類,BeanNameAutoProxyCreator沒有重寫postProcessAfterInitialization方法.下面看看這個方法:
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (bean != null) { Object cacheKey = getCacheKey(bean.getClass(), beanName); if (!this.earlyProxyReferences.contains(cacheKey)) { //關鍵程式碼在這裡 return wrapIfNecessary(bean, beanName, cacheKey); } } return bean; }
再看看wrapIfNecessary方法:
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; } //這個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; }
再看看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()); }
再看看org.springframework.aop.framework.ProxyFactory#getProxy(java.lang.ClassLoader)如下:
public Object getProxy(ClassLoader classLoader) { return createAopProxy().getProxy(classLoader); }
再看看org.springframework.aop.framework.ProxyCreatorSupport#createAopProxy
public Object getProxy(ClassLoader classLoader) { return createAopProxy().getProxy(classLoader); }
再看看createAopProxy方法
protected final synchronized AopProxy createAopProxy() { if (!this.active) { activate(); } return getAopProxyFactory().createAopProxy(this); }
剩下的就與ProxyFactoryBean建立代理類似了.
手動實現自動代理實現AOP
我們也可以寫一個類,來實現DefaultAdvisorAutoProxyCreator自動代理的功能!
首先,我們需要實現一個介面,也就是BeanPostProcessor介面。
BeanPostProcessor介面作用是:如果我們需要在Spring容器完成Bean的例項化、配置和其他的初始化前後新增一些自己的邏輯處理,我們就可以定義一個或者多個BeanPostProcessor介面的實現,然後註冊到容器中。
而我們想要在原型物件bean被建立之後就代理了,就必須在原來的容器中拿到原來的原型物件,需要拿到原來spring容器中的切面物件,這個時候,我們就需要原來的容器,這個時候就需要另一個介面,也就是ApplicationContextAware介面!
通過這2個介面,我們就可以實現自動代理了。
package cn.hncu.xmlImpl; import org.springframework.aop.Advisor; import org.springframework.aop.framework.ProxyFactoryBean; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; public class MyAutoProxy implements BeanPostProcessor,ApplicationContextAware{ private ApplicationContext applicationContext=null; //bean建立之前呼叫 @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean;//在這裡,我們直接放行 } //bean建立之後呼叫 @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { ProxyFactoryBean factory = new ProxyFactoryBean(); //把原型物件放入代理工廠 factory.setTarget(bean); //在這裡 Advisor adv = applicationContext.getBean(Advisor.class); factory.addAdvisor(adv); //返回被代理後的物件 return factory.getObject(); } //拿到原來的spring中的容器 @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext=applicationContext; } }
bean.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:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="person4" class="com.zidongdaili.Person4"></bean>
<bean id="myAdvice4" class="com.zidongdaili.MyAdvice4"></bean>
<bean class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> <!-- id="advisor" 可以不寫 -->
<property name="pattern">
<value>.*say.+</value> <!-- 業務實現方法名匹配 -->
</property>
<property name="advice">
<ref bean="myAdvice4" />
</property>
</bean>
<!-- 自己寫的自動代理 -->
<bean class="cn.hncu.xmlImpl.MyAutoProxy"></bean>
</beans>