静态代理非常简单,但是使用较少,因为对目标对象的每个方法的增强都是手动完成的。
定义一个接口,发消息:
public interface SmsService {
String send(String message);
}
实现这个接口:
public class SmsServiceImpl implements SmsService {
public String send(String message) {
System.out.println("send message:" + message);
return message;
}
}
创建代理类并实现接口,将实现类注入进代理类:
public class SmsProxy implements SmsService {
private final SmsService smsService;
public SmsProxy(SmsService smsService) {
this.smsService = smsService;
}
@Override
public String send(String message) {
//调用方法之前的操作
System.out.println("before");
smsService.send(message);
//调用方法之后的操作
System.out.println("after");
return "success";
}
}
使用时:
public class Main {
public static void main(String[] args) {
SmsService smsService = new SmsServiceImpl();
SmsProxy smsProxy = new SmsProxy(smsService);
smsProxy.send("hello");
}
}
执行代码,控制台将输出:
before
send message:hello
after
从上面的实现可以看出,我们必须手动给每个方法实现增强。非常不方便,并且接口修改接口后,实现类和代理类都需要修改,令人崩溃!!!
使用动态代理,不需要针对每个目标类都单独创建一个代理类,并且也不需要目标类必须实现接口,可以直接代理实现类( CGLIB 动态代理)。
动态代理是在运行时动态生成类字节码,并加载到 JVM 中的。
JDK动态代理,要求目标类必须实现接口。
Proxy类是JDK动态代理的核心类,其中最为重要的方法为newProxyInstance(),用于生成一个代理对象。
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
......
}
他的参数含义如下:
InvocationHandler 接口是JDK动态代理的核心接口。其重要的方式是 invoke 。
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
** 当使用代理对象调用方法的时候实际会调用到这个方法。**
他的参数含义如下:
proxy : 动态生成的代理类
method : 与代理类对象调用的方法相对应
args : 当前 method 方法的参数
通过Proxy 类的 newProxyInstance() 创建的代理对象在调用方法的时候,实际会调用到实现InvocationHandler 接口的类的 invoke()方法
使用JDK动态代理的步骤如下:
同样用发消息的接口为例。
接口:
public interface SmsService {
String send(String message);
}
实现类:
public class SmsServiceImpl implements SmsService {
public String send(String message) {
System.out.println("send message:" + message);
return message;
}
}
实现 InvocationHandler 接口:
public class TestInvocationHandler implements InvocationHandler {
//真实对象
private final Object target;
public TestInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws InvocationTargetException, IllegalAccessException {
//调用方法之前的操作
System.out.println("before method " + method.getName());
Object result = method.invoke(target, args);
//调用方法之后的操作
System.out.println("after method " + method.getName());
return result;
}
}
当动态代理对象调用原生方法的时候,最终实际上调用到的是 invoke() 方法,然后 invoke() 方法去调用了被代理对象的原生方法。
获取代理对象的工厂类:
public class JdkProxyFactory {
public static Object getProxy(Object target) {
return Proxy.newProxyInstance(
target.getClass().getClassLoader(), // 目标类的类加载器
target.getClass().getInterfaces(), // 代理需要实现的接口,可指定多个
new TestInvocationHandler(target) // 代理对象对应的自定义 InvocationHandler
);
}
}
工厂类主要作用就是获取代理对象。
实际使用:
SmsService smsService = (SmsService) JdkProxyFactory.getProxy(new SmsServiceImpl());
smsService.send("hello");
运行代码,控制台打印:
before method send
send message:hello
after method send
JDK动态代理要求目标类必须要实现接口,原因是生成的代理类,继承了Proxy类。如下:
CGLIB(Code Generation Library)是一个基于ASM的字节码生成库,它允许我们在运行时对字节码进行修改和动态生成。CGLIB 通过继承方式实现代理。
方法拦截器。
MethodInterceptor 接口中的重要方法是 intercept 方法,用于拦截增强被代理类的方法。
public interface MethodInterceptor extends Callback {
public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args,MethodProxy proxy) throws Throwable;
}
参数含义如下:
Enhancer 类可以动态获取被代理类,当代理类调用方法的时候,实际调用的是 MethodInterceptor 中的 intercept 方法。
其中的重要方式是create():
使用CGLIB需要添加依赖
<dependency>
<groupId>cglibgroupId>
<artifactId>cglibartifactId>
<version>3.3.0version>
dependency>
还是以发消息为例。
使用阿里云发消息:
public class AliSmsService {
public String send(String message) {
System.out.println("send message:" + message);
return message;
}
}
实现 MethodInterceptor 接口:
public class TestMethodInterceptor implements MethodInterceptor {
/**
* @param o 被代理的对象(需要增强的对象)
* @param method 被拦截的方法(需要增强的方法)
* @param args 方法入参
* @param methodProxy 用于调用原始方法
*/
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
//调用方法之前的操作
System.out.println("before method " + method.getName());
Object object = methodProxy.invokeSuper(o, args);
//调用方法之后的操作
System.out.println("after method " + method.getName());
return object;
}
}
获取动态代理类的工厂:
public class CglibProxyFactory {
public static Object getProxy(Class<?> clazz) {
// 创建动态代理增强类
Enhancer enhancer = new Enhancer();
// 设置类加载器
enhancer.setClassLoader(clazz.getClassLoader());
// 设置被代理类
enhancer.setSuperclass(clazz);
// 设置方法拦截器
enhancer.setCallback(new TestMethodInterceptor());
// 创建代理类
return enhancer.create();
}
}
实际使用:
AliSmsService aliSmsService = (AliSmsService) CglibProxyFactory.getProxy(AliSmsService.class);
aliSmsService.send("hello");
运行代码,控制台打印:
before method send
send message:hello
after method send
本篇分析了静态代理与动态代理,其中动态代理分析了JDK动态代理与CGLIB动态代理。
JDK动态代理要求被代理类必须实现接口,CGLIB以继承的方式实现代理。
在众多框架中,广泛使用了动态代理,了解动态代理能更好的理解和学习各种框架的原理。