SpringAOP进阶(生活例子——>代理模式——>源码分析)

        继前两篇的基础之后,终于来到Spring AOP知识的学习

        在讲AOP之前仍旧先来讲讲其中运用的设计模式————代理设计模式

什么叫代理?为什么要有代理?

生活小例子:

现在很多公寓式租房就是采用代理模式

角色:

房东、租房代理、房客

角色需求:

房东:需要房客入租客房,只想签合同收钱,但房源信息的宣传,打广告等额外的工作较为繁杂

房客:需要了解房源信息,以确保租房体验得到保证

矛盾:房东不想要宣传和打广告这些繁杂的工作,他只希望房客直接来跟他交钱签合同,然后入住就ok了,但是,如果没有宣传和广告这部分工作就会导致房客不能看到相应的房源信息,得不到保障,从而不敢或者不愿意去租房

解决问题者:租房代理

租房代理责任:代替房东,对房源信息进行宣传和做广告,包括一切帮助房客了解房源的工作



最后再来梳理一下每个角色的需求和责任:

房东:负责入住合同的签署和收取房费

代理:负责对房源信息进行宣传和帮助房客了解房源,大型代理还可以帮助房东管理房客入住后的所有事情(合同收钱除外)

房客:通过房源信息广告和实地了解后跟房东签署入住合同

这样上面的矛盾就得到了解决,并且房东和房客都不会有太多的顾虑和多余的繁杂事儿

分析:

根据以上生活实例,租房代理需要调用房东签合同的方法,基于这样的要求,那么我们的代理对象和被代理对象(目标对象)就得实现相同的接口来确保代理对象能够拥有被代理对象(目标对象)的方法,在代理对象的签合同方法中调用被代理对象(目标对象)的签合同方法

从以上的生活小例子中就可以看出代理的重要性,和它存在的意义——————解决房东和房客之间因房源信息而带来的矛盾

可能上面那就话理解有些混乱,现在我们落实到Java代码中去看

JEE开发中主要分为三层:Controller——>Service——>Dao

其中Service层为最重要的一层,因为我们的业务代码就写在该层,一起来看看Service层都包含了哪些代码

Service  = 核⼼功能 (业务代码 ) + 额外功能 ( 附加功能 )
 
1. 核⼼功能
        业务运算
        DAO调⽤
2. 额外功能
       1. 不属于业务
       2. 可有可⽆
       3. 代码量很⼩
 
      额外功能都有哪些:事务、⽇志、性能 ...
 

此时存在一个问题,可以直接将额外功能代码直接书写在Service层中不行吗?为啥还需要将它提出来作为单独的一块?

分析:

service调用者:希望额外功能书写在Service层————————房客

软件设计者:希望Service层只写业务代码,不需要额外功能的代码来混淆业务————————房东

如此就会产生了分歧!————这里类似房东和房客之间因房源信息而产生的矛盾

解决方式:使用代理模式!

代理模式

代理(Proxy)是一种设计模式,提供了对目标对象的另一种(非直接)访问方式

访问者通过代理对象来间接的访问目标对象

优点:实现目标对象业务功能的同时,能增加额外的功能和操作,即扩展目标对象

日常开发思想:不要随意去修改别人已经写好的代码或者方法,如需修改,则可以通过代理的方式来扩展该方法

代理模式的关键点:代理对象和目标对象,代理对象是对目标对象的扩展,并会调用目标对象

代理模式概念:通过代理类,来增加原始类(目标)的额外功能

目标类、原始类:指的是 业务类 (核⼼功能 -->业务运算 DAO调⽤)

目标方法、原始方法:目标类或者原始类中的方法

额外功能、附加功能:日志、事务、性能

代理类 = ⽬标类(原始类) + 额外功能 + 和原始类(⽬标类)实现相同的接⼝

#接口
public interface UserService{

        A(); //A方法
        B(); //B方法

}

#原始类(目标类)————房东

public class UserServiceImpl implements UserService{

