设计模式java-04.代理模式

java的三种代理模式
代理模式提供了对目标对象的另外的访问方式,即通过代理对象访问目标对象。这样做的好处是尅在目标对象实现的基础上,增强额外的操作功能,即扩张目标对象的功能。
举个栗子来说明代理的作用:假如我们想买辆车,那么并不是直接找汽车生产厂家,而是找4S店(四儿子),来达到同样买车的目的。这里的车就是一个目标对象,厂家只要负责生产车,而其他的事情就交由厂家的代理4S店来完成。

一,静态代理
静态代理在使用时需要定义接口或者父类,被代理对象和代理对象一起实现相同的接口或者是继承相同的父类。
下面还是以消费者买车为例子来解释:
汽车生产厂家要卖车,但是不是自己直接卖,而是交由4S店来卖:
这样我们定义一个卖车的接口

public interface IBaseSaleCar{
    void saleCar();
}

汽车生产厂家来实现这个接口,它要把车卖出去:

public class CarFactorySale implements IBaseSaleCar{
    @Override
    public void saleCar(){
        System.out.println("卖我(汽车厂家)生产的车");
    }

}

接下来4S店来代理卖汽车厂家生产的车了:

public class FourShopSaleCar implements IBaseSaleCar{
    private IBaseSaleCar target;
    public FourShopSaleCar(IBaseSaleCar target){
        this.target = target;
    }
    @Override
    public void saleCar(){
        target.saleCar();
    }

}

我们来测试下代码:

public class TestProxy{
    public static void main(String[] args){
        CarFactorySale target = new CarFactorySale();
        FourShopSaleCar proxy = new FourShopSaleCar(target);
        proxy.saleCar();
    }
}

这样就是一个完整的静态代理的例子,通过FourShopSaleCar来间接完成卖车的动作,接下来我们看看4S店是怎么揩油加价的,为了方便我们引入一个Car对象;

public class Car {

    /**品牌 */
    private String brand;

    /** 价格  */
    private Long price;

    public Car(String brand, Long price){
        this.brand = brand;
        this.price = price;
    }

    public Long getPrice() {
        return price;
    }

    public void setPrice(Long price) {
        this.price = price;
    }

    public String getBrand() {
        return brand;
    }

    public void setBrand(String brand) {
        this.brand = brand;
    }

    public String toString(){
        return "我卖"+this.brand+"牌子的车,我卖"+this.price+"块钱";
    }

}
public interface IBaseSaleCar {
    void saleCar(Car car);
}
public class CarFactorySale implements IBaseSaleCar{

    @Override
    public void saleCar(Car car){
        System.out.println(car.toString());
    }

}

4S店加价出售

public class FourShopSaleCar implements IBaseSaleCar{
    private IBaseSaleCar target;
    public FourShopSaleCar(IBaseSaleCar target){
        this.target = target;
    }
    @Override
    public void saleCar(Car car){
        //加了200块
        car.setPrice(car.getPrice() + 200L);
        target.saleCar(car);
    }
}

然后就卖出去了

    public class TestProxy{
        public static void main(String[] args){
            //厂家定价1000块
            Car car = new Car("Benz", 1000L);
            CarFactorySale target = new CarFactorySale();
            FourShopSaleCar proxy = new FourShopSaleCar(target);
            //4S店卖的时候偷偷加了200块
            proxy.saleCar(car);
        }
    }

执行结果,多买了200块,多卖的这两百块就是代理模式的好处了,可以在卖车前和卖车后做做手脚,搞搞其他的事情,赚钻外快什么的。

我卖Benz牌子的车,我卖1200块钱

这个就是静态代理了,下面我们了解下动态代理。静态代理的缺点就是需要实现目标对象接口方法,也就是这里的FourShopSaleCar.saleCar方法。

