1. 引入依赖  102

CGLIB既可以代理接口,又可以代理类。底层采用继承的方式实现。所以被代理的目标类不能使用final修饰。

使用CGLIB,需要引入它的依赖:

<dependency>
  <groupId>cglib</groupId>
  <artifactId>cglib</artifactId>
  <version>3.3.0</version>
</dependency>

2. 具体实现  102

我们准备一个没有实现接口的类,如下:

package com.powernode.proxy.service;

/**
 * 目标类  CGLIB动态代理  102
 **/
public class UserService {

    // 目标方法
    public boolean login(String username, String password){
        System.out.println("系统正在验证身份...");
        if ("admin".equals(username) && "123".equals(password)) {
            return true;
        }
        return false;
    }

    // 目标方法
    public void logout(){
        System.out.println("系统正在退出...");
    }
}

使用CGLIB在内存中为UserService类生成代理类,并创建对象:

package com.powernode.proxy.client;

import com.powernode.proxy.service.TimerMethodInterceptor;
import com.powernode.proxy.service.UserService;
import net.sf.cglib.proxy.Enhancer;

//CGLIB动态代理  102
public class Client {
    public static void main(String[] args) {
        // 创建字节码增强器对象
        // 这个对象是CGLIB库当中的核心对象,就是依靠它来生成代理类。
        Enhancer enhancer = new Enhancer();

        // 告诉CGLIB父类是谁。告诉CGLIB目标类是谁。
        enhancer.setSuperclass(UserService.class);

        // 设置回调(等同于JDK动态代理当中的调用处理器。InvocationHandler)
        // 在CGLIB当中不是InvocationHandler接口,是方法拦截器接口:MethodInterceptor
        enhancer.setCallback(new TimerMethodInterceptor());

        // 创建代理对象
        // 这一步会做两件事:
        // 第一件事:在内存中生成UserService类的子类,其实就是代理类的字节码。
        // 第二件事:创建代理对象。
        // 父类是UserService,子类这个代理类一定是UserService
        UserService userServiceProxy = (UserService) enhancer.create();

        // 建议大家能够把CGLIB动态代理生成的代理对象的名字格式有点印象。
        // 根据这个名字可以推测框架底层是否使用了CGLIB动态代理
        System.out.println(userServiceProxy);

        // 调用代理对象的代理方法。
        boolean success = userServiceProxy.login("admin", "123");
        System.out.println(success ? "登录成功" : "登录失败");

        userServiceProxy.logout();
    }
}


// 底层本质
//class UserService$$EnhancerByCGLIB$$82cb55e3 extends UserService{}

和JDK动态代理原理差不多,在CGLIB中需要提供的不是InvocationHandler,而是:net.sf.cglib.proxy.MethodInterceptor

编写MethodInterceptor接口实现类:

MethodInterceptor接口中有一个方法intercept(),该方法有4个参数:

第一个参数:目标对象

第二个参数:目标方法

第三个参数:目标方法调用时的实参

第四个参数:代理方法

package com.powernode.proxy.service;

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
 * CGLIB动态代理  102
 **/
public class TimerMethodInterceptor implements MethodInterceptor {

    @Override
    public Object intercept(Object target, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        // 前面增强
        long begin = System.currentTimeMillis();

        // 怎么调用目标对象的目标方法呢?
        Object retValue = methodProxy.invokeSuper(target, objects);

        // 后面增强
        long end = System.currentTimeMillis();
        System.out.println("耗时"+(end - begin)+"毫秒");

        return retValue;
    }
}

对于高版本的JDK,如果使用CGLIB,需要在启动项中添加两个启动参数:

CGLIB动态代理_System

● --add-opens java.base/java.lang=ALL-UNNAMED

● --add-opens java.base/sun.net.util=ALL-UNNAMED

CGLIB动态代理_CGLIB动态代理_02