【Spring框架】Spring底层的两种代理模式:JDK动态代理与cglib动态代理的区别和实现

本文详细介绍了Spring中的AOP代理模式,包括JDK动态代理和CGLIB代理的实现过程。通过实例展示了如何使用这两种代理在方法调用前后插入事务管理、日志记录等辅助功能,强调了JDK代理基于接口,CGLIB代理基于继承的特点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

代理设计模式

将核心功能与辅助功能(事务、日志、性能监控代码)分离,达到核心业务功能更纯粹、辅助业务功能可复用。

在这里插入图片描述

Spring中的AOP代理,可以是JDK动态代理,也可以是CGLIB代理

JDK动态代理

JDK动态代理是通过java.lang.reflect.Proxy类来实现的,我们可以调用Proxy类的newProxyInstance()方法来创建代理对象

1、创建Service层的接口和实现类,实现两个方法

public interface UserService {
    int insertUser();
    int deleteUser();
}
public class UserServiceImpl implements UserService {
    public int insertUser() {
        System.out.println("insertUser...");
        return 0;
    }

    public int deleteUser() {
        System.out.println("deleteUser...");
        return 0;
    }
}

2、创建切面类

  • 也就是要在某个方法的前后执行的方法
  • (这里也采用接口的方式创建)
public interface MyAspect {
    void beginTransaction();
    void closeTransaction();
}
public class MyAspectImpl implements MyAspect {
    public void beginTransaction() {
        System.out.println("模拟开启事务...");
    }
    public void closeTransaction() {
        System.out.println("模拟关闭事务");
    }
}

3、创建JDK代理

  • 需要创建一个代理对象,执行Service中方法的时候,由代理对象来执行
  • method.invoke就是执行调用的方法
package com.robot.proxy;

import com.robot.aspect.MyAspect;
import com.robot.service.UserService;

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

/**
 * JDK动态代理。
 *
 * @Author 张宝旭
 * @Date 2020/9/28
 */
public class JdkProxy implements InvocationHandler {
    UserService userService; // 方法参数传递
    MyAspect myAspect; // setter方法注入
    
    public void setMyAspect(MyAspect myAspect) {
        this.myAspect = myAspect;
    }

    // 创建代理对象
    public Object createProxy(UserService userService) {
        this.userService = userService;
        // 当前类的类加载器
        ClassLoader classLoader = JdkProxy.class.getClassLoader();
        // 被代理对象实现的所有接口
        Class<?>[] clazz = userService.getClass().getInterfaces();
        // 返回代理对象
        return Proxy.newProxyInstance(classLoader, clazz, this);
    }

    // 执行
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 执行前置增强方法
        myAspect.beginTransaction();
        // 执行调用的方法
        Object result = method.invoke(userService, args);
        // 执行后置增强方法
        myAspect.closeTransaction();
        return result;
    }
}

4、配置applicationContext.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean name="myAspect" class="com.robot.aspect.impl.MyAspectImpl"/>
    <bean name="userService" class="com.robot.service.impl.UserServiceImpl"/>
    <bean name="jdkProxy" class="com.robot.proxy.JdkProxy">
        <property name="myAspect" ref="myAspect"/>
    </bean>
</beans>

5、测试

public class JdkProxyTest {
    @Test
    public void JdkProxyTest() {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        // 创建代理
        JdkProxy jdkProxy = (JdkProxy) applicationContext.getBean("jdkProxy");
        // 创建被代理对象
        UserService userService = (UserService) applicationContext.getBean("userService");
        // 创建代理对象
        UserService proxy = (UserService) jdkProxy.createProxy(userService);
        // 使用代理执行方法
        proxy.insertUser();
        proxy.deleteUser();
    }
}

结果

模拟开启事务...
insertUser...
模拟关闭事务
模拟开启事务...
deleteUser...
模拟关闭事务

结果表明,在调用insertUser方法和deleteUser方法的时候,都在其前后执行了指定的方法,也就是将切面类中的方法,切入到指定方法的前后执行

  • 如果由对象本身去执行此方法,则只是输出调用方法的输出结果
  • 如果由代理对象去执行此方法,那么会输出如上显示的结果(前后都被添加了增强方法)

