您的位置:首页 > 编程语言 > Java开发

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

2018-03-01 19:51 597 查看
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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息