Spring AOP实现原理(递归拦截器)

摘要:
向现有类6添加新属性或方法。目标:创建新代理对象的过程。编译时编织:仅支持方法级切入点2。Spring实现了AOP 1。经典的基于代理的AOP:之前,返回后,传递后,周围,简介4。Spring AOP工厂1。如果尚未自定义AdvisedSupport通知程序。

一、AOP(Aspect Orient Programming,面向切面编程)相关术语:

Spring AOP实现原理(递归拦截器)第1张

1. 切面(Aspect):实现通用问题的类,例如日志,事务管理,定义了切入点和通知的类,通知和切入点共同组成了切面:时间、地点、做什么

2. 通知(Advice):类似Spring拦截器或者Servlet过滤器,是方法,定义切面要做什么,何时使用,有before,after,around..

3. 连接点(Joinpoint): 程序能够使用通知(Advice)的一个时机,即程序执行过程中明确的点,一般是方法的调用,或异常被抛出

4. 切入点(Pointcut): 定义切面发生在哪里,带了通知的连接点,例如某个类或方法的名称,在程序中主要体现为切入点表达式

5. 引入(Introduction): 向现有的类添加新的属性或方法

6. 目标(Target): 被通知的对象

7. 代理(Proxy):AOP框架创建的对象,代理就是目标对象的增强,Advice + Target = Proxy

8. 织入(Weaving):把切面应用到目标对象时,创建新的代理对象的过程

Spring AOP实现原理(递归拦截器)第2张

a. 编译时织入:AspectJ,静态AOP,生成的字节码融入了增强后的AOP对象,性能更好

b. 加载时织入: Instrument

c. 运行时织入:Spring AOP动态代理,在内存中临时生成一个AOP对象,并在特定的切点做增强处理,仅支持方法级别的切点

二、Spring实现AOP的方式

1. 经典的基于代理的AOP:通过在xml文件配置进行代理

2. 自动代理的AOP:在配置文件中,切点跟通知自动匹配

3. AOP标签配置到切面

4. @AspectJ注解

三、支持的通知类型:Before、After-returning、After-throwing、Around、Introduction

四、Spring AOP工厂

1. 如果AdvisedSupport通知器没有进行自定义设置,默认使用JDK动态代理

2. 如果AdvisedSupport进行了设置,就判断要代理的类是不是接口,接口使用JDK动态代理,否则使用Cglib动态代理

3. JDK动态代理和Cglib动态代理,都是基于 AdvisedSupport 的 MethodInterceptor 链实现的

    @Override
    public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
        if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
            Class<?> targetClass = config.getTargetClass();
            if (targetClass == null) {
                throw new AopConfigException("TargetSource cannot determine target class: " +
                        "Either an interface or a target is required for proxy creation.");
            }
            if (targetClass.isInterface()) {
                return new JdkDynamicAopProxy(config);
            }
            return new ObjenesisCglibAopProxy(config);
        }
        else {
            return new JdkDynamicAopProxy(config);
        }
    }

五、基于JDK动态代理实现的AOP,要去实现InvocationHandler接口,调用invoke()方法

1. Spring会根据AdvisedSupport获取一个方法拦截器MethodInterceptor的链条chain

2. 如果chain为空,直接反射调用原方法

3. 否则,调用proceed()方法,而此方法是一个递归方法

@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 {
            if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
                // The target does not implement the equals(Object) method itself.
                return equals(args[0]);
            }
            if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
                // The target does not implement the hashCode() method itself.
                return hashCode();
            }
            if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
                    method.getDeclaringClass().isAssignableFrom(Advised.class)) {
                // Service invocations on ProxyConfig with the proxy config...
                return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
            }

            Object retVal;

            if (this.advised.exposeProxy) {
                // Make invocation available if necessary.
                oldProxy = AopContext.setCurrentProxy(proxy);
                setProxyContext = true;
            }

            // 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()) {
                // We can skip creating a MethodInvocation: just invoke the target directly
                // Note that the final invoker must be an InvokerInterceptor so we know it does
                // nothing but a reflective operation on the target, and no hot swapping or fancy proxying.
                retVal = AopUtils.invokeJoinpointUsingReflection(target, method, args);
            }
            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();
            }

            // Massage return value if necessary.
            Class<?> returnType = method.getReturnType();
            if (retVal != null && retVal == target && returnType.isInstance(proxy) &&
                    !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
                // Special case: it returned "this" and the return type of the method
                // is type-compatible. Note that we can't help if the target sets
                // a reference to itself in another returned object.
                retVal = proxy;
            }
            else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
                throw new AopInvocationException(
                        "Null return value from advice does not match primitive return type for: " + method);
            }
            return retVal;
        }
        finally {
            if (target != null && !targetSource.isStatic()) {
                // Must have come from TargetSource.
                targetSource.releaseTarget(target);
            }
            if (setProxyContext) {
                // Restore old proxy.
                AopContext.setCurrentProxy(oldProxy);
            }
        }
    }

