动态代理/装饰者模式(静态代理)

装饰者模式: 静态代理

1.壳子(装饰者类)必须跟 原本对象 实现同一个接口,保证调用方法 跟以前没有区别 上层不用改
2.装饰者类 必须持有原本对象的引用 (提供构造方法,传入原本实现类),装饰者不是真正逻辑,只是为了给原方法额外增加一些通用型逻辑(例如添加sql事务)
3.不需要增强的就完全直接调用原本实现类
4.需要增强得随便加逻辑

优点:可以无侵入(不修改源代码)增强逻辑

缺点: 需要给每一个需要增强的类手动套壳,
  需要实现接口中的所有抽象方法
  导致类结构爆炸
  
一句话概述:不改变需要装饰(增强)的类的代码.新写一个包装类,给需要装饰的类的方法添加上新的业务代码,不需要装饰的方法则返回原方法.

动态代理:

	实现方式:在java的java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过这个类和这个接口可以生成JDK动态代理类和动态代理对象。
		好处:不需要导入别人的包 
		坏处:要被增强对象必须有个爹(接口)
		
java.lang.reflect.Proxy 类
Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)  

返回一个已经被增强过的对象
	loader:获取类的加载器(需要使用自己的类来获取。不是自己的加载器无法使用:例如使用String类获取的加载器就无法使用)
	interfaces: 获取原本对象的接口数组(接口可以多实现,所以接口可能会有多个)
	InvocationHandler:执行处理类 
		//接口InvocationHandler中实际干活的方法
		Object invoke(Object proxy, Method method, Object[] args)		
		proxy:即将返回的被增强对象,仅在链式编程(下方的didi方法)时使用,其他情况勿用
		method:在调用 需要增强对象(ferrari)的方法(例如run)的时候的方法(method)本身
		args:在调用增强对象(ferrari)的方法(例如run)的时候的方法本身传入的实参
		
一句话概述:不改变需要装饰(增强)的类的原代码.使用jdk原生的proxy类的方法newProxyInstance来创建一个实现类,完成对原方法的逻辑增强,	多态的形式将实现类返回给一个接口对象

一个简单的例子,辅助理解

接口Car

package com.xxx.proxy.wrapper;

public interface Car {
    void run();
    void stop();
    int oilTank();
    void driver(String driverName);
    Car didi();
}

实现类Ferrari

package com.xxx.proxy.wrapper;

public class Ferrari implements Car {

	@Override
	public void run() {
		try {
			Thread.sleep(500);
			System.out.println("法拉利完成百公里加速");
		} catch (Exception e) {
		}
	}

	@Override
	public void stop() {
		System.out.println("法拉利刹车了");
	}

	@Override
	public int oilTank() {
		return 100;
	}

	@Override
	public void driver(String driverName) {
		System.out.println(driverName+"正在驾驶法拉利");
	}

	@Override
	public Car didi() {
		System.out.println("法拉利正在滴滴滴");
		return this;
	}

}

静态代理增强Ferrari

package com.xxx.proxy.wrapper;


/**
 * 装饰者模式: 静态代理
 *
 * 	1.壳子(装饰者类)必须跟 原本对象 实现同一个接口,保证调用方法 跟以前没有区别 上层不用改
 * 	2.装饰者类 必须持有原本对象的引用 (提供构造方法,传入原本实现类),装饰者不是真正逻辑,只是为了给原方法额外增加一些通用型逻辑(例如添加sql事务)
 * 	3.不需要增强的就完全直接调用原本实现类
 * 	4.需要增强得随便加逻辑
 *
 * 	优点:可以无侵入(不修改源代码)增强逻辑
 *
 * 	缺点: 需要给每一个需要增强的类手动套壳,
 * 		  需要实现接口中的所有抽象方法
 * 		  导致类结构爆炸
 */
public class FerrariWrapper implements Car {
	private Car ferrari;
	public FerrariWrapper(Car ferrari) {
		super();
		this.ferrari = ferrari;
	}