二,动态代理
动态代理一般分为使用jdk自带api来处理,和使用cglib来代理两种模式。
动态代理的特点就是不需要实现目标对象的接口方法,主要使用到了java.lang.reflect.Proxy类中的newProxyInstance方法。
这个方法有三个参数:
ClassLoader loader指定当前目标对象使用类加载器,获取加载器的方法是固定的
Class<> interfaces目标对象实现的接口的类型,使用泛型方式确认类型
InvocationHandler h 事件处理,执行目标对象的方法时,会触发事件处理器的方法,会把当前执行目标对象的方法作为参数传入
让我们来创建一个代理工厂类:

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

public class ProxyFactory {
    //要代理的对象
    private Object target;

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

    public Object getProxyFactoryInstance(){
        return Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                new InvocationHandler(){
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{
                        Object returnValue = method.invoke(target, args);
                        return returnValue;
                    }
                });
    }
}

测试一下我们的代理工厂类:

public class ProxyTest {
    public static void main(String[] args){
        Car car = new Car("BMW", 1000L);
        IBaseSaleCar target = new CarFactorySale();
        System.out.println(target.getClass());
        IBaseSaleCar proxy = (IBaseSaleCar) new ProxyFactory(target).getProxyFactoryInstance();
        System.out.println(proxy.getClass());
        proxy.saleCar(car);

    }
}

执行结果

class com.test.CarFactorySale
class com.sun.proxy.$Proxy0
我卖BMW牌子的车,我卖1000块钱

这里我们已经不需要像静态代理中的FourShopSaleCar一样来实现IBaseSaleCar中的saleCar接口了。这个就是动态代理的优势了。

三、cglib动态代理

jdk动态代理只能针对实现了接口的类,一般没有实现接口的类不能代理。cglib就是针对类来实现代理的,它的原理是针对指定目标类生成一个子类,并覆盖其方法增强其实现。因为采用的是继承,因此不能针对final修饰的类进行代理。使用cglib进行动态代理,完全不受代理类必须实现接口的限制,而且cglib底层使用ASM字节码生成框架,使用字节码技术生成代理类,比java反射的效率要高。

下面来看个例子:
使用cglib动态代理我们需要引入2个包:cglib.jar,asm.jar
定义了一个拦截器,在调用目标方法之前,cglib回调MethodInterceptor接口方法拦截,来实现自己的业务逻辑,类似
于JDK中的InvocationHandler接口。

public class UserDaoImpl {  

    public void save() {  
        System.out.println("Mysql执行保存...");  
    }  

}  
import java.lang.reflect.Method;  
import net.sf.cglib.proxy.Enhancer;  
import net.sf.cglib.proxy.MethodInterceptor;  
import net.sf.cglib.proxy.MethodProxy;  

public class CglibProxyFactory {  

    private Object obj;  

    public CglibProxyFactory(Object obj) {  
        super();  
        this.obj = obj;  
    }  

    public Object getProxyFactory(){  
        //Enhancer类是cglib中的一个字节码增强器,它可以方便的为你所要处理的类进行扩展  
        Enhancer enhancer = new Enhancer();  
        enhancer.setSuperclass(obj.getClass());//将目标对象所在的类作为Enhaner类的父类  
        enhancer.setCallback(new MethodInterceptor() {  
            //通过实现MethodInterceptor实现方法回调  
            @Override  
            public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {  
                System.out.println("事务开启...");  
                method.invoke(obj, args);  
                System.out.println("事务结束...");  
                return proxy;  
            }  
        });  

        return enhancer.create();//生成目标对象并返回  
    }  
}  
public class TestCglibProxy {   
    public static void main(String[] args){  
        UserDaoImpl userDao = new UserDaoImpl();  
        UserDaoImpl userDaoProxy = (UserDaoImpl) new CglibProxyFactory(userDao).getProxyFactory();  
        userDaoProxy.save();  
        System.out.println("目标对象类型:"+userDao.getClass());  
        System.out.println("代理对象类型:"+userDaoProxy.getClass());  
    }  
}  

参考:https://www.cnblogs.com/cenyu/p/6289209.html

猜你喜欢

转载自blog.csdn.net/sukiyou_xixi/article/details/79416326