文档章节

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

青离
 青离
发布于 2017/09/08 19:56
字数 1173
阅读 221
收藏 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);
}

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

© 著作权归作者所有

共有 人打赏支持
青离
粉丝 276
博文 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
Spring中的AOP(二)——AOP基本概念和Spring对AOP的支持

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

摆渡者
2014/03/17
0
3
day33_Spring学习笔记_02

一、AOP 1.1、AOP介绍 1.1.1、什么是AOP? 在软件业,AOP为Aspect Oriented Programming的缩写,意为:,通过和实现程序功能的统一维护的一种技术。AOP是OOP(面向对象编程)的延续,是软件开...

黑泽明军
08/03
0
0
spring introduction aop

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

Catelyn
2015/04/15
0
0

没有更多内容

加载失败,请刷新页面

加载更多

网站优化技术包括哪些内容

网站优化Incapsula超越简单的内容缓存,可以优化网站性能或应用程序的用户体验,优化包括内容缩小、动态文件压缩、图像压缩、会话重用优化、TCP优化和连接预合并。 动态文件压缩,普通的web...

上树的熊
16分钟前
1
0
业界 | Teradata全球调研:四分之三企业分析项目数据科学家“缺货”

当地时间10月15日,2018 Teradata全球用户大会在美国拉斯维加斯举行。来自15个国家的3000多位数据人参与了本次峰会。 大会第一日,Teradata发布了针对“企业数据分析”的2018年调研结果。 调...

Mr_zebra
17分钟前
1
0
java 通过Unsafe不使用构造器直接创建对象

这里有一个User没有无参构造 public class User { public User(String username, String password) { this.username = username; this.password = password; } ......

ValSong
19分钟前
1
0
eureka 高可用配置 unavailable-replicas 问题.

在使用spring cloud 配置eureka 高可用配置时.发现配置的节点一直无法获取心跳. eureka控制台界面上一直显示的挂载节点 是 unavailable-replicas 查看日志.就是获取心跳的地址不对. 默认的健...

拖鞋莫止步
19分钟前
1
0
Vue2 模板template的四种写法

<div id="app">    <h1>我是直接写在构造器里的模板1</h1></div> <template id="demo3">    <h1 style="color:red">我是选项模板3</h1></template> <script type="x-t......

粒子数反转
19分钟前
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部