        A(){} //A方法
        B(){} //B方法

}

#代理类(额外功能)————————租房代理

public class UserServiceProxy implements UserService{

        A(){} //A方法
        B(){} //B方法

}


好了,到这里位置对代理从生活到代码应该都是解释得明明白白的了,那么现在我们就来深入了解代理设计模式!

代理模式分类

静态代理:为每⼀个原始类,⼿⼯编写⼀个代理类 (.java .class)

动态代理:不需要为每一个代理类手工编写代理类,代理类由第三方的动态字节码技术为创建代理类提供.class字节码模板

扩展:

正常情况下我们创建一个类————在这里指的是代理类

1.从本地或网络加载.calss文件
2.根据.class文件在方法区中创建一个class字节码模板用于提供该类的模板信息(元信息)
3.根据class字节码模板在堆空间中创建一个java.lang.class对象
4.当我们需要实例对象的时候再通过class对象在堆空间中创建相应的实例对象


在这些步骤中,最重要的是我们必须要拿到.class字节码模板,
但是在动态代理中这个字节码模板是由第三方动态字节码技术来提供的——————具体是哪些第三方技术,请往下看!!!!!

理解了理论,接下来解释,上代码!!!!

静态代理:

步骤:
1.提供业务接口
2.编写业务类(目标类)及方法
3.编写代理类,要求和目标类实现相同的接口,并调用目标类的方法
4.调用代理类的方法

1.提供业务接口

public interface UserService {

    public void register();
}

2.编写业务类(目标类)及方法

public class UserServiceImpl  implements UserService {
    @Override
    public void register() {
        System.out.println("我是业务代码!!!");
    }
}

3.编写代理类,要求和目标类实现相同的接口,并调用目标类的方法

public class UserServiceProxy implements UserService {

    private final UserServiceImpl userService = new UserServiceImpl();
    @Override
    public void register() {
        System.out.println("我是额外功能!!!");
        userService.register();
    }
}

4.调用代理类的方法

5.结果

好了,理论和编码都实现了,接下来就是分析

分析:

静态代理

     优点:实现了不修改业务类(目标类)代码的前提下,对业务类进行了增强

     缺点:因为代理类需要与目标类实现相同的接口,所以假如业务类越多,相应的也会有很多代理类,同时,一旦接口增加方法,目标对象与代理对象都要维护

那么怎么解决静态代理带来的问题呢?!

这个时候Spring的动态代理开着它的玛莎拉蒂奔驰而来!

 

先来看看怎么用Spring的动态代理开发:

动态代理:

Spring动态代理编程步骤:
1.引入依赖
2.创建接口和目标类(业务类)
3.编写额外功能类
4.定义切入点
5.组装切面(组装步骤3和步骤4)
6.调用测试
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-aop</artifactId>
  <version>5.1.14.RELEASE</version>
</dependency>
<dependency>
  <groupId>org.aspectj</groupId>
  <artifactId>aspectjrt</artifactId>
  <version>1.8.8</version>
</dependency>
<dependency>
  <groupId>org.aspectj</groupId>
  <artifactId>aspectjweaver</artifactId>
  <version>1.8.3</version>
</dependency>
#准备接口和目标类(业务类)

public interface OrderService {

    void productOder();

    Boolean consumOrder();

}

public class OrderServiceImpl implements OrderService {
    @Override
    public void productOder() {
        System.out.println("下单");
    }

    @Override
    public Boolean consumOrder() {
        System.out.println("消费订单");
        return true;
    }
}

#准备额外功能类

public class OrderExtra1 implements MethodBeforeAdvice {
    @Override
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        System.out.println("我是订单需要的额外功能!!!");
    }
}

#在xml中将目标类和额外功能类交给Spring去管理

<bean id="orderSevice" class="com.xiaozhao.spring.service.impl.OrderServiceImpl"/>

<bean id="orderExtra" class="com.xiaozhao.spring.service.extra.orderExtra1"/>

