1.代理模式简介
代理模式的定义:为其他对象提供一种代理以控制对这个对象的访问。使用代理模式创建代理对象,让代理对象控制目标对象的访问(目标对象可以是远程的对象、创建开销大的对象或需要安全控制的对象),并且可以在不改变目标对象的情况下添加一些额外的功能。
所谓代理,就是一个人或者一个机构代表另一个人或者另一个机构采取行动。在一些情况下,一个客户不想或者不能够直接引用一个对象,而代理对象可以在客户端和目标对象之前起到中介的作用。
代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。
代理模式一般涉及到的角色有:
抽象角色:声明真实对象和代理对象的共同接口;可以是抽象类,也可以是接口,是一个最普通的业务类型定义,无特殊要求。
代理角色:代理对象角色内部含有对真实对象的引用,从而可以操作真实对象,同时代理对象提供与真实对象相同的接口以便在任何时刻都能代替真实对象。同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装。
也叫委托类、代理类。它把所有抽象角色定义的方法给真实角色实现,并且在真实角色处理完毕前后做预处理和善后工作。(最简单的比如打印日志)(自己并未实现业务逻辑接口,而是调用真实角色来实现。)
真实角色:代理角色所代表的真实对象,是我们最终要引用的对象。也叫被委托角色、被代理角色。是业务逻辑的具体执行者。(真正实现了业务逻辑接口。)
代理模式是常用的java设计模式,如上图所示,代理类和实现类实现了同一个接口,而代理类中则具有现有对象,通过代理类去操作现有对象,那么我们就可以在代理类中增加很多操作,包括前置增强、后置增强、环绕增强、异常增强等,这也就能解决我们实际当中遇到的一些问题,例如延迟对象的创建、控制对象的访问等。
代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口。
代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。
代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。
按照代理的创建时期,代理类可以分为两种。
静态代理:由程序员创建或特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。
动态代理:在程序运行时,运用反射机制动态创建而成。
2.静态代理
静态代理是由程序员创建或特定工具自动生成源代码,再对其编译。最大的特点就是在程序运行时代理类的.class文件就已经存在了,但是这有一个很大的缺陷即每一个代理类只能为一个接口服务。静态代理的实现如下
首先定义一个接口(抽象角色)
package com.test.DesignPattern.ProxyPattern;
/**
* 抽象角色
* 产品接口
* @author lxx
*
*/
public interface ProductBiz {
public void addProduct();
}
真实角色
package com.test.DesignPattern.ProxyPattern;
/**
* 真实角色
* 产品实现类
* @author lxx
*
*/
public class ProductImpl implements ProductBiz{
@Override
public void addProduct(){
System.out.println("增加产品");
}
}
代理角色
package com.test.DesignPattern.ProxyPattern;
/**
* 代理角色
* 产品实现代理类
* @author lxx
*
*/
public class ProductImplProxy implements ProductBiz{
//在代理类中必须有一个真实对象的引用
private ProductBiz productBiz;
public ProductImplProxy(ProductBiz productBiz){
this.productBiz=productBiz;
}
@Override
public void addProduct(){
before();
productBiz.addProduct();
after();
}
private void before(){
System.out.println("***********");
System.out.println("前置增强");
System.out.println("***********");
}
private void after(){
System.out.println("***********");
System.out.println("后置增强");
System.out.println("***********");
}
}
测试类:
package com.test.DesignPattern.ProxyPattern;
public class test {
public static void main(String[] args){
ProductBiz p=new ProductImpl();
ProductBiz pb=new ProductImplProxy(p);
pb.addProduct();
}
}
测试结果:
***********
前置增强
***********
增加产品
***********
后置增强
另一个实例:
首先是抽象角色类,可以定义一个接口或者抽象类:
/**
* 真实对象和代理对象的共同接口
*
*/
public abstract class Subject
{
public abstract void request();
}
之后是真实角色的类,实现接口,并完成实际工作:
/**
* 真实角色
*/
public class RealSubject extends Subject
{
@Override
public void request()
{
System.out.println("From Real Subject!");
}
}
然后定义代理角色,内部包含对真实角色对象的引用,并且封装功能:
/**
* 代理角色
*
*/
public class ProxySubject extends Subject
{
//代理角色对象内部含有对真实对象的引用
private RealSubject realSubject;
@Override
public void request()
{
//在真实角色操作之前所附加的操作
preRequest();
if(null == realSubject)
{
realSubject = new RealSubject();
}
//真实角色所完成的事情
realSubject.request();
//在真实角色操作之后所附加的操作
postRequest();
}
private void preRequest()
{
System.out.println("Pre Request.");
}
private void postRequest()
{
System.out.println("Post Request");
}
}
在客户端进行调用时:
/**
* 客户类
*
*/
public class Client
{
public static void main(String[] args)
{
Subject subject = new ProxySubject();
subject.request();
}
}
问题
如果要按照上述的方式(静态代理)使用代理模式,那么真实角色必须是实现已经存在的,并将其作为代理对象的内部属性。
但是实际使用时,一个真实角色必须对应一个代理角色,但如果大量使用会导致类的急剧膨胀;此外,如果事先并不知道真实角色,该如何使用代理呢?这个问题可以通过Java的动态代理类来解决。
3.动态代理
动态代理:在程序运行时,运用反射机制动态创建代理类(Proxy)而成。
Java动态代理类位于java.lang.reflect包下,一般主要涉及到以下两个类:
(1)Interface InvocationHandler(InvocationHandler 是代理实例的调用处理程序实现的接口。invoke()在代理实例上处理方法调用并返回结果。)
该接口中仅定义了一个方法:
Object invoke(Object proxy, Method method, Object[] args)
在实际使用时,第一个参数obj一般是指代理类,method是被代理的方法,如上例中的request(),args为该方法的参数数组(无参时设置为null)。
这个抽象方法在代理类中动态实现。
(2)Proxy(Proxy 提供用于创建动态代理类和实例的静态方法.)
该类即为动态代理类,作用类似于上文例子中的ProxySubject,其中主要包含如下内容:
protected Proxy(InvocationHandler h): 构造函数,用于给内部的invocation handler赋值。
static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces) : loader是类装载器,interfaces是真实类所拥有的全部接口的数组。
static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) :返回代理类的一个实例,返回后的代理类可以当作被代理类使用(可使用被代理类在Subject接口中声明过的方法)。
参数说明:
ClassLoader loader:类加载器,用于创建类(会创建一个类$Proxy0)
Class<?>[] interfaces:被代理的类实现的全部的接口 (创建类$Proxy0需要给类添加方法,就是从这些接口得知需要添加哪些方法。)
这也是为什么java原生的动态代理是基于接口的。而不能基于类。
InvocationHandler h:InvocationHandler接口的子类实例的invoke方法具体完成了需要代理类做的额外的事情(相对于原始类的方法)。
动态代理类说明
所谓Dynamic Proxy是这样一种class:
它是在运行时生成的class,在生成它时你必须提供一组interface给它,然后该class就宣称它实现了这些interface。
你当然可以把该class的实例当作这些interface中的任何一个来用。
当然,这个Dynamic Proxy其实就是一个Proxy,它不会替你做实质性的工作,在生成它的实例时你必须提供一个handler,由它接管实际的工作。
在使用动态代理类时,我们必须实现InvocationHandler接口。每一个动态代理类都会有一个与之关联的invocation handler。
真正的调用是在invocation handler的invoke()方法里完成的。
实例说明:
首先定义抽象角色和真实角色类:
public interface Subject
{
public void request();
}
public class RealSubject implements Subject
{
@Override
public void request()
{
System.out.println("From real subject!");
}
}
之后定义一个DynamicSubject类:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* 该代理类的内部属性是Object类型,实际使用的时候通过该类的构造方法传递进来一个对象。
* 该类实现了invoke()方法,该方法中的method.invoke()其实就是调用被代理对象的将要执行的方法,
* 方法参数sub表示该方法从属于sub。
* 通过动态代理类,我们可以在执行真实对象的方法前后加入自己的一些额外方法
*
*/
public class DynamicSubject implements InvocationHandler
{
//对真实对象的引用
private Object sub;
public DynamicSubject(Object obj)
{
this.sub = obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable
{
System.out.println("Before calling: " + method);
//通过反射来调用方法
method.invoke(sub, args);
System.out.println("After calling: " + method);
return null;
}
}
使用的时候:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class Client
{
public static void main(String[] args)
{
RealSubject realSubject = new RealSubject();
InvocationHandler handler = new DynamicSubject(realSubject);
Class<?> classType = handler.getClass();
// 生成代理
// 动态生成一个类(实现了指定的接口),生成类的对象,转换成接口类型
Subject subject = (Subject) Proxy.newProxyInstance(classType
.getClassLoader(), realSubject.getClass().getInterfaces(),
handler);
subject.request();
// 调用方法时,转移给handler接管,由其中的invoke()方法实际完成方法执行
System.out.println(subject.getClass());// 打印出:class $Proxy0
// $Proxy0是在运行期间动态生成的一个类
}
}
通过这种方式,被代理的对象(RealSubject)可以在运行时动态改变,需要控制的接口(Subject接口)可以在运行时改变,控制的方式(DynamicSubject类)也可以动态改变,从而实现了非常灵活的动态代理关系。
动态代理
动态代理是指客户通过代理类来调用其他对象的方法。
动态代理使用场合:
调试。
远程方法调用(RMI)。
动态代理步骤
1.创建一个实现接口InvocationHandler的类,它必须实现invoke()方法。
2.创建被代理的类以及接口。
3.通过Proxy的静态方法newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)创建一个代理。
4.通过代理调用方法。
动态代理实现例子2:
这个例子中定义了一个接口:
public interface Foo
{
public void doAction();
}
这个接口有两个实现类:
public class FooImpl1 implements Foo
{
@Override
public void doAction()
{
System.out.println("From Implement 1 !");
}
}
public class FooImpl2 implements Foo
{
@Override
public void doAction()
{
System.out.println("From Implement 2 !");
}
}
定义invocation handler,其中的set方法使得实际对象是可更换的:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class CommonInvocationHandler implements InvocationHandler
{
private Object target;
public CommonInvocationHandler()
{
}
public CommonInvocationHandler(Object obj)
{
this.target = obj;
}
public void setTarget(Object target)
{
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable
{
return method.invoke(target, args);
}
}
使用:
import java.lang.reflect.Proxy;
public class Demo
{
public static void main(String[] args)
{
CommonInvocationHandler handler = new CommonInvocationHandler();
Foo f = null;
handler.setTarget(new FooImpl1());
f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),
new Class[] { Foo.class }, handler);
f.doAction();
System.out.println("----------------------------");
handler.setTarget(new FooImpl2());
f.doAction();
}
}
程序运行后输出:
From Implement 1 !
----------------------------
From Implement 2 !