1.理解(与装饰模式对比):
从某种意义上来说,他就是一种代理设计模式,相比较于装饰模式而言,它们很相似,但比装饰模式更加的灵活
===============================
2.静态代理
若代理类在程序运行前就已经存在,那么这种代理方式被成为 静态代理 ,这种情况下的代理类通常都是我们在Java代码中定义的。 通常情况下, 静态代理中的代理类和被代理类(目标对象类)会实现同一接口或是派生自相同的父类。
下面我们用Vendor类代表代理类,BusinessAgent类代表被代理类,来介绍下静态代理的简单实现,被代理类(目标对象类)和代理类都实现了Sell接口。
Sell接口的定义如下:
public interface Sell {
void sell();
void ad();
}
被代理类Vendor类的定义如下:
public class Vendor implements Sell {
public void sell() {
System.out.println("销售方法");
}
public void ad() {
System.out.println("广告的方法")
}
}
代理类BusinessAgent的定义如下:
public class BusinessAgent implements Sell {
Vendor vendor=new Vendor();
vendor.sell();
vendor.ad();
}
从BusinessAgent类的定义我们可以了解到,静态代理可以通过聚合来实现,让代理类持有一个被代理类(目标对象类)的引用即可。
下面我们考虑一下这个需求:给Vendor类增加一个过滤功能,只卖货给大学生。通过静态代理,我们无需修改Vendor类的代码就可以实现,只需在BusinessAgent类中的sell方法中添加一个判断即可如下所示:
public class BusinessAgent implements Sell {
public void sell() {
if (isCollegeStudent()) {
vendor.sell();
}
}
}
这对应着我们上面提到的使用代理的第二个优点:可以实现客户与被代理类间的解耦,在不修改被代理类代码的情况下能够做一些额外的处理。静态代理的局限在于运行前必须编写好代理类,下面我们重点来介绍下运行时生成代理类的动态代理方式。
下面我们有一个需求,就是代理类再增加自己特有的增强功能,也就是代理将增强被代理类的一些特征:
在执行被代理类中的方法之前输出“before”,在执行完毕后输出“after”
public class BusinessAgent implements Sell {
private Vendor mVendor;
public BusinessAgent(Vendor vendor) {
this.mVendor = vendor;
}
public void sell() {
System.out.println("before");
mVendor.sell();
System.out.println("after");
}
public void ad() {
System.out.println("before");
mVendor.ad();
System.out.println("after");
}
}
从以上代码中我们可以了解到,通过静态代理实现我们的需求需要我们在每个方法中都添加相应的逻辑,这里只存在两个方法所以工作量还不算大,假如Sell接口中包含上百个方法呢?这时候使用静态代理就会编写许多冗余代码。通过使用动态代理,我们可以做一个“统一指示”,动态添加增强方法,从而对所有代理类的方法进行统一处理,而不用逐一修改每个方法。下面我们来具体介绍下如何使用动态代理方式实现我们的需求。
3.动态代理
3.1 什么是动态代理
代理类在程序运行时创建的代理方式被成为 动态代理。 也就是说,这种情况下,代理类并不是在Java代码中定义的,而是在运行时根据我们在Java代码中的“指示”动态生成的。相比于静态代理, 动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类的函数。
(1)InvocationHandler接口
在使用动态代理时,我们需要定义一个位于代理类与被代理类之间的中介类,这个中介类被要求实现InvocationHandler接口,这个接口的定义如下:
public interface InvocationHandler {
Object invoke(Object proxy, Method method, Object[] args);
}
从InvocationHandler这个名称我们就可以知道,实现了这个接口的中介类用做“调用处理器”。当我们调用代理类对象的方法时,这个“调用”会转送到invoke方法中,代理类对象作为proxy参数传入,参数method标识了我们具体调用的是代理类的哪个方法,args为这个方法的参数。这样一来,我们对代理类中的所有方法的调用都会变为对invoke的调用,这样我们可以在invoke方法中添加统一的处理逻辑(也可以根据method参数对不同的代理类方法做不同的处理)。因此我们只需在中介类的invoke方法实现中输出“before”,然后调用委托类的invoke方法,再输出“after”。下面我们来一步一步具体实现它。
(2)被代理类的定义
动态代理方式下,要求被代理类必须实现某个接口,这里我们实现的是Sell接口。被代理Vendor类的定义如下:
public class Vendor implements Sell {
public void sell() {
System.out.println("销售方法");
}
public void ad() {
System.out.println("广告的方法")
}
}
(3)中介类(InvocationHandler接口)
上面我们提到过,中介类必须实现InvocationHandler接口,作为调用处理器”拦截“对代理类方法的调用。中介类的定义如下:
public class DynamicProxy implements InvocationHandler {
private Object obj; //obj为被代理类对象;
public DynamicProxy(Object obj) {
this.obj = obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before");
Object result = method.invoke(obj, args);
System.out.println("after");
return result;
}
}
从以上代码中我们可以看到,中介类持有一个被代理类对象引用,在invoke方法中调用了被代理类对象的相应方法,看到这里是不是觉得似曾相识?通过聚合方式持有被代理类对象引用,把外部对invoke的调用最终都转为对被代理类对象的调用。这不就是我们上面介绍的静态代理的一种实现方式吗?实际上,中介类与委托类构成了静态代理关系,在这个关系中,中介类是代理类,委托类就是委托类; 代理类与中介类也构成一个静态代理关系,在这个关系中,中介类是被代理类,代理类是代理类。也就是说,动态代理关系由两组静态代理关系组成,这就是动态代理的原理。下面我们来介绍一下如何”指示“以动态生成代理类。
(4)动态生成代理类
动态生成代理类的相关代码如下:
public class Main {
public static void main(String[] args) {
//创建被代理类实例
DynamicProxy inter = new DynamicProxy(new Vendor());
//加上这句将会产生一个$Proxy0.class文件,这个文件即为动态生成的代理类文件
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
//获取代理类实例sell
Sell sell = (Sell)(Proxy.newProxyInstance(Sell.class.getClassLoader(), new Class[] {Sell.class}, inter));
//通过代理类对象调用代理类方法,实际上会转到invoke方法调用
sell.sell();
sell.ad();
}
}
在以上代码中,我们调用Proxy类的newProxyInstance方法来获取一个代理类实例。这个代理类实现了我们指定的接口并且会把方法调用分发到指定的调用处理器。这个方法的声明如下:
复制代码 代码如下:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
方法的三个参数含义分别如下:
loader:定义了代理类的ClassLoder;
interfaces:代理类实现的接口列表
h:调用处理器,也就是我们上面定义的实现了InvocationHandler接口的类实例
上面我们已经简单提到过动态代理的原理,这里再简单的总结下:首先通过newProxyInstance方法获取代理类实例,而后我们便可以通过这个代理类实例调用代理类的方法,对代理类的方法的调用实际上都会调用中介类(调用处理器)的invoke方法,在invoke方法中我们调用被代理类的相应方法,并且可以添加自己的处理逻辑。
4.结合工厂模式,做一个代理工厂实例,当看得懂下边的例子,那么动态代理就完全理解了!
4.1新建接口Waiter
public interface Waiter {
void severs();
}
4.2新建实现接口的ManWaiter
public class ManWaiter implements Waiter{
public void severs() {
System.out.println("请用餐!");
}
}
4.3新建两个增强的接口(我们要给被代理的对象加强的方法,写成接口)
public interface AfterAdvice {
public void xuYuan();
}
public interface BeforAdvice {
public void xunZhao();
}
4.4重点来了:新建类ProxyFactory (定义的变量要创建get,set方法)
public class ProxyFactory {
private Object object;// 被代理对象
private BeforAdvice beforAdvice;// 增强类对象
private AfterAdvice afterAdvice;// 增强类对象
public Object creatProxy() {
// 通过类加载器获得类对象
ClassLoader classLoader = this.getClass().getClassLoader();
// 获得接口数组
Class[] interfaces = object.getClass().getInterfaces();
// 获得调用处理器
InvocationHandler h = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// 当不等于空时调用增强方法
if (beforAdvice != null) {
beforAdvice.xunZhao();
}
// 此处为被代理对象原来的方法
method.invoke(object, args);
// 当不等于空时调用增强方法
if (afterAdvice != null) {
afterAdvice.xuYuan();
}
return null;
}
};
//创建中介类(代理类)对象
Object newProxyInstance = Proxy.newProxyInstance(classLoader,
interfaces, h);
//返回对象
return newProxyInstance;
}
public ProxyFactory() {
super();
}
public Object getObject() {
return object;
}
public void setObject(Object object) {
this.object = object;
}
public BeforAdvice getBeforAdvice() {
return beforAdvice;
}
public void setBeforAdvice(BeforAdvice beforAdvice) {
this.beforAdvice = beforAdvice;
}
public AfterAdvice getAfterAdvice() {
return afterAdvice;
}
public void setAfterAdvice(AfterAdvice afterAdvice) {
this.afterAdvice = afterAdvice;
}
}
注意:在上面new InvocationHandler()时,实现的方法invoke(Object proxy, Method method, Object[] args)中
第一个参数:被代理对象
第二个参数:被代理对象的方法
第三个参数:参数列表,没有就不写,这是可变数组
4.5新建测试类,验证我们的工厂代理
public class Test01 {
@Test
public void loader() {
//创建对象
ProxyFactory factory = new ProxyFactory();
//调用工厂方法
factory.setObject(new ManWaiter());
//调用我们的增强方法
factory.setBeforAdvice(new BeforAdvice() {
@Override
public void xunZhao() {
System.out.println("您好!");
}
});
//调用我们的增强方法
factory.setAfterAdvice(new AfterAdvice() {
@Override
public void xuYuan() {
System.out.println("谢谢光临!");
}
});
//创建被代理对象并调用被代理对象的方法
Waiter creatProxy = (Waiter) factory.creatProxy();
creatProxy.severs();
}
}
这个例子比较难,但是很实用,以后就是这么用。这样抽取出来做出工厂类,就可以实现随时用,代码灵活性高,看到这,小伙伴们懂了吗!