#设置切入点和组装切面

<aop:config>
   <aop:pointcut id="extra" expression="execution(* *(..))"/> //切入点
   <aop:advisor advice-ref="orderExtra" pointcut-ref="extra"/> //组装
</aop:config>
测试:

ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext.xml");

OrderService orderSevice = (OrderService) ctx.getBean("orderSevice");
orderSevice.productOder();
orderSevice.consumOrder();

分析:

public class orderExtra1 implements MethodBeforeAdvice {
    @Override
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        System.out.println("我是订单需要的额外功能!!!");
    }
}

从结果中我们可以看出MethodBeforeAdvice接⼝的作⽤:

参数解析:

Method method :目标方法(目标类中的方法)

Object[] objects : 目标方法的参数

Object o :目标类(目标对象)

将额外功能书写在MethodBeforeAdvice接口实现类的before方法中,运⾏在原始⽅法执⾏之前,进而增强目标方法,但是这个方法有一个弊端就是只能在原始方法之前加相应的额外功能,如果我们想要在原始方法之前和之后都添加相应的额外功能呢?比如事务就需要在原始方法之前开启事务,在原始方法之后关闭事务,才能保证我们的操作时原子操作,在这样的需求下该接口就不能满足我们的需求了!

我们考虑到了这个弊端,显然Spring的设计者也会想到,所以Spring为我们准备另一个接口,帮助我们解决这个问题

MethodInterceptor(方法拦截器):

@FunctionalInterface
public interface MethodInterceptor extends Interceptor {
    Object invoke(MethodInvocation var1) throws Throwable;
}

它的作用和MethodBeforeAdvice接口一样也是给原始方法添加额外功能,但是区别在于MethodInterceptor可以在原始方法前和后都添加额外功能,具体它是怎么实现的,我们就接着上代码!

#在这里重复的步骤我就不在贴出来了,就是简单的将上面的MethodBeforeAdvice实现添加额外功能中额外功能的代码替换成一下代码:


/**
 * @author : Carson-Zhao
 * @date : 2020/9/16 23:02
 */
public class OrderExtra2 implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {

        System.out.println("原始方法之前!");//原始方法前

        Object ret = methodInvocation.proceed();//原始方法回调

        System.out.println("原始方法之后!");//原始方法后

        return ret;
    }
}


#在xml中修改组装的额外功能

<bean id="orderExtra" class="com.xiaozhao.spring.service.extra.OrderExtra2"/>


#最后调用一下这两个方法

ClassPathXmlApplicationContext ctx =
                new ClassPathXmlApplicationContext("/applicationContext.xml");

        OrderService orderSevice = (OrderService) ctx.getBean("orderSevice");

        orderSevice.productOder();

        System.out.println("我是分割线================");

        orderSevice.consumOrder();


运行结果:

分析:

public class OrderExtra2 implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {

        System.out.println("原始方法之前!");//原始方法前

        Object ret = methodInvocation.proceed();//原始方法回调

        System.out.println("原始方法之后!");//原始方法后

        return ret;
    }
}

在MethodInterceptor接口中有一个方法invoke,方法参数为methodInvocation,使用这个参数的proceed方法能够将原始方法回调(回调的意思就是让原始方法执行),返回的是原始方法的返回值(注意!如果原始方法没有返回值那么这里的ret就是null),我们可以在这行代码之前或之后以及前后添加我们的额外功能代码(事务、日志、监测性能.......等等操作)

扩展:

在使用MethodInterceptor接口来进行额外功能的添加时,如果原始方法抛出异常,在catch块中也是能进行额外功能的添加(场景:异常日志),具体实现就是简单的一个try/catch块,这里就不演示了!

补充:

虽然MethodInterceptor接口比较好用,也比较常见,但是它也并非完美,他有一个不是弊端的弊端,就是在方法回调的过程后,可以改变原始方法的返回值,从而改变原始方法的返回值

