传统代理设计模式的弊端
所有的代理设计模式如果按照设计要求来讲, 必须是基于接口的设计,需要首先定义出核心接口的组成。
【范例】代理设计
package demo;
public class JavaAPIDemo {
public static void main(String[] args) throws Exception {
IMessage msg = new MessageProxy(new MessageReal());
msg.send();
}
}
interface IMessage{//传统代理设计必须有接口
public void send();//业务方法
}
class MessageReal implements IMessage{
@Override
public void send() {
System.out.println("【发送消息】~~~~~");
}
}
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();
}
}
}
【连接中】。。。。。。
【发送消息】~~~~~
【消息代理】关闭消息通道
客户端的接口与具体的子类产生了耦合问题,所以这样的操作从实际的开发来讲最好在引入工厂设计模式进行代理对象的获取。
以上代理设计模式为静态代理设计模式,这种静态代理设计的特点在于,一个代理类只为一个接口服务。
现在需要解决的问题:如何可以让一个代理类满足于所有的业务接口操作要求。
动态代理设计模式
通过静态代理设计模式的缺陷可以发现,最好的做法是为所有功能一致的业务操作接口提供有同意的代理处理操作,而这可以通过动态代理机制来实现,需要考虑到如下几点问题:
- 不管是动态代理类还是静态代理欸都一定要接收真实业务实现子类对象;
- 由于动态代理类不再与某一个具体的接口进行捆绑,所以应该可以动态获取类的接口信息;
在进行动态代理实现的操作之中,首先需要关注的就是一个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 staitc Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces, InvocationHandler h)
|- ClassLoader loader:获取当前真实主题类的CLassLoader;
|- Class<?>[]interfaces:代理是围绕接口进行的,所以一定要获取最真实主题类的接口信息;
|- InvocationHandler h:代理处理的方法
【范例】实现动态代理机制
package demo;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class JavaAPIDemo {
public static void main(String[] args) throws Exception {
IMessage msg = (IMessage) new MLDNProxy().bind(new MessageReal());
msg.send();
}
}
class MLDNProxy 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;
}
}
interface IMessage{//传统代理设计必须有接口
public void send();//业务方法
}
class MessageReal implements IMessage{
@Override
public void send() {
System.out.println("【发送消息】~~~~~");
}
}
********【执行方法:】public abstract void demo.IMessage.send()
【消息代理】进行消息发送通道的链接
【发送消息】~~~~~
【消息代理】关闭消息通道。
CGLIB实现代理设计模式
如果要想实现代理设计模式,那么一定是基于接口的应用,所以在官方给出的Proxy类创建代理对象时都需要传递该对象所有的接口信息:
Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
部分开发者认为不应该强迫性的基于接口来实现代理设计,所以就开发处了CGLIB开发包,利用这个开发包就可以实现基于类的代理设计模式。
1、CGLIB是一个第三方的程序包,需要单独在Eclipse中,properties进行配置
cglib-nodep安装包下载链接
2、编写程序类,该类不实现任何接口。
class Message {
public void send() {
System.out.println("【发送消息】~~~~~");
}
}
3、利用CGLIB编写代理类,但是这个代理类需要做一个明确,此时相当于使用了类的形式实现了代理设计的处理,所以该代理设计需要通过CGLIB来生成代理对象,定义一个代理类:
class MLDNProxy implements MethodInterceptor{
private Object target;//保存真实主体对象
public MLDNProxy(Object target) {
this.target = target;//保存真实主体对象
}
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) 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("【消息代理】关闭消息通道。");
}
}
4、此时如果要想创建代理类对象,则就必须进行一系列的CGLIB处理。
package demo;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
public class JavaAPIDemo {
public static void main(String[] args) throws Exception {
Message realObject = new Message();//真实主体对象
Enhancer enhancer = new Enhancer();//负责代理操作的程序类
enhancer.setSuperclass(realObject.getClass());//假定一个父类
enhancer.setCallback(new MLDNProxy(realObject));//设置代理类
Message proxyObject = (Message) enhancer.create();//创建代理对象
proxyObject.send();
}
}
class MLDNProxy implements MethodInterceptor{
private Object target;//保存真实主体对象
public MLDNProxy(Object target) {
this.target = target;//保存真实主体对象
}
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) 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("【消息代理】关闭消息通道。");
}
}
class Message {
public void send() {
System.out.println("【发送消息】~~~~~");
}
}
//本段代码由于导报出现问题,暂时未能实现
在进行代理设计模式定义的时候除了可以使用接口外,也可以不受接口的限制而实现基于类的代理设计,但是从正常的设计模式来讲,强烈建议还是基于接口的设计会比较合理。
参考子类:https://edu.aliyun.com/lesson_1012_9064#_9064