在我们通常的应用中,代理模式也是我们常用的设计模式之一。所谓的代理模式是指客户端并不直接调用实际的对象,而是通过调用代理对象,来间接的调用实际的对象。
为什么要采用这种间接的形式来调用对象呢?一般是因为客户端不想访问实际的对象,或者访问实际的对象存在困难,因此通过一个代理对象来完成间接的访问。
在我们现实生活中,这种情形也是非常常见非常常见的,就比如,黄牛买票,黄牛相当于是火车站的代理,我们可以通过黄牛买票,但只能去火车站进行改签和退票。
在代码实现中,相当于为一个委托对象Station(车站)提供一个代理对象Scalper(黄牛),通过Scalper(黄牛)可以调用Station(车站)的部分功能,并添加一些额外的业务处理,同时可以屏蔽Station(车站)中未开放的接口。
代理模式的UML图
代理模式类图
1,Station(车站)是委托类,Scalper(黄牛)是代理类;
2,Subject是委托类和代理类的接口;
3,sell()是委托类和代理类的共同方法;
从UML图中,可以看出代理类与真正实现的类都是实现了抽象的接口,这样的好处的在于代理类可以与实际的类有相同的方法,可以保证客户端使用的透明性。
二 Java常用的三种代理
2.1 静态代理
在代码实现中相当于为一个委托对象realSubject提供一个代理对象proxy,通过proxy可以调用realSubject的部分功能,并添加一些额外的业务处理,同时可以屏蔽realSubject中未开放的接口。
1、RealSubject 是委托类,Proxy 是代理类;
2、Subject 是委托类和代理类的接口;
3、request() 是委托类和代理类的共同方法;
具体代码实现如下:
interface Subject { void request();
}class RealSubject implements Subject { public void request(){
System.out.println("RealSubject");
}
}class Proxy implements Subject { private Subject subject; public Proxy(Subject subject){ this.subject = subject;
} public void request(){
System.out.println("begin");
subject.request();
System.out.println("end");
}
}public class ProxyTest { public static void main(String args[]) {
RealSubject subject = new RealSubject();
Proxy p = new Proxy(subject);
p.request();
}
}
静态代理实现中,一个委托类对应一个代理类,代理类在编译期间就已经确定。
2.2 动态代理
动态代理有以下特点:
在运行期,通过反射机制创建一个实现了一组给定接口的新类;
在运行时生成的class,必须提供一组interface给它,然后该class就宣称它实现了这些 interface。该class的实例可以当作这些interface中的任何一个来用。但是这个Dynamic Proxy其实就是一个Proxy, 它不会替你作实质性的工作,在生成它的实例时你必须提供一个handler,由它接管实际的工作。
动态代理也叫做:JDK代理,接口代理
接口中声明的所有方法都被转移到调用处理器(handler)一个集中的方法中处理(InvocationHandler.invoke)。这样,在接口方法数量比较多的时候,我们可以进行灵活处理,而不需要像静态代理那样每一个方法进行中转。而且动态代理的应用使我们的类职责更加单一,复用性更强。
JDK中生成代理对象的API
代理类所在包:java.lang.reflect.Proxy
JDK实现代理只需要使用newProxyInstance方法,但是该方法需要接收三个参数,完整的写法是:
static Object newProxyInstance(ClassLoader loader, Class [] interfaces, InvocationHandler handler)
注意该方法是在Proxy类中的静态方法,且接收的三个参数依次为:
ClassLoader loader:指定当前目标对象使用的类加载器,用null表示默认类加载器
Class [] interfaces:需要实现的接口数组
InvocationHandler handler:调用处理器,执行目标对象的方法时,会触发调用处理器的方法,从而把当前执行目标对象的方法作为参数传入
java.lang.reflect.InvocationHandler:这是调用处理器接口,它自定义了一个 invoke 方法,用于集中处理在动态代理类对象上的方法调用,通常在该方法中实现对委托类的代理访问。
// 该方法负责集中处理动态代理类上的所有方法调用。第一个参数是代理类实例,第二个参数是被调用的方法对象,第三个参数是方法参数的数组形式
// 第三个方法是调用参数。
Object invoke(Object proxy, Method method, Object[] args)
代码示例:
package model;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;interface IUserDao { void save();
}class UserDao implements IUserDao { public void save() {
System.out.println("----已经保存数据!----");
}
}class ProxyFactory { private Object target; public ProxyFactory(Object target) { this.target = target;
} // 给目标对象生成代理对象,其class文件是由 JVM 在运行时动态生成
public Object getProxyInstance() { return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() { @Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("开始"); // 执行目标对象方法,方法参数是target,表示该方法从属于target
Object returnValue = method.invoke(target, args);
System.out.println("提交"); return returnValue;
}
});
}
}public class Client { public static void main(String[] args) { // 目标对象
IUserDao target = new UserDao();
System.out.println(target.getClass()); // 代理对象
IUserDao proxy = (IUserDao) new ProxyFactory(target).getProxyInstance();
System.out.println(proxy.getClass());
proxy.save();
}
}
输出:
class model.UserDaoclass model.$Proxy0开始
----已经保存数据!----
提交
总结:
代理对象不需要实现接口,但是目标对象一定要实现接口,否则不能用动态代理
2.3 Cglib代理
上面的静态代理和动态代理模式都是要求目标对象实现一个接口或者多个接口,但是有时候目标对象只是一个单独的对象,并没有实现任何的接口,这个时候就可以使用构建目标对象子类的方式实现代理,这种方法就叫做:Cglib代理。
Cglib代理,也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能的扩展。
Cglib是一个强大的高性能的代码生成包,它可以在运行期扩展java类与实现java接口。它广泛的被许多AOP的框架使用,例如Spring AOP和synaop,为他们提供方法的interception(拦截)
Cglib包的底层是通过使用字节码处理框架ASM来转换字节码并生成新的子类
代理的类不能为final,否则报错;目标对象的方法如果为final/static,那么就不会被拦截。
代码示例:
目标对象类:UserDao.java
/**
* 目标对象,没有实现任何接口
*/
public class UserDao { public void save() {
System.out.println("----已经保存数据!----");
}
}
Cglib代理工厂:ProxyFactory.java
/**
* Cglib子类代理工厂
* 对UserDao在内存中动态构建一个子类对象
*/
public class ProxyFactory implements MethodInterceptor{ //维护目标对象
private Object target; public ProxyFactory(Object target) { this.target = target;
} //给目标对象创建一个代理对象
public Object getProxyInstance(){ //1.工具类
Enhancer en = new Enhancer(); //2.设置父类
en.setSuperclass(target.getClass()); //3.设置回调函数
en.setCallback(this); //4.创建子类(代理对象)
return en.create();
} @Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("开始事务..."); //执行目标对象的方法
Object returnValue = method.invoke(target, args);
System.out.println("提交事务..."); return returnValue;
}
}
测试类:
/**
* 测试类
*/
public class App { @Test
public void test(){ //目标对象
UserDao target = new UserDao(); //代理对象
UserDao proxy = (UserDao)new ProxyFactory(target).getProxyInstance(); //执行代理对象的方法
proxy.save();
}
}