AOP的增强(Advice)方式有很多种,前置增强,后置增强,环绕增强等,都是通过代理类改变原始类中方法的行为,这些都是基于原始类中已存在的方法。存在这样一种情况,我想让原始类实现某一行为,然而原始类因为某种原因不能或不方便直接实现,故而考虑是否可以也用代理的方式来间接实现,也称为引入增强。
Spring AOP通过CGLIB将包含要实现的方法的接口对象与原始对象合成新的代理对象的方式也支持了此种增强。下面来看下具体的使用和源码实现。
1.引入增强的使用
原始类为Origin,类的具体内容不重要,想要引入的方法是doSomething,为此方法创建一个接口Introduction.java
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,它有四个属性可配置。
- types-matching:要作用的类的表达式
- implement-interface:引入增强的方法所在的接口
- delegate-ref:引入增强的实现bean的id
- default-impl:引入增强的实现类的全路径名称
delegate-ref和default-impl必须配置其一,delegate-ref是引用Spring管理的bean,而default-impl则是直接通过Class对象的newInstance实例化生成对象。
来看下测试
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接口的代理对象了。
注解配置
首先定义切面类
@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注解,注解有两个属性可配置。
- value:相当于XML中的types-matching,引入增强作用的类的表达式
- 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);
}
可以看到最终是用反射的方式直接调用引入增强的实现类,从而达到目的。