关于java:爱上源码重学Spring-AOP深入

5次阅读

共计 24206 个字符,预计需要花费 61 分钟才能阅读完成。

AOP(Aspect Orient Programming):直译过去就是 面向切面编程。AOP 是一种编程思维

用处:Transactions(事务调用办法前开启事务,调用办法后提交敞开事务)、日志、性能(监控办法运行工夫)、权限管制等

也就是对业务办法做了加强

1.1 Spring AOP 环境介绍

指标:意识 AOP 根底环境,前面讲应用这个根底环境进行源码解说

tips:

沿用 ioC 的工厂

1)引入起步依赖
    compile(project(':spring-aop'))
    compile(project(':spring-context'))
    compile 'org.aspectj:aspectjweaver:1.9.2'
2)新建接口和实现
public interface Slaver {void work();
}
import org.springframework.stereotype.Service;

@Service
public class SlaverImpl implements Slaver {public void work() {System.out.println("进入实现类 work.....");
        try {Thread.sleep(1000);
        } catch (InterruptedException e) {e.printStackTrace();
        }

    }

}
3)新建切面类
package com.spring.test.aop.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.util.Arrays;

// 将这个类申明为一个切面,须要将其放入 IOC 容器中
@Aspect// 申明这是一个切面
@Component// 申明这是一个组件
/**
 * 执行程序
 * @Around 进入盘绕告诉...
 * @Before 进入前置告诉:[]
 * 进入实现类 work.....
 * @Around 办法执行耗时 >>>>>: 1001
 * @After 进入后置告诉...
 * @AfterReturning 进入最终告诉...End!
 */
public class SlaverAspect {

    // 盘绕告诉(连贯到切入点开始执行,下一步进入前置告诉,在下一步才是执行操作方法)@Around(value = "pointCut()")
    public void around(ProceedingJoinPoint joinPoint) throws Throwable {System.out.println("@Around 进入盘绕告诉...");
        long startTime = System.currentTimeMillis();
        joinPoint.proceed();
        long endTime = System.currentTimeMillis();
        System.out.println(String.format("@Around 办法执行耗时 >>>>>: %s", endTime - startTime));
    }

    // 前置告诉(进入盘绕后执行,下一步执行办法)@Before(value = "pointCut()")
    public void before(JoinPoint joinPoint) {System.out.println("@Before 进入前置告诉:" + Arrays.toString(joinPoint.getArgs()));
    }

    // 异样告诉(出错时执行)@AfterThrowing(value = "pointCut()", throwing = "ex")
    public void afterThrowing(JoinPoint joinPoint, Throwable ex) {System.out.println("@AfterThrowing 进入异样告诉" + Arrays.toString(joinPoint.getArgs()));
        System.out.println("@AfterThrowing 异样信息:" + ex);
    }

    // 后置告诉(返回之前执行)
    @After(value = "pointCut()")
    public void after() {System.out.println("@After 进入后置告诉...");
    }

    // 最终告诉(失常返回告诉,最初执行)@AfterReturning(value = "pointCut()")
    public void afterReturning() {System.out.println("@AfterReturning 进入最终告诉...End!");
    }

    // 定义一个切入点 前面的告诉间接引入切入点办法 pointCut 即可
//    @Pointcut("execution(public * com.spring.test.aop.impl.SlaverImpl.work())")
    @Pointcut(value = "execution(* com.spring.test.aop.impl.SlaverImpl.*(..))")
    public void pointCut() {}

}

AspectJ 反对 5 种类型的告诉注解:
@Before: 前置告诉, 在办法执行之前执行
@After: 后置告诉, 在办法执行之后执行
@AfterRunning: 返回告诉, 在办法返回后果之后执行
@AfterThrowing: 异样告诉, 在办法抛出异样之后
@Around: 盘绕告诉, 围绕着办法执行

execution:用于匹配办法执行的连接点;

4)新建配置文件

resources/application-aop.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"
>
    <!-- 使 AspectJ 的注解起作用 -->
    <aop:aspectj-autoproxy/>
    <!-- 扫描带有注解的类,交给 ioc 容器治理 -->
    <context:component-scan base-package="com.spring.test.aop"/>
</beans>

常见谬误

配置文件不足 xsd 的援用

5)新建入口类
public class Main {public static void main(String[] args) {
        ApplicationContext context =
                new ClassPathXmlApplicationContext("classpath*:application-aop.xml");
        Slaver slaver=(Slaver)context.getBean("slaverImpl");
        slaver.work();}

}
6)运行成果

1.2 SpringAOP 和 AspectJ 分割

tips:

十个人有九 集体弄不懂的关系

Spring AOP:

Spring AOP 旨在通过 Spring IoC 提供一个简略的 AOP 实现,以解决编码人员面临的最常呈现的问题。这并不是残缺的 AOP 解决方案,它只能用于 Spring 容器治理的 beans。

