代理模式(结构型模式)
代理模式(Proxy Pattern):所谓代理模式,就是为A提供一个B对象以控制对A的访问。这种类型的设计模式属于结构型模式。在代理模式中,我们创建具有现有对象的对象,并执行现有对象的相关方法,以便向外界提供功能接口。一般代理模式主要解决直接访问对象不合适的情形。比如说:要访问的对象在远程的机器上。在面向对象系统中,有些对象由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时加上一个对此对象的访问层,即使用代理模式。
代理模式的应用实例:windows里面的快捷方式,spring aop,远程代理,虚拟代理,防火墙代理等。
代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问。代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理。
在代理设计模式中,虽然可以实现在不修改真实方法的源码基础上完成功能的扩展,但是我需要改变调用方式由直接调用真实对象的真实方法,变成调用代理对象的代理方法。为了方便我们进行替换,一般我们会将代理方法和真实方法设计得方法名和形参和返回值一样,为了规范代码的编写,由如下两种实现:
(1)JDK动态代理:真实对象和代理对象实现相同的接口。JDK动态代理只能代理接口,真实对象必须有父接口。
(2)Cglib动态代理:代理对象继承真实对象。Cglib动态代理采用ASM字节码生成框架,使用字节码技术生成代理类,效率更高,但是CGLib不能对final修饰的方法进行代理,因为Cglib是动态生成被代理类的子类,而final修饰的方法不能被继承。
在java的动态代理机制中,有两个重要的类和接口。一个是InvocationHandler、另一个则是Proxy,这一个类和接口是实现动态代理所必须用到的。
(1)InvocationHandler:每一个动态代理类都必须要实现InvocationHandler这个接口,并且每个代理类的实例都关联了一个handler(处理器),当我们动态代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的invoke()方法来进行调用。
//每次生成动态代理类对象时都需要指定一个实现了InvocationHandler接口的调用处理器对象
public class InvocationHandlerImpl implements InvocationHandler {
private Object subject; // 这个就是我们要代理的真实对象,也就是真正执行业务逻辑的类
public InvocationHandlerImpl(Object subject) {// 通过构造方法传入这个被代理对象
this.subject = subject;
}
/**
* 该方法负责集中处理动态代理类上的所有方法调用
* 调用处理器根据这三个参数进行预处理或分派到委托类实例上反射执行
* @param proxy 最终生成的代理类实例
* @param method 被调用的方法对象
* @param args 调用上面method时传入的参数
* @return method对应的方法的返回值
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
System.out.println("当前调用的方法是" + method.getName());
//当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用
result = method.invoke(subject, args);// 需要指定被代理对象和传入参数
System.out.println(method.getName() + "方法的返回值是" + result);
return result;// 返回method方法执行后的返回值
}
}
在源码中invok()方法并没有被显示的调用,而是在生成的代理方法中调用了。在生成的代理方法中是这样的。
public class 代理类 extends Proxy implements 父接口{
public final void bye(){
super.h.invoke(this,m3,null)
}
}
(2)Proxy:这个类的作用就是用来动态创建一个代理对象的类,它提供了许多的方法,但是我们用的最多的就是newProxyInstance()这个方法:
/**
* loader:一个ClassLoader对象,定义了由哪个ClassLoader来对生成的代理对象进行加载
* interfaces:一个Interfaces对象的数组,表示的是我将要给我需要代理的对象提供一组声明接口,
* 如果我提供了一组接口给他,那么这个代理对象就宣称实现了该接口(多态),这样
* 我就能调用这组接口中的方法了
* h:一个InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到
* 哪一个InvocationHand-ler对象上
*/
public static Object newProxyInstance(ClassLoader loader, Class>[] interfaces,
InvocationHandler h) throws IllegalArgumentException{}
其实我们所说的DynamicProxy是这样一种class:它是在运行时生成的class,在生成它时你必须提供一组interfce给它,然后该class就宣称它实现了这些interface。如此一来,我们可以把该class的实例当做这些interface中的任何一个来用。当然,这个DynaicProxy其实就是一个Proxy,它不会做实质性的工作,在生成它的实例时你必须提供一个handler,由它接管时机的工作。
(3)示例
//需要动态代理的接口
public interface Subject {
public void hello(String name);
public String bye();
}
//被代理类
public class RealSubject implements Subject{
@Override
public void hello(String name) {
System.out.println("hello "+name);
}
@Override
public String bye() {
System.out.println("bye");
return "bye";
}
}
//test
public class Test {
public static void main(String[] args) {
// 被代理的对象
Subject realSubject = new RealSubject();
/**
* 通过InvocationHandlerImpl的构造器生成一个InvocationHandler对象,
* 需要传入被代理对象作为参数
*/
InvocationHandler handler = new InvocationHandlerImpl(realSubject);
ClassLoader loader = realSubject.getClass().getClassLoader();
Class[] interfaces = realSubject.getClass().getInterfaces();
// 需要指定类装载器、一组接口及调用处理器生成动态代理类实例
Subject subject = (Subject) Proxy.newProxyInstance(loader, interfaces, handler);
System.out.println("动态代理对象的类型:" + subject.getClass().getName());
subject.hello("Tom");
subject.bye();
}
}
前面我们详细的介绍了JDK自身的AOP所系统的一种动态代理的实现,它的实现相对而言是简单的,但是却有一个非常致命性的缺陷,就是只能为接口中的方法完成代理,而委托类自己的方法或者父类中的方法都不可能被代理。CGLB应运而生,它是一个高性能的,底层基于ASM框架的一个代码生成框架,它完美的解决了JDK版本的动态代理只能为借口方法代理的单一性不足问题。CGLIB的底层是基于Enhancer和MethodInterceptor这两个类。
(1)Enhancer:这是CGLIB动态代理中的字节码生成器。首先将被代理的类Student设置成父类,然后设置代理对象的回调对象MyMethodInterceptor,最后执行enhancer.create()动态生成一个代理类,并从Object强制转型为父类型Student。
public class Test{
public static void main(String[] args){
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Student.class);
enhancer.setCallback(new MyMethodInterceptor());
Student s = (Student)enhancer.create();
s.speak();
s.run();
s.study();
}
}
(2)MethodInterceptor:一个拦截器(父接口Interceptor)。在调用目标方法时,CGLib会回调MethodInterceptor接口方法拦截,来实现你自己的代理逻辑,类似于JDK中的InvocationHandler接口。
public class MyMethodInterceptor implements MethodInterceptor{
//obj真实对象 method真实方法 arg真实方法形参列表 proexy代理方法
public Object intercept(Object obj,Method method,Object[] arg,MethodInterceptor proxy){
System.out.println("Before:"+method);
Object object = proxy.invokeSuper(obj,arg);
System.out.println("After:"+method);
return object;
}
}
(3)示例
//父类
public class Father{
public void sayHello(){
System.out.println("this is father");
}
}
//父接口
public interface Rerson{
void speack();
void run();
}
//委托类(真实对象)
public class Student extends Father implements Person{
public void study(){
System.out.println("i can study,,");
}
@Override
public void speak(){
System.out.println("i am a student,i can speak..");
}
@Override
public void run(){
System.out.println("i am a student,i can run..");
}
}