专业的编程技术博客社区

网站首页 > 博客文章 正文

终于搞懂静态代理、JDK和CGLIB动态代理,面试再也不慌了

baijin 2024-09-04 02:03:03 博客文章 10 ℃ 0 评论

代理模式

概念:为其他对象提供一种代理,控制对这个对象的访问,代理对象起到了中介的作用,去掉了某些功能,或者是增加了一些额外的服务

应用场景:火车票代售处购买火车票,火车票代售处就是火车站的代理

代理分类

虚拟代理、智能代理、远程代理、保护代理

智能代理(开发中最常见):比如,日志处理、权限管理、事务处理等。

代理实现方式

静态代理:代理和被代理对象在代理之前是确定的,他们都实现相同的接口或者继承相同的抽象类

静态代理实现方式:

继承代理:通过继承需要代理的类,在子类调用父类,在调用前后记录日志

public class Car implements Moveable
{
    @Override
    public void move()
    {
        try
        {
            Thread.sleep(new Random().nextInt(1000));
            System.out.println("汽车正在行驶。。。");
            Thread.sleep(new Random().nextInt(1000));
        } catch (Exception e)
        {
            e.printStackTrace();
        }
    }
}
/**
 * 使用继承方式实现代理
 */
public class ProxyCar extends Car
{
    @Override
    public void move()
    {
        long startTime = System.currentTimeMillis();
        System.out.println("汽车开始行驶。。。");
        super.move();
        long endTime = System.currentTimeMillis();
        System.out.println("汽车结束行驶,共行驶时间:" + (endTime - startTime) + "毫秒");
    }
}
Test:
//继承方式代理
//        ProxyCar proxyCar=new ProxyCar();
//        proxyCar.move();

聚合代理:代理类和被代理类,都要实现相同接口,被代理类作为代理类的成员变量,通过构造器传入代理类,在代理类中调用被代理类方法

/**
 * 一个类有一个类的实体引用(类中的类),则它称为聚合
 */
public class ProxyCar2 implements Moveable
{
    private Car car;
    public ProxyCar2(Car car)
    {
        this.car = car;
    }
    @Override
    public void move()
    {
        long startTime = System.currentTimeMillis();
        System.out.println("汽车开始行驶。。。");
        car.move();
        long endTime = System.currentTimeMillis();
        System.out.println("汽车结束行驶,共行驶时间:" + (endTime - startTime) + "毫秒");
    }
}
Test:
	//聚合方式代理
//        Car car=new Car();
////        ProxyCar2 proxyCar2=new ProxyCar2(car);
////        proxyCar2.move();

动态代理

实现对不同类,不同方法代理

ProxyHandler:代理类和被代理类之间加入实现InvocationHandler的类ProxyHandler

Proxy:该类即为动态代理类,

Class cls=car.getClass();

Moveable m= (Moveable) Proxy.newProxyInstance(cls.getClassLoader(),cls.getInterfaces(),ih);

返回代理类的一个实例,返回后的代理类可以当作被代理类使用(可使用被代理类在接口中声明过的方法)

JDK动态代理

JDK动态代理实现的前提条件

1.被代理类需要实现某个interface,为什么?

由于java的单继承,动态生成的代理类已经继承了Proxy类的,就不能再继承其他的类,所以只能靠实现被代理类的接口的形式,故JDK的动态代理必须有接口

动态代理的思路便是根据我们传入的参数生成一个新类

2.代理类,必须实现InvocationHandler接口

动态代理实现步骤:

1.创建一个实现InvocationHandler接口的类,它必须实现invoke方法

public class TimeHandler implements InvocationHandler
{
    private Object target;

    public TimeHandler(Object target)
    {
        this.target = target;
    }

    /**
     * @param proxy  被代理对象
     * @param method 被代理对象方法
     * @param args   被代理对象方法参数
     * @return 方法返回值
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
    {
        long startTime = System.currentTimeMillis();
        System.out.println("汽车开始行驶。。。");
        method.invoke(target);
        long endTime = System.currentTimeMillis();
        System.out.println("汽车结束行驶,共行驶时间:" + (endTime - startTime) + "毫秒");
        return null;
    }

2.创建被代理类和接口

3.调用Proxy的静态方法,创建一个代理类对象

 public static void main(String[] args)
    {
        Car car=new Car();
        InvocationHandler ih=new TimeHandler(car);
        Class cls=car.getClass();

        /**
         * 参数1:类加载器
         * 参数2:实现接口
         * 参数3:实现的处理器
         */
        Moveable m= (Moveable) Proxy.newProxyInstance(cls.getClassLoader(),cls.getInterfaces(),ih);
        System.out.println(m.getClass().getName());
        m.move();
    }

4.通过代理类对象调用方法

CGLIB动态代理

public class Train
{
    public void move(){
        System.out.println("火车行驶中...");
    }
}
public class CglibProxy implements MethodInterceptor
{
    private Enhancer enhancer=new Enhancer();
    public Object getProxy(Class clazz){
        //设置创建子类的类,也就是为哪个类产生代理类
        enhancer.setSuperclass(clazz);
        enhancer.setCallback(this);//回调
        return enhancer.create();//创建子类实例
    }

    /**
     * 拦截所有目标类方法的调用
     * @param obj 目标类的实例
     * @param method 目标方法反射对象
     * @param args 方法参数
     * @param proxy 代理类实例
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable
    {
        System.out.println("日志开始...");
        //代理类调用父类方法,cglib使用继承方式,所有代理类是父类子类
        proxy.invokeSuper(obj,args);
        System.out.println("日志结束...");
        return null;
    }
 public static void main(String[] args)
    {
        CglibProxy proxy=new CglibProxy();
        Train train= (Train) proxy.getProxy(Train.class);
        train.move();
    }

注意:CGLIB是invokeSuper别调错了

所需jar包,只有cglib的jar包还不够,还需要asm的jar包,可取maven仓库下载

JDK动态代理和CGLIB动态代理区别

JDK动态代理:1.只能代理实现接口的类 2.没有实现接口的类不能实现jdk动态代理

CGLIB动态代理:1.针对类实现代理 2.原理对指定目标类产生一个子类,覆盖其中方法实现功能增强 3.因为是继承所以不能对final修饰的类进行代理









Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表