设计模式——动态代理

152 阅读3分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

静态代理和动态代理的区别

  1. 静态代理需要自己去实现代理类
  2. 动态代理直接使用代理类(JDK、CGlib)

静态代理就是上面结构性模式——代理模式中的代码的体现

动态代理

使用动态代理的原因

  1. 使用代理的原因
  2. 动态代理相对于静态代理不需要有各种冗余的类编写

jdk动态代理

整体的实现基于以下几点:

  1. JDK自带的java.lang.reflect.Proxy
  2. 反射机制
  3. 固定规则InvocationHandler

逻辑实现

  1. 创建基础接口Linux
  2. 创建真是的目标类ServerLinux
  3. 创建动态代理类JdkProxy,在代理类中实现代理的强化逻辑,并在最后调用目标类进行执行
  4. 使用反射机制生成代理对象来进行操作

代码实现

1、2与静态代理一致

  1. 实现JdkProxy
/**
 * @ClassName JdkProxy
 * @Desc jdk动态代理
 * @Author YangMingYu
 * @Date 2021/10/26 4:19 下午
 * @Version 1.0
 **/

/**
 * 1. 实现InvocationHandler接口,及其方法
 * 2. 传入目标类(这里的目标类可以是继承的模式来生成的逻辑)
 * 3. 在invoke中进行代理增强逻辑处理
 */
public class JdkProxy implements InvocationHandler {

    Linux tarjet;

    public JdkProxy(Linux tarjet) {
        this.tarjet = tarjet;
    }


    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        //处理身份验证
        //调用服务器

        String username = String.valueOf(args[0]);
        String password = String.valueOf(args[1]);
        if (checkAuth(username, password)) {
            Object invoke = method.invoke(tarjet, "root", "123456");
            return invoke;
        } else {
            //处理异常
            return "跳板机验证失败";
        }
        //进行一些处理结果的操作
    }

    public Boolean checkAuth(String name, String password) {
        if ("张三".equals(name) && "123".equals(password)) {
            return true;
        }
        return false;
    }

    public static void main(String[] args) {
        ServerLinux serverLinux = new ServerLinux();
        JdkProxy jdkProxy = new JdkProxy(serverLinux);

        Linux linux = (Linux) Proxy.newProxyInstance(jdkProxy.getClass().getClassLoader(), serverLinux.getClass().getInterfaces(), jdkProxy);

        linux.play("张三", "123");

    }
}


CGLIB动态代理

  1. 导入两个包cglib(动态代理)和asm(操作字节码.class文件)

  2. 实现代理逻辑

/**
 * @ClassName CglibProxy
 * @Desc CglibProxy
 * @Author YangMingYu
 * @Date 2021/10/26 4:51 下午
 * @Version 1.0
 **/

/**
 * 1. 倒入cglib和asm包
 * 2. 创建代理类继承MethodInterceptor
 * 3。 实现其中的方法并实现代理的增强业务逻辑
 * 4。 使用Enhancer对象来创建代理类
 */
public class CglibProxy implements MethodInterceptor {

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        String username = String.valueOf(objects[0]);
        String password = String.valueOf(objects[1]);
        if (checkAuth(username, password)) {
            //Master
            String[] strings = new String[]{"root", "123456"};
            Object invokeSuper = methodProxy.invokeSuper(o, strings);
            return invokeSuper;
        } else {
            //处理异常
            return "跳板机验证失败";
        }
    }


    public Boolean checkAuth(String name, String password) {
        if ("张三".equals(name) && "123".equals(password)) {
            return true;
        }
        return false;
    }

    public static void main(String[] args) {

        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(ServerLinux.class);
        enhancer.setCallback(new CglibProxy());
        Linux linux = (Linux) enhancer.create();
        linux.play("张三", "123");

    }

}

cglib与jdk实现动态代理的区别以及一些问题

  1. jdk使用Java内部的反射机制实现
  2. cglib使用asm来加载并操作字节码生成子类实现
  3. jdk代理必须统一接口(实现同一个接口)

如果目标对象实现了接口,默认使用jdk来实现

cglib的效率要比jdk高

Spring会根据目标类是否实现接口来判断使用哪个代理方式

SpringAop中有前置通知、后置通知

主要用来“打印日志”,“权限控制”,“控制层进行线程全局日志的监控”