Java王者修炼手册【Spring 篇 - Bean核心原理】:从 Bean 定义注册到动态代理全链路修炼

137 阅读16分钟

大家好,我是程序员强子。

又来刷英雄熟练度咯~今天专攻 Spring Bean 核心逻辑~

2001627.jpg

我们来看一下,今晚我们准备练习哪些内容:

  • Bean 的定义与注册:3 种定义方式,BeanDefinition 元数据,BeanDefinitionRegistry 注册器
  • 依赖注入的实现:3 种注入方式及场景选型,@Autowired 匹配规则,底层逻辑
  • 动态代理: JDK 动态代理(InvocationHandler)、CGLIB 动态代理(MethodInterceptor)核心原理
  • Bean 的作用域与线程安全:5 种作用域实战场景,单例 Bean 线程安全问题,ThreadLocal 应用,线程安全 3 种解决方案

Bean 的定义与注册

核心概念

BeanDefinition 是IoC 容器中描述 Bean 的元数据接口

BeanDefinition 封装了 Bean所有配置信息

  • 类名
  • 作用域
  • 属性
  • 依赖
  • 初始化 / 销毁方法
  • ..

Spring 容器通过解析 BeanDefinition 来实例化配置管理 Bean

核心属性

所有 BeanDefinition 实现类都继承自抽象类 AbstractBeanDefinition,它定义了通用属性

属性说明
beanClassBean 的类型(Class 对象或类名)
scopeBean 的作用域(默认 singleton,可选 prototype、request 等)
lazyInit是否懒加载(singleton Bean 是否在容器启动时初始化)
initMethodName初始化方法名称,对应 XML 的 init-method
destroyMethodName销毁方法名称对应 XML 的 destroy-method
constructorArgumentValues构造器参数(用于构造器注入)
propertyValues属性值(用于 Setter 注入)
dependsOn当前 Bean 依赖的其他 Bean 名称(确保依赖 Bean 先初始化)
autowireMode自动装配模式(如 byName、byType,默认不自动装配)
parentName父 BeanDefinition 名称(用于继承配置)
primary是否为首选 Bean(自动装配时优先选择)
factoryBeanName工厂 Bean 名称(通过工厂方法创建 Bean 时使用)
factoryMethodName工厂方法名称(通过静态工厂或实例工厂方法创建 Bean)

常用的 BeanDefinition 子类

GenericBeanDefinition

Spring 推荐的默认实现,可替代传统的 RootBeanDefinitionChildBeanDefinition

适合于 编程式注册 Bean(Java API 方式)、动态生成 BeanDefinition 等场景

// 创建 GenericBeanDefinition
GenericBeanDefinition userServiceDefinition = new GenericBeanDefinition();
userServiceDefinition.setBeanClass(UserService.class); // 设置 Bean 类型
userServiceDefinition.setScope(BeanDefinition.SCOPE_SINGLETON); // 作用域
userServiceDefinition.setLazyInit(true); // 懒加载
userServiceDefinition.getPropertyValues().add("userDao", new RuntimeBeanReference("userDao")); // 注入依赖

// 注册到容器
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
beanFactory.registerBeanDefinition("userService", userServiceDefinition);

RootBeanDefinition & ChildBeanDefinition

  • RootBeanDefinition:无父Bean 的 BeanDefinition

    • Spring 内部处理合并BeanDefinition 时(如子 Bean 合并父 Bean 配置)会使用
  • ChildBeanDefinition: 必须依赖父 BeanDefinition,继承父的配置并可覆盖部分属性

    • Spring 3.0 后推荐用 GenericBeanDefinition + parentName 属性替代,灵活性更高
  • 合并机制

    • 当 BeanDefinition 存在父子继承关系(如 ChildBeanDefinition 或 GenericBeanDefinition 指定 parentName),
    • Spring 会在初始化 Bean 前将子 BeanDefinition 与父 BeanDefinition 合并,生成最终的 RootBeanDefinition

