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

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/chenghan_yang/article/details/102746399

前言

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

应用场景

静态代理

        // 数据准备
        Order order = new Order();order.setUserId(1);
        Money money = new Money();
        // 创建静态代理
        OrderServiceStaticProxy orderServiceStaticProxy = new OrderServiceStaticProxy();
        // 使用静态代理保存订单号操作, 静态代理代理了saveOrder方法
        orderServiceStaticProxy.saveOrder(order);

动态代理

        // 数据准备
        Order order = new Order();order.setUserId(2);
        Money money = new Money();
        // 创建动态代理对象
        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 0;
    }
}

修改后的静态代理应用层代码,跟动态代理 一模一样

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

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

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

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

1.2 代理同一个类,拓展代理多个方法

动态代理的实现:十分方便得代理了一个类的所有方法,设置了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...
        return object;
    }

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

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		if (method.getName().容器.是否有("XXX")) {
			// before after
		} 
        return object;
    }

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

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

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

静态代理 --> instanceof判断避免误用,多次方法拷贝,bind()方法不能确定具体代理哪几个类

    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;
    }
    /**
     * 通过增加方法间接实现代理
     */
    public int update(Money money) {
		
    }

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

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

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

动态代理的优势

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;

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

		private LinkedList<Connection> pool = new LinkedList<>();
		// 建造者模式创造出来的代理类
        proxy = new ConnectionProxy.Builder(connection)
               .buildConnectionPool(this)
               .buildAimClass(Connection.class, MyInterface.class)
               .build();
		// 转化为Connection的对象
        proxyConnection = (Connection) proxy;
        // 直接获取的是
        pool.addLast(proxyConnection);

猜你喜欢

转载自blog.csdn.net/chenghan_yang/article/details/102746399