	/**
	*	给ferrari的run方法增加一个计时,不改变原代码的情况下,给方法新增了逻辑
	*/
	@Override
	public void run() {
		long start = System.currentTimeMillis();
		ferrari.run();
		long end = System.currentTimeMillis();
		System.out.println("百公里加速用时"+(end-start));
	}

	@Override
	public void stop() {
		ferrari.stop();
	}

	@Override
	public int oilTank() {
		return ferrari.oilTank();
	}

	@Override
	public void driver(String driverName) {
		ferrari.driver(driverName);
	}

	@Override
	public Car didi() {
		return this;
	}
	
}

动态代理增强Ferrari

package com.xxx.proxy.wrapper;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class CarTest {
    public static void main(String[] args) {
        //创建一个实现类对象ferrari
        final Ferrari ferrari = new Ferrari();
        
        /**
         * 套壳 使用java.lang.reflect.Proxy的方法newProxyInstance
         * loader:类加载器  传入一个负责把被增强对象所对应的类加载到内存的类加载 传入原本对象的类加载器 或者接口的类加载器
         * interfaces: 传入原本对象的接口 数组
         * InvocationHandler:执行处理接口
         */
         
        Car proxyFerrari = (Car) Proxy.newProxyInstance(
                //获取类的加载器(需要使用自己的类来获取,不是自己的加载器无法使用:例如使用String类获取的加载器就无法使用)
                CarTest.class.getClassLoader(),
                //获取原本对象的接口数组(接口可以多实现,所以接口可能会有多个)
                new Class[]{Car.class},
                //InvocationHandler接口,执行处理类
                new InvocationHandler() {
                
                    /**
                     * 底层会生成一个实现类
                     * @param proxy 即将返回的被增强对象,仅在链式编程(下方的didi方法)时使用,其他情况勿用
                     * @param method 在调用 需要增强对象(ferrari)的方法(例如run)的时候的方法(method)本身
                     * @param args 在调用增强对象(ferrari)的方法(例如run)的时候的方法本身传入的实参
                     */
                     
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        if ("run".equals(method.getName())) {
                            //原方法无形参,无返回值,使用proxy增加一个逻辑(运行耗时)
                            long start = System.currentTimeMillis();
                            method.invoke(ferrari, args);
                            long end = System.currentTimeMillis();
                            System.out.println("百公里加速耗时:" + (end - start));
                            
                        } else if ("oilTank".equals(method.getName())) {
                            //原方法oilTank有int类型的返回值,使用proxy修改原返回值
                            int oilTank = (int) method.invoke(ferrari, args);
                            return oilTank + 50;
                            
                        } else if ("driver".equals(method.getName())) {
                            //原方法需要形参driverName,使用proxy修改传入的driverName
                            args[0] = "老司机" + args[0];
                            method.invoke(ferrari, args);
                            
                        } else if ("didi".equals(method.getName())) {
                            //为了链式变成,要求方法的返回值是自己
                            System.out.println("增强对象还在运行...");
                            Object invoke = method.invoke(ferrari, args);
                            
                            //返回incoke,链式编程从第二次调用开始就是走原方法的逻辑
							//return invoke;
							
                            //返回proxy,链式编程继续走proxy逻辑
                            return proxy;
                            
                        } else {
                            //不需要修改原方法,直接运行原方法
                            method.invoke(ferrari, args);
                        }
                        return null;
                    }
                }
        );

        System.out.println("================华丽的分割线================");
        proxyFerrari.run();
        System.out.println("================华丽的分割线================");
        System.out.println(proxyFerrari.oilTank());
        System.out.println("================华丽的分割线================");
        proxyFerrari.driver("拓海");
        System.out.println("================华丽的分割线================");
        proxyFerrari.didi().didi();
    }
}

代码运行结果
动态代理/装饰者模式(静态代理)_第1张图片

你可能感兴趣的:(设计模式)