/**
 * @author : Carson-Zhao
 * @date : 2020/9/16 23:02
 */
public class OrderExtra2 implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {

        System.out.println("原始方法之前!");
        Object ret = methodInvocation.proceed();
        System.out.println("原始方法之后!");

        return true;//不将原始方法的返回值进行返回,就可以改变原始方法的返回值
        
    }
}

总结:

        相比之下这两个接口MethodBeforeAdvice和MethodInterceptor在实际开发中,常见的就是MethodInterceptor,前者几乎不用,所以在这里前者主要是做了解,后者则是要学会真正的运用!但是MethodInterceptor接口中的invoke方法能够改变原始方法的返回值。

好了,到目前为止,我们只是较为笼统的讲了动态代理的基础使用原理,为了更加深刻的理解Spring动态代理(AOP),再来稍微升华一下,一起来看看底层实现原理

以上例子中得知,Spring动态代理再也不需要我们手动写代理类,只需要专注于业务类的业务处理,对比静态代理来说显然是很大的优点,那么思考一下这个过程中有没有代理类?如果有,AOP是如何创建代理类的?Spring是如何加工代理对象的?

代理类的创建方式:

JDK动态代理:

Spring中动态代理类的相关细节分析:

动态代理类特点:

1.访问修饰符为public,final和非抽象类型的

2.继承了java.lang.reflect.Proxy类,实现了getProxyClass()和newProxyInstance方法

3.有一个public类型的构造方法,该方法有一个InvocationHandler类型的参数

4.当程序调用动态代理实例的方法时,会调用与之关联的InvocationHandler对象的invoke方法

扩展:

1、Proxy类的isProxyClass静态方法可以用来判断指定的类是否为动态代理类

由以上特点可知,动态代理类和java.lang.reflect.Proxy息息相关,那么我们就来看一下Proxy的源码假装分析一波


JDK实现的动态代理:

Object ProxyClass = Proxy.newProxyInstance(classLoader,interfaces,invocationHandler);

分析:

ProxyClass:动态代理类

classLoader:类加载器

interfaces: 原始类实现的接口

invocationHandler:额外功能



为了减省太多干扰信息,我就截取相关重要的部分源码进行强行分析

public class Proxy implements java.io.Serializable {

    //生成代理类实例
    @CallerSensitive
    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        //验证传入的InvocationHandler是否为空
        Objects.requireNonNull(h);
        
