【java_设计模式】动态代理和静态代理的本质区别

38 篇文章 0 订阅
23 篇文章 2 订阅

前言

网上有很多讲动态代理和静态代理的写法作为区分原因,但是都没有讲到本质上
本文主要从应用层自上而下看待这个问题

应用场景

静态代理
        // 数据准备
        Order order = new Order();order.setUserId(1);
        // 创建静态代理
        OrderServiceStaticProxy orderServiceStaticProxy = new OrderServiceStaticProxy();
        // 使用静态代理保存订单号操作, 静态代理代理了saveOrder方法
        orderServiceStaticProxy.saveOrder(order);
动态代理
        // 数据准备
        Order order = new Order();order.setUserId(2);
        // 创建动态代理对象
        IOrderService orderProxy = (IOrderService) new ServiceDynamicProxy(new OrderServiceImpl()).bind();
        // 使用动态代理保存订单号操作
        orderProxy.saveOrder(order);

更多动态代理的应用场景

看代码区别
		// 创建静态代理
        OrderServiceStaticProxy orderServiceStaticProxy = new OrderServiceStaticProxy();
 		// 创建动态代理对象
        IOrderService orderProxy = (IOrderService) new ServiceDynamicProxy(new OrderServiceImpl()).bind();
误区(不完全对): 静态代理不是面向接口编程,证明:

修改静态代理代码

public class StaticProxy {
    private Object target;

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

    public Object bind() {
        return target;
    }

    public int saveOrder(Order order) throws Throwable {
        IOrderService service;
        if (target instanceof IOrderService) {
            service = (IOrderService) target;
        } else {
            throw new RuntimeException("目标类不支持该方法");
        }
		// 省略增强逻辑
        return service.saveOrder(order);
    }
}

修改后的静态代理应用层代码,跟动态代理 一模一样下文将提到一个代理类代理多个被代理类时,静态代理的面向接口特性实质是存在bug的

IOrderService orderProxy = (IOrderService) new StaticProxy(new OrderServiceImpl()).bind();

本质区别 1. 拓展被代理方法的难易程度

1.1 代理同一个类,并且只代理一个方法

动态代理和静态代理的代码量差不多,但是动态代理的效率更低,因为动态代理Proxy.newProxyInstance()会生成class文件开销较大。

1.2 代理同一个类,拓展代理多个方法
静态代理
public class StaticProxy {
    private Object target;

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

    public Object bind() {
        return target;
    }

    public int saveOrder(Order order) throws Throwable {
        IOrderService service;
        if (target instanceof IOrderService) {
            service = (IOrderService) target;
        } else {
            throw new RuntimeException("目标类不支持该方法");
        }
		// 省略增强逻辑
        return service.saveOrder(order);
    }
    
    /**
     * 对代理类新增一个需要被代理的方法,每增加一个方法,就要再写一个同名方法出来
     * 
     */
    public int deleteOrder(Order order) throws Throwable {
        IOrderService service;
        if (target instanceof IOrderService) {
            service = (IOrderService) target;
        } else {
            throw new RuntimeException("目标类不支持该方法");
        }
		// 省略增强逻辑
        return service.deleteOrder(order);
    }
}
动态代理的实现

十分方便得代理了一个类的所有方法,设置了before 和 after 方法。

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        beforeMethod(argObject);
        // 使用反射得到的方法和参数 
        Object object = method.invoke(target, args); // 被代理类的原本逻辑,获得的返回值
        afterMethod();
        return object;
    }

如果只代理一个类的几个方法,有两种实现
第一种

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		if (method.getName().equals("XXX")) {
			// before after
		} else if...
		else {
			Object object = method.invoke(target, args); // 被代理类的原本逻辑,获得的返回值
		}
        return object;
    }

第二种
容器可以写在类里,最好写在配置文件里面

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		if (容器.是否有(method.getName())) {
			// before after
		} else {
			Object object = method.invoke(target, args); // 被代理类的原本逻辑,获得的返回值
		}
        return object;
    }

小结 : 拓展同一个类的多个方法,动态代理远优于静态代理

本质区别 2. 拓展被代理的难易程度

2.1 同一个代理类代理多个被代理类

静态代理 --> instanceof判断避免误用,多次方法拷贝,bind()方法会有Bug

    public Object bind() {
        return target;
    }

 	public int saveOrder(Order order) throws Throwable {
        IOrderService service;
        if (target instanceof IOrderService) { // 需要类型检查
            service = (IOrderService) target;
        } else {
            throw new RuntimeException("目标类不支持该方法");
        }
        return result;
    }

	/**
     * 新增一个被代理类StorageService 
     * 
     */
    public int reduceStorage(Order order) throws Throwable {
        StorageService service;
        if (target instanceof StorageService) {
            service = (StorageService ) target;
        } else {
            throw new RuntimeException("目标类不支持该方法");
        }
		// 省略增强逻辑
        return service.reduceStorage(order);
    }

	IOrderService orderProxy = (IOrderService) new StaticProxy(new OrderServiceImpl()).bind();
	// 有隐患的实现
	StaticProxy staticProxy = (StaticProxy) new StaticProxy(new OrderServiceImpl()).bind();
	staticProxy.reduceStorage(); // 这个语句会暴露给调用者,调用即抛异常