AspectJ:

AspectJ 是最原始的 AOP 实现技术,提供了残缺的 AOP 解决方案。AspectJ 更为强壮,绝对于 Spring AOP 也显得更为简单

总结

AOP 是面向切面的一个思维
他有两种实现
1、Spring AOP
2、Aspectj
Spring AOP 的实现没有 AspectJ 弱小
所以,Spring 把 Aspectj 给集成(如果用,还须要独自引 jar)进来了
然而;spring aop 已能满足咱们的需要
在进行开发时候,这两个框架是齐全兼容的

说白了,就是两个框架能一起应用,就看你我的项目需要用到的哪种水平了
简略的;spirng aop 够用了,然而 spring aop 借助了 aspectj 的注解性能,在高级点,比方切面很多,上万个,这是就要用到 aspectj 的高级性能了

区别 :AspectJ 应用的是 编译期和类加载 时进行织入,Spring AOP 利用的是 运行时织入

依赖:如果应用 @Aspect 注解,在 xml 里加上 <aop:aspectj-autoproxy />。然而这须要额定的 jar 包(aspectjweaver.jar)

因为 spring 间接应用 AspectJ 的注解性能,留神只是应用了它 的注解性能而已。并不是外围性能!

运行成果如下:

1.3 找到解决 AOP 的源头

1、Spring 解决 AOP 源头在哪里(织入)

AspectJ 编译期和类加载时进行织入、Spring AOP 利用的是运行时织入

猜测:1. 在容器启动时创立?2. 在 getBean 时创立?

容器启动

2、代理对象到底长什么样?

将断点打在 getBean 的返回对象上,发现这并不是一个咱们定义的对象自身,而是一个 Proxy

接下来,咱们会找到 $Proxy 生成的始末

3、我的接口是怎么被 A0P 治理上的?

com.spring.test.aop.aop.SlaverAspect#pointCut

// 定义一个切入点 前面的告诉间接引入切入点办法 pointCut 即可
    // 参数:// 第一个”*“符号; 示意返回值的类型任意
    //.*(..)示意任何办法名,括号示意参数,两个点示意任何参数类型
    @Pointcut(value = "execution(* com.spring.test.aop.impl.SlaverImpl.*(..))")
    public void pointCut() {System.out.println("@进入切点...");
    }

tips:

当然是 execution 申明,改成 SlaverImpl.aaaa*(..),再来看 getBean 的对象,不再是 proxy

断点一下……

1.4 代理对象是怎么生成的

1、AOP 其实就是用的动静代理模式,创立代理

2、AOP 织入源头在哪里

指标:通过源头找代理对象是如何生成的

​ 找到生成代理的中央 Proxy.newProxyInstance

0)回顾助学

简略回顾一下,留神,这里不仅仅是学习代理模式,还必须搞懂它的实现形式,尤其是动静代理。

开始之前,咱们先必须搞懂一件事件:

那就是:代理模式

设计模式【动态代理】【动静代理】回顾(见第 2 大节)

1)从源头找代理对象生成

记住指标:

spring aop 应用的就是代理模式,那咱们的指标就明确了:找到生成代理的中央 Proxy.newProxyInstance

先来张流程图:

上面咱们沿着图中的调用链,找到 aop 代理诞生的中央

tips:从后置处理器开始

为什么要从后置处理器动手?

很容易了解,没初始化好没法用,等你初始化好了性能完备了,我再下手,代替你

找到后置处理器

重点关注 postProcessAfterInitialization

此处最好应用断点表达式,否则要循环很屡次

因为在 refresh 办法中的 invokeBeanFactoryPostProcessors 办法也会调用到这个中央

断点表达式:

关注点:

tips:

在 BeanPostProcessor 循环中,察看 AnnotationAwareAspectJAutoProxyCreator

这货就是切面的后置处理器

AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsAfterInitialization

    // 指标:循环所有的后置处理器进行调用
    // 留神://AOP 调试,此处最好应用断点表达式,否则要循环很屡次
    // 因为在 refresh 办法中的 invokeBeanFactoryPostProcessors 办法也会调用到这个中央
    @Override
    public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
            throws BeansException {

        Object result = existingBean;
        //aop
        // 此处 getBeanPostProcessors()有 8 个内置后置处理器;生成代理会调用外面的 AnnotationAwareAspectJAutoProxyCreator
        for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
            //Aop 调用 AbstractAutoProxyCreator#postProcessAfterInitialization,
            Object current = beanProcessor.postProcessAfterInitialization(result, beanName);
            if (current == null) {return result;}
            result = current;
        }
        return result;
    }

如上所见

也就是说 AOP 模块是通过实现 BeanPostProcessor 集成进来的

2)进入后置处理器

tips:

aop 这是 spring 内置的一个后置处理器,失效在 postProcessAfterInitialization 办法