cglib动态代理

1、创建一个实现类

package com.robot.service.impl;

/**
 * cglib动态代理测试实现类。
 *
 * @Author 张宝旭
 * @Date 2020/9/29
 */
public class CartService {
    public void insert() {
        System.out.println("cglib insert...");
    }
    public void delete() {
        System.out.println("cglib delete...");
    }
}

2、创建切面类(和上面JDK动态代理的通用)

package com.robot.aspect;

/**
 * @Author 张宝旭
 * @Date 2020/9/29
 */
public interface MyAspect {
    void beginTransaction();
    void closeTransaction();
}
package com.robot.aspect.impl;

import com.robot.aspect.MyAspect;

/**
 * 切面类。
 *
 * @Author 张宝旭
 * @Date 2020/9/28
 */
public class MyAspectImpl implements MyAspect {
    public void beginTransaction() {
        System.out.println("模拟开启事务...");
    }
    public void closeTransaction() {
        System.out.println("模拟关闭事务");
    }
}

3、创建代理工厂

package com.robot.proxy;

import com.robot.aspect.MyAspect;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
 * cglib动态代理。
 *
 * @Author 张宝旭
 * @Date 2020/9/29
 */
public class CglibProxy implements MethodInterceptor {
    // 创建切面类,setter方法注入
    MyAspect myAspect;

    public void setMyAspect(MyAspect myAspect) {
        this.myAspect = myAspect;
    }

    // 创建代理对象
    public Object createProxy(Object target) {
        // 创建动态类对象
        Enhancer enhancer = new Enhancer();
        // 确定需要增强的类
        enhancer.setSuperclass(target.getClass());
        // 添加回调函数
        enhancer.setCallback(this);
        // 返回创建的代理类
        return enhancer.create();
    }
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        // 执行前置增强
        myAspect.beginTransaction();
        // 执行调用的方法
        Object result = methodProxy.invokeSuper(o, objects);
        // 执行后置增强
        myAspect.closeTransaction();
        return result;
    }
}

4、配置applicationContext.xml文件

  • (接着上面JDK动态代理的写)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean name="myAspect" class="com.robot.aspect.impl.MyAspectImpl"/>
    <bean name="cartService" class="com.robot.service.impl.CartService"/>
    <bean name="userService" class="com.robot.service.impl.UserServiceImpl"/>
    <bean name="jdkProxy" class="com.robot.proxy.JdkProxy">
        <property name="myAspect" ref="myAspect"/>
    </bean>
    <bean name="cglibProxy" class="com.robot.proxy.CglibProxy">
        <property name="myAspect" ref="myAspect"/>
    </bean>
</beans>

5、测试

package com.robot.proxy;

import com.robot.service.impl.CartService;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * 测试cglib动态代理。
 * 
 * @Author 张宝旭
 * @Date 2020/9/29
 */
public class CglibProxyTest {
    @Test
    public void cglibProxyTest() {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        CglibProxy cglibProxy = (CglibProxy) applicationContext.getBean("cglibProxy");
        CartService cartService = (CartService) applicationContext.getBean("cartService");
        CartService proxy = (CartService) cglibProxy.createProxy(cartService);
        proxy.insert();
        proxy.delete();
    }
}

结果

模拟开启事务...
cglib insert...
模拟关闭事务
模拟开启事务...
cglib delete...
模拟关闭事务

和上面JDK动态代理的结果相同,说明也成功的使用了代理来执行指定方法,并在前后都执行了指定增强方法

总结

JDK动态代理需要使用接口的方式,是基于接口的,假如将以上JDK动态代理的例子中UserService这个接口去掉,直接写它的实现类,那么则会报错,因为必须要写接口

cglib则不需要使用接口,是基于继承实现的,直接创建实现类即可,比如以上cglib动态代理的例子,无需写接口

  • Spring的代理底层使用的就是这两种代理,可以根据需要,指定具体使用哪种代理生成机制(默认为JDK动态代理)
  • 基本规则是:目标业务类如果有接口则用JDK代理,没有接口则用CGLib代理
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值