spring之动态代理

文章介绍了Java中的动态代理技术,包括JDK动态代理和CGLIB动态代理。JDK动态代理基于接口实现,适用于代理实现了接口的目标对象,而CGLIB则是通过继承目标类来创建代理,因此可以代理没有接口的类。文中详细展示了两种方式的实现步骤,包括接口定义、目标对象、客户端程序以及调用处理器或方法拦截器的实现,最后通过示例展示了动态代理在性能监控等方面的应用。
摘要由CSDN通过智能技术生成


前言

在程序运行阶段,在内存中动态生成代理类,被称为动态代理,目的是为了减少代理类的数量。解决代码复用的问题。
在内存中生成动态生成类的技术常见的包括:

  • 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;
    }
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值