Spring技术内幕:Spring AOP的实现原理(五)
7、Advice通知的实现
AopProxy代理对象生成时,其拦截器也一并生成。以下我们来分析下Aop是怎样对目标对象进行增强的。在为AopProxy配置拦截器的实现中,有一个取得拦截器配置过程,这个过程由DefaultAvisorChainFactory实现的,而这个工厂类负责生成拦截器链,在它的getInterceptorsAndDynamicInterceptionA-
dvice方法中,有一个适配器的注冊过程。通过配置Spring预先设计好的拦截器,Spring增加了它对Aop实现的处理。
为具体了解这个过程,先从Defau-
ltAdvisorChainFactory的实现開始。通过以下代码能够看到,在DefaultAdvisorChainFactory实现中。首先构造了一个GlobalAdvisorAdapterRegistry单件,然后对配置的Advisor通知器进行逐个遍历,这些通知链都是配置在interceptorNames中的,从getInterceptorsAndDynamicInterceptionAdvice传递进来的advised參数对象中,能够方便的取得配置的通知器,有了这些通知器,接着就是一个由
GlobalAdvisorAdapterRegistry来完毕的拦截器的适配和注冊。
/**
* A simple but definitive way of working out an advice chain for a Method,
* given an {@link Advised} object. Always rebuilds each advice chain;
* caching can be provided by subclasses.
* @author Juergen Hoeller
* @author Rod Johnson
* @author Adrian Colyer
* @since 2.0.3
*/
@SuppressWarnings("serial")
public class DefaultAdvisorChainFactory implements AdvisorChainFactory, Serializable {
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
Advised config, Method method, Class targetClass) {
// This is somewhat tricky... we have to process introductions first,
// but we need to preserve order in the ultimate list.
List<Object> interceptorList = new ArrayList<Object>(config.getAdvisors().length);
boolean hasIntroductions = hasMatchingIntroductions(config, targetClass);
// 得到注冊器GlobalAdvisorAdapterRegistry,这是一个单件模式的实现
AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
for (Advisor advisor : config.getAdvisors()) {
if (advisor instanceof PointcutAdvisor) {
// Add it conditionally.
PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(targetClass)) {
MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
if (MethodMatchers.matches(mm, method, targetClass, hasIntroductions)) {
if (mm.isRuntime()) {
// Creating a new object instance in the getInterceptors() method
// isn't a problem as we normally cache created chains.
for (MethodInterceptor interceptor : interceptors) {
interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm));
}
}
else {
interceptorList.addAll(Arrays.asList(interceptors));
}
}
}
}
else if (advisor instanceof IntroductionAdvisor) {
IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
if (config.isPreFiltered() || ia.getClassFilter().matches(targetClass)) {
Interceptor[] interceptors = registry.getInterceptors(advisor);
interceptorList.addAll(Arrays.asList(interceptors));
}
}
else {
Interceptor[] interceptors = registry.getInterceptors(advisor);
interceptorList.addAll(Arrays.asList(interceptors));
}
}
return interceptorList;
}
GlobalAdvisorAdapterRegistry的getInterceptors方法为AOP的实现做出了非常大的贡献,这种方法封装着advice织入实现的入口。我们先从GlobalAdvisorAdapterRegistry的实现入手,他基本起一个适配器的作用,但同一时候也是单件模式,代码例如以下:
/**
* Keep track of a single instance so we can return it to classes that request it.
*/
// 使用静态变量来保持一个唯一实例
private static AdvisorAdapterRegistry instance = new DefaultAdvisorAdapterRegistry();
/**
* Return the singleton {@link DefaultAdvisorAdapterRegistry} instance.
*/
public static AdvisorAdapterRegistry getInstance() {
return instance;
}
到这里,我们知道在DefaultAdvisorAdapterRegistry中,设置了一系列的adapter适配器,这是这些适配器的实现。为Spring的advice提供了编织能力。以下我们看看DefaultAdvisorAdapterRegistry到底发生了什么?adapter的作用具体分为两个:
1、调用adapter的support方法,通过这种方法来推断取得的advice属于什么类型的advice通知,从而依据不同的advice类型来注冊不同的AdviceInterceptor,也就是前面我们看到的拦截器
2、这些AdviceInterceptor都是Spring AOP框架设计好的,是为实现不同的advice功能提供服务的。
有了这些AdviceInterceptor,能够方便的使用由Spring提供的各种不同的advice来设计AOP应用。也就是说,正是这些AdviceInterceptor终于实现了advice通知在AopProxy对象中的织入功能。
/**
* Default implementation of the {@link AdvisorAdapterRegistry} interface.
* Supports {@link org.aopalliance.intercept.MethodInterceptor},
* {@link org.springframework.aop.MethodBeforeAdvice},
* {@link org.springframework.aop.AfterReturningAdvice},
* {@link org.springframework.aop.ThrowsAdvice}.
*
* @author Rod Johnson
* @author Rob Harrop
* @author Juergen Hoeller
*/
@SuppressWarnings("serial")
public class DefaultAdvisorAdapterRegistry implements AdvisorAdapterRegistry, Serializable {
private final List<AdvisorAdapter> adapters = new ArrayList<AdvisorAdapter>(3);
/**
* Create a new DefaultAdvisorAdapterRegistry, registering well-known adapters.
*/
public DefaultAdvisorAdapterRegistry() {
registerAdvisorAdapter(new MethodBeforeAdviceAdapter());
registerAdvisorAdapter(new AfterReturningAdviceAdapter());
registerAdvisorAdapter(new ThrowsAdviceAdapter());
}
public Advisor wrap(Object adviceObject) throws UnknownAdviceTypeException {
if (adviceObject instanceof Advisor) {
return (Advisor) adviceObject;
}
if (!(adviceObject instanceof Advice)) {
throw new UnknownAdviceTypeException(adviceObject);
}
Advice advice = (Advice) adviceObject;
if (advice instanceof MethodInterceptor) {
// So well-known it doesn't even need an adapter.
return new DefaultPointcutAdvisor(advice);
}
for (AdvisorAdapter adapter : this.adapters) {
// Check that it is supported.
if (adapter.supportsAdvice(advice)) {
return new DefaultPointcutAdvisor(advice);
}
}
throw new UnknownAdviceTypeException(advice);
}
public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {
List<MethodInterceptor> interceptors = new ArrayList<MethodInterceptor>(3);
Advice advice = advisor.getAdvice();
if (advice instanceof MethodInterceptor) {
interceptors.add((MethodInterceptor) advice);
}
for (AdvisorAdapter adapter : this.adapters) {
if (adapter.supportsAdvice(advice)) {
interceptors.add(adapter.getInterceptor(advisor));
}
}
if (interceptors.isEmpty()) {
throw new UnknownAdviceTypeException(advisor.getAdvice());
}
return interceptors.toArray(new MethodInterceptor[interceptors.size()]);
}
public void registerAdvisorAdapter(AdvisorAdapter adapter) {
this.adapters.add(adapter);
}
}
剥茧抽丝,继续看adapter,在DefaultAdvisorRegistry的getInterceptors调用中。从MethodBeforeAdviceAdapter、AfterReturningAdviceAdapter、ThrowsAdviceAdaper这几个通知适配器的名字上能够看出和Advice一一相应,在这里,他们作为适配器被增加到adapter的List中,他们都是实现AdvisorAdapter接口的同一层次的类。仅仅是各自承担着不同的适配的任务,一对一的服务于不同的advice实现。
以MethodBeforeAdviceAdapter为例。代码例如以下:
class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {
public boolean supportsAdvice(Advice advice) {
return (advice instanceof MethodBeforeAdvice);
}
// 把advice从通知器中取出
public MethodInterceptor getInterceptor(Advisor advisor) {
MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice();
return new MethodBeforeAdviceInterceptor(advice);
}
}
到这里就非常清楚了。Spring AOP为了实现advice的织入,设计了特定拦截器对这些功能进行了封装。
尽管应用不会直接用到这些拦截器。但却是advice发挥作用必不可少的准备。还是以MethodBeforeAdviceInterceptor为例,我们看看advice是怎样封装的。
在invoke回调方法中,看到首先触发了advice的before的回调,然后才是MethodInvocation的proceed方法的调用。看到这里。就已经和前面的在ReflectionMethodInvocation的Proceed分析中联系起来。回顾了一下,在AopProxy代理对象触发的ReflectionMethodInvocation的proceed方法中,在取得拦截器以后,启动了对拦截器invoke方法的调用。
依照AOP的规则,ReflectiveMethodInvocation触发的拦截器invoke方法,终于会依据不同的advice类型,触发Spring对不同的advice的拦截器封装,比方对MethodBeforeAdvice,终于会依据不同的advice类型触发Spring对不同的advice的拦截器封装。比方对MethodBeforeAdvice。终于会触发MethodBeforeAdviceInterceptor的invoke方法。在MethodBeforeAdviceInterceptor方法中。会调用advice的before方法。这就是MethodBeforeAdvice所须要的对目标对象的增强效果:在方法调用之前通知增强。
public class MethodBeforeAdviceInterceptor implements MethodInterceptor, Serializable {
private MethodBeforeAdvice advice;
/**
* Create a new MethodBeforeAdviceInterceptor for the given advice.
* @param advice the MethodBeforeAdvice to wrap
*/
// 为指定的Advice创建相应的MethodBeforeAdviceInterceptor对象
public MethodBeforeAdviceInterceptor(MethodBeforeAdvice advice) {
Assert.notNull(advice, "Advice must not be null");
this.advice = advice;
}
// 这个invoke方法是拦截器的回调方法,会在代理对象的方法被调用时触发回调
public Object invoke(MethodInvocation mi) throws Throwable {
this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis() );
return mi.proceed();
}
}
完毕MethodBeforeAdviceInterceptor的调用。然后启动advice通知的afterReturning回调,代码例如以下:
/**
* Create a new AfterReturningAdviceInterceptor for the given advice.
* @param advice the AfterReturningAdvice to wrap
*/
public AfterReturningAdviceInterceptor(AfterReturningAdvice advice) {
Assert.notNull(advice, "Advice must not be null");
this.advice = advice;
}
public Object invoke(MethodInvocation mi) throws Throwable {
Object retVal = mi.proceed();
this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());
return retVal;
}
ThrowsAdvice的实现和上面相似,也是封装在相应的AdviceInterceptor中,ThrowsAdvice的回调方法要复杂一些,他维护了一个exceptionHandlerMap来相应不同的方法调用场景,这个exceptionHandlerMap中的handler的取得时与触发ThrowsAdvice增强的异常相关的。
/**
* Interceptor to wrap an after-throwing advice.
*
* <p>The signatures on handler methods on the {@code ThrowsAdvice}
* implementation method argument must be of the form:<br>
*
* {@code void afterThrowing([Method, args, target], ThrowableSubclass);}
*
* <p>Only the last argument is required.
*
* <p>Some examples of valid methods would be:
*
* <pre class="code">public void afterThrowing(Exception ex)</pre>
* <pre class="code">public void afterThrowing(RemoteException)</pre>
* <pre class="code">public void afterThrowing(Method method, Object[] args, Object target, Exception ex)</pre>
* <pre class="code">public void afterThrowing(Method method, Object[] args, Object target, ServletException ex)</pre>
*
* <p>This is a framework class that need not be used directly by Spring users.
*
* @author Rod Johnson
* @author Juergen Hoeller
*/
public class ThrowsAdviceInterceptor implements MethodInterceptor, AfterAdvice {
private static final String AFTER_THROWING = "afterThrowing";
private static final Log logger = LogFactory.getLog(ThrowsAdviceInterceptor.class);
private final Object throwsAdvice;
/** Methods on throws advice, keyed by exception class */
private final Map<Class, Method> exceptionHandlerMap = new HashMap<Class, Method>();
/**
* Create a new ThrowsAdviceInterceptor for the given ThrowsAdvice.
* @param throwsAdvice the advice object that defines the exception
* handler methods (usually a {@link org.springframework.aop.ThrowsAdvice}
* implementation)
*/
public ThrowsAdviceInterceptor(Object throwsAdvice) {
Assert.notNull(throwsAdvice, "Advice must not be null");
this.throwsAdvice = throwsAdvice;
// 配置ThrowsAdvice回调方法
Method[] methods = throwsAdvice.getClass().getMethods();
for (Method method : methods) {
if (method.getName().equals(AFTER_THROWING) &&
(method.getParameterTypes().length == 1 || method.getParameterTypes().length == 4) &&
Throwable.class.isAssignableFrom(method.getParameterTypes()[method.getParameterTypes().length - 1])
) {
// Have an exception handler
// 配置异常处理
this.exceptionHandlerMap.put(method.getParameterTypes()[method.getParameterTypes().length - 1], method);
if (logger.isDebugEnabled()) {
logger.debug("Found exception handler method: " + method);
}
}
}
if (this.exceptionHandlerMap.isEmpty()) {
throw new IllegalArgumentException(
"At least one handler method must be found in class [" + throwsAdvice.getClass() + "]");
}
}
public int getHandlerMethodCount() {
return this.exceptionHandlerMap.size();
}
/**
* Determine the exception handle method. Can return null if not found.
* @param exception the exception thrown
* @return a handler for the given exception type
*/
private Method getExceptionHandler(Throwable exception) {
Class exceptionClass = exception.getClass();
if (logger.isTraceEnabled()) {
logger.trace("Trying to find handler for exception of type [" + exceptionClass.getName() + "]");
}
Method handler = this.exceptionHandlerMap.get(exceptionClass);
while (handler == null && !exceptionClass.equals(Throwable.class)) {
exceptionClass = exceptionClass.getSuperclass();
handler = this.exceptionHandlerMap.get(exceptionClass);
}
if (handler != null && logger.isDebugEnabled()) {
logger.debug("Found handler for exception of type [" + exceptionClass.getName() + "]: " + handler);
}
return handler;
}
public Object invoke(MethodInvocation mi) throws Throwable {
try {
// 把目标对象方法调用放在try catch中。并在catch中触发。
// ThrowsAdvice的回调,把异常接着向外抛出,不做过多的处理
return mi.proceed();
}
catch (Throwable ex) {
Method handlerMethod = getExceptionHandler(ex);
if (handlerMethod != null) {
invokeHandlerMethod(mi, ex, handlerMethod);
}
throw ex;
}
}
// 通过反射启动对ThrowsAdvice回调方法的调用
private void invokeHandlerMethod(MethodInvocation mi, Throwable ex, Method method) throws Throwable {
Object[] handlerArgs;
if (method.getParameterTypes().length == 1) {
handlerArgs = new Object[] { ex };
}
else {
handlerArgs = new Object[] {mi.getMethod(), mi.getArguments(), mi.getThis(), ex};
}
try {
method.invoke(this.throwsAdvice, handlerArgs);
}
catch (InvocationTargetException targetEx) {
throw targetEx.getTargetException();
}
}
}
未完待续……
Spring技术内幕:Spring AOP的实现原理(五)的更多相关文章
- Spring技术内幕总结 - AOP概述
AOP是Aspect-Oriented Programming(面向方面/切面编程)的简称.Aspect是一种新的模块化机制,用来描述分散在对象.类或函数中的横切关注点.分离关注点使解决特定领域问题的 ...
- Spring技术内幕:SpringIOC原理学习总结
前一段时候我把Spring技术内幕的关于IOC原理一章看完,感觉代码太多,不好掌握,我特意又各方搜集了一些关于IOC原理的资料,特加深一下印象,以便真正掌握IOC的原理. IOC的思想是:Spring ...
- Spring技术内幕:设计理念和整体架构概述(转)
程序员都很崇拜技术大神,很大一部分是因为他们发现和解决问题的能力,特别是线上出现紧急问题时,总是能够快速定位和解决. 一方面,他们有深厚的技术基础,对应用的技术知其所以然,另一方面,在采坑的过程中不断 ...
- 《spring技术内幕》读书笔记(1)——什么是POJO模式
今天在看<spring技术内幕>,第一章中多次提到了使用POJO来完成开发,就百度了一下,在此保留 1. 什么是POJO POJO的名称有多种,pure old java obje ...
- 深入探索spring技术内幕(一): spring概述
一.Spring是什么? Spring是一个开源的控制反转 ( IoC ) 和面向切面 ( AOP ) 的容器框架, 它的主要目的是简化企业开发. 二.控制反转(IoC) 控制反转: 所谓的控制反转就 ...
- 《Spring技术内幕》笔记-Spring的设计理念和总体架构
1.Spring的主要子项目: -1.Spring Framework(Core):Spring项目的核心.提供IoC,AOP,MVC等核心功能. -2.Spring Web Flow ...
- Spring技术内幕:Spring AOP的实现原理(二)
**二.AOP的设计与实现 1.JVM的动态代理特性** 在Spring AOP实现中, 使用的核心技术时动态代理.而这样的动态代理实际上是JDK的一个特性.通过JDK的动态代理特性,能够为随意Jav ...
- Spring技术内幕:Spring AOP的实现原理(三)
生成SingleTon代理对象在getSingleTonInstance方法中完毕,这种方法时ProxyFactoryBean生成AopProxy对象的入口.代理对象会封装对target目标对象的调用 ...
- Spring技术内幕:Spring AOP的实现原理(一)
一.SpringAOP的概述 1.AOP概念 AOP是Aspect-Oriented Programming(面向切面编程)的简称.维基百科的解释例如以下: Aspect是一种新的模块化机制,用来描写 ...
随机推荐
- Java 8 (6) Stream 流 - 并行数据处理与性能
在Java 7之前,并行处理集合非常麻烦.首先你要明确的把包含数据的数据结构分成若干子部分,然后你要把每个子部分分配一个独立的线程.然后,你需要在恰当的时候对他们进行同步来避免竞争,等待所有线程完成. ...
- dubbo与zookeeper学习中的问题
环境: spring5.1.5 dubbo 2.6.2 异常信息: java.lang.NoClassDefFoundError: org/apache/curator/RetryPolicy at ...
- TypeError: string indices must be integers, not str
1. TypeError: string indices must be integers, not str 字符串类型取第index个字符的时候,应该传入int而不是str.如 1 a='abcde ...
- 梦想CAD控件关于标注的系统变量说明
主要用到函数说明: IMxDrawDimension::SetDimVarDouble 设置标注属性的实数类型变量值,详细说明如下: 参数 说明 [in] LONG iType 该属性的类形值 dVa ...
- 新安装数据库sqlserver2008r2,使用javaweb连接不上问题处理
鼠标右键[计算机]-->[管理],打开界面如下: 选择自己数据库的实例名: 选择TCP/IP:右键[属性],将所有TCP动态端口的[0]删掉,TCP端口设为1433:重启服务,即可连接. PS: ...
- 数据库——DBUtils和连接池
第一章 DBUtils如果只使用JDBC进行开发,我们会发现冗余代码过多,为了简化JDBC开发,本案例我们讲采用apache commons组件一个成员:DBUtils.DBUtils就是JDBC的简 ...
- java学习日志--char和int的相互转换
package shugen; /*ASCLL码表 * 48 数字0 * 49 1 * 50 2 * 51 3 * 52 4 * 53 5 * 54 6 * 55 7 * 56 8 * 57 9 */ ...
- Bet(The 2016 ACM-ICPC Asia China-Final Contest 思路题)
题目: The Codejamon game is on fire! Fans across the world are predicting and betting on which team wi ...
- Djang学习笔记-1
1.django的生命周期: url匹配 -> 视图函数 -> 返回用户字符串 url匹配 -> 视图函数 -> 打开一个HTML文件,并读取内容2.创建Django proj ...
- python 基础知识及运算符
可变类型:列表.字典 不可变类型:整形.字符串.元组 标示符: 1.字母数字和下划线组成 2.不能以数字开头 3.区分大小写 4.不能以保留字命名 变量: 1.用描述性的单词命名变量,不要用保留字.汉 ...