AOP-方法拦截器-笔记
方法拦截器的继承层次图:
这些拦截器具体长什么样??
一、MethodBeforeAdviceInterceptor
这个拦截器只有一个属性就是前置通知。需要注意的是前置通知和返回通知的拦截器才会持有的通知的引用,也就是拦截器会有一个属性是前置通知或返回通知。其他三个既是通知又是拦截器。如:AspectJAfterAdvice 既是通知又是拦截器,AspectJAfterThrowingAdvice、AspectJAroundAdvice也同样既是通知又是拦截器。
/**
* Interceptor to wrap am {@link org.springframework.aop.MethodBeforeAdvice}.
* Used internally by the AOP framework; application developers should not need
* to use this class directly.
*
* @author Rod Johnson
*/
@SuppressWarnings("serial")
public class MethodBeforeAdviceInterceptor implements MethodInterceptor, Serializable { private MethodBeforeAdvice advice;//前置通知 /**
* Create a new MethodBeforeAdviceInterceptor for the given advice.
* @param advice the MethodBeforeAdvice to wrap
*///构造器
public MethodBeforeAdviceInterceptor(MethodBeforeAdvice advice) {
Assert.notNull(advice, "Advice must not be null");
this.advice = advice;
}
//注意:前置通知拦截器是在调用proceed()方法之前调用前置通知方法的。而返回通知拦截器是在调用proceed()方法之后才调用返回通知方法的。后置通知也是在proceed()方法之后,但还是它在finally块中,因为后置通知不管是否抛异常都要执行。
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis() );//首先调用前置通知方法。
return mi.proceed();//然后调用proceed()方法。
} }
二、AspectJAfterAdvice
既是通知又是拦截器,所以它不需要持有通知的引用。
/**
* Spring AOP advice wrapping an AspectJ after advice method.
*
* @author Rod Johnson
* @since 2.0
*/
@SuppressWarnings("serial")
public class AspectJAfterAdvice extends AbstractAspectJAdvice
implements MethodInterceptor, AfterAdvice, Serializable { public AspectJAfterAdvice(
Method aspectJBeforeAdviceMethod, AspectJExpressionPointcut pointcut, AspectInstanceFactory aif) { super(aspectJBeforeAdviceMethod, pointcut, aif);
} @Override
public Object invoke(MethodInvocation mi) throws Throwable {
try {
return mi.proceed();//先调用的proceed()方法。
}
finally {
invokeAdviceMethod(getJoinPointMatch(), null, null);//因为是后置通知,所以不管程序有没有抛异常都会执行后置通知方法。所以后置通知方法方法finally中调用。
}
} @Override
public boolean isBeforeAdvice() {
return false;
} @Override
public boolean isAfterAdvice() {
return true;
} }
三、AfterReturningAdviceInterceptor
返回通知拦截器。持有一个返回通知的引用。
/**
* Interceptor to wrap am {@link org.springframework.aop.AfterReturningAdvice}.
* Used internally by the AOP framework; application developers should not need
* to use this class directly.
*
* @author Rod Johnson
*/
@SuppressWarnings("serial")
public class AfterReturningAdviceInterceptor implements MethodInterceptor, AfterAdvice, Serializable { private final AfterReturningAdvice advice;//持有一个返回通知的引用 /**
* 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;
} @Override
public Object invoke(MethodInvocation mi) throws Throwable {
Object retVal = mi.proceed();//先调用proceed()方法,如果proceed()方法抛出了异常那么程序就中断了,无法执行返回通知方法了。
this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());//返回通知方法,上面的proceed()方法抛异常则无法执行。
return retVal;
} }
四、AspectJAfterThrowingAdvice
既是通知又是拦截器。没有持有异常通知的引用。异常通知方法在catch块中执行。
/**
* Spring AOP advice wrapping an AspectJ after-throwing advice method.
*
* @author Rod Johnson
* @since 2.0
*/
@SuppressWarnings("serial")
public class AspectJAfterThrowingAdvice extends AbstractAspectJAdvice
implements MethodInterceptor, AfterAdvice, Serializable { public AspectJAfterThrowingAdvice(
Method aspectJBeforeAdviceMethod, AspectJExpressionPointcut pointcut, AspectInstanceFactory aif) { super(aspectJBeforeAdviceMethod, pointcut, aif);
}
//省略掉一些代码
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
try {
return mi.proceed();
}
catch (Throwable ex) {
if (shouldInvokeOnThrowing(ex)) {
invokeAdviceMethod(getJoinPointMatch(), null, ex);//如果抛出异常,在catch块中调用异常通知方法。
}
throw ex;
}
} //省略掉一些代码。 }
五、AspectJAroundAdvice
既是通知又是拦截器。稍后。。。。。。。。。
/**
* Spring AOP around advice (MethodInterceptor) that wraps
* an AspectJ advice method. Exposes ProceedingJoinPoint.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @since 2.0
*/
@SuppressWarnings("serial")
public class AspectJAroundAdvice extends AbstractAspectJAdvice implements MethodInterceptor, Serializable { public AspectJAroundAdvice(
Method aspectJAroundAdviceMethod, AspectJExpressionPointcut pointcut, AspectInstanceFactory aif) { super(aspectJAroundAdviceMethod, pointcut, aif);
} @Override
public boolean isBeforeAdvice() {
return false;
} @Override
public boolean isAfterAdvice() {
return false;
} @Override
protected boolean supportsProceedingJoinPoint() {
return true;
} @Override
public Object invoke(MethodInvocation mi) throws Throwable {
if (!(mi instanceof ProxyMethodInvocation)) {
throw new IllegalStateException("MethodInvocation is not a Spring ProxyMethodInvocation: " + mi);
}
ProxyMethodInvocation pmi = (ProxyMethodInvocation) mi;
ProceedingJoinPoint pjp = lazyGetProceedingJoinPoint(pmi);
JoinPointMatch jpm = getJoinPointMatch(pmi);
return invokeAdviceMethod(pjp, jpm, null, null);
} /**
* Return the ProceedingJoinPoint for the current invocation,
* instantiating it lazily if it hasn't been bound to the thread already.
* @param rmi the current Spring AOP ReflectiveMethodInvocation,
* which we'll use for attribute binding
* @return the ProceedingJoinPoint to make available to advice methods
*/
protected ProceedingJoinPoint lazyGetProceedingJoinPoint(ProxyMethodInvocation rmi) {
return new MethodInvocationProceedingJoinPoint(rmi);
} }
六、目标方法对应的通知方法拦截器链是如何创建的
当我们调用目标方法时,程序会将调用目标方法的任务转交给JdkDynamicAopProxy的invoke方法来执行。在invoke方法中根据目标方法为其创建拦截器链。也就是这个目标方法一共对应多少个通知方法,为每个通知方法建一个通知,然后将这些通知再包装成拦截器(其实有写通知本身就是拦截器),最后将这些拦截器组成一条拦截器链。拦截器链构建好以后就是执行通知方法和目标方法了。
下面是JdkDynamicAopProxy的invoke方法的代码(这个方法的代码都很重,只是这里讨论拦截器是如何创建的,所以删掉了部分代码):
/**
* Implementation of {@code InvocationHandler.invoke}.
* <p>Callers will see exactly the exception thrown by the target,
* unless a hook method throws an exception.
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
MethodInvocation invocation;
Object oldProxy = null;
boolean setProxyContext = false; TargetSource targetSource = this.advised.targetSource;
Class<?> targetClass = null;
Object target = null; try {
//省略部分代码// May be null. Get as late as possible to minimize the time we "own" the target,
// in case it comes from a pool.
target = targetSource.getTarget();
if (target != null) {
targetClass = target.getClass();
} // Get the interception chain for this method.这行代码执行创建拦截器链的操作,将目标方法和所属对象的类对象作为参数
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass); // Check whether we have any advice. If we don't, we can fallback on direct
// reflective invocation of the target, and avoid creating a MethodInvocation.
if (chain.isEmpty()) { //省略
}
else {
// We need to create a method invocation...
invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
// Proceed to the joinpoint through the interceptor chain.
retVal = invocation.proceed();
} //省略掉部分代码return retVal;
}
finally {
//省略
}
}
上面的代码中调用了AdvisedSupport的方法,如下:
/**
* Determine a list of {@link org.aopalliance.intercept.MethodInterceptor} objects
* for the given method, based on this configuration.
* @param method the proxied method
* @param targetClass the target class
* @return List of MethodInterceptors (may also include InterceptorAndDynamicMethodMatchers)
*/
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, Class<?> targetClass) {
MethodCacheKey cacheKey = new MethodCacheKey(method);
List<Object> cached = this.methodCache.get(cacheKey);//先从缓存里查看目标方法对应的拦截器链是否已经存在,如果不存在则创建拦截器链。
if (cached == null) {
cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(//缓存中没有,所以要创建拦截器链,这个任务交给了AdvisorChainFactory来完成。
this, method, targetClass);
this.methodCache.put(cacheKey, cached);//拦截器链创建好以后放到缓存中以便下次使用
}
return cached;//返回拦截器链。
}
下面看看AdvisorChainFactory是如何创建的拦截器链的。(AdvisorChainFactory是接口,它有一个实现类DefaultAdvisorChainFactory)
/**
* 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 { @Override
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);//创建一个拦截器链,也就是一会儿创建的拦截器会放到这个链条中。
Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());
boolean hasIntroductions = hasMatchingIntroductions(config, actualClass);
AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
//Advisor对象是单例非懒加载的,所以他们在bean工厂初始化的时候就已经实例化好了。每一个Advisor中包含的一个通知和一个切点,
//因此现在要把目标方法对应的所有通知组成拦截器链,可以从Advisor中取出通知,再将通知封装到拦截器中(有些通知本身就是拦截器,以直接转化为拦截器类型就可以)
for (Advisor advisor : config.getAdvisors()) {//循环目标方法对应的所有Advisor对象,将每个Advisor中的通知封装到拦截器中。
if (advisor instanceof PointcutAdvisor) {
// Add it conditionally.
PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
//判断是否预过滤,或是Advisor中的这个通知是否适用于(或者说是否想匹配)目标对象。
if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
//我一开始会奇怪为什么以一个Advisor作为参数却获得一个拦截器数组,不是应该一个Advisor对应一个拦截器吗??
//en。。。。确实是一个Advisor对应一个拦截器。所以它每次返回来的数组都只有一个元素。
MethodInterceptor[] interceptors = registry.getInterceptors(advisor);//标记一
MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
//刚才是判断通知是否跟目标对象匹配,但是跟目标对象匹配不一定跟目标对象中的目标方法匹配呀,所以这里还需要判断是否与目标方法匹配。
if (MethodMatchers.matches(mm, method, actualClass, 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(actualClass)) {
Interceptor[] interceptors = registry.getInterceptors(advisor);
interceptorList.addAll(Arrays.asList(interceptors));
}
}
else {
Interceptor[] interceptors = registry.getInterceptors(advisor);
interceptorList.addAll(Arrays.asList(interceptors));
}
} return interceptorList;//返回拦截器链。
}
看看上述代码中标记一处的代码是如何实现的,这个代码在DefaultAdvisorAdapterRegistry中
@Override
public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {
List<MethodInterceptor> interceptors = new ArrayList<MethodInterceptor>(3);
Advice advice = advisor.getAdvice();//从Advisor中取出通知
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()]);//返回
}
AOP-方法拦截器-笔记的更多相关文章
- spring---aop(3)---Spring AOP的拦截器链
写在前面 时间断断续续,这次写一点关于spring aop拦截器链的记载.至于如何获取spring的拦截器,前一篇博客已经写的很清楚(spring---aop(2)---Spring AOP的JDK动 ...
- 手撸了一个HTTP框架:支持Sprng MVC、IOC、AOP,拦截器,配置文件读取...
https://github.com/Snailclimb/jsoncat :仿 Spring Boot 但不同于 Spring Boot 的一个轻量级的 HTTP 框架 距离上一次给小伙伴们汇报简易 ...
- Node.js与Sails~方法拦截器policies
回到目录 policies sails的方法拦截器类似于.net mvc里的Filter,即它可以作用在controller的action上,在服务器响应指定action之前,对这个action进行拦 ...
- struts2 参数注入 方法拦截器
web.xml: <?xml version="1.0" encoding="UTF-8"?><web-app xmlns:xsi=" ...
- Struts2之类范围拦截器和方法拦截器
1.Struts2拦截器的体系结构 Struts2拦截器最大的特点是其透明性,即用户感觉不到它的存在,但我们在使用Struts2框架时,拦截器时时刻刻都在帮助我们处理很多事情. 包括: 文件上传 表单 ...
- .NET 简易方法拦截器
伟大的无产阶级Willaim曾说过:"无论你觉得自己多么的了不起,也永远有人比你更强".对,我说过!我就是william. 今天想记录一下在项目中遇到的一个比较有意思的东西,异常拦 ...
- Spring Aop、拦截器、过滤器的区别
Filter过滤器:拦截web访问url地址.Interceptor拦截器:拦截以 .action结尾的url,拦截Action的访问.Spring AOP拦截器:只能拦截Spring管理Bean的访 ...
- MethodFilterInterceptor(方法拦截器)配置excludeMethors
由于该类有setExcludeMethods方法,因此在xml中可以配置一个excludeMethods参数 刚开始老是拦截不成功,tomcat显示这个参数没找到,后来终于找到错误:不应该在拦截器栈中 ...
- spring中的多线程aop方法拦截
日常开发中,常用spring的aop机制来拦截方法,记点日志.执行结果.方法执行时间啥的,很是方便,比如下面这样:(以spring-boot项目为例) 一.先定义一个Aspect import org ...
随机推荐
- Java中泛型通配符的一点概念
以List<T>为例,通常如果我们想在List中存储某种类型的数据,我们会用下面的语法进行声明和使用: List<String> allMsg = new ArrayList& ...
- Why we should overwrite the hashCode() when we overwrite the equals()
Preface Though I have used Java programme language for almost a year, I'm not familiar with a notion ...
- 【学习笔记】--- 老男孩学Python,day8 知识点补充 join,列表不能循环删除,深浅copy
1. 补充基础数据类型的相关知识点 1. str. join() 把列表变成字符串 2. 列表不能再循环的时候删除. 因为索引会跟着改变 3. 字典也不能直接循环删除. 把要删除的内容记录在列表中. ...
- js中var、let、const区别
javascript中有三种声明变量的方式:var.let.const 1.var 作用域:全局或局部 var的作用域可以是全局或是局部,以下分四种情况说明: (1).当var关键字声明于函数内时是局 ...
- 【代码笔记】iOS-gif图片播放
一,效果图. 二,工程图. 三,代码. RootViewController.h #import <UIKit/UIKit.h> @interface RootViewController ...
- angular2.0---服务Service,使用服务进行数据处理
1.创建服务 打开命令窗口,cd到项目目录下,输入 ng g service myData1 回车 创建服务,如下图所示: 这样就成功创建了服务,此时,可以在项目的app文件夹下生成了两个serv ...
- 关于session销毁的问题,invalidate() 和removeAttribute()
request.getSession().invalidate(); 销毁当前会话域中的所有属性 request.getSession().removeAttribute("username ...
- react组件直接在document上添加事件
demo:比如组件里有个div写的框框,点击document body的背景色变红,点击div写的框框没效果 componentDidMount(){ document.onclick = this. ...
- JavaSE——转换流和缓冲流
转换流: 类 InputStreamReader(字符输入转换流): InputStream 即读取字节流,Reader 为读取字符流. InputStreamReader将字节流转换成字符流.便于一 ...
- 怎么打印 sql 语句
1.添加jar包 <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api< ...