您的位置:首页 > 编程语言 > Java开发

【JAVA,动态代理,设计模式】设计模式浅析之动态代理模式

2018-10-25 15:00 183 查看

设计模式浅析之动态代理模式

近期在看Spring以及Mybatis的相关书籍,其中Spring的AOP(Aspect Oriented Programming)面向切面编程的实现使用了动态代理模式。本文简述个人对于动态代理模式中的动态代理技术中的两种的理解。

java中动态代理技术有JDK,CGLIB,Javassist,ASM。其中最常用的两种就是本文要说的JDK,CGLIB。

JDK动态代理:是JDK自带的功能,它必须借助接口才能产生动代理对象。

CGLIB(Code Generation Library):它是第三方提供的一个技术。CGLIB项目是一个开源的项目,扩展性非常强。

1.什么是动态代理模式。

动态代理的意义在于生成一个代理对象,来代理真实对象,从而控制外部对真实对象的访问。

举个简单的例子来看:代理对象就相当于公司的前台,真实对象就相当于公司的高管。有人来访一般会在前台登记,前台会判断来访者是否可以会见高管,然后前台会让来访者进行登记,访问高管,访问之后再在对来访记录进行登记。

可以用一张图来理解:

外部的访问可以理解成来访者,前台可以比作代理对象,高管是真实对象。在来访者访问到高管之前,可能会有一些安来访登记的操作,以及访问之后来访登记的结束记录。如果利用一个前台代理高管的一个工作,高管只需要接受来访者的到访,同时还可以去除一些没有必要的访问。

代理模式中的前台作为代理对象,那么他需要和高管即真实对象之间,建立一个代理关系,不能说A公司的前台,给B公司高管处理来访的人员登记吧。这样就会出问题。所以代理必须满足下面的条件:

1、代理对象必须和真实对象之间建立代理关系。A公司的前台,给B公司高管处理来访的人员登记,需要在A公司离职,在B公司入职。

2、实现代理对象的代理逻辑方法。前台咨询来访者是不是有预约、以及来访登记等等。

使用的场景:

图片来源:https://www.geek-share.com/detail/2691651649.html

2、JDK动态代理

JDK动态代理是java.lang.reflect.*包中提供的方式,它必须借助一个接口才能产生代理对象。

[code]//代理对象需要借助的接口
public interface HelloWorld {
public void sayHelloWorld();
}

//真实对象,实现接口
public class HelloWorldImpl implements HelloWorld {

@Override
public void sayHelloWorld() {
System.out.println("Hello World");
}

}

按照代理必须满足的条件,要先建立起来代理对象和真实对象之间的代理关系,然后实现代理逻辑。

JDK动态代理中,要实现代理逻辑类必须实现java.lang.reflect.InvocationHandler接口,它定义了一个invoke方法,并提供接口数组用于下挂代理对象。

[code]import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class JdkProxyExample implements InvocationHandler {

// 真实对象
private Object target = null;

/**
* 建立代理对象和真实对象的代理关系,并返回代理对象
* @param target真实对象
* @return 代理对象
*/
public Object bind(Object target) {
this.target = target;
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}

/**
* 代理方法逻辑
*
* @param proxy --代理对象
* @param method --当前调度方法
* @param args --当前方法参数
* @return 代理结果返回
* @throws Throwable 异常
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("进入代理逻辑方法");
System.out.println("在调度真实对象之前的服务");
Object obj = method.invoke(target, args);// 相当于调用sayHelloWorld方法
System.out.println("在调度真实对象之后的服务");
return obj;
}
}

❤建立代理对象和真实对象的关系。使用bind方法完成,方法里首先使用target类保存真实对象,然后通过代码生成代理对象。

newProxyInstance方法包含的3个参数:

  • 第一个是类加载器,采用了target本身的类加载器。
  • 第二个是把生成的动态代理对象下挂在哪些接口下,这个写法就是放在target实现的接口HelloWorld下。
  • 第三个是定义实现方法逻辑的代理类,this鞭尸当前对象,它必须实现InvocationHandler接口的invoke方法,它就是代理逻辑方法的实现方法。

❤实现代理逻辑的方法。invoke方法可以实现代理逻辑。

invoke方法包含的3个参数:

  • proxy,代理对象,就是bind方法生成的对象。就是公司的前台。
  • method,当前调度的方法。(如果不知道的话,可以去看下java的反射,后续可以下一篇问,说一说。)
  • args,调度方法的参数。

测试一下:

[code]	public static void testJdkProxy() {
JdkProxyExample jdk = new JdkProxyExample();
// 绑定关系,因为挂在接口HelloWorld下,所以声明代理对象HelloWorld proxy
HelloWorld proxy = (HelloWorld) jdk.bind(new HelloWorldImpl());
// 注意,此时HelloWorld对象已经是一个代理对象,它会进入代理的逻辑方法invoke里
proxy.sayHelloWorld();
}

测试运行结果:

 

3、CGLIB动态代理

如果一个类没有实现的接口那么就不能,那么用JDK动态代理技术是没有办法实现代理的。如果说高管没有一个稳定的办公场所,就比方说这个公司是一个初创公司比较小,没有资金花再办公场所的建设上。

这时候只能采用第三方技术,CGLIB动态代理。它的优势在于不需要提供接口,是要一个非抽象类就可以实现动态代理。

看这个一个非抽象类:

[code]public class ReflectServiceImpl {
public void sayHello(String name) {
System.err.println("Hello " + name);
}

}

使用CGLIB动态代理代码如下:

[code]import java.lang.reflect.Method;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

public class CglibProxyExample implements MethodInterceptor {
/**
* 生成CGLIB代理对象
* @param cls -- Class类
* @return Class类的CGLIB代理对象
*/
public Object getProxy(Class cls) {
// CGLIB enhancer增强类对象
Enhancer enhancer = new Enhancer();
// 设置增强类型
enhancer.setSuperclass(cls);
// 定义代理逻辑对象为当前对象,要求当前对象实现MethodInterceptor方法
enhancer.setCallback(this);
// 生成并返回代理对象
return enhancer.create();
}

/**
* 代理逻辑方法
* @param proxy 代理对象
* @param method 方法
* @param args 方法参数
* @param methodProxy 方法代理
* @return 代理逻辑返回
* @throws Throwable异常
*/
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.err.println("调用真实对象前");
// CGLIB反射调用真实对象方法
Object result = methodProxy.invokeSuper(proxy, args);
System.err.println("调用真实对象后");
return result;
}
}

这里用了CGLIB的Enhance,通过设置超类的方法(setSuperclass),然后通过setCallback方法设置为哪个类做代理。

这里的两个必要条件:

1、getProxy方法,建立代理对象和真实对象之间的关系

2、intercept方法,实现代理逻辑。

测试一下:

[code]public static void tesCGLIBProxy() {
CglibProxyExample cpe = new CglibProxyExample();
ReflectServiceImpl obj = (ReflectServiceImpl)cpe.getProxy(ReflectServiceImpl.class);
obj.sayHello("张三");
}

测试结果:

动态代理模式,我个人理解也就这个多。其中说的来访者、前台还有高管,纯属个人理解,如果觉得不合适的话,欢迎留言指正。

本文是看书《Java EE互联网轻量级框架整合开发》,个人感觉可以一起分享出来。

 

 

阅读更多
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: