大家好,我是程序员强子。
又来刷英雄熟练度咯~今天专攻 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,它定义了通用属性
| 属性 | 说明 |
|---|---|
| beanClass | Bean 的类型(Class 对象或类名) |
| scope | Bean 的作用域(默认 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 推荐的默认实现,可替代传统的 RootBeanDefinition 和 ChildBeanDefinition
适合于 编程式注册 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.TYPE, ElementType.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 类 + 实现目标接口
-
回调入口
- InvocationHandler 的 invoke 方法是增强逻辑的核心入口,调用目标方法时会触发该方法,
- 可在其中嵌入前置 / 后置增强逻辑,最终通过反射调用目标对象的方法
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 的 存活时间和 复用范围
| 作用域 | 含义 | 实战场景 |
|---|---|---|
| singleton | 容器内唯一实例(默认) | Service、Dao、工具类(无状态 Bean) |
| prototype | 每次 getBean 都新建实例 | 有状态 Bean |
| request | 一次 HTTP 请求一个实例 | Web 场景:存储请求级别的上下文(比如用户 Token) |
| session | 一个 Session 一个实例 | Web 场景:用户登录信息、购物车 |
| application | Web 容器内全局唯一(极少用) | 全局配置信息 |
单例 Bean 的线程安全问题:
- 无状态 Bean:没有成员变量 或者 成员变量是不可变的,天然线程安全;
- 有状态 Bean:有可变成员变量,多线程下会出问题
解决方案1
可以用 ThreadLocal 隔离线程状态
比如 Spring 的RequestContextHolder,用 ThreadLocal 存储请求上下文
方案2
用 prototype作用域:每次请求新建实例,避免共享状态;
方案3
同步锁 / 不可变对象:对可变状态加锁(性能差),或用不可变对象
总结
今天把Spring的底层地基给学扎实了!
本文聚焦 Spring 四大核心实战内容:
- Bean 的 3 种定义方式及 BeanDefinition 元数据、BeanDefinitionRegistry 注册器的底层逻辑;
- 构造器 /setter/ 字段 3 种依赖注入方式的场景选型,以及 @Autowired 的匹配规则与底层实现;
- JDK 动态代理(InvocationHandler)和 CGLIB 动态代理(MethodInterceptor)的核心原理;
- 是 Bean 的 5 种作用域实战,以及单例 Bean 线程安全问题与 3 种解决办法(无状态设计、同步锁、ThreadLocal)
帮 我们 吃透 Spring 底层,从会用到懂原理~~
熟练度刷不停,知识点吃透稳,下期接着练~