AnnotatedBeanDefinition

  • 专门用于封装基于注解的 Bean 元数据(如 @Component、@Bean 注解的类),包含注解属性、方法元数据等

  • 子实现

    • ScannedGenericBeanDefinition:组件扫描(@ComponentScan)时生成,对应 @Component/@Service/@Repository 等注解的类。
    • AnnotatedGenericBeanDefinition:通过 @Bean 注解方法或 @Import 导入的类生成。

ConfigurationClassBeanDefinition

专门用于 @Configuration 类中的 @Bean 方法生成的 BeanDefinition,支持配置类的特殊处理(如 CGLIB 代理)

FactoryBeanDefinition

用于封装 FactoryBean 类型的 Bean 元数据

Spring 会先实例化 FactoryBean,再通过 getObject() 方法获取实际 Bean

那FactoryBean 到底有什么作用呢?来,紧跟强子的脚步,我们详细剖析~

FactoryBean

首先,本身是一个 Bean~

Spring 不会直接把它当作你要的最终对象,而是会调用它的 生产方法-getObject, 拿到它造出来的真正对象

FactoryBean 到底解决什么问题?

主要用来处理对象创建比较复杂的场景

比如对象需要很多参数配置创建步骤繁琐,或者需要根据不同条件动态生成对象

  • 封装复杂对象的创建逻辑

    • 有些对象的创建像 搭乐高:要设置一堆参数调用多个方法、甚至依赖其他组件
    • 创建数据库连接池(需要配置 URL、用户名、密码、最大连接数等一堆参数);
    • 创建 MyBatis 的 SqlSessionFactory(需要加载映射文件、配置数据源、设置插件等);
    • 创建 Redis客户端(需要配置地址、密码、序列化方式等)
  • 动态生成对象

    • 可以根据不同条件,让 FactoryBean 造不同的对象
    • 开发环境返回 测试数据源,生产环境返回 正式数据源
    • 根据用户配置,返回不同的缓存实例本地缓存 / Redis缓存

注册

XML 配置方式

使用 标签,指定 id(Bean 名称)、class(全类名)

<!-- 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">

  <!-- 注册 UserService Bean,指定名称为 userService -->
  <bean id="userService" class="com.example.service.UserService"/>

  <!-- 非命名方式:省略 id,Spring 自动生成名称(全类名#0) -->
  <bean class="com.example.service.OrderService"/>

</beans>

注解方式

组件扫描方式

在配置类添加 @ComponentScan 指定扫描包;

在类上添加下面这些注解,Spring 自动注册 Bean

  • @Component:通用组件注解
  • @Controller:MVC 控制器
  • @Service:业务逻辑层
  • @Repository:数据访问层
@Bean 注解

用于配置类中方法,方法返回值为 Bean 实例

Spring 注册该 Bean,默认名称为方法名

@Configuration
public class AppConfig {

    // 命名方式:注册 userDao Bean(名称为 userDao)
    @Bean
    public UserDao userDao() {
        return new UserDao();
    }

    // 非命名方式:自定义名称为 myUserService
    @Bean("myUserService")
    public UserService userService() {
        return new UserService(userDao()); // 注入 userDao
    }
}
高级注解:@Import

用于批量导入 Bean 或配置类

demo: 导入单个类

// 导入单个类(自动注册为 Bean)
@Import(UserService.class)
@Configuration
public class AppConfig {}

demo: 导入配置类

// 导入配置类
@Import(AppConfig2.class)
@Configuration
public class AppConfig {}

demo: 导入 ImportSelector

// 导入 ImportSelector(动态选择类)
public class MyImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata metadata) {
        return new String[]{"com.example.service.OrderService"}; // 注册 OrderService
    }
}

@Import(MyImportSelector.class)
@Configuration
public class AppConfig {}
条件注册:@Conditional

根据条件动态注册 Bean

@Bean
@Conditional(WindowsCondition.class) // 仅 Windows 系统注册
public SystemService windowsSystemService() {
    return new WindowsSystemService();
}

Spring Boot 提供的条件注解(@ConditionalOnXXX), 非常好用,总结一下~

如何基于Class是否存在动态注册Bean?

@ConditionalOnClass / @ConditionalOnMissingClass

