代理模式:jdk动态代理 和 cglib动态代理的实现
使用jdk的动态代理
jdk的动态代理用于实现了接口的类的动态代理。
业务例子
首先crud的业务代码如下:
public interface UserService {
public void addUser();
public Integer deleteUser();
}
public class UserServiceImpl implements UserService {
@Override
public void addUser() {
System.out.println("addUser");
}
@Override
public Integer deleteUser() {
System.out.println("deleteUser");
return 1;
}
}
为此我们希望对UserServiceImpl中的方法进行增强,如事务管理,方法计时等等……并且UserServiceImpl实现了UserService接口,因此可以采用jdk的方式实现动态代理。
使用jdk动态代理实现方法增强
首先需要编写一个代理处理器类
/**
* 使用jdk实现动态代理的类,需要实现InvocationHandler接口
*/
public class UserInvocationHander implements InvocationHandler {
/**
* 目标对象,指的是被代理的类
*/
private Object target;
public UserInvocationHander(Object target) {
this.target = target;
}
/**
* 通过动态代理增强目标对象所执行的方法
* @param proxy 代理对象
* @param method 目标对象所执行的方法
* @param args 目标对象所执行的方法的参数
* @Return Object,目标对象所执行的方法的返回值
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object o = null;
System.out.println("前置处理");
o = method.invoke(target, args);// 这里通过反射调用目标对象的方法
System.out.println("后置处理");
return o;
}
/**
* 获取代理对象
* @return
*/
public Object getProxy(){
/**
* 第一个参数获取目标对象的类加载器,
* 第二个参数获取目标对象所实现的接口,从而可以通过代理对象调用目标对象的方法
* 第三个参数是InvocationHandler接口的实现类,告诉 JVM生成的代理对象应该使用的哪个 InvocationHandler 来处理
*
* 返回值是代理对象,这个代理对象和目标对象使用了同一个类加载器,实现了相同的接口,并且使用了当前InvocationHandler处理
* 这个代理对象使用方法时,会调用当前InvocationHandler接口的invoke方法
*/
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
}
这个类包含三个方法,一个成员变量(细节见注释)。
- 成员变量target表示被代理的对象,类型为Object,表示可以代理任意类型的类。
- 构造方法UserInvocationHander,传参获取被代理类的对象
- 方法invoke,表示对方法增强的操作,这里实现了方法调用前后的增强
- 方法getProxy,获取代理对象
使用方式
可以通过以下代码调用增强后的方法
// 构造函数,传参获取目标对象new UserServiceImpl()
UserInvocationHander userInvocationHander = new UserInvocationHander(new UserServiceImpl());
// 通过getProxy获得代理对象
UserService userService = (UserService) userInvocationHander.getProxy();
System.out.println(userService.getClass());
System.out.println();
// 调用增强了的方法addUser
userService.addUser();
System.out.println();
// 调用增强了的方法 deleteUser
Integer deleteUserNum = userService.deleteUser();
System.out.println("deleteUserNum = " + deleteUserNum);
注:通过getProxy获得代理对象中,getProxy()的返回值类型是object,但是其中Proxy.newProxyInstance创建的代理对象实现了与target相同的接口,因此可以直接强转为UserService,即UserServiceImpl实现了的接口类型。
运行结果
class com.sun.proxy.$Proxy86
前置处理
addUser
后置处理
前置处理
deleteUser
后置处理
deleteUserNum = 1
使用cglib的动态代理
cglib的动态代理用于没有实现接口的类的动态代理。
业务例子
这里需要修改上面UserServiceImpl的代码,取消对UserService接口的实现
public class UserServiceImpl {
@Override
public void addUser() {
System.out.println("addUser");
}
@Override
public Integer deleteUser() {
System.out.println("deleteUser");
return 1;
}
}
使用cglib动态代理实现方法增强
首先编写一个UserInterceptor拦截器,拦截器实现MethodInterceptor,其中MethodInterceptor需要导入cglib包,可以通过maven导入,或者spring框架也已经导入了这个包。
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
<dependency>
public class UserInterceptor implements MethodInterceptor {
private Object target;
public UserInterceptor(Object target) {
this.target = target;
}
/**
* 通过动态代理增强目标对象所执行的方法
* @param o cglib生成的代理对象
* @param method 目标对象所执行的方法
* @param objects 目标对象所执行的方法的参数
* @param methodProxy 生成的代理类对方法的代理引用
* @return
* @throws Throwable
*/ @Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("前置处理");
Object result = methodProxy.invokeSuper(o, objects);
//Object result = methodProxy.invoke(o, objects);
//Object result = method.invoke(target, objects);
System.out.println("后置处理");
return result;
}
public Object getInstance() {
// 创建增强器
Enhancer enhancer = new Enhancer();
// 设置增强的对象类型(目标对象),也就是代理对象的父类类型
enhancer.setSuperclass(target.getClass());
// 设置回调函数,即代理对象要执行的方法intercept
enhancer.setCallback(this);
// 生成代理对象并返回
return enhancer.create();
}
}
UserInterceptor拦截器的实现方式和上面jdk基本一致。主要两个不同。
- intercept方法,需要区别method和methodProxy两个参数;
- method表示被代理对象本身的方法,因此通过反射调用的时候需要传的参数为target,即被代理对象本身,如果直接使用method.invoke,那么还会再调用触发一次代理逻辑。
- methodProxy表示代理对象的方法,因此通过反射调用的时候需要传的参数为o,即代理对象。
- methodProxy的invokeSuper和invoke也有不同。invokeSuper表示直接调用代理对象的父类(也就是被代理对象)方法;invoke表示直接调用代理对象的方法,如果使用 methodProxy.invoke则会递归调用增强后的代理对象的方法(可能会栈溢出)。
- getInstance方法,创建代理对象的方式不同,cglib通过Enhancer增强器创建。setSuperclass()和setCallback()类似jdk动态代理创建代理对象的第二和第三个参数。
使用方式
UserInterceptor userInterceptor = new UserInterceptor(new UserServiceImpl());
UserServiceImpl userService = (UserServiceImpl) userInterceptor.getInstance();
System.out.println(userService);
System.out.println();
userService.addUser();
System.out.println();
userService.deleteUser();
System.out.println();
运行结果
com.example.service.UserServiceImpl$$EnhancerByCGLIB$$addc4573@6428591a
前置处理
addUser
后置处理
前置处理
deleteUser
后置处理
总结
使用jdk动态代理是创建了一个和被代理对象实现了相同接口的代理对象;使用cglib动态代理是创建了一个被代理对象的子类的对象。spring中一般使用jdk动态代理的方式实现AOP。

浙公网安备 33010602011771号