Java动态代理

本文介绍了Java动态代理,包括JDK动态代理的原理、CGLIB动态代理的区别,以及它们在SpringAOP中的应用。讨论了两种代理方式的优缺点和使用场景,强调了动态代理在代码重用、解耦和性能优化中的重要性。
摘要由CSDN通过智能技术生成

Java动态代理

1、什么是动态代理

动态代理是一种运行时生成代理类的机制,能够在代理类中处理被代理类的方法调用。 代理类是自己定义好的,

在程序运行之前就已经编译完成。然而动态代理,代理类并不是在Java代码中定义的,而是在运行时根据我们在

Java代码中的指示动态生成的。相比于静态代理, 动态代理的优势在于可以很方便的对代理类的函数进行统一的

处理,而不用修改每个代理类中的方法。

动态代理是一种设计模式,它允许在运行时创建代理对象,并将方法调用重定向到不同的实际对象。它使我们能够

在不修改现有代码的情况下增加或改变某个对象的行为。

动态代理是反射的一个非常重要的应用场景。动态代理常被用于一些 Java 框架中。例如 Spring 的 AOP ,Dubbo

的 SPI 接口,就是基于 Java 动态代理实现的。

动态代理提供了一种灵活且非侵入式的方式,可以对对象的行为进行定制和扩展。它在代码重用、解耦和业务逻辑

分离、性能优化以及系统架构中起到了重要的作用。

  • 增强对象的功能:通过动态代理,可以在不修改原始对象的情况下,对其方法进行增强或添加额外的行为。可

    以在方法执行前后进行一些操作,比如日志记录、性能监测、事务管理等。

  • 解耦和业务逻辑分离:动态代理可以将对象的特定操作从业务逻辑中解耦,使得代码更加模块化和可维护。代

    理对象可以负责处理一些通用的横切关注点,而业务对象可以专注于核心业务逻辑。

  • 实现懒加载:通过动态代理,可以延迟加载对象,只有在真正需要使用对象时才会进行创建和初始化,从而提

    高性能和资源利用效率。

  • 实现远程方法调用:动态代理可以用于实现远程方法调用(RPC)和分布式系统中的服务代理。客户端通过代理

    对象调用远程服务,并隐藏了底层网络通信的细节。

  • 实现AOP编程:动态代理是实现面向切面编程(AOP)的基础。通过代理对象,可以将横切关注点(如日志、事

    务、安全性)与业务逻辑进行解耦,提供更高层次的模块化和可重用性。

2、动态代理的两种方式

两种方式:

1、JDK动态代理:利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处

理。

2、CGLIB 动态代理,利用ASM(开源的Java字节码编辑库,操作字节码)开源包,对代理对象类的class文件加载

进来,通过修改其字节码生成子类来处理。

Java动态代理和 cglib 比较,区别:

  • 生成代理类技术不同:

    java动态代理:jdk自带类ProxyGenerator生成class字节码

    cglib:通过ASM框架生成class字节码文件

  • 生成代理类的方式不同:

    java动态代理:代理类继承java.lang.reflect.Proxy,实现被代理类的接口

    cglib:代理类继承被代理类,实现net.sf.cglib.proxy.Factory

  • 生成类数量不同

    java动态代理:生成一个proxy类

    cglib:生成一个proxy,两个fastclass类

  • 调用方式不同

    java动态代理:代理类->InvocationHandler->反射调用被代理类方法

    cglib:代理类->MethodInterceptor->调用索引类invoke->直接调用被代理类方法

JDK代理只能对实现接口的类生成代理;CGlib是针对类实现代理,对指定的类生成一个子类,并覆盖其中

的方法,这种通过继承类的实现方式,不能代理final修饰的类。

3、JDK动态代理案例

在Java中,可以使用Java的反射机制来实现动态代理。

Java提供了 java.lang.reflect.Proxy 类和 java.lang.reflect.InvocationHandler 接口来实现动态代理。

3.1 UserService接口

package com.example.dynamicproxy.jdkproxy;

/**
 * 目标接口类
 */
public interface UserManager {
    void addUser(String id, String password);

    void delUser(String id);
}

3.2 UserServiceImpl实现类

package com.example.dynamicproxy.jdkproxy;

/**
 * 接口实现类
 */
public class UserManagerImpl implements UserManager {

    @Override
    public void addUser(String id, String password) {
        System.out.println("调用了UserManagerImpl.addUser()方法!");
    }

    @Override
    public void delUser(String id) {
        System.out.println("调用了UserManagerImpl.delUser()方法!");
    }
}

3.3 UserProxy代理类

实现InvocationHandler接口重写invoke方法。

package com.example.dynamicproxy.jdkproxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * JDK动态代理类
 * 实现InvocationHandler接口
 */
public class UserProxy implements InvocationHandler {

    /**
     * 需要代理的目标对象
     */
    private Object targetObject;

    public Object newProxy(Object targetObject) {
        // 将目标对象传入进行代理
        this.targetObject = targetObject;
        // 返回代理对象
        return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(), targetObject.getClass().getInterfaces(), this);
    }

    // invoke方法
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("动态代理前置操作");
        // 进行逻辑处理的函数
        checkPopedom();
        // 调用invoke方法
        Object ret = method.invoke(targetObject, args);
        System.out.println("动态代理后置操作");
        return ret;
    }

    private void checkPopedom() {
        // 模拟检查权限
        System.out.println("检查权限:checkPopedom()!");
    }
}

3.4 测试

package com.example.dynamicproxy.jdkproxy;

public class JdkDynamicProxyExample {

    public static void main(String[] args) {

        // 创建目标对象
        UserManager userService = new UserManagerImpl();
        // 创建InvocationHandler实例
        UserProxy userProxy = new UserProxy();
        UserManager proxy = (UserManager) userProxy.newProxy(userService);
        proxy.addUser("admin","admin");
        proxy.delUser("admin");
    }
}
# 程序输出
动态代理前置操作
检查权限:checkPopedom()!
调用了UserManagerImpl.addUser()方法!
动态代理后置操作
动态代理前置操作
检查权限:checkPopedom()!
调用了UserManagerImpl.delUser()方法!
动态代理后置操作

4、CGlib动态代理案例

4.1 导入依赖

CGlib不像是JDK动态代理,CGlib需要导入Jar包,那么我用SpringBoot直接导入依赖。

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

4.2 UserServiceImpl被代理类

package com.example.dynamicproxy.cglibproxy;

public class UserServiceImpl {

    public void addUser() {
        System.out.println("添加了一个用户");
    }

    public void deleteUser() {
        System.out.println("删除了一个用户");
    }

}

4.3 UserServiceCGlib代理

创建代理类,里面主要有2个方法,一个为获取代理的实例方法,另一个为需要重写的方法,此类需要实现

MethodInterceptor接口,该接口很简单,此包含一个方法intercept。

package com.example.dynamicproxy.cglibproxy;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class UserServiceCGlib implements MethodInterceptor {

    /**
     * 指定cglib代理模式的代理类
     */
    private Object target;

    public UserServiceCGlib(){
        
    }

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

    /**
     * 返回一个代理对象,是target对象的代理对象
     * 在创建实例前,我们需要使用一个叫Enhancer的对象,此对象用来创建代理对象,设置目标类为超类后的同时需要回调当前类
     *
     * @return
     */
    public Object getProxyInstance() {
        // 1. 创建一个工具类
        Enhancer enhancer = new Enhancer();
        // 2. 设置父类
        enhancer.setSuperclass(target.getClass());
        // 3. 设置回调函数
        enhancer.setCallback(this);
        // 4. 创建子类对象,即代理对象
        return enhancer.create();
    }

    /**
     * @param o           表示目标对象
     * @param method      表示当前调度的方法
     * @param objects     表示执行方法的参数列表
     * @param methodProxy 表示执行目标方法的代理对象
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        // 注意,这里传入的o是代理对象,而不是目标类对象
        System.out.println("增强开始~~~");
        // invokerSuper或许可以理解为调用目标对象方法
        Object result = methodProxy.invokeSuper(o, objects);
        System.out.println("增强结束~~~");
        return result;
    }
}
package com.example.dynamicproxy.cglibproxy;

public class CglibDynamicProxyExample {

    public static void main(String[] args) {
        UserServiceCGlib serviceCGlib = new UserServiceCGlib(new UserServiceImpl());
        UserServiceImpl userService = (UserServiceImpl) serviceCGlib.getProxyInstance();
        userService.addUser();
        userService.deleteUser();
    }
}
# 程序输出
增强开始~~~
添加了一个用户
增强结束~~~
增强开始~~~
删除了一个用户
增强结束~~~
package com.example.dynamicproxy.cglibproxy;

import net.sf.cglib.proxy.Enhancer;

public class CglibDynamicProxyExample1 {

    public static void main(String[] args) {
        //c生成一个增强器
        Enhancer enhancer = new Enhancer();
        // 设置要代理的类
        enhancer.setSuperclass(UserServiceImpl.class);
        // 设置回调的拦截器数组
        enhancer.setCallback(new UserServiceCGlib());
        // 创建代理对象
        UserServiceImpl userService= (UserServiceImpl) enhancer.create();
        userService.addUser();
        userService.deleteUser();
    }
}
# 程序输出
增强开始~~~
添加了一个用户
增强结束~~~
增强开始~~~
删除了一个用户
增强结束~~~

5、使用场景

到这里相信各位小伙伴们已经基本掌握了JDK动态代理和CGlib动态代理的区别和实现。

那么,这两个动态代理的使用场景是什么呢?

答案:Spring AOP

以下是Spring AOP创建代理的方法:

@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {

   if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
      Class<?> targetClass = config.getTargetClass();
      if (targetClass == null) {
         throw new AopConfigException("TargetSource cannot determine target class: " +
               "Either an interface or a target is required for proxy creation.");
      }
      //如果
      if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
         return new JdkDynamicAopProxy(config);
      }
      return new ObjenesisCglibAopProxy(config);
   }
   else {
      return new JdkDynamicAopProxy(config);
   }
}

1、如果目标对象实现了接口,默认情况下会采用JDK的动态代理。

2、如果目标对象实现了接口,也可以强制使用CGLIB。

3、如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换。

如果需要强制使用CGLIB来实现AOP,需要配置spring.aop.proxy-target-class=true或

@EnableAspectJAutoProxy(proxyTargetClass = true)

6、总结

1、JDK代理使用的是反射机制实现aop的动态代理,CGLIB代理使用字节码处理框架asm,通过修改字节码生成子

类。所以jdk动态代理的方式创建代理对象效率较高,执行效率较低,cglib创建效率较低,执行效率高;

2、JDK动态代理机制是委托机制,具体说动态实现接口类,在动态生成的实现类里面委托hanlder去调用原始实现

类方法,CGLIB则使用的继承机制,具体说被代理类和代理类是继承关系,所以代理类是可以赋值给被代理类的,

如果被代理类有接口,那么代理类也可以赋值给接口。

动态代理在许多地方都有用处,比如日志记录、性能监测、权限验证等。这种动态代理的设计模式使得我们能够以

一种非侵入式的方式对对象的行为进行定制和扩展,提供了更高的灵活性和可维护性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值