AbstractAutoProxyCreator#postProcessAfterInitialization

重点关注 wrapIfNecessary

    // 如果以后的 bean 适宜被代理,则须要包装指定的 bean
    @Override
    public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) throws BeansException {if (bean != null) {
            // 依据给定的 bean 的 class 和 name 构建一个 key
            Object cacheKey = getCacheKey(bean.getClass(), beanName);
            if (!this.earlyProxyReferences.contains(cacheKey)) {

                // 如果以后的 bean 适宜被代理,则须要包装指定的 bean
                return wrapIfNecessary(bean, beanName, cacheKey);
            }
        }
        return bean;
    }

经验 wrapIfNecessary 办法,重点关注点有两个:

1 是:getAdvicesAndAdvisorsForBean,找到哪些切面会作用在以后 bean 上,满足条件的抓进去!

2 是:createProxy,生成代理,代替 slaverImpl 去做事

    // 指标
    //1、判断以后 bean 是否曾经生成过代理对象,或者是否是应该被略过的对象,是则间接返回,否则进行下一步
    //2、拿到切面类中的所有加强办法(拦截器:盘绕、前置、后置等)//3、生成代理对象
    protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
        // 判断是否为空
        // 判断以后 bean 是否在 TargetSource 缓存中存在,如果存在,则间接返回以后 bean
        if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {return bean;}
        // 这里 advisedBeans 缓存了不须要代理的 bean(为 false 的),如果缓存中存在,则能够间接返回
        if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {return bean;}
        //Infrastructure 基础设施
        // 用于判断以后 bean 是否为 Spring 零碎自带的 bean,自带的 bean 是
        // 不必进行代理的;shouldSkip()则用于判断以后 bean 是否应该被略过
        if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
            // 对以后 bean 进行缓存
            this.advisedBeans.put(cacheKey, Boolean.FALSE);
            return bean;
        }
    
    
    
    
    
        //AOP:【关键点 1】反射来过滤,看看哪些 aspect 的 execution 能匹配上以后 bean
        // ===【【【【留神!这货要分两步调试解说,批改切面表达式做比照】】】】====
        // 将 SlaverAspect 的 execution 改成 SlaverImpl.aaa*(..)  试试,你将失去一个空数组!!!// 匹配上的话列出前后和置换的办法(拦截器:盘绕、前置、后置等)Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);

    
    
        // 如果拿到的加强办法不为空
        if (specificInterceptors != DO_NOT_PROXY) {
            // 对以后 bean 的代理状态进行缓存
            this.advisedBeans.put(cacheKey, Boolean.TRUE);
      
      
      
            // 开始创立 AOP 代理
      //  ===【关键点 2】===
            Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
      
      
      
      
            // 缓存生成的代理 bean 的类型,并且返回生成的代理 bean
            this.proxyTypes.put(cacheKey, proxy.getClass());
            // 此处返回的代理和在 Main 函数中返回的是一样的
            // 阐明此处代理胜利创立
            return proxy;
        }
        // 如果拿到的加强办法为空, 缓存起来(应用 false 标记不须要代理)
        this.advisedBeans.put(cacheKey, Boolean.FALSE);
        return bean;
    }
3)开始创立代理对象

重点关注最上面的关键点:proxyFactory.getProxy(getProxyClassLoader())

    //beanClass:指标对象 class
    //beanaName
    //specificInterceptors:拦截器外面的拦挡办法
    //targetSource: 指标资源
    // 指标:开始为 bean 创立代理
    protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
                                 @Nullable Object[] specificInterceptors, TargetSource targetSource) {
        // 为 true,DefaultListableBeanFactory
        if (this.beanFactory instanceof ConfigurableListableBeanFactory) {// 给以后的 bd 设置属性 setAttribute("originalTargetClass",bean 的 class), 进入
            AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
        }
        // 创立一个默认的代理工厂 DefaultAopProxyFactory,父类无参结构器
        ProxyFactory proxyFactory = new ProxyFactory();


        //proxyFactory 通过复制配置进行初始化
        //this 为 AbstractAutoProxyCreator 对象,阐明 AbstractAutoProxyCreator 继承参数理论类型
        proxyFactory.copyFrom(this);
        /**
         * isProxyTargetClass(),默认 false
         * true
         * 指标对象没有接口(只有实现类) – 应用 CGLIB 代理机制
         * false
         * 指标对象实现了接口 – 应用 JDK 代理机制(代理所有实现了的接口)
         */
        if (!proxyFactory.isProxyTargetClass()) {// 来判断 @EnableAspectJAutoProxy 注解或者 XML 的 proxyTargetClass 参数(true 或者 false)
            // 看看用户有没有指定什么形式生成代理
            // 如果没配置就为空,此处返回 false
            if (shouldProxyTargetClass(beanClass, beanName)) {proxyFactory.setProxyTargetClass(true);
            } else {
                // 评估接口的合理性,一些外部回调接口,比方 InitializingBean 等,不会被实现 jdk 代理
                evaluateProxyInterfaces(beanClass, proxyFactory);
            }
        }
        // 把 advice(加强)类型的加强包装成 advisor 类型(强制类型转换)
        Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
        // 退出到代理工厂
        proxyFactory.addAdvisors(advisors);
        // 设置要代理的类(指标类)
        proxyFactory.setTargetSource(targetSource);
        // 子类实现,定制代理
        customizeProxyFactory(proxyFactory);
        // 用来管制代理工厂被设置后是否还容许批改告诉,缺省值为 false
        proxyFactory.setFrozen(this.freezeProxy);
        // 明明是 false?? 此处留神, 他是在子类 AbstractAdvisorAutoProxyCreator 重写了 advisorsPreFiltered 办法
        if (advisorsPreFiltered()) {
            // 设置预过滤
            proxyFactory.setPreFiltered(true);
        }

        //【关键点】通过类加载期获取代理;getProxyClassLoader 为默认的类加载器
        return proxyFactory.getProxy(getProxyClassLoader());
    }

