反射与代理设计模式
代理设计是在程序开发之中使用最多的设计模式,代理设计模式的是有真是业务实现类与代理业务实现了,并且代理类要完成比真实业务更多的处理操作。
传统代理设计模式的弊端(静态代理设计模式)
所有的代理设计模式如果按照设计要求来讲,必须是基于接口的设计,也就是说需要首先定义出核心接口的组成,下面模拟一个消息发送的代理操作结构。
传统代理设计
package cn.mldn.demo;
interface IMessage{ //传统代理设计必须有接口
public void send() ; //业务方法
}
class MessageReal implements IMessage{
@Override
public void send() {
// TODO Auto-generated method stub
System.out.println("*******Message send*******");
}
}
class MessageProxy implements IMessage{ //代理类
private IMessage message; //代理对象,一定是业务接口实例
public MessageProxy(IMessage message) {
this.message = message;
}
public boolean connect() {
System.out.println("[消息代理]进行消息发送通道的连接!");
return true;
}
public void close() {
System.out.println("[消息代理]关闭消息发送通道!");
}
@Override
public void send() {
if(this.connect()) {
this.message.send(); //消息的发送处理
this.close();
}
}
}
public class Proxy_study {
public static void main(String[] args) {
IMessage msg = new MessageProxy(new MessageReal());
msg.send();
}
}
以上操作代码是标准的代理设计,但是进一步思考会发现客户端接口与具体的子类产生了耦合问题,这样的操作从实际开发中来讲应引入工厂类进行代理对象的获取。
以上的代理设计模式为静态代理设计,这种静态代理设计的特点在于:一个代理类只为一个接口服务,那么假设现在有较多的业务接口,按照此种做法就意味着需要编写多个代理类,且这些代理类处理形式类似。
所以现在需要解决的问题在于:使用一个业务代理类满足所有的业务接口操作需求。
动态代理设计模式(重点)
通过静态代理设计模式的缺陷可以发现,最好的做法是为所有功能一致的业务操作接口提供有统一的大理处理操作,而这就可以通过动态代理机制来实现,动态代理机制需要如下几个问题:
- 不管是动态代理类还是静态代理类都一定要接收真实业务实现子类对象;
- 由于动态代理类不在与某个具体的接口进行捆绑,所以应该可以动态获取类的接口信息;
在进行动态代理实现的操作中,首先需要关注的就是一个InvocationHandler接口,这个接口规定了代理方法的执行。
public interface InvocationHandler{
/**
* 代理方法调用,代理主题类中执行的方法最终都是此方法
* @param proxy 要代理的对象
* @param method 要执行的接口方法名臣
* @param args 传递的参数
* @return 某一个方法的返回值
* @throws Throwable 方法调用出现的错误继续向上抛出
*/
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
在进行动态代理设计的时候对于动态对象的创建是由JVM底层完成的,此时主要依靠的是java.lang.reflect.Proxy程序类,这个程序类中只提供有一个核心方法:
- 创建代理对象:
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
- ClassLoader:获取当前真实主体类的ClassLoader;
- Class<?>[] interfaces:代理是围绕接口进行的,所以一定要获取真实主体类的接口信息;
- InvocationHandler h:代理处理的方法;
实现动态代理机制
package cn.mldn.demo;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class Proxy_study {
public static void main(String[] args) {
IMessage msg = (IMessage)new MessageProxy().bind(new MessageReal());
msg.send();
}
}
interface IMessage{ //传统代理设计必须有接口
public void send() ; //业务方法
}
class MessageReal implements IMessage{
@Override
public void send() {
System.out.println("*******Message send*******");
}
}
class MessageProxy implements InvocationHandler{
private Object target; //保存真实业务对象
/**
* 进行真实业务对象与代理业务对象之间的绑定处理
* @param target 真实业务对象
* @return Proxy生成的代理业务对象
*/
public Object bind(Object target) {
this.target = target;
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
public boolean connect() {
System.out.println("[消息代理]进行消息发送通道的连接!");
return true;
}
public void close() {
System.out.println("[消息代理]关闭消息发送通道!");
}
@Override
public Object invoke(Object pro, Method method, Object[] args) throws Throwable {
System.out.println("[执行方法]"+ method);
Object returnData = null;
if(this.connect()) {
returnData = method.invoke(this.target, args);
this.close();
}
return returnData;
}
}
如果认真观察系统提供的newProxyInstance()方法会发现该方法会使用大量的底层机制来进行代理对象的动态创建。所有的代理类是符合所有相关功能需求的操作功能类,它不在代表具体的接口,这样在处理的时候就必须依赖于类加载器与接口进行代理对象的伪造。
CGLIB实现代理设计模式
从Java官方来讲已经明确要求了如果要想实现代理设计模式,那么一定是基于接口的应用,所以在官方给出的Proxy类创建代理对象时都需要传递该对象所有的接口信息。
但是有一部分开发者认为不应该强迫性的基于接口实现代理设计,所以一些开发者开发出了CGLIB的开发包,利用这个开发包就可以实现基于类的代理设计模式。
- CGLIB是一个第三方的程序包,需要单独在Eclipse中进行配置,假定程序包路径为:“C:\jar-lib\cglib-nodep-3.2.6.jar”,需要打开Eclipse打开项目属性安装第三方开发包。
- 编写程序类,该类不实现任何接口
class MessageReal{
public void send() {
System.out.println("*******Message send*******");
}
}
- CGLIB编写代理类,但是这个代理了需要做一个明确,此时相当于使用类的形式实现了代理设计的一个出来,所以需要通过CGLIB生成代理对象。
class MessageProxy implements MethodInterceptor{ //拦截器配置
private Object target; //保存真实主题对象
public MessageProxy(Object target) {
this.target = target;
}
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy metohdProxy) throws Throwable {
Object returnData = null;
if(this.connect()) {
returnData = method.invoke(this.target, args);
this.close();
}
return returnData;
}
public boolean connect() {
System.out.println("[消息代理]进行消息发送通道的连接!");
return true;
}
public void close() {
System.out.println("[消息代理]关闭消息发送通道!");
}
}
- 此时要想创建代理类对象,那么就必须进行一系列的CGLIB处理
public class Proxy_study {
public static void main(String[] args) {
Message realObject = new Message(); //真实主题对象
Enhancer enhancer = new Enhancer(); //负责代理操作的程序类
enhancer.setSuperclass(realObject.getClass()); //假定一个父类
enhancer.setCallback(new MessageProxy(realObject)); //设置代理类
Message proxyObject = (Message)enhancer.create(); //创建代理对象
proxyObject.send();
}
}
在进行代理设计模式定义的时候除了可以使用接口之外,也可以不受接口限制实现基于类的代理设计,但是从正常设计角度来讲,还是基于接口设计比较合理。