文档章节

Spring源码-AOP(八)-引入增强

青离
 青离
发布于 2017/09/08 19:56
字数 1173
阅读 241
收藏 11

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,它有四个属性可配置。

  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实例化生成对象。

来看下测试

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注解,注解有两个属性可配置。

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

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

© 著作权归作者所有

共有 人打赏支持
青离
粉丝 278
博文 51
码字总数 114378
作品 0
海淀
后端工程师
私信 提问
spring-AOP原理与应用

什么是AOP Spring是解决实际开发中的一些问题: * AOP解决OOP中遇到的一些问题.是OOP的延续和扩展. AOP作用 对程序进行增强:不修改源码的情况下. * AOP可以进行权限校验,日志记录,性能监控,事...

叫我北北
06/29
0
0
Spring AOP就是这么简单啦

前言 只有光头才能变强 上一篇已经讲解了Spring IOC知识点一网打尽!,这篇主要是讲解Spring的AOP模块~ 之前我已经写过一篇关于AOP的文章了,那篇把比较重要的知识点都讲解过了一篇啦:Sprin...

Java3y
05/24
0
0
AOP 那点事儿 ( 续集 )

本文是《AOP 那点事儿》的续集。 在上篇中,我们从写死代码,到使用代理;从编程式 Spring AOP 到声明式 Spring AOP。一切都朝着简单实用主义的方向在发展。沿着 Spring AOP 的方向,Rod Joh...

JAVA高级架构v
11/29
0
0
Spring中的AOP(二)——AOP基本概念和Spring对AOP的支持

AOP的基本概念 AOP从运行的角度考虑程序的流程,提取业务处理过程的切面。AOP面向的是程序运行中的各个步骤,希望以更好的方式来组合业务逻辑的各个步骤。AOP框架并不与特定的代码耦合,AOP...

摆渡者
2014/03/17
0
3
spring introduction aop

众所周知,spring aop中一个advisor=advice or methodinterceptor + pointcut,之后将不同的advisor调用ProxyFactory的addAdvisor方法添加进去并排序(顺序特别重要,在最新的源码里是需要实...

Catelyn
2015/04/15
0
0

没有更多内容

加载失败,请刷新页面

加载更多

开发者和架构师之间最大的区别是什么?

1、开发者和架构师之间最大的区别是什么? 架构师和开发者一样,也经常写代码,简单的说,开发者和架构师之间最大的区别就是技术领导力。 软件架构师的角色需要理解最重要的架构驱动力是什么...

James-
26分钟前
1
0
java框架学习日志-4

补充一些spring配置文件的方法。 设置别名: <!--通过name直接设置别名--> <bean name="user2" class="cn.sxt.factory.UserDynamicFactory"> </bean> <!--有id的情况下也可以设置......

白话
28分钟前
2
0
20181213 上课截图

小丑鱼00
44分钟前
1
0
nginx+php-fpm配置后页面显示空白的解决方法以及用nginx和php-fpm解决“502 Bad Gateway”问题

https://stackoverflow.com/questions/15423500/nginx-showing-blank-php-pages For reference, I am attaching my location block for catching files with the .php extension: location ~......

Yao--靠自己
52分钟前
3
0
mac 没声音

somehow不时就会出现这种情况。之前都得重启。 其实可以直接在terminal里打以下命令: sudo kextunload /System/Library/Extensions/AppleHDA.kext sudo kextload /System/Library/Extension...

dubox
今天
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部