代理模式
代理模式的主要作用是不想在原有的类上做扩展,所以可以借助一个代理类来调用原有类的方法,然后将扩展内容在代理类中进行扩展(增强)。其中代理对象起到调用者和目标类之间的一个桥梁作用(类似于中介)。
动态代理更为灵活,无需对每个需要扩展的类(目标类)单独创建一个代理类,也不要我们实现目标类的接口,可以直接代理实现类。
从jvm角度理解,动态代理是运行时动态生成字节码文件,并加载入jvm中。 java中主要有两种动态代理方法,一种jdk动态代理,另一种就是CGLib动态代理。
例子:
有一个接口SendMessage
接口,其中有一个发送短信方法sendDefineMsg(String msg)
,我们想要在客户端调用发送短信的方法,并且在发送短信时,做一些扩展内容,但是不想在SendMessage
接口实现类SendMessageImpl
中增加代码。那么如何通过动态代理来实现呢?
public interface SendMessage {
public void SendDefineMessage(String msg);
}
public class SendMessageImpl implements SendMessage {
public void SendDefineMessage(String msg) {
System.out.println("send the message: "+msg);
}
}
下面我们从动态代理的两种主要方式来解决该问题:
jdk动态代理
jdk动态代理的核心主要是invocationHandler
接口和proxy
类。
其中invocationHandler
接口起到一个类似于中转的作用,在调用目标类的方法时,需要先要调用invoke(Object proxy, Method method, Object[] args)
方法(invocationHandler
接口必须要实现的方法), 那么要扩展的内容可以就在invoke()
方法中进行增强。其中proxy
是代理类的对象,method
用于发起对目标类中方法的调用,args
为调用方法的参数。
proxy
类用于生成代理类对象。其中借助newProxyInstance(ClassLoader loader, Class<?> intefaces, InvocationHandler h)
可以生成目标类的代理类对象。loader
是目标类的类加载器, interfaces
为目标类实现的接口集合, h
为invocationHandler
的实现类对象。
根据上面jdk动态代理提供的相关接口和类,可以按照下面的步骤来使用动态代理实现短信发送的增强:
- 自定义实现
InvocationHandler
接口,并实现invoke()
方法
public class ProxyInvocationHandlerImpl implements InvocationHandler {
// 被代理的类对象
private Object object;
public ProxyInvocationHandlerImpl(Object object){
this.object = object;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 执行被代理类所调用的方法之前扩展的内容
System.out.println("执行sendMessage()之前:......");
// 代理类会调用invoke()发放,通过向invoke()方法中传入被代理类的对象,从而实现被代理类中的方法
Object res = method.invoke(object, args);
// 执行被代理类所调用的方法之后扩展的内容
System.out.println("执行sendMessage()之后:......");
return res;
}
}
- 通过
proxy
类生成代理对象
public class ProxyFactory {
// 这里传入的object是被代理的类对象
public static Object GetStaticProxy(Object object){
return Proxy.newProxyInstance(
// 被代理类的类加载器
object.getClass().getClassLoader(),
// 被代理类所实现的接口
object.getClass().getInterfaces(),
// 自定义的InvocationHandler实现类
new ProxyInvocationHandlerImpl(object)
);
}
}
- 通过代理对象调用短信发送方法
public class Test {
public static void main(String[] args) {
// 被代理的类对象
SendMessage sendMessage = new SendMessageImpl();
// 代理调用sendMessage()方法
SendMessage proxy = (SendMessage) ProxyFactory.GetProxy(new SendMessageImpl());
proxy.SendDefineMessage("消息");
}
}
//执行sendMessage()之前:......
//send the message: 消息
//执行sendMessage()之后:......
注意:jdk动态代理只能代理接口实现类,若想要代理未实现接口类中的方法时,就需要通过下面的CGLIB动态代理。
CGLib动态代理
CGLIBopen in new window(Code Generation Library)是一个基于ASMopen in new window的字节码生成库,它允许我们在运行时对字节码进行修改和动态生成。CGLIB 通过继承方式实现代理。很多知名的开源框架都使用到了CGLIBopen in new window, 例如 Spring 中的 AOP 模块中:如果目标对象实现了接口,则默认采用 JDK 动态代理,否则采用 CGLIB 动态代理。
CGLIB动态代理的核心是MethodInterceptor
接口和Enhancer
类。
其中MethodInterceptor
接口内部包含一个类似于invoke()
方法的intercept()
方法,用于拦截增强被代理类的方法。intercept(Obbject obj, Method method, Object[] args, MethodProxy proxy)
。其中obj
为被代理的对象,method
被拦截的方法,args
方法的参数,proxy
用于调用原始方法
Enhancer
类用于生成代理对象。
由于CGLIB动态代理最大的不同之处在于,其可以代理未实现任何接口的类。我们将例子中的发送短信接口SendMessage
改为类。
public class SendMessage {
public void SendDefineMessage(String msg) {
System.out.println("send the message: "+msg);
}
}
- 实现
Interceptor
接口,并实现intercept()
方法
public class MethodInterceptorImpl implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
// 执行被代理类所调用的方法之前扩展的内容
System.out.println("执行sendMessage()之前:......"+method.getName());
// 代理类会调用invoke()发放,通过向invoke()方法中传入被代理类的对象,从而实现被代理类中的方法
Object res = methodProxy.invokeSuper(o, args);
// 执行被代理类所调用的方法之后扩展的内容
System.out.println("执行sendMessage()之后:......"+method.getName());
return res;
}
}
- 使用
Enhancer
类生成代理对象
public class ProxyFactory {
public static Object GetProxy(Class<?> clazz){
// 创建动态代理增强类
Enhancer enhancer = new Enhancer();
// 设置类加载器
enhancer.setClassLoader(clazz.getClassLoader());
// 设置被代理类
enhancer.setSuperclass(clazz);
// 设置方法拦截器
enhancer.setCallback(new MethodInterceptorImpl());
// 创建代理类
return enhancer.create();
}
}
- 使用代理对象调用调用短信发送方法
public class Test {
public static void main(String[] args) {
SendMessage sendMessage = (SendMessage) ProxyFactory.GetProxy(SendMessage.class);
sendMessage.SendDefineMessage("消息2");
}
}
//执行sendMessage()之前:......SendDefineMessage
//send the message: 消息2
//执行sendMessage()之后:......SendDefineMessage
注意:CGLIB是一个开源项目,使用前需要导入依赖
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
两者的对比
- JDK动态代理实现了接口的类或者直接代理接口,而CGLIB可以代理未实现任何接口的类。另外CGLIB是通过生成一个被代理类的子类来拦截被代理类的方法调用,因此不能代理被
final
修饰的类和方法。 - 大部分情况下,jdk动态代理的效率更加优秀。