Spring源碼-AOP(八)-引入加強

AOP的加強(Advice)方式有不少種,前置加強,後置加強,環繞加強等,都是經過代理類改變原始類中方法的行爲,這些都是基於原始類中已存在的方法。存在這樣一種狀況,我想讓原始類實現某一行爲,然而原始類由於某種緣由不能或不方便直接實現,故而考慮是否能夠也用代理的方式來間接實現,也稱爲引入加強。java

Spring AOP經過CGLIB將包含要實現的方法的接口對象與原始對象合成新的代理對象的方式也支持了此種加強。下面來看下具體的使用和源碼實現。spring

1.引入加強的使用

原始類爲Origin,類的具體內容不重要,想要引入的方法是doSomething,爲此方法建立一個接口Introduction.javaide

public interface Introduction {

	void doSomething();
}

它的具體實現爲測試

public class IntroductionImpl implements Introduction{

	[@Override](https://my.oschina.net/u/1162528)
	public void doSomething() {
		System.out.println("do something");
	}

}

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-3.0.xsd
	http://www.springframework.org/schema/aop
	http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
	
	<!-- 原始對象 -->
	<bean id="origin" class="com.lcifn.spring.aop.bean.Origin"/>
	<!-- 加強實現 -->
	<bean id="introductionImpl" class="com.lcifn.spring.aop.bean.IntroductionImpl"/>
	
	<!-- 引入加強配置 -->
	<aop:config proxy-target-class="true">
		<aop:aspect>
			<aop:declare-parents types-matching="com.lcifn.spring.aop.bean.*"
				 implement-interface="com.lcifn.spring.aop.bean.Introduction"
				 delegate-ref="introductionImpl"/>
		</aop:aspect>
	</aop:config>
</beans>

因爲引入加強必須使用CGLIB,配置proxy-target-class爲true。對於引用加強,Spring AOP中使用的標籤是aop:declare-parents,它有四個屬性可配置。this

  1. types-matching:要做用的類的表達式
  2. implement-interface:引入加強的方法所在的接口
  3. delegate-ref:引入加強的實現bean的id
  4. default-impl:引入加強的實現類的全路徑名稱

delegate-ref和default-impl必須配置其一,delegate-ref是引用Spring管理的bean,而default-impl則是直接經過Class對象的newInstance實例化生成對象。.net

來看下測試代理

public class AspectJAopIntroductionTest {

	public static void main(String[] args) {
		ApplicationContext context = new ClassPathXmlApplicationContext("aop/aspectj-aop-introduction.xml");
		Origin origin = (Origin) context.getBean("origin");
		Introduction in = (Introduction) origin;
		in.doSomething();
	}
}

從Spring容器中根據原始類的beanId獲取實例,直接強轉成Introduction接口對象便可調用。由於從Spring實例化造成的對象已是實現了Introduction接口的代理對象了。rest

註解配置

首先定義切面類code

@Component
[@Aspect](https://my.oschina.net/aspect)
public class AspectJIntroductionAnnotationAdvice {

	@DeclareParents(value="com.lcifn.spring.aop.bean.*", defaultImpl=IntroductionImpl.class)
	private Introduction in;
}

@Aspect聲明一個切面類,定義一個field屬性,類型爲引入加強的接口,在field上使用@DeclareParents註解,註解有兩個屬性可配置。xml

  1. value:至關於XML中的types-matching,引入加強做用的類的表達式
  2. defaultImpl:引入加強的具體實現

直接使用@Configuration的方式來配置

@Configuration
@ComponentScan("com.lcifn.spring.aop.bean,com.lcifn.spring.aop.advice")
@EnableAspectJAutoProxy(proxyTargetClass=true)
public class AppConfig {

}

測試以下

public class AspectJAopIntroductionAnnotationTest {

	public static void main(String[] args) {
		ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
		Origin origin = (Origin) context.getBean("origin");
		Introduction in = (Introduction) origin;
		in.doSomething();
	}
}

2.引入加強源碼解析

Spring AOP中對於引入加強的處理都是獨立的,以註解的方式來介紹

首先解析切面類時,對@DeclareParents註解進行處理,在ReflectiveAspectJAdvisorFactory的getAdvisors方法中

// Find introduction fields.
for (Field field : aspectClass.getDeclaredFields()) {
	Advisor advisor = getDeclareParentsAdvisor(field);
	if (advisor != null) {
		advisors.add(advisor);
	}
}

建立DeclareParentsAdvisor的方法中獲取引入加強的接口對象,要做用的類表達式(type-matching)以及加強的具體實現。

private Advisor getDeclareParentsAdvisor(Field introductionField) {
	DeclareParents declareParents = introductionField.getAnnotation(DeclareParents.class);
	if (declareParents == null) {
		// Not an introduction field
		return null;
	}

	if (DeclareParents.class.equals(declareParents.defaultImpl())) {
		// This is what comes back if it wasn't set. This seems bizarre...
		// TODO this restriction possibly should be relaxed
		throw new IllegalStateException("defaultImpl must be set on DeclareParents");
	}

	return new DeclareParentsAdvisor(
			introductionField.getType(), declareParents.value(), declareParents.defaultImpl());
}

在DeclareParentsAdvisor的構造方法中實例化了引入加強的Advice類DelegatePerTargetObjectIntroductionInterceptor。

public DeclareParentsAdvisor(Class<?> interfaceType, String typePattern, Class<?> defaultImpl) {
	this(interfaceType, typePattern, defaultImpl,
		 new DelegatePerTargetObjectIntroductionInterceptor(defaultImpl, interfaceType));
}

private DeclareParentsAdvisor(Class<?> interfaceType, String typePattern, Class<?> implementationClass, Advice advice) {
	this.introducedInterface = interfaceType;
	ClassFilter typePatternFilter = new TypePatternClassFilter(typePattern);

	// Excludes methods implemented.
	ClassFilter exclusion = new ClassFilter() {
		@Override
		public boolean matches(Class<?> clazz) {
			return !(introducedInterface.isAssignableFrom(clazz));
		}
	};

	this.typePatternClassFilter = ClassFilters.intersection(typePatternFilter, exclusion);
	this.advice = advice;
}

當實際調用引入加強的方法時,CGLIB使用CglibMethodInvocation進行鏈式調用全部Advisor封裝成的攔截器MethodInterceptor,執行invoke方法。引入加強的Advice類DelegatePerTargetObjectIntroductionInterceptor,它同時實現了MethodInterceptor接口。

public Object invoke(MethodInvocation mi) throws Throwable {
	if (isMethodOnIntroducedInterface(mi)) {
		Object delegate = getIntroductionDelegateFor(mi.getThis());

		// Using the following method rather than direct reflection,
		// we get correct handling of InvocationTargetException
		// if the introduced method throws an exception.
		// 反射調用引入加強實現類中的方法
		Object retVal = AopUtils.invokeJoinpointUsingReflection(delegate, mi.getMethod(), mi.getArguments());

		// Massage return value if possible: if the delegate returned itself,
		// we really want to return the proxy.
		// 處理流式調用返回this的狀況
		if (retVal == delegate && mi instanceof ProxyMethodInvocation) {
			retVal = ((ProxyMethodInvocation) mi).getProxy();
		}
		return retVal;
	}

	return doProceed(mi);
}

能夠看到最終是用反射的方式直接調用引入加強的實現類,從而達到目的。

相關文章
相關標籤/搜索