        //克隆代理类实现的接口
        final Class<?>[] intfs = interfaces.clone();
        //获得安全管理器
        final SecurityManager sm = System.getSecurityManager();
        //检查创建代理类所需的权限
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }

        /*
         * Look up or generate the designated proxy class.
         * 查找或者生成特定的代理类(如果缓存中存在,则直接获取)
         */
        // 查找缓存或者生成一个特定的代理类对象(缓存中没有对应的代理对象时,就会去生成新的代理对象)—————————关键!!!!
        Class<?> cl = getProxyClass0(loader, intfs);

        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
            //权限校验
            if (sm != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }
            //获取参数类型是InvocationHandler.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;
                    }
                });
            }
            //传入InvocationHandler实例去构造一个代理类的实例,所有代理类都继承自Proxy,而Proxy构造方法需要InvocationHandler实例作为参数
            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);
        }
    }

    /**
     * Generate a proxy class.  Must call the checkProxyAccess method
     * to perform permission checks before calling this.
     */
    private static Class<?> getProxyClass0(ClassLoader loader,
                                           Class<?>... interfaces) {
        if (interfaces.length > 65535) {
            throw new IllegalArgumentException("interface limit exceeded");
        }

        // If the proxy class defined by the given loader implementing
        // the given interfaces exists, this will simply return the cached copy;
        // otherwise, it will create the proxy class via the ProxyClassFactory
        //如果缓存中有实现给定接口的代理类存在,就返回缓存中的副本;否则,将通过ProxyClassFactory创建代理类
        return proxyClassCache.get(loader, interfaces);
    }

    //当缓存中不存在副本时,则由ProxyClassFactory生成
    /**
     * A factory function that generates, defines and returns the proxy class given
     * the ClassLoader and array of interfaces.
     */
    private static final class ProxyClassFactory
        implements BiFunction<ClassLoader, Class<?>[], Class<?>>
    {
        // prefix for all proxy class names 做一个标识所有代理类名称前缀
        private static final String proxyClassNamePrefix = "$Proxy";

        // next number to use for generation of unique proxy class names 用原子类来生成代理类的序号, 保证序号唯一
        private static final AtomicLong nextUniqueNumber = new AtomicLong();

        @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 {
                    interfaceClass = Class.forName(intf.getName(), false, loader);
                } catch (ClassNotFoundException e) {
                }
                //intf是否可以由指定的类加载进行加载
                if (interfaceClass != intf) {
                    throw new IllegalArgumentException(
                        intf + " is not visible from class loader");
                }
                /*
                 * Verify that the Class object actually represents an
                 * interface.//判断intf是不是接口
                 */
                if (!interfaceClass.isInterface()) {
                    throw new IllegalArgumentException(
                        interfaceClass.getName() + " is not an interface");
                }
                /*
                 * Verify that this interface is not a duplicate//验证intf在数组中是不是重复的
                 */
                if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
                    throw new IllegalArgumentException(
                        "repeated interface: " + interfaceClass.getName());
                }
            }
            //生成代理类的包名
            String proxyPkg = null;     // package to define proxy class in
            //代理类的访问标志, 默认是public和final
            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();
                //如果接口的访问标志不是public, 那么生成代理类的包名和接口包名相同
                if (!Modifier.isPublic(flags)) {
                    //生成代理类的访问标志设置改为final
                    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)) {
                        throw new IllegalArgumentException(
                            //代理类如果实现不同包接口, 并且接口都不是public的, 那么就会在这里报错
                            "non-public interfaces from different packages");
                    }
                }
            }

            if (proxyPkg == null) {
                // if no non-public proxy interfaces, use com.sun.proxy package 如果不存在非公正代理包则使用com.sun.proxy package
                proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
            }

            /*
             * Choose a name for the proxy class to generate.
             * 生成代理类的全限定名, 包名+前缀+序号, 例如:com.sun.proxy.$Proxy0
             */
            long num = nextUniqueNumber.getAndIncrement();
            String proxyName = proxyPkg(包名) + proxyClassNamePrefix(类名前缀) + num(序号);

            /*
             * Generate the specified proxy 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());
            }
        }
    }

    //根据指引我们来到generateProxyClass这个方法
    public static byte[] generateProxyClass(final String var0, Class<?>[] var1, int var2) {        
        ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2);//构造ProxyGenerator对象
        final byte[] var4 = var3.generateClassFile();——————————————获取字节码!!!!!——————关键方法
        //如果saveGeneratedFiles为true,则表示需要保存生成的字节码文件,将字节码文件写入磁盘
        if (saveGeneratedFiles) {
            AccessController.doPrivileged(new PrivilegedAction<Void>() {
                public Void run() {
                    try {
                        int var1 = var0.lastIndexOf(46);
                        Path var2;
                        if (var1 > 0) {
                            Path var3 = Paths.get(var0.substring(0, var1).replace('.', File.separatorChar));//路径
                            Files.createDirectories(var3);
                            var2 = var3.resolve(var0.substring(var1 + 1, var0.length()) + ".class");
                        } else {
                            var2 = Paths.get(var0 + ".class");
                        }

                        Files.write(var2, var4, new OpenOption[0]);//写入磁盘
                        return null;
                    } catch (IOException var4x) {
                        throw new InternalError("I/O exception saving generated file: " + var4x);
                    }
                }
            });
        }
        //将字节码数组返回
        return var4;
    }

    //继续跟随以上方法,追溯ProxyGenerator.generateClassFile方法

    private byte[] generateClassFile() {
        //第一步、将所有方法组装成ProxyMethod对象,并为代理类生成toString, hashCode, equals等代理方法
        this.addProxyMethod(hashCodeMethod, Object.class);
        this.addProxyMethod(equalsMethod, Object.class);
        this.addProxyMethod(toStringMethod, Object.class);
        Class[] var1 = this.interfaces;
        int var2 = var1.length;

        int var3;
        Class var4;
        for(var3 = 0; var3 < var2; ++var3) {
            //遍历每一个接口的每一个方法, 并生成ProxyMethod对象
            var4 = var1[var3];
            Method[] var5 = var4.getMethods();
            int var6 = var5.length;

            for(int var7 = 0; var7 < var6; ++var7) {
                Method var8 = var5[var7];
                this.addProxyMethod(var8, var4);
            }
        }

        Iterator var11 = this.proxyMethods.values().iterator();

        List var12;
        while(var11.hasNext()) {
            var12 = (List)var11.next();
            checkReturnTypes(var12);
        }
        //第二步、组装要生成的class文件的所有字段信息和方法信息
        Iterator var15;
        try {
            添加构造器
            this.methods.add(this.generateConstructor());
            var11 = this.proxyMethods.values().iterator();
            //遍历缓存中的代理方法
            while(var11.hasNext()) {
                var12 = (List)var11.next();
                var15 = var12.iterator();

                while(var15.hasNext()) {
                    ProxyGenerator.ProxyMethod var16 = (ProxyGenerator.ProxyMethod)var15.next();
                    //添加代理类的静态字段
                    this.fields.add(new ProxyGenerator.FieldInfo(var16.methodFieldName, "Ljava/lang/reflect/Method;", 10));
                    //添加代理类的代理方法
                    this.methods.add(var16.generateMethod());
                }
            }
            //添加代理类的静态字段初始化方法
            this.methods.add(this.generateStaticInitializer());
        } catch (IOException var10) {
            throw new InternalError("unexpected I/O Exception", var10);
        }

        if (this.methods.size() > 65535) {
            throw new IllegalArgumentException("method limit exceeded");
        } else if (this.fields.size() > 65535) {
            throw new IllegalArgumentException("field limit exceeded");
        } else {
            //第三步、写入最终class文件
              //验证常量池中存在代理类的全限定名
              this.cp.getClass(dotToSlash(this.className));
              //验证常量池中存在代理类父类的全限定名
              this.cp.getClass("java/lang/reflect/Proxy");
              var1 = this.interfaces;
              var2 = var1.length;
  
              //验证常量池存在代理类接口的全限定名
              for(var3 = 0; var3 < var2; ++var3) {
                  var4 = var1[var3];
                  this.cp.getClass(dotToSlash(var4.getName()));
              }
  
              //开始写入文件了,设置常量池只读
              this.cp.setReadOnly();
              ByteArrayOutputStream var13 = new ByteArrayOutputStream();
              DataOutputStream var14 = new DataOutputStream(var13);
  
              try {
                  //1.写入魔数
                  var14.writeInt(-889275714);
                  //2.写入次版本号
                  var14.writeShort(0);
                  //3.写入主版本号
                  var14.writeShort(49);
                  //4.写入常量池
                  this.cp.write(var14);
                  //5.写入访问修饰符
                  var14.writeShort(this.accessFlags);
                  //6.写入类索引
                  var14.writeShort(this.cp.getClass(dotToSlash(this.className)));
                  //7.写入父类索引, 生成的代理类都继承自Proxy
                  var14.writeShort(this.cp.getClass("java/lang/reflect/Proxy"));
                  //8.写入接口计数值
                  var14.writeShort(this.interfaces.length);
                 
                 Class[] var17 = this.interfaces;
                int var18 = var17.length;
 
                 //9.写入接口集合
                 for(int var19 = 0; var19 < var18; ++var19) {
                     Class var22 = var17[var19];
                     var14.writeShort(this.cp.getClass(dotToSlash(var22.getName())));
                 }
                 //10.写入字段计数值
                 var14.writeShort(this.fields.size());
                 var15 = this.fields.iterator();
                 //11.写入字段集合 
                 while(var15.hasNext()) {
                     ProxyGenerator.FieldInfo var20 = (ProxyGenerator.FieldInfo)var15.next();
                     var20.write(var14);
                 }
                 //12.写入方法计数值
                 var14.writeShort(this.methods.size());
                 var15 = this.methods.iterator();
                 //13.写入方法集合
                 while(var15.hasNext()) {
                     ProxyGenerator.MethodInfo var21 = (ProxyGenerator.MethodInfo)var15.next();
                     var21.write(var14);
                 }
                  //14.写入属性计数值, 代理类class文件没有属性所以为0
                 var14.writeShort(0);
                 //转换成二进制数组输出
                 return var13.toByteArray();
             } catch (IOException var9) {
                 throw new InternalError("unexpected I/O Exception", var9);
             }
         }
    }
}

以上代码可能会比较晕乎乎的
    简单做一下总结:

思想:通过类加载器获取类字节码,通过类实现的接口反射获得该类的属性和方法,并生成新的字节码文件

参数分析:

参数一:

类加载器————————创建实例对象所需的类加载器,可借用(前文有提及创建实例对象需要类加载器)

参数二:

原始类实现的接口———————JDK动态代理最重要的就是保证代理类和原始类实现相同的结果,代理类的代理方法中调用原始类的原始方法,所以我们这里需要传一个原始类实现的接口作为参数

参数三:

额外功能——————需要实现InvocationHandler接口的invoke方法

CGLIB动态代理:

CGLIB动态代理原理和JDK动态代理原理有些类似

区别是:

 在Object ProxyClass =Proxy.newProxyInstance(classLoader,SuperClass,invocationHandler)
 这里的时候第二个参数不再是是原始类实现的接口,而是直接传原始类

    原因:代理类不再需要和原始类实现同一个接口去获取方法,而是直接继承原始类来获取原始方法


在这里Spring给我们提供了一个新的对象Enhancer,通过这个对象来set所需的三个参数(类加载器,原始类,额外功能)


例子:


/**
 * @author : Carson-Zhao
 * @date : 2020/9/11 23:44
 */
