Java成长之路

为什么Spring能这么牛逼!我们来掰扯看看。

2020-10-26  本文已影响0人  路人甲java

前言

Spring中有个非常重要的知识点——AOP,即面向切面编程,spring中提供的一些非常牛逼的功能都是通过aop实现的,比如

spring中的aop功能主要是通过2种代理来实现的:1、jdk动态代理,2、cglib代理。

JDK动态代理

1.使用方式
第一种简介的创建动态代理的方式,通过重写InvocationHandler#invoke方法

@Test
public void m2() throws Exception {
    // 1\. 创建代理类的处理器
    InvocationHandler invocationHandler = new InvocationHandler() {
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("InvocationHandler被调用的方法是:" + method.getName());
            return null;
        }
    };
    // 2\. 创建代理实例
    IService proxyService = (IService) Proxy.newProxyInstance(IService.class.getClassLoader(), 
        new Class[]{IService.class}, invocationHandler);
    // 3\. 调用代理的方法
    proxyService.m1();
    proxyService.m2();
    proxyService.m3();
}

第二种可以是通过实现InvocationHandler接口,如:

public class CostTimeInvocationHandler implements InvocationHandler {

    private Object target;

    public CostTimeInvocationHandler(Object target){
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        long starTime = System.nanoTime();
        Object result = method.invoke(this.target, args);
        long endTime = System.nanoTime();
        System.out.println(this.target.getClass() + 
                ".m1()方法耗时(纳秒):" + (endTime - starTime));
        return result;
    }

    /**
     * 用来创建targetInterface接口的代理对象
     * @param target          需要被代理的对象
     * @param targetInterface 被代理的接口
     * @param <T>
     * @return
     */
    public static <T> T createProxy(Object target, Class<T> targetInterface) {
        if (!targetInterface.isInterface()) {
            throw new IllegalStateException("targetInterface必须是接口类型!");
        } else if (!targetInterface.isAssignableFrom(target.getClass())) {
            throw new IllegalStateException("target必须是targetInterface接口的实现类!");
        }
        return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(), 
                target.getClass().getInterfaces(), new CostTimeInvocationHandler(target));
    }
}

流程总结和原理

Java中的开发流程一般如下:

1、首先必须要通过实现 InvocationHandler 接口创建自己的调用处理器;这个接口中只定义了一个方法:

Interface InvocationHandler(Object proxy, Method method, Object[] args)

2、创建被代理的接口和类;

3、通过Proxy的静态方法
newProxyInstance(ClassLoaderloader, Class[] interfaces, InvocationHandler h)创建一个代理;

4、通过代理调用方法;

JDK 动态代理具体的原理主要也是在InvocationHandler类和Proxy类中实现:

1、Invocation负责对方法进行包装增强。
动态代理就是要生成一个包装类对象,由于代理的对象是动态的,所以叫动态代理。这个包装类需要我们来实现,但是jdk给出了约束,它必须实现InvocationHandler,该InvocationHandler包含被代理对象,并负责分发请求给被代理对象,分发前后均可以做增强。

2、Proxy负责生成具体的动态代理类
动态代理生成的类型是 $Proxy+数字的“新的类型”,继承自Proxy,且实现了被代理的接口(所以JDK的代理需要有接口)。代理类持有InvocationHandler(继承自Proxy),继承了被代理的接口(能拿到所有的方法信息),在执行每个接口的方法直接会去调用Invocation中增强过的方法。

public final class $Proxy0 extends Proxy implements 被代理的Interface
    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

Cglib增强

使用流程

public class CglibTest {

    @Test
    public void test1() {
        //使用Enhancer来给某个类创建代理类,步骤
        //1.创建Enhancer对象
        Enhancer enhancer = new Enhancer();
        //2.通过setSuperclass来设置父类型,即需要给哪个类创建代理类
        enhancer.setSuperclass(Service1.class);
        /*3.设置回调,需实现org.springframework.cglib.proxy.Callback接口,
        此处我们使用的是org.springframework.cglib.proxy.MethodInterceptor,
        也是一个接口,实现了Callback接口,
        当调用代理对象的任何方法的时候,都会被MethodInterceptor接口的invoke方法处理*/
        enhancer.setCallback(new MethodInterceptor() {
            /**
             * 代理对象方法拦截器
             * @param o 代理对象
             * @param method 被代理的类的方法,即Service1中的方法
             * @param objects 调用方法传递的参数
             * @param methodProxy 方法代理对象
             * @return
             * @throws Throwable
             */
            @Override
            public Object intercept(Object o, Method method, Object[] objects, 
                        MethodProxy methodProxy) throws Throwable {
                System.out.println("调用方法:" + method);
                //可以调用MethodProxy的invokeSuper调用被代理类的方法
                Object result = methodProxy.invokeSuper(o, objects);
                return result;
            }
        });
        //4.获取代理对象,调用enhancer.create方法获取代理对象,
        //这个方法返回的是Object类型的,所以需要强转一下
        Service1 proxy = (Service1) enhancer.create();
        //5.调用代理对象的方法
        proxy.m1();
        proxy.m2();
    }
}

2.CGLIB原理

CGLIB动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。主要工作流程分为4个步骤:

CGLIB创建代理的过程,相当于创建了一个新的类,可以通过CGLIB来配置这个新的类需要实现的接口,以及需要继承的父类

CGLIB可以为类创建代理,但是这个类不能是final类型的,CGLIB为类创建代理的过程,实际上为通过继承来实现的,相当于给需要被代理的类创建了一个子类,然后会重写父类中的方法,来进行增强,继承的特性大家应该都知道,final修饰的类是不能被继承的,final修饰的方法不能被重写,static修饰的方法也不能被重写,private修饰的方法也不能被子类重写,而其他类型的方法都可以被子类重写,被重写的这些方法可以通过CGLIB进行拦截增强

JDK和CGLIB的区别

Java动态代理只能够对接口进行代理,不能对普通的类进行代理(因为所有生成的代理类的父类为Proxy,Java类继承机制不允许多重继承);CGLIB非常强大,不管是接口还是类,都可以来创建代理。

Java动态代理使用Java原生的反射API进行操作,在生成类上比较高效;CGLIB使用ASM框架直接对字节码进行操作,在类的执行过程中比较高效

生成类上,JDK动态代理只能对实现了接口的类生成代理,而不能针对类。CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法

代理的就介绍到这里,spring中很多地方会用到这块,所以大家一定要熟悉,欢迎大家留言交流

Spring AOP

概念

Spring默认使用JDK动态代理,在需要代理类而不是代理接口的时候,Spring会自动切换为使用CGLIB代理,不过现在的项目都是面向接口编程,所以JDK动态代理相对来说用的还是多一些。

最后

如果你看到这里,觉得本文对你有帮助的话,不要吝啬的三连哦!

提莫在这里也还整理了一些平时自己学习的技术文档与群友平时去面试的面试资料。
如果看完后对你有帮助,记得点赞支持一下哦!

Ps:有需要的小伙伴可以点点我,即可领取,免费获取。

面试专题文档。

在这里插入图片描述

技术文档

在这里插入图片描述

真实大厂面经

在这里插入图片描述
上一篇下一篇

猜你喜欢

热点阅读