java中的静态代理、jdk动态代理以及CGLIB 动态代理

代理模式是一种比较好理解的设计模式。简单来说就是 我们使用代理对象来代替对真实对象(real object)的访问,这样就可以在不修改原目标对象的前提下,提供额外的功能操作,扩展目标对象的功能
那以下文章主要谈三种代理模式,分别是静态代理,jdk的动态代理,cglib的动态代理

本文相关代码已提交至gitee仓库 链接放在了文末 有需要自取。

1.静态代理

简要描述

静态代理中,我们对目标对象的每个方法的增强都是手动完成的,非常不灵活(比如接口一旦新增加方法,目标对象和代理对象都要进行修改)且麻烦(需要对每个目标类都单独写一个代理类)

实现

1.创建一个接口

/**
 * @program: Blog-agency
 * @description:
 * @author: Daigl
 * @create: 2022-12-02 20:37
 **/
public interface SmsService {

    String send(String message);
}

2.创建一个实现类

该类实现上面的接口,并重写里面的send方法。即我们的目标方法

/**
 * @program: Blog-agency
 * @description:
 * @author: Daigl
 * @create: 2022-12-02 20:38
 **/
public class SmsServiceImpl implements SmsService{


    @Override
    public String send(String message) {
        System.out.println("目标方法:" + message);
        return message;
    }
}

3.创建一个代理类

/**
 * @program: Blog-agency
 * @description:
 * @author: Daigl
 * @create: 2022-12-02 20:39
 **/
public class SmsProxy implements SmsService{

    //定义一个smsService的字段
    private SmsService smsService;

    //在初始化该代理类的时候,带一个参数 smsService 给上面的字段赋值
    public SmsProxy(SmsService smsService){
        this.smsService = smsService;
    }

    //重写send方法,并对send方法做加强(前后新增输出)
    //这也是静态代理的麻烦之处 当我们代理的接口新增方法时,我们的代理类须同时修改
    @Override
    public String send(String message) {
        System.out.println("前置增强,可新增自己的业务代码");

        String targetMethod = smsService.send(message);

        System.out.println("后置增强,可新增自己的业务代码");

        return null;
    }
}

4.创建主函数

/**
 * @program: Blog-agency
 * @description:
 * @author: Daigl
 * @create: 2022-12-02 20:42
 **/
public class Main {

    public static void main(String[] args) {
        SmsServiceImpl smsService = new SmsServiceImpl();
        SmsProxy smsProxy = new SmsProxy(smsService);
        smsProxy.send("java");
    }

}

5.输出

前置增强,可新增自己的业务代码
目标方法:java
后置增强,可新增自己的业务代码

总结

静态代理很好的实现了在不修改原有代码的基础上进行业务拓展的功能,即我们上面的代码 实现的在目标方法执行前后 执行我们的业务代码

但是静态代理是代理类在创建的时候,接口以及代理类就已经确定了,因此一个静态代理类只能代理一个类,并且当目标类的方法新增后,静态代理类也得随之修改,造成代码的冗余。

2.jdk动态代理

简要描述

为了解决静态代理的不足(代码冗余以及代码写死的缺点),于是便有了动态代理

动态代理是指代理类在程序运行时进行创建的代理方式。这种情况下,代理类并不是在Java代码中定义的,而是在运行时根据Java代码动态生成的。(动态代理中的代理类并不要求在编译期就确定,而是可以在运行期动态生成,从而实现对目标对象的代理功能)
但他有一个致命的缺点,只能代理实现了接口的类

实现

1.创建一个接口

/**
 * @program: Blog-agency
 * @description:
 * @author: Daigl
 * @create: 2022-12-02 20:37
 **/
public interface SmsService {

    String send(String message);
}

2.创建一个实现类

public class SmsServiceImpl implements SmsService {
    @Override
    public String send(String message) {
        System.out.println("目标方法:" + message);
        return message;
    }
}

3.创建一个代理类

/**
 * @program: Blog-agency
 * @description:
 * @author: Daigl
 * @create: 2022-12-02 21:21
 **/
//代理类
public class DebugInvocationHandler{
        private SmsService smsService;

        //1.构造器 传入动态绑定的对象  接口的某一子类
        public DebugInvocationHandler(SmsService smsService){
            this.smsService = smsService;
        }