动态代理 --> 根据传参确认需要代理的接口,bind()方法传入的接口即确定了需要代理几个类

    /**
     * 直接通过传入接口的Class对象,增加代理的类
     */
    public Object bind(){
        // 获取代理类的class文件
        Class cls = target.getClass();
        ClassLoader classLoader = cls.getClassLoader();
        Class[] interfaces = cls.getInterfaces(); // 也可以作为参数传入
        return Proxy.newProxyInstance(classLoader, interfaces,this);
    }

以上动态代理返回的代理类,消除了静态代理的隐患。因为动态代理是代理接口,静态代理代理的是具体的类,如果要一个代理,代理两个类,静态代理存在隐患。jdk动态代理消除隐患的前提是,两个类都是接口实现类。如果不是,用cglib的动态代理能补偿jdk动态代理的局限。

   /**
	* invoke 就是所有被代理方法的路由,不用进行instanceof 的合法性校验
	*/
	@Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		if (method.getName().equals("XXX")) {
			// before after
		} else if...
		else {
			Object object = method.invoke(target, args); // 被代理类的原本逻辑,获得的返回值
		}
        return object;
    }

小结 :动态代理能在应用层传入参数的情况下就能做到增加被代理类,业务逻辑的复杂度两者并没有很大的差距,关键在于拓展代理类的能力上,动态代理远优于静态代理

动态代理的优势

1. 仅在业务逻辑中代理单个对象,并增强方法

静态代理实现起来较为复杂,动态代理把代码集中在filter即可完成

 // filter 中拦截评论中的脏话,代理request
 HttpServletRequest proxyInstance = (HttpServletRequest) Proxy.newProxyInstance(
                req.getClass().getClassLoader(),
                new Class[]{HttpServletRequest.class}, // 代理传入servlet的request
                new InvocationHandler() {	// 匿名代理实现
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        if (method.getName().equals("getParameter")) {
                            String value = (String) method.invoke(req, args); // req是filter拦截到的请求
                            // 去除所有空格, 注意返回值
                            value = value.replace(" ", "");
                            // 将脏话按照长度替换成*号, list是存储脏话的集合
                            for (String s : list) {
                                if (value.contains(s)) {
                                    StringBuilder temp = new StringBuilder();
                                    for (int i = 0; i < s.length(); i++) {
                                        temp.append("*");
                                    }
                                    value = value.replace(s, temp.toString());
                                }
                            }
                            return value;
                        }
                        return method.invoke(req, args);
                    }
                });

2. 可以对方法进行深层次的处理

静态代理
无法修改被代理类的方法的内部实现

public class StaticProxy {
    private Connection connection;

    public StaticProxy(Connection connection) {
        this.connection = connection;
    }

    public Object bind(){
        return connection;
    }
    
	/**
	 * 无法在代理内修改close()的内部实现
	 */
    public void close() throws SQLException {
    	beforeMethod();
        connection.close();
        afterMethod();
    }
}

动态代理 --> 提取参数,修改内部实现

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result = null;
        if (method.getName().equals("close")) {
            before(); 
            if(method.getParameterCount() == 2) {
                result = method.invoke(target, args[0], null); // 屏蔽入参
            }
            if(method.getParameterCount() == 3) {
            	// 修改内部实现
              	result = new AnotherInst();
            }
            after(args[args.length - 1]); 
            return result ;
        } else {
            return method.invoke(target, args);
        }
    }

3. 不需要适配器就可以代理整个类

静态代理 --> 代理整个类(数据库连接),代价大
如果要代理MySQL的Connection, 还需要把实现拷贝在代理类中,是个十分繁杂的过程

	// 转化为Connection的对象,要正常使用需要很大的代价,相当于要引入适配器
    proxyConnection = (Connection) proxy;

在这里插入图片描述
动态代理

		// 建造者模式创造出来的代理类
        proxy = new ConnectionProxy.Builder(connection)
               .buildConnectionPool(this)
               // 把自己的接口和Connection接口融合,一起给jdk动态代理代理
               .buildAimClass(Connection.class, MyInterface.class) 
               .build();
		// 转化为Connection的对象,可被用于jdbc的api
        proxyConnection = (Connection) proxy;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值