当类路径中存在指定类 /不存在指定类时,才注册 Bean

// 仅当类路径中有 RedisTemplate 类(引入 spring-data-redis 依赖)时,注册 RedisService
@Bean
@ConditionalOnClass(RedisTemplate.class)
public RedisService redisService() {
    return new RedisService();
}

// 当类路径中没有 MongoTemplate 类时,注册本地缓存 Service
@Bean
@ConditionalOnMissingClass("org.springframework.data.mongodb.core.MongoTemplate")
public LocalCacheService localCacheService() {
    return new LocalCacheService();
}

如何基于Bean是否存在动态注册bean?

@ConditionalOnBean / @ConditionalOnMissingBean

当容器中存在/不存在 指定 Bean时,才注册当前 Bean

// 当容器中有 DataSource Bean 时,注册事务管理器
@Bean
@ConditionalOnBean(DataSource.class)
public PlatformTransactionManager transactionManager(DataSource dataSource) {
    return new DataSourceTransactionManager(dataSource);
}

// 当容器中没有 UserService Bean 时,注册默认的 UserService
@Bean
@ConditionalOnMissingBean(UserService.class)
public UserService defaultUserService() {
    return new DefaultUserService();
}

如何基于配置属性是否存在动态注册Bean?

当配置文件(Properties/YAML)中存在指定属性且满足条件时,才注册 Bean

@ConditionalOnProperty

  • prefix:属性前缀(比如 service.user);
  • name:属性名(比如 enabled);
  • havingValue:属性需要匹配的值,默认只要属性存在即满足
  • matchIfMissing:属性不存在时是否匹配(默认 false)

配置文件:

# application.yml
service:
  user:
    enabled: true

代码demo

// 仅当 service.user.enabled=true 时,注册 UserService
@Bean
@ConditionalOnProperty(prefix = "service.user", name = "enabled", havingValue = "true")
public UserService userService() {
    return new UserService();
}

// 若 service.order.enabled 未配置,默认注册 OrderService(matchIfMissing=true)
@Bean
@ConditionalOnProperty(prefix = "service.order", name = "enabled", matchIfMissing = true)
public OrderService orderService() {
    return new OrderService();
}

如何基于资源是否存在动态注册bean?

@ConditionalOnResource

当类路径中存在指定资源文件时,才注册 Bean

// 仅当类路径中有 config/user.properties 文件时,注册 UserConfigService
@Bean
@ConditionalOnResource(resources = "classpath:config/user.properties")
public UserConfigService userConfigService() {
    return new UserConfigService();
}

如何基于表达式是否满足条件动态注册bean?

@ConditionalOnExpression

// 当配置中 service.enabled=true 且环境是 dev 时,注册 DevService
@Bean
@ConditionalOnExpression("${service.enabled:true} && 'dev'.equals(${spring.profiles.active})")
public DevService devService() {
    return new DevService();
}

如何自定义 @Conditional 注解 ?

如果需要复用某个条件逻辑,可以自定义组合注解(封装 @Conditional + 自定义 Condition)。

示例:自定义 “仅生产环境注册” 的注解

// 1. 自定义 Condition
public class ProdEnvCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        return "prod".equals(context.getEnvironment().getProperty("spring.profiles.active"));
    }
}

