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 ...
随机推荐
- 计算机网络:自顶向下方法(第七版)Wireshark实验指南
这本书的每一章后面都提供了一个Wireshark实验,通过使用Wireshark抓包并手动对包进行分析可以帮助我们更好地理解各种协议和相关知识.然而,这个资源在网上好像很难找,我历经千辛万苦找到之后, ...
- TortoiseGit用户手册
3 配置TortoiseGit 3.1 生成公钥 生成SSH安全密钥,提供给GIT版本库管理员以访问Git 版本库,点击桌面上生成的图标 然后执行执行“ssh-keygen”生成自己的公钥: 一路回车 ...
- Hibernate中查询优化策略
Hibernate查询优化策略 ² 使用延迟加载等方式避免加载多余数据 ² 通过使用连接查询,配置二级缓存.查询缓存等方式减少select语句数目 ² 结合缓存机制,使用iterate()方法减少查询 ...
- Java 如何重写对象的 equals 方法和 hashCode 方法
前言:Java 对象如果要比较是否相等,则需要重写 equals 方法,同时重写 hashCode 方法,而且 hashCode 方法里面使用质数 31.接下来看看各种为什么. 一.需求: 对比两个对 ...
- PHP ServerPush (推送) 技术的探讨[整理]
需求: 我想做个会员站内通知的功能.不想用以前的ajax查询,听说有个推技术.以下文章介绍的不错,来自转载, ============================================= ...
- 移动端h5实现拍照上传图片并预览&webuploader
.移动端实现图片上传并预览,用到h5的input的file属性及filereader对象:经测除了android上不支持多图片上传,其他基本ok实用: 一:先说一下单张图片上传(先上代码): html ...
- c++之stringstream类的用法
简介: 今天利用opecv提取每一帧图片并保存到本地指定目录下的时,对于保存的每一帧的图片希望第几帧体现在图片名中, 这里便用到了stringstream类的将数字转化为字符串这一功能 C++ Str ...
- vue项目部署上线
前言 今天把自己写的demo登录写完了,就想着试着走一下部署上线的流程.参考了很多的文档,终于成功进行了部署.在这里将服务器的搭建和vue项目的 部署上线进行整理(都是基础的知识,希望对大家有帮助.对 ...
- Android使用xml文件中的array资源
Android中有种使用数组的非常简单的用法,在xml文件中获取. 创建数组资源 在value目录下创建arrays.xml文件 然后在arrays.xml文件中使用<string-array& ...
- 带你从零学ReactNative开发跨平台App开发(六)
ReactNative跨平台开发系列教程: 带你从零学ReactNative开发跨平台App开发(一) 带你从零学ReactNative开发跨平台App开发(二) 带你从零学ReactNative开发 ...