通过代理模式可以在不修改原对象代码的基础上,对业务逻辑与功能性逻辑进行解耦,对源对象的功能进行修改或者增强(日志、权限控制)。
定义以及目的
- 定义:代理模式(Proxy Pattern)即为某一个对象提供一个代理对象,由代理对象来接管被代理对象的各个方法的访问。
通俗说,房东想要出租房屋,出租房屋需要:找租客—>看房—>租客不应定满意,循环上一过程—>办各种手续—>签合同。而房东的意图很简单,就是把房屋出租,所以都是累赘的步骤,这个时候就有了房屋中介,代理房东,去找租客、看房、办手续、最后签订三方合同,成功把房屋出租出去。在这个过程中,房屋中介就承担了代理的作用,帮助房东完成多余步骤,达到出租房屋的根本目的。
代理模式,就是在不改变主体方法的情况下,对主体方法的增强(日志、记录等操作)
- UML类图
- Subject(共同接口):客户端使用的现有接口
- RealSubject(真实对象):真实对象的类
- ProxySubject(代理对象):代理类
按照代理类的创建时期,代理类可分为两种:
- 静态代理类:由程序员创建或由特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。
- 动态代理类:在程序运行时,运用反射机制动态创建而成
Java中可以通过三种方式创建代理对象:
- 静态代理
- 基于JDK(接口)的动态代理
- 基于CGLIB(父类)的动态代理
静态代理
需要手工编写代理类代码,经过编译后执行。编写代理类,实现目标类的接口或直接继承目标类,完成逻辑的修改。 ```java public interface Subject { public void dosomething(); }
public class RealSubject implements Subject { @Override public void dosomething() { System.out.println(“dosomething….”); } }
public class SubjectProxy implements Subject{
public Subject subject;
public SubjectProxy(Subject subject){
this.subject = subject;
}
@Override
public void dosomething() {
dosomethingBefore();
subject.dosomething();
dosomethingAfter();
}
private void dosomethingAfter() {
System.out.println("dosomthing-before....");
}
private void dosomethingBefore() {
System.out.println("dosomthing-after....");
}
}
public static void main(String[] args) {
Subject subject = new SubjectProxy(new RealSubject());
subject.dosomething();
}
———调用结果 dosomthing-after…. dosomething…. dosomthing-before….
静态代理虽然简单,但是缺点也很明显:在需求接口和实现类很多的情况下,要为每个需求都建立一个工厂类(例如为需求增加日志需求,需要为所有service都添加代理类吗?),在接口或者父类发生变动的时候,代理类也需要进行改变。
<a name="SiwDN"></a>
## 动态代理
动态代理是在内存中生成代理对象的一种技术(代理类的字节码将在运行时生成并载入当前的ClassLoader),也就是整个代理过程在内存中进行,不需要像静态代理那样需要去手写代理类代码,也不存在代理类编译过程,动态代理合理的避免了静态代理的那种方式,不用事先为要代理的类而构建好代理类。而是在运行时通过反射机制创建。尽管动态代理需要利用到反射机制和动态生成字节码,导致其性能会比静态代理稍差一些,但是相对于它的有点,这点劣势可以忽略不计。一般动态代理使用JDK或者CGLIB实现静态代理。<br />动态代理模式通过使用反射,可以在运行期决定加载哪个类,避免了一个类对应一个代理的问题;同时,通过统一的invoke方法,统一了代理类对原函数的处理过程,使用动态代理很大程度上减少了重复的代码,降低了维护的复杂性和成本。
<a name="PGoVt"></a>
### 基于JDK(接口)的动态代理
Jdk的动态代理是基于接口的。现在想要为RealSubject这个类创建一个动态代理对象,Jdk主要会做一下工作:
1. 获取RealSubject上的所有接口列表
1. 确定要生成的代理类的类名,默认为:com.sun.proxy.$ProxyXXXX;
1. 根据需要实现的接口信息,在代码中动态创建该Proxy类的字节码;
1. 将对应的字节码转换为对于的class对象;
1. 创建InvocationHandler实例handler,用来处理Proxy所有方法的调用;
1. Proxy的class对象以创建的handler对象为参数,实例化一个proxy对象;
Jdk通过java.lang.reflect.Proxy包来支持动态代理,在Java中要创建一个代理对象,必须调用Proxy类的静态方法newProxyInstance,该方法的原型如下:
```java
- ClassLoader loader : 表示类加载器,对于不同来源(系统库或网络等)的类需要不同的类加载器来加载,这是Java安全模型的一部分。可以使用null来使用默认的加载器;不过一般使用被代理类的加载器
- Class<?>[] interfaces : 表示接口或对象的数组,指被代理对象和真实对象都必须共有的父类或者接口;
- InvocationHandler handler : 表示调用处理器,它必须是实现了InvocationHandler接口的对象,其作用是定义代理对象中需要执行的具体操作。
Object Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler handler) throws IllegalArgumentException
InvocationHandler之于Proxy,就如Runnable之于Thread。InvocationHandler接口中只有一个方法invoke,它的作用就跟Runnable中的run方法类似,定义了代理对象在执行真实对象的方法时所希望执行的动作。其原型如下:
- Object proxy : 表示执行这个方法的代理对象;
- Method method : 表示真实对象实际需要执行的方法
- Object[] args : 表示真实对象实际执行方法时所需的参数
Object invoke(Object proxy, Method method, Object[] args) throws Throwable
在实际的编程中,需要优先定义一个实现InvocationHandler接口的调用处理器对象,然后将它作为创建代理类实例的参数。(抑或在调用newProxyInstance方法时使用匿名内部类。)这样就得到了代理对象。
真实对象本身的实例化在调用处理器对象内部完成,实例化时需要的参数也应该及时传入调用处理器对象中。这样一来就完成了代理对象对真实对象的包装,而代理对象需要执行的额外操作也在invoke方法中处理。
CGLIB动态代理
Jdk的动态代理依靠接口实现,如果有些类并没有实现接口,则不能使用jdk代理,这就要用到CGLIB代理了。CGLIB是针对类来实现的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理。
CGLIB创建某个类A的动态代理类的模式是:
- 查找A上的所有非final的public类型的方法定义
- 将这些方法的定义转换成字节码
- 将组成的字节码转换成相应的代理的class对象
- 实现MethodInterceptor接口,用来处理对代理类上所有方法的请求(这个接口和Jdk动态代理InvocationHandler的功能和角色是一样的)
```java
public class RealSubjectCglib
{
public String operate(){
} }return "RealSubjectCglib";
public class ProxyCglib implements MethodInterceptor { private Object target;
public Object getInstance(Object target)
{
this.target = target;
//Cglib中的加强器,用来创建动态代理
Enhancer enhancer = new Enhancer();
//设置要创建动态代理的类
enhancer.setSuperclass(this.target.getClass());
//设置回调,这里相当于是对于代理类上所有方法的调用,都会调用Callback,而Callback则需要实现intercept()方法进行拦截
enhancer.setCallback(this);
Object obj = enhancer.create();
return obj;
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable
{
System.out.print("I'm Proxy, I'm invoking...");
Object object = proxy.invokeSuper(obj, args);
System.out.println(object);
return object;
}
}
ProxyCglib proxy = new ProxyCglib(); RealSubjectCglib cglib = (RealSubjectCglib)proxy.getInstance(new RealSubjectCglib()); cglib.operate();
```