public class demo {

    public static void main(String[] args) {

        OrderServiceImpl orderService = new OrderServiceImpl();

        Enhancer enhancer = new Enhancer();
        enhancer.setClassLoader(demo.class.getClassLoader());
        enhancer.setSuperclass(orderService.getClass());

        MethodInterceptor interceptor = new MethodInterceptor() {
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {

                System.out.println("额外功能1");
                Object ret = method.invoke(orderService, args);
                System.out.println("额外功能2");
                return ret;
            }
        };
        enhancer.setCallback(interceptor);

        OrderService orderServiceProxy = (OrderService) enhancer.create();
        orderServiceProxy.productOder();
        orderServiceProxy.consumOrder();

    }
}

以上代码的运行结果:

总结:

AOP创建动态代理的方式有两种:

1.JDK动态代理

2.CGLIB动态代理

两者区别:

JDK是以代理类和原始类实现相同接口来实现的调用原始类的原始方法,CGLIB则是以代理类实现原始类的方式来实现对原始类中原始方法的调用

JDK————实现原始类实现的相同接口

CGLIB————继承原始类

唠叨:

到这里就差不多将AOP讲清楚了,有基础的,也有深一点点的,又梳理了一遍知识点也算是巩固吧,希望看到文章的胖友一起共勉

计划:

下一篇计划是学习对RedisTemplate的二次封装,主要是用于我们的日常开发

最后:

每个牛逼的人都有一段苦逼的岁月,但是只要你像SB一样的去坚持,终将牛逼!

猜你喜欢

转载自blog.csdn.net/weixin_43562937/article/details/108433077
今日推荐