代理模式: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);  
    }  
}

这个类包含三个方法,一个成员变量(细节见注释)。

  1. 成员变量target表示被代理的对象,类型为Object,表示可以代理任意类型的类。
  2. 构造方法UserInvocationHander,传参获取被代理类的对象
  3. 方法invoke,表示对方法增强的操作,这里实现了方法调用前后的增强
  4. 方法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基本一致。主要两个不同。

  1. intercept方法,需要区别method和methodProxy两个参数;
    1. method表示被代理对象本身的方法,因此通过反射调用的时候需要传的参数为target,即被代理对象本身,如果直接使用method.invoke,那么还会再调用触发一次代理逻辑。
    2. methodProxy表示代理对象的方法,因此通过反射调用的时候需要传的参数为o,即代理对象。
    3. methodProxy的invokeSuper和invoke也有不同。invokeSuper表示直接调用代理对象的父类(也就是被代理对象)方法;invoke表示直接调用代理对象的方法,如果使用 methodProxy.invoke则会递归调用增强后的代理对象的方法(可能会栈溢出)。
  2. 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。

posted @ 2025-04-20 22:03  eien  阅读(101)  评论(0)    收藏  举报