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);
}

能够看到最终是用反射的方式直接调用引入加强的实现类,从而达到目的。

相关文章
相关标签/搜索