进入 getProxy

// 通过类加载期获取代理
public Object getProxy(@Nullable ClassLoader classLoader) {
   // 别离进入 createAopProxy 和 getProxy
   return createAopProxy().getProxy(classLoader);
}

先看 createAopProxy

查看返回 jdk 代理还是 cglib 代理

public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
        // isOptimize: 是否对代理进行优化
        // isProxyTargetClass: 值为 true,应用 CGLIB 代理,默认 false
        // hasNoUserSuppliedProxyInterfaces:
        //1、如果长度为 0;也就是接口为空,返回 false
        //or(或的关系)
        //2、如果接口类型不是 SpringProxy 类型的;返回 flase

        // 如果条件不满足;间接走 JDK 动静代理(return)
        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.");
            }
            // 如果 targetClass 是接口类,应用 JDK 来生成 Proxy
            //Tips
            // 如果指标对象实现了接口,默认状况下会采纳 JDK 动静代理,// 但也能够通过配置(proxy-target-class=true)强制应用 CGLIB。if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
                //jdk
                return new JdkDynamicAopProxy(config);
            }
            //cglib
            return new ObjenesisCglibAopProxy(config);
        } else {
            //jdk
            return new JdkDynamicAopProxy(config);
        }
    }

再看 getProxy

    // 获取最终的代理对象(由 JDK 生成;运行时织入)@Override
    public Object getProxy(@Nullable ClassLoader classLoader) {if (logger.isDebugEnabled()) {logger.debug("Creating JDK dynamic proxy: target source is" + this.advised.getTargetSource());
        }
        // 获取代理对象须要实现的接口(业务接口和内置接口)Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
        // 判断接口中是否重写了 equals 和 hashCode 办法
        findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);

        /**
         * 第一个参数是类加载器(指标类)* 第二个参数是代理类须要实现的接口,即指标类实现的接口(含零碎接口)(数组)* 第三个是 InvocationHandler,本类实现了此接口
         */
        return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
    }

找到了!代理对象在 bean 初始化阶段拆卸进了 spring

4)代理对象验证

tips:

后面创立实现了代理对象,上面咱们看下它调用的时候,是不是走了代理

这是咱们一开始的后果,上面咱们来再debug 到 work 办法时,点击 debug into 试试,发现调到哪里去了???

论断:

没错!代理对象精准的调用了 JdkDynamicAopProxy 外面的 invoke 办法

这阐明 jdk 动静代理失效,然而它生成的 proxy 字节码在 jvm 里,咱们是看不到的,怎么破?

arthas 上!

2)Arthas 代理类验证

arthas,阿里神器,主页:https://arthas.aliyun.com/zh-cn/

获取之前,咱们须要做点小事件,打出咱们的 slaver 的类门路,让 arthas 能找到它!

源码:

package com.aoptest;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Main {public static void main(String[] args) throws InterruptedException {
        ApplicationContext context =
                new ClassPathXmlApplicationContext("classpath*:application-aop.xml");
        Slaver slaver=(Slaver)context.getBean("slaverImpl");
        slaver.work();
        System.out.printf(slaver.getClass().getName());  // 打印以后对象的类名称:com.sun.proxy.$Proxy17
        Thread.sleep(Integer.MAX_VALUE); // 必须停留在这里,debug 是不行的,arthas 会连不上
    }

}

开启 arthas:

  • 很简略,java -jar arthas-boot.jar
  • 启动后,在 arthas 里执行:jad com.sun.proxy.$Proxy17 反编译咱们的代理类

留神,jad 前面的,换成你上一步控制台打印进去的

见证奇观的时刻……

