Proxy与CGLib实现动态代理

0、静态代理

  • 参考链接:https://www.yusian.com/blog/?p=1978

  • 简单地说:一个对象实现了另一个对象的功能,我们就将前者称为后者的代理。然而单纯地实现原始对象的功能没有太大的意义,一般而言,代理的目的是在原始对象功能的基础上进行额外的扩展,我们称之为附加功能

  • 静态代理很好地诠释了代理的作用,但由于静态代理的缺陷,利用反射机制对其进行改造便有了动态代理。

  • 只要是代理,其核心目的都是在原始对象的基础上扩展附加功能,这一点始终不变。

  • 常用的动态代理有2种,一种JDK自带的Proxy类及对其进行封装的相关类,另一种是以CGLib为代表的第三方库

1、Proxy

1.1、基本原理

  • Proxy基于接口,动态生成字节码文件给JVM,得到该接口另一个实现类的实例,由于都是对一个接口的实现,利用多态的特性,可以代理原始对象完成接口所有方法的调用。
  • 调用过程中通过对所有方法的拦截,实现附加功能的扩展;
  • 这个拦截的过程通过接口的方式实现;

1.2、两个重要的方法

  • 生成代理类的方法:

    /**
    * classLoader: 生成字节码文件需要的类加载器,由于该类是动态生成的,没有类加载器,这里可以写任意一个存在的类的类加载器
    * interfaces: 原始对象的接口,代理对象需要实现与原始相同的接口
    * invocationHandler: 实现拦截方法的对象
    */
    Proxy.newProxyInstance(classLoader, interfaces, invocationHandler)
    
  • 拦截方法
    /**
    * 原始方法对应的参数在该方法中通过参数的方式传递进来,当前方法的返回值将替代原始方法的返回值
    * 可在当前方法中执行原始方法,得到原始方法的结果,从而又可以在原始方法的调用前后进行附加功能扩展
    */
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      return ret;
    }
    

1.3、示例说明

UserService

package com.yusian.service;

import com.yusian.domain.User;

public interface UserService {

    void register(User user);

    boolean login(String username, String password);

}

UserServiceImpl

package com.yusian.service;

import com.yusian.domain.User;

public class UserServiceImpl implements UserService {
    @Override
    public void register(User user) {
        System.out.println("UserServiceImpl.register--------原始功能");
    }

    @Override
    public boolean login(String username, String password) {
        System.out.println("UserServiceImpl.login--------原始功能");
        return false;
    }
}

Proxy代理

UserService userService = new UserServiceImpl();
UserService userServiceProxy = (UserService) Proxy.newProxyInstance(UserService.class.getClassLoader(), UserServiceImpl.class.getInterfaces(), new InvocationHandler() {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("before....");
        Object ret = method.invoke(userService, args);
        System.out.println("after....");
        return ret;
    }
});
userServiceProxy.login("yusian", "123456");

执行结果:

before...
UserService.login
after...

Process finished with exit code 0

2、CGLib

2.1、基本原理

  • 对标Proxy通过实现原始中对象相同的接口来获取相同的接口调用能力,CGLib通过继承原始对象的类获取相同的接口调用能力。
  • CGLib的使用与Proxy高度类似;
  • 相对Proxy,通过继承的方式来代理原始对象更具有通用性;

2.2、几个重要的方法

  • 初始化一个Enhancer工具类的实例对象

  • 指定类加载器方法setClassLoader,与Proxy类似;

  • 指定父类setSuperclass,指定原始对象的类型,相当于Proxy的interfaces;

  • 指定回调setCallback,参数是一个接口,与Proxy类似;

  • 创建代理对象create,生成一个代理对象,类似Proxy的newProxyInstance;

  • 拦截方法

    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
      return null;
    }
    

2.3、示例说明

UserService

package com.yusian.service;

public class UserService {

    /**
     * 登录
     * @param username 用户名
     * @param password 密码
     */
    public void login(String username, String password) {
        System.out.println("UserService.login------原始方法");
    }
}

CGLib代理

UserService userService = new UserService();
Enhancer enhancer = new Enhancer();
enhancer.setClassLoader(UserService.class.getClassLoader());
enhancer.setSuperclass(UserService.class);
enhancer.setCallback(new MethodInterceptor() {
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("before...");
        Object ret = method.invoke(userService, objects);
        System.out.println("after...");
        return ret;
    }
});

UserService userServiceProxy = (UserService) enhancer.create();
userServiceProxy.login("yusian", "123456");

执行结果

before...
UserService.login------原始方法
after...

Process finished with exit code 0

Leave a Reply