设计模式(七):代理模式

设计模式(七):代理模式

目录

概述

为什么我可以去做这件事情但是非要代理类去做?例如办理签证,手续相当复杂,一般会选择把材料寄过去,由别人代理办理然后寄过来,代理的好处自然就是提高效率。、

从代码的角度上来讲应该算是职责的分离已经公共业务的封装。例如spring AOP中的代理模式就将jdbc的连接以及事务的管理交给了代理类处理,使我们只需要关系业务代码,相同的逻辑交给代理类统一封装。

静态代理

场景介绍

  • 首先创建用户接口
public interface UserDao {

    void insertUser();
}
  • 然后创建被代理类
public class UserDaoImpl implements UserDao{
    @Override
    public void insertUser() {
        System.out.println("一条用户数据插入完成");
    }
}
  • 创建代理类,
public class UserDaoProxy implements UserDao{

    private UserDao userDao;

    public UserDaoProxy() {
        //代理的对象很明确,可以在这里直接创建
        this.userDao = new UserDaoImpl();
    }

    @Override
    public void insertUser() {
        System.out.println("代理类正在记录日志,开启事务");
        userDao.insertUser();
        System.out.println("插入成功,提交事务");
    }
}
  • 创建测试类
public class StaticProxyTest {

    public static void main(String[] args) {

        UserDao proxy = new UserDaoProxy();
        proxy.insertUser();
    }
}

总结

和装饰器的区别

写完我的第一反应就是装饰器模式很像啊,

相同点:

  • 同源(实现同一接口)
  • 都对原有类做功能的增强,持有被代理或装饰对象的引用

不同点:

  • 原有类经过装饰后可能会改变原有的功能(覆盖原有的方法,例如前面举例的茶加了果汁改变了原有的味道)。代理更多的是不改变原有的实现,在实现以外的环节做功能的增强。
  • 经过装饰后有更多额外的功能(比如茶加了果汁还提供了维他命)。而代理类的方法签名和被代理类一致。

静态代理的优缺点

优点
  • 在对原有类没有侵入的情况下实现了扩展
缺点
  • 当需要代理同一个类的多个方法时重复代码很多
  • 当需要被代理的类很多时重复代码很多

动态代理

JDK实现动态代理

场景介绍

现在新增需求,执行UserDao所有方法的前后都需要各个打印一句话,比如请求参数和返回结果用于日志记录。这一需求刚好体现了静态代理的不足。

首先写一个包含多个方法的接口,再创建实现类

public interface DynamicUserDao {

    int insert(String name);

    String getName(int id);

    boolean deleteById(int id);

}

public class DynamicUserDaoImpl implements DynamicUserDao{
    @Override
    public int insert(String name) {
        System.out.println("插入成功");
        return 1;
    }

    @Override
    public String getName(int id) {
        System.out.println("查询成功");
        return "user123";
    }

    @Override
    public boolean deleteById(int id) {
        System.out.println("删除成功");
        return true;
    }
}

创建代理类

public class DynamicUserDaoProxy implements InvocationHandler{

    private DynamicUserDao dynamicUserDao;

    public Object getInstance(DynamicUserDao dynamicUserDao) throws Exception{
        this.dynamicUserDao = dynamicUserDao;
        Class<?> clazz = dynamicUserDao.getClass();
        //创建代理类对象
        return Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //开始打印请求参数
        System.out.println("代理类开始打印请求参数");
        for(Object o : args){
            System.out.println("请求参数:"+o);
        }

        Object res = method.invoke(this.dynamicUserDao, args);
        if(null != res){
            System.out.println("打印返回结果:"+res);
        }

        return res;
    }
}

创建测试类

public class DynamicProxyTest {

    public static void main(String[] args) throws Exception {
        DynamicUserDao proxy = (DynamicUserDao) new DynamicUserDaoProxy().getInstance(new DynamicUserDaoImpl());
        
        //通过反编译工具可以查看源代码,jd-gui或者javap -public $Proxy0.class
        byte [] bytes = ProxyGenerator.generateProxyClass("$Proxy0",new Class[]{DynamicUserDao.class});
        try (FileOutputStream os = new FileOutputStream("E://$Proxy0.class")) {
            os.write(bytes);
            os.close();
        }
        
        System.out.println(proxy.getClass());
        //class com.sun.proxy.$Proxy0

        proxy.getName(123);
        /**
         * 代理类开始打印请求参数
         * 请求参数:123
         * 查询成功
         * 打印返回结果:user123
         */
        System.out.println("-----------------");
        proxy.deleteById(123);
        /**
         * 代理类开始打印请求参数
         * 请求参数:123
         * 删除成功
         * 打印返回结果:true
         */
    }
}
总结