找到外面的 work 办法,后果它长这样……

那么,this.h 呢?

杀死过程,回到 debug 模式,看看

没错,就是咱们的 jdk 动静代理类!

当初调用关系明确了,接下来,咱们就来剖析下这个 invoke

1.5 代理对象如何调用

1)先看张图

代理对象调用链如下图 (责任链,先有个印象,很长,很长……)

2)了解责任链

代码中给大家筹备了一个责任链小 demo,它就是咱们 spring aop 切面调用链的缩影

spring-aop-test 我的项目下的 com.spring.test.aop.chain 包。

执行外面的 main 办法

生存中的例子,相似于:

  • 全公司站成一队,从队首开始签名

    如果先签完再交给下一个,你就是前置拦挡。(签名 = 执行切面工作)

    如果先交给下一个,等传回来的时候再签,你就实现了后置,回来的时候再补

  • 一个个传到底后,达到队尾,老板盖章(真正的业务逻辑失去执行)
  • 而后一个个往回传(return),后面没签的那些家伙补上(后置切面工作失去执行)
3)invoke 办法源码剖析

通过上大节咱们晓得,代理模式下执行的是以下 invoke

org.springframework.aop.framework.JdkDynamicAopProxy#invoke

代码重点关注

// 2. 从 ProxyFactory(this.advised)中构建拦截器链,蕴含了指标办法的所有切面办法
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass)
        
// 责任链开始调用:ReflectiveMethodInvocation.proceed();重点关注!!!retVal = invocation.proceed();

责任链构建:chain 外面为责任链的具体任务

接下来,咱们具体看看责任链具体的解决逻辑

4)AOP 外围之责任链

思考:

责任链调用,调用的什么?

责任链指标:

AOP 责任链调用流程简图

留神:上图的色彩辨别

彩色:示意正向调用,invoke 的时候,在前或后执行本人的切面逻辑,而后推动责任链往下走

红色:示意以后切面工作触发的点

备注:ExposeInvocationInterceptor 是 spring 帮咱们加上做上下文传递的,本图不波及(它在最后面)

留神,咱们的链条如下:

调试技巧:

从 ReflectiveMethodInvocation.invoke() 开始,对照下面的责任链嵌套调用图

每次通过 debug into 进行查看,每到一环,留神对照图上的节点,看到那个 invocation 了!

案例:第一环,遇 proceed,再 debug into

顺次循环,步步跟进,其乐无穷~~~

2 AOP 根底 – 代理模式(助学)

2.1 设计模式之代理

背景
举个例子
假如咱们想邀请一位明星, 不分割明星
而是分割明星的经纪人, 来达到邀请的的目标
明星就是一个指标对象, 他只有负责流动中的节目, 而其余琐碎的事件就交给他的代理人 (经纪人) 来解决.
这就是代理思维在事实中的一个例子

什么是代理?

代理 (Proxy) 是一种设计模式

提供了对指标对象另外的拜访形式; 即通过 代理对象 拜访 指标对象.

这样做的益处是: 能够在指标对象实现的根底上,加强 额定的性能操作

代理模式分类

1、动态代理

2、动静代理

2.2 动态代理模式

动态代理在应用时:

须要定义接口、指标对象与代理对象

重要特点:

动态代理是由程序员创立或工具生成代理类的源码,再编译代理类。

接口

// 接口
public interface IStartBusiness {
    // 邀请明星唱歌
    void sing();}

指标对象,实现类

// 指标对象,实现类
public class StartBusinessImpl implements IStartBusiness {
    @Override
    public void sing() {System.out.println("sing>>>>>>>>>>>>>>>>>>>>>>>>>>>");
    }
}

代理对象, 动态代理

package com.spring.test.aop.pattern.proxy.staticproxy;

// 代理对象, 动态代理
public class AgentProxy implements IStartBusiness {

    // 代理类持有一个指标类的对象援用
    private IStartBusiness iStartBusiness;
    // 结构注入指标对象
    public AgentProxy(IStartBusiness iStartBusiness) {this.iStartBusiness = iStartBusiness;}

    @Override
    public void sing() {

        //********** 办法前加强 ****************


        //do something
        // 将申请分派给指标类执行;通过注入进入来的指标对象进行拜访
        this.iStartBusiness.sing();

        //do after

        //********** 办法后加强 ****************
    }
}

动态代理测试

package com.spring.test.aop.pattern.proxy.staticproxy;

// 动态代理测试
public class Test {public static void main(String[] args) {
        // 指标对象
        IStartBusiness target = new StartBusinessImpl();
        // 代理对象, 把指标对象传给代理对象, 建设代理关系
        IStartBusiness proxy = new AgentProxy(target);
        // 调用的时候通过调用代理对象的办法来调用指标对象
        proxy.sing();}

}

输入

长处:

能够在被代理办法的执行前或后退出别的代码,实现诸如权限及日志的操作。

