文章目录
前言
在程序运行阶段,在内存中动态生成代理类,被称为动态代理,目的是为了减少代理类的数量。解决代码复用的问题。
在内存中生成动态生成类的技术常见的包括:
- JDK动态代理技术:只能代理接口
- CGLIB动态代理技术:CGLIB是一个开源项目。是一个强大的,高性能,高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口。它既可以代理接口,又可以代理类,底层是通过继承方式实现的。性能比JDK动态代理要好。(底层有一个小而快的字节码处理框架ASM)
- Javassist动态代理技术:Javassist是一个开源的分析、编辑和创建Java字节码的类库。
一、JDK动态代理
1、业务接口OrderService
public interface OrderService {
void generate();
void modify();
void detail();
}
2、目标对象OrderServiceImpl
public class OrderServiceImpl implements OrderService{
@Override
public void generate() {
//模拟生成订单的耗时
try{
Thread.sleep(1234);
}catch(Exception e){
e.printStackTrace();
}
System.out.println("订单已生成");
}
@Override
public void modify() {
try{
Thread.sleep(443);
}catch(Exception e){
e.printStackTrace();
}
System.out.println("订单已修改");
}
@Override
public void detail() {
try{
Thread.sleep(546);
}catch(Exception e){
e.printStackTrace();
}
System.out.println("查看订单详情");
}
}
3、客户端程序Client
newProxyInstance翻译为:新建代理对象,也就是说,通过调用这个方法可以创建代理对象
本质上,这个Proxy.newProxyInstance() 方法的执行,做了两件事:
* 第一件事:在内存中动态的生成了一个代理类的字节码class。
* 第二件事:new对象了。通过内存中生成的代理类这个代码,实例化了对象
三个参数的含义:
第一个参数:ClassLoader loader
在内存当中生成的字节码也是class文件,要执行也得先加载到内存当中,加载类就需要类加载器,所以这里指定需要类加载器,并且JDK要求,目标类的类加载器必须和代理类的类加载器使用同一个(target.getClass().getClassLoader())
第二个参数:Class<?>[] interface
* 代理类和目标类要实现同一个接口或同一些接口
* 在内存中生成代理类的时候,这个代理类是需要你告诉他实现哪些接口的
* target.getClass().getInterface()
第三个参数:InvocationHandler h 调用处理器,是一个接口
* 在调用处理器接口中编写的就是增强代码。
* 因为具体要增强什么代码,JDK动态代理是猜不到的。
* 既然是接口,就要写接口的实现类
public class Client {
public static void main(String[] args) {
//创建目标对象
OrderService target = new OrderServiceImpl();
//创建代理对象(借助JDK中的类)
OrderService proxy = (OrderService)Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),new TimeInvocationHandler(target));
//调用代理对象的代理方法
//注意:调用代理对象的代理方法的时候,如果要做增强的话,目标对象的目标方法得保证执行
proxy.generate();
}
}
4、InvocationHandler 的实现类TimeInvocationHandler
- 专门负责计时的一个调用处理器对象
- 在这个调用处理器当中编写计时相关的增强代码
1、为什么强行要求必须实现InvocationHandler接口?
因为一个类实现接口就必须实现接口中实现的方法
2、invoke()方法什么时候调用呢?
当代理对象调用代理方法的时候,注册在InvocationHandler调用处理器当中的invoke()方法被调用
3、invoke方法的三个参数:
invoke方法是JDK负责调用的,所以JDK调用这个方法的时候会自动给我们传过来这三个参数第一个参数:Object proxy 代理对象的引用
第二个参数:Method method 目标对象的目标方法(要执行的目标方法就是它)
第三个参数:Object[] args 目标方法上的实参
public class TimeInvocationHandler implements InvocationHandler {
private Object target;
public TimeInvocationHandler(Object target) {
//赋值给成员变量
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//这个接口的目的就是为了让你有地方写增强代码
long begin = System.currentTimeMillis();
//调用目标对象上的目标方法
//方法四要素:那个对象 哪个方法 传什么参数 返回什么值
Object retValue = method.invoke(target,args);
long end = System.currentTimeMillis();
System.out.println("耗时"+(end-begin)+"毫秒");
//注意这个invoke方法的返回值,如果代理对象调用代理方法之后,需要返回结果的话,invoke方法必须将目标对象的目标方法执行结果继续返回
return retValue;
}
}
这里考虑传哪个对象时,由于客户端程序中创建了一个目标对象,那么我们把这个变量传到代理类中,即在代理类中创造一个构造方法,完成赋值。
5、运行结果
代码得到了复用,不会造成类爆炸
二、CGLIB动态代理
CGLIB既可以代理接口,又可以代理类。底层采用继承的方式实现。所以被代理的类不能使用final修饰。
1、先引入依赖
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
2、目标类 UserService
public class UserService {
//目标方法
public boolean login(String username,String password){
System.out.println("系统正在验证身份");
if("admin".equals(username) && "123".equals(password)){
return true;
}
else return false;
}
//目标方法
public void logout(){
System.out.println("系统正在退出...");
}
}
3、客户端程序Client
在CGLIB当中不是InvocationHandler接口,是方法拦截器:MethodInterceptor
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 TimeMethodInterceptor());
//创建代理对象
//这一步会做两件事:
//第一件事:在内存中生成UserService类的子类,其实就是代理类的字节码
//第二件事:创建代理对象
//父类是UserService 子类这个代理类一定实现UserService
UserService userServiceProxy = (UserService) enhancer.create();
//调用代理对象的代理方法
boolean success = userServiceProxy.login("admin", "123");
System.out.println(success ? "登陆成功":"登录失败");
userServiceProxy.logout();
}
}
4、MethodInterceptor的实现类TimeMethodInterceptor
public class TimeMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object target, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
//增强语句
long begin = System.currentTimeMillis();
//调用方法(目标对象的目标方法)
Object retValue = methodProxy.invoke(target, objects);
//增强语句
long end = System.currentTimeMillis();
System.out.println("耗时"+(end-begin)+"毫秒");
return retValue;
}
}