// 2. 自定义组合注解
@Target({ElementType.TYPEElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Conditional(ProdEnvCondition.class) // 绑定 Condition
public @interface ConditionalOnProdEnv {}

// 3. 使用自定义注解
@Bean
@ConditionalOnProdEnv
public ProdMonitorService prodMonitorService() {
    return new ProdMonitorService();
}

Java API 方式

通过 Spring 底层 API 手动注册 Bean,适用于动态生成 Bean

  • BeanDefinitionRegistry:注册 BeanDefinition 的接口(如 DefaultListableBeanFactory);
  • GenericBeanDefinition:BeanDefinition 的通用实现
  • ApplicationContext.registerBean():简化的编程式注册

非命名方式

// 1. 创建 BeanFactory
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();

// 2. 定义 BeanDefinition(指定类)
GenericBeanDefinition userServiceDefinition = new GenericBeanDefinition();
userServiceDefinition.setBeanClass(UserService.class);

// 3. 注册 Bean(非命名:Spring 自动生成名称)
beanFactory.registerBeanDefinition(null, userServiceDefinition);

// 或使用 ApplicationContext 简化
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.registerBean(UserService.class); // 非命名注册
context.refresh();

命名方式(指定 Bean 名称)

// 方式1:通过 BeanDefinitionRegistry
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
GenericBeanDefinition userDaoDefinition = new GenericBeanDefinition();
userDaoDefinition.setBeanClass(UserDao.class);
beanFactory.registerBeanDefinition("userDao", userDaoDefinition); // 指定名称 userDao

// 方式2:通过 ApplicationContext
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.registerBean("myUserService", UserService.class); // 指定名称 myUserService
context.refresh();

// 获取 Bean
UserService userService = context.getBean("myUserService", UserService.class);

外部配置集成方式

通过外部配置(Properties/YAML/ 环境变量 / 配置中心)动态配置 Bean

@PropertySource 加载 Properties/YAML

  • 加载 properties:直接使用 @PropertySource
  • 加载 yaml:需引入 snakeyaml 依赖,并配合 YamlPropertySourceLoader

配置文件 app.properties

# app.properties
user.name=test
user.age=20

demo

@Configuration
@PropertySource("classpath:app.properties") // 加载 Properties 文件
public class AppConfig {

    @Value("${user.name}") // 绑定配置值
    private String userName;

    @Bean
    public User user() {
        return new User(userName, Integer.parseInt("${user.age}"));
    }
}

// YAML 加载示例(需自定义 PropertySourceFactory)
@Configuration
@PropertySource(value = "classpath:app.yaml", factory = YamlPropertySourceFactory.class)
public class AppConfig {}

环境变量 & 命令行参数

Spring 自动加载环境变量命令行参数(优先级更高),通过 Environment 或 @Value 获取

@Autowired
private Environment environment;

public void test() {
    String env = environment.getProperty("ENV"); // 获取环境变量 ENV
    String cmdArg = environment.getProperty("user.id"); // 获取命令行参数 --user.id=100
}

拿到 了 BeanDefinition,然后呢?

我们平时写CURD的时候会写Service接口对吧,

BeanDefinitionRegistry 相当于Service接口了

最常用的实现类是DefaultListableBeanFactory , 实现了BeanDefinitionRegistry 接口,同时也显示了很多其他的接口~

public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
  implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {
            ...
}

demo

// 创建BeanFactory(容器)
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();

// 构建BeanDefinition
BeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(UserServiceImpl.class)
        .setScope("singleton")
        .addPropertyReference("userDao""userDao"// 依赖userDao
        .getBeanDefinition();

// 注册BeanDefinition
beanFactory.registerBeanDefinition("userService", beanDefinition);

// 获取Bean
UserService userService = beanFactory.getBean(UserService.class);

Spring 容器启动时,不管是解析 XML、扫描注解还是处理@Bean,最终都是调用BeanDefinitionRegistry的注册方法,把 BeanDefinition 存到容器里

依赖注入

Spring 自动帮你把 Bean 的依赖 塞进去,不用手动new。

那问题就来了,这两步很关键~

  • 怎么注入: 对应注入方式, 场景不同,选型不同
  • 注入什么 & 怎么找依赖 : 对应着怎么查找依赖

注入方式

构造器注入

通过构造方法注入,确保 Bean 创建时依赖必存在,解决 NullPointerException 问题

@Service
public class UserServiceImpl implements UserService {
    private final UserDao userDao; // 不可变,线程安全
    
    // 构造器注入(Spring 4.3+可省略@Autowired)
    public UserServiceImpl(UserDao userDao) {
        this.userDao = userDao;
    }
}

构造器注入的成员变量可以不加 final,但推荐加final

  • 保证变量初始化后不可修改,能明确这是依赖注入的 不可变依赖

同时, 可以加上 @RequiredArgsConstructor注解,这样就可以不用手动写构造器了

Setter 注入

通过 setter 方法注入

@Service
public class UserServiceImpl implements UserService {
    private UserDao userDao;
    
    @Autowired // 可选:没有userDao也能创建Bean
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }
}

字段注入

直接在字段上用@Autowired,代码最简洁,但缺点明显

@Service
public class UserServiceImpl implements UserService {
    @Autowired // 字段注入
    private UserDao userDao;
}

自动注入的匹配规则

  • byType:先按类型找(比如找所有UserDao类型的 Bean);
  • byName:如果类型匹配到多个(比如有userDaoImpl1和userDaoImpl2),就按字段名 / 参数名匹配 Bean 名称;
  • @Qualifier:如果 byName 也匹配不到,就用@Qualifier("指定Bean名称")精准定位。
@Repository("userDaoImpl1")
public class UserDaoImpl1 implements UserDao {}

@Repository("userDaoImpl2")
public class UserDaoImpl2 implements UserDao {}

@Service
public class UserServiceImpl {
    // 用@Qualifier指定注入userDaoImpl1
    @Autowired
    @Qualifier("userDaoImpl1")
    private UserDao userDao;
}

底层原理

AutowiredAnnotationBeanPostProcessor(Bean 后置处理器)实现的

它在 Bean 初始化前的populateBean阶段

  • 扫描 Bean 的字段 / 方法上的@Autowired注解;
  • 根据注解信息,通过BeanFactory查找匹配的依赖 Bean;
  • 反射把依赖注入到目标 Bean

延时注入

ObjectFactory

ObjectFactory 核心作用是延迟获取 Bean 实例

避免在依赖注入阶段立即初始化目标 Bean

适用于需要按需创建 Bean 或解决循环依赖的场景

核心特性是什么?

  • 仅包含getObject() 方法,每次调用都会触发 Bean 的获取
  • 注入的是ObjectFactory容器,而非目标 Bean 本身,只有调用getObject()时才会实际获取 / 初始化目标 Bean
  • Spring 内部也通过ObjectFactory处理单例 Bean 的循环依赖
@Component
public class ServiceA {
    // 注入ObjectFactory延迟获取ServiceB
    @Autowired
    private ObjectFactory<ServiceB> serviceBFactory;

    public void doSomething() {
        // 调用getObject()时才初始化/获取ServiceB
        ServiceB serviceB = serviceBFactory.getObject();
        serviceB.execute();
    }
}

@Component
public class ServiceB {}

ObjectProvider

ObjectProvider是 Spring 4.3 + 引入的接口,继承自ObjectFactory

扩展了更灵活的延迟注入能力,是 Spring 官方推荐的替代ObjectFactory的方案

核心增强特性

  • 容错能力

    • 提供getIfAvailable(),Bean 不存在时返回 null,避免 NPE
    • getIfUnique(),多个候选 Bean 时抛异常,否则返回
    • ...
  • 多 Bean 处理:支持stream()遍历所有候选 Bean(如同一接口的多个实现类);

  • 自定义回调

    • ifAvailable(Consumer)(Bean 存在时执行回调)
    • getIfAvailable(Supplier)(Bean 不存在时返回默认值)
  • 兼容ObjectFactory:保留getObject()方法,无缝替换

@Component
public class ServiceA {
    // 注入ObjectProvider延迟获取ServiceB
    @Autowired
    private ObjectProvider<ServiceB> serviceBProvider;

    public void doSomething() {
        // 容错获取:ServiceB不存在时返回null
        ServiceB serviceB = serviceBProvider.getIfAvailable();
        if (serviceB != null) {
            serviceB.execute();
        }

        // 遍历同一接口的所有实现类(如ServiceB有多个子类)
        serviceBProvider.stream().forEach(ServiceB::execute);

        // 无Bean时返回默认实例
        ServiceB defaultServiceB = serviceBProvider.getIfAvailable(() -> new ServiceB());
    }
}

@Lazy

用于标记 Bean 延迟初始化,或延迟注入依赖

核心作用是优化容器启动速度、解决循环依赖

延时初始化demo

// ServiceB延迟初始化,容器启动时不创建
@Component
@Lazy
public class ServiceB {}

@Component
public class ServiceA {
    @Autowired
    private ServiceB serviceB; // 容器启动时,ServiceB未初始化,第一次调用serviceB时才创建
}

解决循环依赖demo

@Component
// 未标记@Lazy,默认立即初始化
public class ServiceB {} // 未标记@Lazy,默认立即初始化

@Component
public class ServiceA {
    // 注入点标记@Lazy,serviceB是代理对象,第一次调用时才获取真实ServiceB
    @Autowired
    @Lazy
    private ServiceB serviceB;
}

原理

  • 标记 Bean 时

    • Spring 容器启动时,将@Lazy的 Bean 注册为 延迟初始化 Bean,不加入早期单例池
    • 第一次获取时才执行实例化初始化流程;
  • 标记注入点时

    • Spring 通过动态代理(JDK/CGLIB)生成目标 Bean 的代理对象,注入到当前类中
    • 第一次调用代理的方法时,才从容器中获取真实 Bean 并执行方法。

动态代理到底是怎么回事?接下来跟着强子仔细探寻一下答案~

动态代理

JDK 动态代理

基于 InvocationHandler

核心原理

  • 代理类生成:通过 Proxy.newProxyInstance() 动态生成字节码(类名形如 $Proxy0),代理类内部会将所有接口方法调用转发给 InvocationHandler 的 invoke 方法

  • 接口依赖: 目标类必须实现接口,JDK 动态代理生成的代理类($Proxy0)会继承 Proxy 类 + 实现目标接口

  • 回调入口

    • InvocationHandlerinvoke 方法是增强逻辑的核心入口,调用目标方法时会触发该方法,
    • 可在其中嵌入前置 / 后置增强逻辑,最终通过反射调用目标对象的方法

demo如下

目标接口 + 实现类

// 1. 目标接口(必须)
interface OrderService {
    void createOrder(String orderNo);
}

// 2. 目标类(实现接口)
class OrderServiceImpl implements OrderService {
    @Override
    public void createOrder(String orderNo) {
        System.out.println("目标类执行:创建订单【" + orderNo + "】");
    }
}

InvocationHandler自定义类

 // 自定义 InvocationHandler(增强逻辑)
class LogInvocationHandler implements InvocationHandler {
    private Object target; // 目标对象

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

    /**
     * 代理方法调用时触发的回调方法
     * @param proxy  代理对象本身(避免直接调用,防止递归)
     * @param method 被调用的目标方法
     * @param args   方法参数
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 前置增强:日志记录
        System.out.println("JDK 代理前置:调用方法【" + method.getName() + "】,参数:" + args[0]);
        
        // 反射调用目标方法
        Object result = method.invoke(target, args);
        
        // 后置增强:结果处理
        System.out.println("JDK 代理后置:方法【" + method.getName() + "】执行完成");
        return result;
    }
}

测试类

// 测试类
public class JdkProxyDemo {
    public static void main(String[] args) {
        // 创建目标对象
        OrderService target = new OrderServiceImpl();
        
        // 创建 InvocationHandler(关联目标对象)
        InvocationHandler handler = new LogInvocationHandler(target);
        
        // 生成代理对象(核心:Proxy.newProxyInstance)
        OrderService proxy = (OrderService) Proxy.newProxyInstance(
            target.getClass().getClassLoader(), // 类加载器
            target.getClass().getInterfaces(),  // 目标接口数组
            handler                             // 回调处理器
        );
        
        // 调用代理方法(触发 invoke 回调)
        proxy.createOrder("ORDER_20250520");
    }
}

执行结果:

JDK 代理前置:调用方法【createOrder】,参数:ORDER_20250520
目标类执行:创建订单【ORDER_20250520】
JDK 代理后置:方法【createOrder】执行完成

CGLIB 动态代理

基于 MethodInterceptor

核心原理

  • 继承依赖

    • 无需目标类实现接口,CGLIB 通过 ASM 框架动态生成目标类的子类作为代理类
    • 目标类 / 方法不能是 final,否则无法继承 / 重写
  • 代理类生成

    • Enhancer 类指定父类(目标类)和回调接口 MethodInterceptor,
    • 生成的代理类名形如 目标类EnhancerByCGLIB\XXX
  • 回调入口

    • MethodInterceptor 的 intercept 方法是增强逻辑入口,调用代理方法时触发
    • 通过 methodProxy.invokeSuper(obj, args) 调用父类(目标类)的方法

Demo

目标类

// 目标类(无需实现接口)
class UserService {
    public void updateUser(String username) {
        System.out.println("目标类执行:更新用户【" + username + "】");
    }
}

自定义 MethodInterceptor

// 自定义 MethodInterceptor(增强逻辑)
class CglibLogInterceptor implements MethodInterceptor {
    /**
     * 代理方法调用时触发的回调方法
     * @param obj         代理对象(目标类的子类实例)
     * @param method      被调用的目标方法
     * @param args        方法参数
     * @param methodProxy 方法代理对象(用于调用父类方法)
     */
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        // 前置增强:权限校验
        System.out.println("CGLIB 代理前置:调用方法【" + method.getName() + "】,参数:" + args[0]);
        
        // 调用父类(目标类)的方法(非反射,效率更高)
        Object result = methodProxy.invokeSuper(obj, args);
        
        // 后置增强:日志记录
        System.out.println("CGLIB 代理后置:方法【" + method.getName() + "】执行完成");
        return result;
    }
}

测试类

// 测试类
public class CglibProxyDemo {
    public static void main(String[] args) {
        // 创建 Enhancer(CGLIB 核心类,用于生成代理类)
        Enhancer enhancer = new Enhancer();
        // 设置父类(目标类)
        enhancer.setSuperclass(UserService.class);
        // 设置回调处理器(MethodInterceptor)
        enhancer.setCallback(new CglibLogInterceptor());
        
        // 生成代理对象(目标类的子类)
        UserService proxy = (UserService) enhancer.create();
        
        // 调用代理方法(触发 intercept 回调)
        proxy.updateUser("张三");
    }
}

执行结果

CGLIB 代理前置:调用方法【updateUser】,参数:张三
目标类执行:更新用户【张三】
CGLIB 代理后置:方法【updateUser】执行完成

Bean 作用域

Spring 定义了 5 种 Bean 作用域,本质是控制 Bean 的 存活时间复用范围

作用域含义实战场景
singleton容器内唯一实例(默认)Service、Dao、工具类(无状态 Bean)
prototype每次 getBean 都新建实例有状态 Bean
request一次 HTTP 请求一个实例Web 场景:存储请求级别的上下文(比如用户 Token)
session一个 Session 一个实例Web 场景:用户登录信息、购物车
applicationWeb 容器内全局唯一(极少用)全局配置信息

单例 Bean 的线程安全问题:

  • 无状态 Bean没有成员变量 或者 成员变量是不可变的,天然线程安全;
  • 有状态 Bean:有可变成员变量,多线程下会出问题

解决方案1

可以用 ThreadLocal 隔离线程状态

比如 Spring 的RequestContextHolder,用 ThreadLocal 存储请求上下文

方案2

prototype作用域:每次请求新建实例,避免共享状态;

方案3

同步锁 / 不可变对象:对可变状态加锁(性能差),或用不可变对象

总结

今天把Spring的底层地基给学扎实了!

本文聚焦 Spring 四大核心实战内容:

  1. Bean 的 3 种定义方式BeanDefinition 元数据、BeanDefinitionRegistry 注册器的底层逻辑;
  2. 构造器 /setter/ 字段 3 种依赖注入方式的场景选型,以及 @Autowired 的匹配规则与底层实现;
  3. JDK 动态代理(InvocationHandler)和 CGLIB 动态代理(MethodInterceptor)的核心原理;
  4. 是 Bean 的 5 种作用域实战,以及单例 Bean 线程安全问题与 3 种解决办法(无状态设计、同步锁、ThreadLocal)

帮 我们 吃透 Spring 底层,从会用到懂原理~~

熟练度刷不停,知识点吃透稳,下期接着练~