文档章节

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

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

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

© 著作权归作者所有

共有 人打赏支持
青离
粉丝 273
博文 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

没有更多内容

加载失败,请刷新页面

加载更多

下一页

day58-20180816-流利阅读笔记-待学习

苹果市值破万亿,iPhone 会涨价吗? Lala 2018-08-16 1.今日导读 苹果教父乔布斯曾经说过:“活着就是为了改变世界。”虽然他在 56 岁时就遗憾离世,但他极具创新和变革的精神早已深埋进苹果...

aibinxiao
21分钟前
4
0
[雪峰磁针石博客]python3快速入门教程1 turtle绘图-2函数

菲波那契序列: >>> # Fibonacci series:... # the sum of two elements defines the next... a, b = 0, 1>>> while b < 10:... print(b)... a, b = b, a+b...112......

python测试开发人工智能安全
今天
0
0
java环境变量配置最正确的方式

原贴:https://blog.csdn.net/qq_40007997/article/details/79784711,十分详细,亲测有效

kitty1116
今天
0
0
49.Nginx防盗链 访问控制 解析php相关 代理服务器

12.13 Nginx防盗链 12.14 Nginx访问控制 12.15 Nginx解析php相关配置(502的问题) 12.16 Nginx代理 扩展 502问题汇总 http://ask.apelearn.com/question/9109 location优先级 http://blog....

王鑫linux
今天
2
0
Nginx防盗链、访问控制、解析php相关配置、Nginx代理

一、Nginx防盗链 1. 编辑虚拟主机配置文件 vim /usr/local/nginx/conf/vhost/test.com.conf 2. 在配置文件中添加如下的内容 { expires 7d; valid_referers none blocked server_names *.tes......

芬野de博客
今天
1
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部