毛病:

1、如果接口减少一个办法,除了所有实现类须要实现这个办法外,所有代理类也须要实现此办法

总结:(记住两点)

1、只须要晓得动态代理是在运行前代理类就曾经被织入进去了

2、大规模应用动态代理难以保护(减少办法)

有没有其余的形式能够缩小代码的保护,那就是动静代理?

2.3 动静代理模式

什么是动静代理?

动静代理类的源码是在 程序运行期间由 JVM 依据反射等机制动静织入的

所以;不存在 代理类的字节码文件,间接进了虚拟机。(然而有方法给抓到他)

JDK 中生成代理对象的 API 最重要类和接口有两个,如下

Proxy

InvocationHandler

1)代理父类 Proxy 回顾

所在包:java.lang.reflect.Proxy

这是 Java 动静代理机制生成的所有动静代理类的父类,

提供了一组静态方法来为一组接口动静地生成代理类及其对象。

Proxy 类的静态方法(理解)

// 办法 1: 该办法用于获取指定代理对象所关联的调用处理器  
static InvocationHandler getInvocationHandler(Object proxy)   
  
// 办法 2:该办法用于获取关联于指定类装载器和一组接口的动静代理类的类对象  
static Class getProxyClass(ClassLoader loader, Class[] interfaces)   
  
// 办法 3:该办法用于判断指定类对象是否是一个动静代理类  
static boolean isProxyClass(Class cl)   
  
// 办法 4:该办法用于为指定类装载器、一组接口及调用处理器生成动静代理类实例  
static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)

tips:重要

重点关注 newProxyInstance

这三个参数十分重要

Spring Aop 也是应用这个机制

    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException

留神该办法是在 Proxy 类中是静态方法, 且接管的三个参数顺次为:

  • ClassLoader loader,: 指定以后 指标对象 应用类加载器 ; 负责将类的字节码装载到 Java 虚拟机(JVM)中并为其定义类对象
  • Class<?>[] interfaces,:指标对象 实现的接口的类型, 应用泛型形式确认类型
  • InvocationHandler h: 事件处理, 执行指标对象的办法时, 会触发事件处理器的办法, 会把以后执行指标对象的办法作为参数传入
2)调用处理器接口回顾

java.lang.reflect.InvocationHandler

这是调用处理器接口,它自定义了一个 invoke 办法(只有一个)

用于集中处理在动静代理类对象上的办法调用,通常在该办法中实现对指标类的代理拜访。

每次生成动静代理类对象时都要指定一个对应的调用处理器对象。

1、指标对象(委托类)通过 jdk 的 Proxy 生成了代理对象、

2、客户端拜访代理对象,代理对象通过调用于处理器接口反射调用了指标对象办法

InvocationHandler 的外围办法

仅仅一个办法

public interface InvocationHandler {

// 第一个参数既是代理类实例
 // 第二个参数是被调用的办法对象  
// 第三个办法是调用参数   
Object invoke(Object proxy, Method method, Object[] args) 

}
3)动静代理代码编写

沿用下面的 例子

tips

代理对象不须要实现接口(业务), 然而指标对象肯定要实现接口, 否则不能用动静代理

接口

指标对象接口

package com.spring.test.aop.pattern.proxy.dynamic;
// 接口
public interface IStartBusiness {
    // 邀请明星唱歌
    void sing();}

指标对象

// 指标对象
public class StartBusinessImpl implements IStartBusiness {
    @Override
    public void sing() {System.out.println("sing>>>>>>>>>>>>>>>>>>>>>>>>>>>");
    }
}

创立动静代理对象

package com.spring.test.aop.pattern.proxy.dynamic;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

 // 动静代理实现
public class DynamicProxy implements InvocationHandler {
    // 这个就是咱们要代理的实在对象
    private Object obj;

    // 构造方法,给咱们要代理的实在对象赋初值
    public DynamicProxy(Object object) {this.obj = object;}

    // 相比动态代理,动静代理减只须要实现一个接口即可实现,而动态代理每次都要实现新加的办法以及保护被代理办法
    
    // 第一个参数既是代理类实例
 // 第二个参数是被调用的办法对象  
// 第三个办法是调用参数   
    @Override
    public Object invoke(Object object, Method method, Object[] args)
            throws Throwable {

        //******************** 办法前加强 ***************************
        // 反射调用指标办法
        return method.invoke(obj, args);
    

        //******************** 办法后加强 ***************************
    }

}

测试

package com.spring.test.aop.pattern.proxy.dynamic;

import com.spring.test.aop.pattern.proxy.staticproxy.IStartBusiness;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