        /**
         * 2.得到一个代理的对象  通过这个方法 可以去调用方法
         * @return
         */
        public SmsService getProxy(){
            //a.凑第一个参数 类加载器
            ClassLoader classLoader = smsService.getClass().getClassLoader();

            //b.凑第二个参数 要代理的对象/被执行对象 的接口信息,底层是通过接口来完成调用
            Class<?>[] interfaces = smsService.getClass().getInterfaces();

            //c.凑第三个参数 InvocationHandler
            //使用了匿名内部类方法
            InvocationHandler invocationHandler = new InvocationHandler() {

                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    System.out.println("前置增强,可新增自己的业务代码");
                    // smsService 是 代理中的 真实对象
                    // args 是方法参数 比如我们调用的是send方法 传过来的参数是java 那这里就是一个数组 里面有一个值 java
                    Object result = method.invoke(smsService, args);
                    System.out.println("后置增强,可新增自己的业务代码 ");
                    return result;
                }
            };
        //最主要的是这个方法  给这个方法凑参数
        //Proxy.newProxyInstance 返回的是Object类型 我们可以给他强转为SmsService 此时他已经是一个单例的对象
        //用该对象去调用某一个方法时,会走到上面的invoke方法
        SmsService proxy = (SmsService) Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);
        return proxy;
    }
}

5.输出

/**
 * @program: Blog-agency
 * @description:
 * @author: Daigl
 * @create: 2022-12-02 21:23
 **/
public class Main {
    public static void main(String[] args) {
        //传入的时需要代理的实现类的对象
        DebugInvocationHandler debugInvocationHandler = new DebugInvocationHandler(new SmsServiceImpl());
        SmsService proxy = debugInvocationHandler.getProxy();
        proxy.send("java");
    }
}

3.CGLIB动态代理

简要描述

一个第三方代码生成类库,运行时在内存中动态生成一个子类对象从而实现对目标对象功能的扩展。 通过继承目标类,在子类中重写父类同名方法,实现功能修改(重写的方法不能是final)

和jdk动态代理形成互补。在java源码中,实现了接口的类的代理用jdk动态代理,没有实现的用cglib代理

实现

1. 创建需要代理的类。此时没有接口

/**
 * @program: Blog-agency
 * @description:
 * @author: Daigl
 * @create: 2022-12-02 21:55
 **/
public class AliSmsService {
    public String send(String message) {
        System.out.println("目标方法:" + message);
        return message;
    }
}

2.引入cglib依赖

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

3.创建代理类

/**
 * @program: Blog-agency
 * @description:
 * @author: Daigl
 * @create: 2022-12-02 21:56
 **/
public class CglibMethodInterceptor implements MethodInterceptor {


    /**
     * @param o           代理对象(增强的对象)
     * @param method      被拦截的方法(需要增强的方法)
     * @param args        方法入参
     * @param methodProxy 用于调用原始方法
     */
    @Override
    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        //调用方法之前,我们可以添加自己的操作
        System.out.println("前置增强,可新增自己的业务代码");
        Object object = methodProxy.invokeSuper(o, args);
        //调用方法之后,我们同样可以添加自己的操作
        System.out.println("后置增强,可新增自己的业务代码");
        return object;
    }

}

4.创建获取代理对象的工厂类

/**
 * @program: Blog-agency
 * @description:
 * @author: Daigl
 * @create: 2022-12-02 21:58
 **/
public class CglibProxyFactory {

    public static Object getProxy(Class<?> clazz) {
        // 创建动态代理增强类
        Enhancer enhancer = new Enhancer();
        // 设置类加载器
        enhancer.setClassLoader(clazz.getClassLoader());
        // 设置被代理类
        enhancer.setSuperclass(clazz);
        // 设置我们自定定义的代理类(方法拦截器)
        enhancer.setCallback(new CglibMethodInterceptor());
        // 创建代理类
        return enhancer.create();
    }
}

5.创建主函数

/**
 * @program: Blog-agency
 * @description: ]
 * @author: Daigl
 * @create: 2022-12-02 21:58
 **/
public class Main {
    public static void main(String[] args) {
        
        Object object = CglibProxyFactory.getProxy(AliSmsService.class);
        AliSmsService aliSmsService = (AliSmsService) object;
        aliSmsService.send("java");

    }
}

