三分钟吃透代理技术!

开发 前端
如果目标对象至少实现了一个接口,那么就用JDK动态代理,所有由目标对象实现的接口将全部都被代理。如果目标对象没有实现任何接口,就是个类,那么就用CGLIB代理。

最近有网友问了我一些问题,什么是代理,又该在什么地方使用。结合之前的讨论,这篇文章我们一起细致的讲解一下关于代理的一些问题。

在 Java 中,代理通常分为两类:

  • 静态代理
  • 动态代理

两者技术实现是不一样的,具体有什么区别呢?下面我们一起来看看。

一、静态代理

我们先说静态代理的实现方式,为什么不推荐使用静态代理?

1.继承方式实现代理(静态代理中的继承代理)

//目标对象

public class UserImpl {
    public void system(){
        System.out.println("输出测试");
    }
}

//代理对象
public class Proxy extends UserImpl {

    @Override
    public void system() {
        super.system();
        System.out.println("增强之后的输出");
    }
}

//测试类

public class TestMain {
    public static void main(String[] args) {
        UserImpl user = new Proxy();
        user.system();
    }
}

静态代理可以看出来一点问题吧?

每次代理都要实现一个类,导致项目中代码很多;你每次想要代理,都要去实现一个类,代码就会成堆的增加,然后你就会发现项目的类就会越来越多,就会导致你们的项目显得很臃肿。而且代码的复用性太低了,并且耦合度非常高,这个我们所说的高内聚低耦合是相悖的。

二、动态代理

我们在看一下这个动态代理:

//接口类
public interface Italk {
    public void talk(String msg);
}
//实现类
public class People implements Italk {
        public String username;
        public String age;
        public String getName() {
        return username;
    }
    public void setName(String name) {
        this.username= name;
    }
    public String getAge() {
        return age;
    }
    public void setAge(String age) {
        this.age = age;
    }
    public People(String name1, String age1) {
        this.username= name1;
        this.age = age1;
    }
    public void talk(String msg) {
        System.out.println(msg+"!你好,我是"+username+",我年龄是"+age);
     }
}
//代理类
public class TalkProxy implements Italk {
        Italk talker;
    public TalkProxy (Italk talker) {
        //super();
        this.talker=talker;
    }
    public void talk(String msg) {
        talker.talk(msg);
    }
    public void talk(String msg,String singname) {
        talker.talk(msg);
        sing(singname);
    }
    private void sing(String singname){
         System.out.println("唱歌:"+singname);
    }
}
//测试
public class MyProxyTest {
    //代理模式
    public static void main(String[] args) {
    //不需要执行额外方法的
    Italk people1=new People("湖海散人","18");
    people1.talk("No ProXY Test");
    System.out.println("-----------------------------");
    //需要执行额外方法的
    TalkProxy talker=new TalkProxy(people1);
    talker.talk("ProXY Test","七里香");
    }
}

代码解析如下:

一个 Italk 接口,有空的方法 talk()(说话),所有的 people 对象都实现(implements)这个接口,实现 talk() 方法,前端有很多地方都将 people 实例化,执行 talk 方法,后来发现这些前端里有一些除了要说话以外还要唱歌(sing),那么我们既不能在 Italk 接口里增加 sing() 方法,又不能在每个前端都增加 sing 方法,我们只有增加一个代理类 talkProxy ,这个代理类里实现 talk 和 sing 方法,然后在需要 sing 方法的客户端调用代理类即可,

这也是实现动态代理的方式,是通过实现(implements)的方式来实现的,这种方法的优点,在编码时,代理逻辑与业务逻辑互相独立,各不影响,没有侵入,没有耦合。

三、cgLib代理

还有一种是cgLib的代理,这种代理则是适合那些没有接口抽象的类代理,而Java 动态代理适合于那些有接口抽象的类代理。

我们来通过代码了解一下他到底是怎么玩的。

/**
 * 业务类,
 */
public class TestService {
    public TestService() {
        System.out.println("TestService的构造");
    }
    /**
     * 该方法不能被子类覆盖,Cglib是无法代理final修饰的方法的
     */
    final public String sayOthers(String name) {
        System.out.println("TestService:sayOthers>>"+name);
        return null;
    }
    public void sayHello() {
        System.out.println("TestService:sayHello");
    }
}
/**
 * 自定义MethodInterceptor
 */
public class MethodInterceptorTest implements MethodInterceptor {
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("======插入前置通知======");
        Object object = methodProxy.invokeSuper(o, objects);
        System.out.println("======插入后者通知======");
        return object;
    }
}
/**
 * 测试
 */
public class Client {
    public static void main(String[] args) {
        // 代理类class文件存入本地磁盘方便我们反编译查看源码
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\code");
        // 通过CGLIB动态代理获取代理对象的过程
        Enhancer enhancer = new Enhancer();
        // 设置enhancer对象的父类
        enhancer.setSuperclass(TestService.class);
        // 设置enhancer的回调对象
        MethodInterceptorTest t =  new  MethodInterceptorTest();
        enhancer.setCallback(t);
        // 创建代理对象
        TestService proxy= (TestService)enhancer.create();
        // 通过代理对象调用目标方法
        proxy.sayHello();
    }
}

运行结果:

CGLIB debugging enabled, writing to 'D:\code'
TestService的构造
======插入前置通知======
TestService:sayHello
======插入后者通知======

实现CGLIB动态代理必须实现MethodInterceptor(方法拦截器)接口,

这个接口只有一个intercept()方法,这个方法有4个参数:

  • obj表示增强的对象,即实现这个接口类的一个对象;
  • method表示要被拦截的方法;
  • args表示要被拦截方法的参数;
  • proxy表示要触发父类的方法对象;

四、代理的使用

那么什么时候使用静态态代理,什么时候使用动态代理和cgLib代理呢?

一般情况静态代理是很少是用的,因为他对代码的复用性或者说是耦合度都非常不友好,不推荐使用。

如果目标对象至少实现了一个接口,那么就用JDK动态代理,所有由目标对象实现的接口将全部都被代理。

如果目标对象没有实现任何接口,就是个类,那么就用CGLIB代理。

责任编辑:武晓燕 来源: Java极客技术
相关推荐

2024-10-15 09:18:30

2024-05-16 11:13:16

Helm工具release

2009-11-09 12:55:43

WCF事务

2023-12-27 08:15:47

Java虚拟线程

2024-01-16 07:46:14

FutureTask接口用法

2022-02-17 09:24:11

TypeScript编程语言javaScrip

2021-04-20 13:59:37

云计算

2024-08-30 08:50:00

2020-06-30 10:45:28

Web开发工具

2013-06-28 14:30:26

棱镜计划棱镜棱镜监控项目

2021-12-17 07:47:37

IT风险框架

2020-06-29 07:42:20

边缘计算云计算技术

2024-07-05 09:31:37

2024-01-12 07:38:38

AQS原理JUC

2009-11-05 16:04:19

Oracle用户表

2021-02-03 14:31:53

人工智能人脸识别

2023-12-04 18:13:03

GPU编程

2023-11-23 19:26:43

2023-12-22 09:37:13

二分查找数组数据库

2020-03-08 16:45:58

数据挖掘学习数据量
点赞
收藏

51CTO技术栈公众号