// 动静代理测试
// 指标://1、晓得如何创立动静代理
//2、如何调用了 invoke 办法(拦挡办法会用到以后知识点)public class Test {public static void main(String[] args) {
        // 指标对象;要代理的实在对象
        IStartBusiness target = new StartBusinessImpl();
      // 咱们要代理哪个实在对象,就将该对象传进去,最初是通过该实在对象来调用其办法的
        InvocationHandler handler = new DynamicProxy(target);
        /*
         * 第一个参数:指标对象加载器
         * 第二个参数:指标对象接口
         * 第三个参数:实现 InvocationHandler 的代理类
         */

      // 生成代理对象
        IStartBusiness iStartBusiness = (IStartBusiness) Proxy.newProxyInstance(target.getClass().getClassLoader(), target
                .getClass().getInterfaces(), handler);
        iStartBusiness.sing();}
}

输入

总结

1、相比动态代理,动静代理减只须要实现一个接口即可实现,而动态代理每次都要实现新加的办法以及保护被代理办法

2、动静代理是靠 Proxy.newProxyInstance() 生成的

3、动静代理在调用(iStartBusiness.sing())的时候,调用到了 implements InvocationHandler 的 invoke

指标明确了:spring 的代理就须要咱们找到 Proxy.newProxyInstance() 在哪里……

4)动静代理原理(理解)

tips:

波及 JDK 字节码,不在 spring 的源码范畴内,感兴趣的同学本人理解一下

参考资料:https://www.baiyp.ren/JAVA%E5…

代理对象外部生成流程调用链,如下图

对象生成过程(外围)

Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
进入 newProxyInstance 办法外部

java.lang.reflect.Proxy#newProxyInstance

重点关注外围办法 getProxyClass0

 
// 应用 jdk 代理工厂创立新的代理实例
@CallerSensitive
    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
         // 判断是否实现 InvocationHandler 接口,如果此处为空抛出异样
        // 以后 h 十分重要,因为在代理对象调用指标办法的时候,就是通过 d 的 invoke 办法反射调用的指标办法
        // 稍后会解说
        Objects.requireNonNull(h);
        // 克隆参数传来的 接口
        final Class<?>[] intfs = interfaces.clone();
        // 零碎外部的平安
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            // 拜访权限的验证
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }

        /*
         * 查找或生成指定的代理类;十分重要,重点关注
         */
        Class<?> cl = getProxyClass0(loader, intfs);

        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {if (sm != null) {
                // 代理权限的查看(此处是新生产的代理对象 c1)checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }
            // 从生成的代理对象(只是 class 对象,还不是实例化对象)中取出结构器
            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            if (!Modifier.isPublic(cl.getModifiers())) {AccessController.doPrivileged(new PrivilegedAction<Void>() {public Void run() {cons.setAccessible(true);
                        return null;
                    }
                });
            }
            // 应用结构器实例化(实例化对象)return cons.newInstance(new Object[]{h});
        } catch (IllegalAccessException|InstantiationException e) {throw new InternalError(e.toString(), e);
        } catch (InvocationTargetException e) {Throwable t = e.getCause();
            if (t instanceof RuntimeException) {throw (RuntimeException) t;
            } else {throw new InternalError(t.toString(), t);
            }
        } catch (NoSuchMethodException e) {throw new InternalError(e.toString(), e);
        }
    }

进入 getProxyClass0

java.lang.reflect.Proxy#getProxyClass0

    private static Class<?> getProxyClass0(ClassLoader loader,
                                           Class<?>... interfaces) {
        // 判断接口数组大小,别大于 65535,如果大于,提醒接口超出限度
        if (interfaces.length > 65535) {throw new IllegalArgumentException("interface limit exceeded");
        }

        // 通过接口和类加载器创立代理

        // 给定的接口存在,这将简略地返回缓存正本;// 否则,它将通过 ProxyClassFactory 创立代理类          
        return proxyClassCache.get(loader, interfaces);// 也就是查问 + 创立的过程
    }

持续进入 get 办法

java.lang.reflect.WeakCache#get

其余不要看

重点关注 apply 办法

    public V get(K key, P parameter) {Objects.requireNonNull(parameter);

        expungeStaleEntries();

        Object cacheKey = CacheKey.valueOf(key, refQueue);

        // lazily install the 2nd level valuesMap for the particular cacheKey
        ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);
        if (valuesMap == null) {
            ConcurrentMap<Object, Supplier<V>> oldValuesMap
                = map.putIfAbsent(cacheKey,
                                  valuesMap = new ConcurrentHashMap<>());
            if (oldValuesMap != null) {valuesMap = oldValuesMap;}
        }

        // create subKey and retrieve the possible Supplier<V> stored by that
        // subKey from valuesMap
        Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
        Supplier<V> supplier = valuesMap.get(subKey);
        Factory factory = null;

        while (true) {if (supplier != null) {
                // supplier might be a Factory or a CacheValue<V> instance
                V value = supplier.get();
                if (value != null) {return value;}
            }
            // else no supplier in cache
            // or a supplier that returned null (could be a cleared CacheValue
            // or a Factory that wasn't successful in installing the CacheValue)

            // lazily construct a Factory
            if (factory == null) {factory = new Factory(key, parameter, subKey, valuesMap);
            }

            if (supplier == null) {supplier = valuesMap.putIfAbsent(subKey, factory);
                if (supplier == null) {
                    // successfully installed Factory
                    supplier = factory;
                }
                // else retry with winning supplier
            } else {if (valuesMap.replace(subKey, supplier, factory)) {
                    // successfully replaced
                    // cleared CacheEntry / unsuccessful Factory
                    // with our Factory
                    supplier = factory;
                } else {
                    // retry with current supplier
                    supplier = valuesMap.get(subKey);
                }
            }
        }
    }