优点显而易见,一个动态代理类代理了所有方法。如果代理类传入的是Object对象就可以代理所有对象了。

实现原理

先来查看一下生成的动态代理类

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

import com.fh.iquery.design.proxy.dynamic.DynamicUserDao;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class $Proxy0 extends Proxy implements DynamicUserDao {
    private static Method m1;
    private static Method m4;
    private static Method m5;
    private static Method m2;
    private static Method m3;
    private static Method m0;

    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final int insert(String var1) throws  {
        try {
            return (Integer)super.h.invoke(this, m4, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final boolean deleteById(int var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m5, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final String getName(int var1) throws  {
        try {
            return (String)super.h.invoke(this, m3, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m4 = Class.forName("com.fh.iquery.design.proxy.dynamic.DynamicUserDao").getMethod("insert", Class.forName("java.lang.String"));
            m5 = Class.forName("com.fh.iquery.design.proxy.dynamic.DynamicUserDao").getMethod("deleteById", Integer.TYPE);
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m3 = Class.forName("com.fh.iquery.design.proxy.dynamic.DynamicUserDao").getMethod("getName", Integer.TYPE);
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

动态代理的核心就是Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this)这个方法,实现的核心在于利用字节码重组技术生成了一个新的类com.sun.proxy.$Proxy0,该类继承了java.lang.reflect.Proxy实现了DynamicUserDao。利用反射代理被代理类的所有方法。

CGLIB实现动态代理

先导入两个jar包:asm-3.3.1.jar,cglib-2.2.2.jar

为了实现效果增加一个两个参数的方法,同时修改实现类

public interface DynamicUserDao {

    int insert(String name);

    String getName(int id);

    boolean deleteById(int id);

    //新增方法
    boolean updateById(int id, String name);

}

public class DynamicUserDaoImpl implements DynamicUserDao{
    @Override
    public int insert(String name) {
        System.out.println("插入成功");
        return 1;
    }

    @Override
    public String getName(int id) {
        System.out.println("查询成功");
        return "user123";
    }

    @Override
    public boolean deleteById(int id) {
        System.out.println("删除成功");
        return true;
    }

    @Override
    public boolean updateById(int id, String name) {
        System.out.println(String.format("将编号为[%s]的姓名更新为[%s]", id, name));
        return true;
    }
}

创建代理类

public class CglibProxy implements MethodInterceptor{

    public Object getInstance(Class<?> clazz) throws  Exception{

        Enhancer enhancer = new Enhancer();
        //要把哪个设置为即将生成的新类父类
        enhancer.setSuperclass(clazz);

        enhancer.setCallback(this);

        return  enhancer.create();

    }

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

        //开始打印请求参数
        System.out.println("代理类开始打印请求参数");
        for(int i=0;i<method.getParameterCount();i++){
            System.out.println(String.format("第[%s]个请求参数类型是[%s],对应的值是[%s]", i, method.getParameterTypes()[i], objects[i]));
        }
        Object res = methodProxy.invokeSuper(o,objects);
        if(null != res){
            System.out.println("打印返回结果:"+res);
        }
        return res;
    }
}

创建测试类

public class CglibProxyTest {
    public static void main(String[] args) throws Exception {
        DynamicUserDao proxy = (DynamicUserDao) new CglibProxy().getInstance(DynamicUserDaoImpl.class);
        proxy.updateById(1,"123");
        /**
         * 代理类开始打印请求参数
         * 第[0]个请求参数类型是[int],对应的值是[1]
         * 第[1]个请求参数类型是[class java.lang.String],对应的值是[123]
         * 将编号为[1]的姓名更新为[123]
         * 打印返回结果:true
         */
    }
}
总结

cglib的实现方式是生成的代理类继承了被代理类,同时覆盖了其中所有的方法,所以被代理类中声明为final的方法是不能被代理的

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

JDK动态代理却不受影响

在这里插入图片描述

JDK VS CGLIB

区别
  • JDK生成的代理类继承Proxy实现了被代理的接口,所有被代理的类必须实现某个接口,不能针对类,cglib可以代理类
  • CGLIB基于生成子类继承被代理,覆盖重写被代理类的方法,所以不能够代理声明为final的方法,jdk动态代理却可以(因为不基于继承实现)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值