写点什么

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

作者:DonaldCen
  • 2025-12-05
    广东
  • 本文字数:12381 字

    阅读完需:约 41 分钟

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

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

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

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

  • 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,它定义了通用属性

常用的 BeanDefinition 子类

GenericBeanDefinition

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

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

// 创建 GenericBeanDefinitionGenericBeanDefinition 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 的 BeanDefinitionSpring 内部处理合并 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,默认名称为方法名

@Configurationpublic 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)@Configurationpublic class AppConfig {}
复制代码

demo: 导入配置类

// 导入配置类@Import(AppConfig2.class)@Configurationpublic 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)@Configurationpublic 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.ymlservice:  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. 自定义 Conditionpublic class ProdEnvCondition implements Condition {    @Override    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {        return "prod".equals(context.getEnvironment().getProperty("spring.profiles.active"));    }}
// 2. 自定义组合注解@Target({ElementType.TYPE, ElementType.METHOD})@Retention(RetentionPolicy.RUNTIME)@Conditional(ProdEnvCondition.class) // 绑定 Conditionpublic @interface ConditionalOnProdEnv {}
// 3. 使用自定义注解@Bean@ConditionalOnProdEnvpublic ProdMonitorService prodMonitorService() {    return new ProdMonitorService();}
复制代码

Java API 方式

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

  • BeanDefinitionRegistry:注册 BeanDefinition 的接口(如 DefaultListableBeanFactory);

  • GenericBeanDefinition:BeanDefinition 的通用实现

  • ApplicationContext.registerBean():简化的编程式注册

非命名方式

// 1. 创建 BeanFactoryDefaultListableBeanFactory 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:通过 BeanDefinitionRegistryDefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();GenericBeanDefinition userDaoDefinition = new GenericBeanDefinition();userDaoDefinition.setBeanClass(UserDao.class);beanFactory.registerBeanDefinition("userDao", userDaoDefinition); // 指定名称 userDao
// 方式2:通过 ApplicationContextAnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();context.registerBean("myUserService", UserService.class); // 指定名称 myUserServicecontext.refresh();
// 获取 BeanUserService userService = context.getBean("myUserService", UserService.class);
复制代码

外部配置集成方式

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

@PropertySource 加载 Properties/YAML

  • 加载 properties:直接使用 @PropertySource

  • 加载 yaml:需引入 snakeyaml 依赖,并配合 YamlPropertySourceLoader

配置文件 app.properties

# app.propertiesuser.name=testuser.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 获取

@Autowiredprivate 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();
// 构建BeanDefinitionBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(UserServiceImpl.class)        .setScope("singleton")        .addPropertyReference("userDao", "userDao") // 依赖userDao        .getBeanDefinition();
// 注册BeanDefinitionbeanFactory.registerBeanDefinition("userService", beanDefinition);
// 获取BeanUserService userService = beanFactory.getBean(UserService.class);
复制代码

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

依赖注入

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

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

  • 怎么注入: 对应注入方式, 场景不同,选型不同

  • 注入什么 & 怎么找依赖 : 对应着怎么查找依赖

注入方式

构造器注入

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

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

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

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

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

Setter 注入

通过 setter 方法注入

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

字段注入

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

@Servicepublic 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 {}
@Servicepublic 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 的循环依赖

@Componentpublic class ServiceA {    // 注入ObjectFactory延迟获取ServiceB    @Autowired    private ObjectFactory<ServiceB> serviceBFactory;
    public void doSomething() {        // 调用getObject()时才初始化/获取ServiceB        ServiceB serviceB = serviceBFactory.getObject();        serviceB.execute();    }}
@Componentpublic class ServiceB {}
复制代码

ObjectProvider

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

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

核心增强特性

  • 容错能力:提供 getIfAvailable(),Bean 不存在时返回 null,避免 NPEgetIfUnique(),多个候选 Bean 时抛异常,否则返回...

  • 多 Bean 处理:支持 stream()遍历所有候选 Bean(如同一接口的多个实现类);

  • 自定义回调:ifAvailable(Consumer)(Bean 存在时执行回调)getIfAvailable(Supplier)(Bean 不存在时返回默认值)

  • 兼容 ObjectFactory:保留 getObject()方法,无缝替换

@Componentpublic 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@Lazypublic class ServiceB {}
@Componentpublic class ServiceA {    @Autowired    private ServiceB serviceB; // 容器启动时,ServiceB未初始化,第一次调用serviceB时才创建}
复制代码

解决循环依赖 demo

@Component// 未标记@Lazy,默认立即初始化public class ServiceB {} // 未标记@Lazy,默认立即初始化
@Componentpublic 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,生成的代理类名形如 目标类 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 的 存活时间复用范围

单例 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 底层,从会用到懂原理~~

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

发布于: 刚刚阅读数: 2
用户头像

DonaldCen

关注

有个性,没签名 2019-01-13 加入

跟我在峡谷学Java 公众号:程序员悟空的宝藏乐园

评论

发布
暂无评论
Java王者修炼手册【Spring 篇 - Bean核心原理】:从 Bean 定义注册到动态代理全链路修炼_bean_DonaldCen_InfoQ写作社区