进入 apply 办法(外部类)

java.lang.reflect.Proxy.ProxyClassFactory#apply

 // 目标
//1、判断;比方以后的 class 是否对加载器可见;拜访权限。是否 public 的
//2、生成 class 字节码文件
//3、调用本地办法;通过 class 字节码文件生成 class 对象

@Override
        public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
            for (Class<?> intf : interfaces) {
                /*
                 * Verify that the class loader resolves the name of this
                 * interface to the same Class object.
                 */
                Class<?> interfaceClass = null;
                try {
                    // 以后的 class 是否对加载器可见
                    interfaceClass = Class.forName(intf.getName(), false, loader);
                } catch (ClassNotFoundException e) { }
                if (interfaceClass != intf) {
                    throw new IllegalArgumentException(intf + "is not visible from class loader");
                }
                /*
                 * 验证类对象是否理论示意接口
                 *  
                 */
                if (!interfaceClass.isInterface()) {
                    throw new IllegalArgumentException(interfaceClass.getName() + "is not an interface");
                }
                /*
                 * 验证此接口不是反复的。*/
                if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
                    throw new IllegalArgumentException("repeated interface:" + interfaceClass.getName());
                }
            }

            String proxyPkg = null;     // package to define proxy class in
            int accessFlags = Modifier.PUBLIC | Modifier.FINAL;

            /*
             * Record the package of a non-public proxy interface so that the
             * proxy class will be defined in the same package.  Verify that
             * all non-public proxy interfaces are in the same package.
             */
            for (Class<?> intf : interfaces) {int flags = intf.getModifiers();
          
                if (!Modifier.isPublic(flags)) {
                    accessFlags = Modifier.FINAL;
                    String name = intf.getName();
                    int n = name.lastIndexOf('.');
                    String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
                    if (proxyPkg == null) {proxyPkg = pkg;} else if (!pkg.equals(proxyPkg)) {
                              // 是否 public 的
                        throw new IllegalArgumentException("non-public interfaces from different packages");
                    }
                }
            }

            if (proxyPkg == null) {
                // 如果没有非公共代理接口,请应用 com.sun.proxy
                proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
            }

            /*
             * 这里须要留神下. 他对应的是代理对象 proxy 后的数值;比方 $proxy100
             * 这里的 100;就是此处原子生成
             */
            long num = nextUniqueNumber.getAndIncrement();
            //$proxy100;十分相熟了;通过断点查看代理对象看到的,就是从这里生成的
            //proxyClassNamePrefix 这个前缀就是 $
            // 这个 proxyPkg 就是 com.sun.proxy
            String proxyName = proxyPkg + proxyClassNamePrefix + num;

            /*
             * 应用 ProxyGenerator 外面的工具类,帮忙咱们生成代理类的 class 内容
             留神。此处存储到 byte 数组;目前还不是 class 对象
             */
            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);
            try {
                // 此处才是真正的 class 对象(留神;尽管是对象;然而还没有实例化)return defineClass0(loader, proxyName,
                                    proxyClassFile, 0, proxyClassFile.length);
            } catch (ClassFormatError e) {
                /*
                 * A ClassFormatError here means that (barring bugs in the
                 * proxy class generation code) there was some other
                 * invalid aspect of the arguments supplied to the proxy
                 * class creation (such as virtual machine limitations
                 * exceeded).
                 */
                throw new IllegalArgumentException(e.toString());
            }
        }
    }

外围代码参看以上正文

断点查看 slaver.work()

        ApplicationContext context =
                new ClassPathXmlApplicationContext("classpath*:application-aop.xml");
        Slaver slaver = (Slaver) context.getBean("slaverImpl");
        // 应用代理调用了 JdkDynamicAopProxy.invoke
        slaver.work();
        System.out.println("over>>>>>>>>>>");

留神外面的 $proxy17, 全部都是在下面生成的

最终这个才是实例化的对象(如上)

如果本文对您有帮忙,欢送 关注 点赞`,您的反对是我保持创作的能源。

转载请注明出处!

正文完
 0