6.输出

前置增强,可新增自己的业务代码
目标方法:java
后置增强,可新增自己的业务代码

谢谢大家看完。相关代码已经放在gitee:https://gitee.com/gaoludai/blog

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
<h3>回答1:</h3><br/>JDK动态代理CGLIB动态代理都是Java动态代理技术,但它们的实现方式不同。 JDK动态代理是基于接口的代理,它要求被代理的类必须实现一个接口,代理类实现该接口并在代理类调用被代理类的方法。JDK动态代理使用Java自带的反射机制实现,因此它的效率比较高。 CGLIB动态代理是基于继承的代理,它不要求被代理的类实现接口,代理类继承被代理类并重写被代理类的方法。CGLIB动态代理使用ASM框架实现,因此它的效率比JDK动态代理略低,但它可以代理没有实现接口的类。 总的来说,JDK动态代理适用于代理实现了接口的类,而CGLIB动态代理适用于代理没有实现接口的类。 <h3>回答2:</h3><br/>JDK动态代理CGLIB动态代理都是常见的Java动态代理技术,但它们在实现方式和使用场景上有很大的不同。 1. 实现方式 JDK动态代理是基于接口的代理技术,它通过生成代理对象实现了目标对象相同的接口,并将方法调用委托给目标对象执行。类似于RMI(远程方法调用)和WebService等技术,只要目标对象实现了相应的接口,就可以使用JDK动态代理生成代理对象,实现对目标对象的代理。 CGLIB动态代理则是基于继承的代理技术,它通过生成目标对象的子类实现对目标对象的代理。在目标对象没有实现接口或者不能实现接口时,CGLIB动态代理是唯一的选择。 2. 性能 由于JDK动态代理是基于接口的代理技术,它的性能比CGLIB动态代理略优。但当目标对象没有实现接口或者需要代理的方法很多时,JDK动态代理就无能为力了。 CGLIB动态代理通过继承目标对象的子类实现对目标对象的代理,因此它的性能相对于JDK动态代理要略强一些。但由于使用了字节码技术,CGLIB动态代理对应用服务器提出了较高的要求,如需要在运行时动态生成目标对象的子类并加载到JVM,可能会导致启动时间变长,但一旦子类被加载到JVM,它的执行效率将会比JDK动态代理的执行效率高。 3. 使用场景 JDK动态代理适用于代理对象实现了接口的情况,如在Spring的AOP开发,就采用了JDK动态代理实现不同的通知类型,如前置通知、后置通知、环绕通知等。 CGLIB动态代理则适用于目标对象没有实现接口或者需要代理的方法很多的情况,如在Hibernate的ORM框架,就采用了CGLIB动态代理实现对实体对象进行代理。在使用CGLIB动态代理时,需要注意的是目标对象的构造函数、私有方法和final方法等无法被代理。 <h3>回答3:</h3><br/>JDK动态代理CGLIB动态代理都是实现Java代理模式的技术手段,其主要区别在于代理对象的生成方式和代理对象的类型。 JDK动态代理是通过实现代理接口的方式进行代理。代理对象必须实现与被代理对象相同的接口,然后利用InvocationHandler接口,在运行时动态生成代理对象。 CGLIB动态代理是通过生成目标类的子类来实现代理。CGLIB使用ASM框架生成字节码,并将其转换为代理类的字节码,从而生成代理类。因为CGLIB动态代理是基于继承的方式进行代理,所以被代理对象必须是可继承的类。 因为JDK动态代理是基于接口实现的,对于没有实现接口的类或者接口没有抽象方法的接口无法使用动态代理实现代理。CGLIB动态代理没有这个问题,它可以代理所有的类,包括final类。 另外,在代理对象的性能上,由于JDK动态代理是基于接口实现的,所以其性能相对较高。而CGLIB动态代理需要通过生成子类来完成代理,因此需要更多的时间和内存,性能相对较低。 综上所述,两种动态代理都有各自的优缺点,具体使用时需要根据实际情况和需求进行选择。如果被代理对象实现了接口或者需要代理的功能比较简单,可以使用JDK动态代理;如果被代理对象没有实现接口或者需要代理的功能比较复杂,可以使用CGLIB动态代理

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

搬砖的阿陆

你的鼓励就是我前进的动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值