六、基于Cglib动态代理实现的AOP,也是基于AdvisedSupport实现Callback(),进行方法拦截

private Callback[] getCallbacks(Class<?> rootClass) throws Exception {
        // Parameters used for optimisation choices...
        boolean exposeProxy = this.advised.isExposeProxy();
        boolean isFrozen = this.advised.isFrozen();
        boolean isStatic = this.advised.getTargetSource().isStatic();

        // Choose an "aop" interceptor (used for AOP calls).
        Callback aopInterceptor = new DynamicAdvisedInterceptor(this.advised);

            ..............
}

七、递归拦截器链

1. 所有拦截器都执行完了,才会调用真正的防范,否则就递归调用拦截器

2. Before拦截器,先执行before再执行proceed,是拦截器链的的最后一环

3. After拦截器,先执行proceed再执行after

Spring AOP实现原理(递归拦截器)第3张

八、java.lang.instrument包进行静态代理实现,LTW加载时织入(Load Time Weaving)

1.  在JVM启动时会装配并应用ClassTransformer,对类字节码进行转换,进而实现AOP的功能

2. 使用起来比较麻烦,不推荐使用

九、AspectJ静态代理

参考:

https://www.cnblogs.com/kevin-yuan/p/5571200.html

https://blog.csdn.net/u012707422/article/details/93894174

https://blog.csdn.net/u011983531/article/details/80359304

https://www.jianshu.com/p/012f950206ca

https://blog.csdn.net/u011983531/article/details/49391129

免责声明:文章转载自《Spring AOP实现原理(递归拦截器)》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇mysql 无法退出sql命令行编辑码云客户端Gitee使用1 上传项目下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

JMeter之BeanShell常用内置对象

 一、什么是Bean Shell BeanShell是一种完全符合Java语法规范的脚本语言,并且又拥有自己的一些语法和方法; BeanShell是一种松散类型的脚本语言(这点和JS类似); BeanShell是用Java写成的,一个小型的、免费的、可以下载的、嵌入式的Java源代码解释器,具有对象脚本语言特性,非常精简的解释器jar文件大小为175k。...

[转载]java中Statement详细用法

1、创建 Statement 对象建立了到特定数据库的连接之后,就可用该连接发送 SQL 语句。Statement 对象用 Connection 的方法 createStatement 创建,如下列代码段中所示:Connection con = DriverManager.getConnection(url, "sunny", "");Statement...

如何退出正在Sleep的线程

    今天有个同事问我Thread的Interrupe方法,这个方法用于终止另一个正在等待(Sleep/Wait/Join)状态的线程,如果那个线程未处于等待状态,则等到下次进入等待状态时再抛出。     这个方法的平时用的机会其实并不大,由于需要线程处于等待状态,很大程度上限制了使用的机会,因此问了下同事实际的使用场景,原来是某些线程进入了长时间的Sl...

TableView的使用

            项目现在基本出了一个版本,虽然做的是边缘工作,但是用到的东西还是整理一下吧,毕竟也算学了一点点东西。首先是TableView的使用。RWT是SWT的子集,因此,RWT中可能没有完全实现SWT的全部接口,也没有SWT那么完善。两者的架构不同,表现在显示形式,界面也不尽相同,但是基本的控件的时候还是相同的。这里首先通过SWT来学习一些常...

SSH框架之-hibernate 三种状态的转换

一、遇到的神奇的事情 使用jpa操作数据库,当我使用findAll()方法查处一个List的对象后,给对这个list的实体进行了一些操作,并没有调用update 或者 saveOrUpdate方法,更改后的数据却神奇的保存到数据库里面去了。 最后简单粗暴的解决办法是把这份从数据里面查出来的List 复制了一份,然后再操作,再返回。数据就正常了,数据库也没...

replace into 批量更新

1、.replace into 批量更新     $sql = replace into test_tbl (id,dr) values (1,'2'),(2,'3'),...(x,'y');   test_tbl 为表名 id 对应的主键或者判断的唯一值,最好是主键,这样数据库检索较快 dr 对应这的字段,有多个字段可以在后面加上 2、insert i...