设计模式——动态代理

设计模式——动态代理

java 设计模式


简介

动态代理是相对就静态代理而言
静态代理是指:代理类通过对被代理类的引用,在代理类中重构被代理对象中需要代理的方法(在调用被代理类的引用对象相应方法的前后,添加我们需要的操作),静态代理对原有代码不会产生任务改变,不会自动生成新的字节码,只需要新建代理类就行,而动态代理会在运行时动态生成代码,取消了对被代理类的扩展限制,遵循开闭原则。
静态代理结构(代码)如下:


    class Student {
    public void study() {
        System.out.println("I am studying");
        }
    }

    class StudentStudyProxy {
    private Student student;

    public StudentStudyProxy(Student student) {
        this.student = student;
    }

    public void study() {
        before();
        student.study();
        after();
    }

    private void before() {
        System.out.println("before study");
    }

    private void after() {
        System.out.println("after study");
    }
}

动态代理

是指可以在代码中通过调用的方式,为任何一个我们想要代理的类,生成代理,而不需要更改原有的代码
动态代理作为spring中极其常见的一种设计模式,可以说贯穿着整个spring 生态,spring 代表作 aop,今天就一起来学习一下,java 的动态代理

动态代理的分类
  • ——基于JDK的实现,主要用到 java.lang.reflect 包里及其之包相关类
  • ——基于CGLib的实现,主要用到 net.sf.cglib.proxy 包里及其之包相关类
JDK动态代理

JDK代理需要被代理类必须要先实现一个接口(前提),代理类需要实现java.lang.reflect.InvocationHandler 接口。

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;

被代理类必须需要先实现一个接口(前提)

public interface Study {
    /**
     * 学习
     */
    public void study();
}

被代理类

public class Student implements Study{
    /**
     * 具体实现
     */
    @Override
    public void study() {
        
    }
}

创建通用的代理类

//泛型T必须是被代理类所实现的接口的类型
public class ObjectProxy implements InvocationHandler {
    Object object;

    /**
     * 通用的jdk动态代理,只需要传入需要被代理类的类类型,这个类必须实现了T
     *
     * @param clazz 需要被代理的类 类型
     * @return
     */
    public <T> T getInstance(Class<? extends T> clazz) throws IllegalAccessException, InstantiationException {
        this.object = clazz.newInstance();
        //获取被对象的 类 类型
        //获取需要被代理对象的所有接口
        Class<?>[] interfaces = clazz.getInterfaces();
        return (T) Proxy.newProxyInstance(clazz.getClassLoader(), interfaces, this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        before();
        Object result = method.invoke(object, args);
        after();
        return result;
    }

    private void before() {
        System.out.println("before");
    }

    private void after() {
        System.out.println("after");
    }
}

代理类的使用

    public static void main(String[] args) throws InstantiationException, IllegalAccessException {
        Study studentProxy = new ObjectProxy().getInstance(Student.class);
        studentProxy.study();
    }

原理分析:

JDK动态代理采用字节码重组,重新生成对象来替代原始对象,以达到动态代理的作用。JDK动态代理生成对象的步骤如下:

                1.获取被代理对象的引用,并且获取它的所有接口(通过反射获取);

                2.JDK动态代理类重新生成一个新的类,在新的类里面实现被代理类实现的所有接口;

                3.动态生成java代码,新加的 after 和 before业务添加到新生成的类中;

                4.编译新生成的java代码,生成class文件;

                5.重新将class文件加载到JVM中运行。

CGLib动态代理

CGLib需要先引入cglib.jar,通过实现方法拦截器去实现动态代理(net.sf.cglib.proxy.MethodInterceptor)
maven pom引入cglib

          <dependency>
                <groupId>cglib</groupId>
                <artifactId>cglib</artifactId>
                <version>3.3.0</version>
            </dependency>

需要被代理的类

class Student {
    public void study() {
        System.out.println("I am studying!!");
    }
}

创建被代理类

class ObjectProxy implements MethodInterceptor {

    /**
     * 根据传入类型,返回同样的类型,因为代理类实际上是创建的一个被代理类的子类
     * @param clazz
     * @param <T>
     * @return
     */
    public <T> T getInstance(Class<T> clazz) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(clazz);
        enhancer.setCallback(this);
        return (T) enhancer.create();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        before();
        Object result = methodProxy.invokeSuper(o, objects);
        after();
        return result;
    }

    private void before() {
        System.out.println("before");
    }

    private void after() {
        System.out.println("after");
    }
}

代理类的使用

    public static void main(String[] args) {
        Student studentProxy = new ObjectProxy().getInstance(Student.class);
        studentProxy.study();
    }

通过上面,我们看到,cglib代理方式,代理类本身不需要实现任何接口,而是cglib新生成一个类,去继承代理类,methodProxy.invokeSuper()去调用父类中的业务逻辑,并返回结果,前后可以加上我们的业务代码。

JDK动态代理和CGLib代理对比

    1.JDK动态代理新生成的类实现了被代理对象所实现的接口,CGLib代理是继承了被代理对象;

    2.JDK动态代理和CGLib代理都是在运行期间生成字节码,JDK动态代理直接写Class字节码,CGLib代理使用ASM框架写Class字节码,CGLib底层更加复杂;

    3.JDK动态代理方法是通过反射机制调用的,而CGLib代理是通过FastClass机制直接调用方法,CGLib理论上执行效率更高。

Spring AOP中的动态代理

在spring aop中提供了两种动态代理方式,那如何进行选择呢?
默认选择使用规则:

        1.当Bean有实现接口时,Spring就会使用JDK动态代理;

        2.当Bean没有实现接口时,Spring就会选择CGLib代理;

        Spring可以通过配置强制使用CGLib代理方式。
posted @ 2021-03-26 13:34  心若向阳花自开  阅读(225)  评论(0编辑  收藏  举报