作者简介:大家好,我是码炫码哥,前中兴通讯、美团架构师,现任某互联网公司CTO,兼职码炫课堂主讲源码系列专题
代表作:《jdk源码&多线程&高并发》,《深入tomcat源码解析》,《深入netty源码解析》,《深入dubbo源码解析》,《深入springboot源码解析》,《深入spring源码解析》,《深入redis源码解析》等
联系qq:184480602,加我进群,大家一起学习,一起进步,一起对抗互联网寒冬。码炫课堂的个人空间-码炫码哥个人主页-面试,源码等
回答
在 Spring 中,有多种方式创建 Bean:
- 通过 XML 配置文件创建 Bean
这是一种比较传统的方式,通过 <bean>
标签在 XML 配置文件中定义 Bean 的 id 和 class 属性。
- 通过注解方式创建 Bean
利用注解 @Component
、@Service
、@Repository
或 @Controller
等注解来标记类,Spring 会自动扫描并将其注册为 Bean。
- 通过 Config 配置类创建 Bean
基于 Java 的配置方式,结合 @Configuration
和 @Bean
注解,定义方法来返回实例,这些方法的返回值会被 Spring 容器管理为 Bean。
- 通过工厂方法创建 Bean
实现 FactoryBean 接口,我们可以自定义更加复杂创建 Bean 的逻辑。
- 使用 @Import 方式
使用 @Import
可以将外部类直接注册为 Bean。
- @Import + ImportSelector 方式
ImportSelector
是一个接口,它支持动态选择要注册的类。使用它有两点好处:
1、把某个功能的相关类放到一起,方面管理和维护
2、重写 selectImports()
时,我们可以根据条件判断某些类是否需要被实例化,或者某个条件实例化这些 Bean,其他的条件实例化那些 Bean 等,我们能够非常灵活的定制化 Bean 的实例化。
- @Import + ImportBeanDefinitionRegistrar 方式
这种方式我们需要实现 ImportBeanDefinitionRegistrar 接口,并重写 registerBeanDefinitions()
,然后定义我们需要注册的 Bean 的定义信息,然后调用 registry.registerBeanDefinition()
注册即可。这种方式比使用 ImportSelector 更加灵活,除了可以注册 Bean 外,还可以自定义 Bean 的名称、作用域等很多参数。
- 使用 BeanDefinitionRegistryPostProcessor 方式
- 使用 BeanFactoryPostProcessor 方式
后面两种方式是基于 Spring 得扩展,使用场景比较少。
详解
通过 XML 配置文件创建 Bean
这是一种非常传统的方式,是 Spring 早期支持的方式,现在基本上被放弃了。
<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 id="userService" class="com.skjava.UserService"/>
</beans>
Spring 在启动时会解析 XML 文件,将 <bean>
标签中定义的类加载并实例化,然后注册到 Spring 容器中。
通过注解方式创建 Bean
可以使用 @Component、@Service、@Repository 或 @Controller 等注解来标记类,Spring 会自动扫描并将其注册为 Bean。目前这是最主流的方式。
@Component
public class UserService {
// business logic
}
通过 Config 配置类创建 Bean
使用基于 Java 的配置方式,结合 @Configuration
和 @Bean
注解,定义方法来返回实例,这些方法的返回值会被 Spring 容器管理为 Bean。
@Configuration
public class AppConfig {
@Bean
public UserService userService() {
return new UserService();
}
}
通过工厂方法创建 Bean
FactoryBean 是一个特殊的 Bean,它允许我们自定义 Bean 的创建,主要有三个方法:
getObject()
:自定义Bean如何创建getObjectType()
:要注册的Bean的类型isSingleton()
:是否单例
public class CustomFactoryBean implements FactoryBean<UserService> {
@Override
public UserService getObject() {
return new UserService();
}
@Override
public Class<?> getObjectType() {
return UserService.class;
}
}
@Bean
public FactoryBean<UserService> userFactoryBean() {
return new CustomFactoryBean();
}
Spring 检测到 FactoryBean 时,会调用其 getObject() 方法获取实际 Bean。它提供了更细粒度的创建控制,适合复杂对象的构建。
使用 @Import 方式
使用 @Import
可以将外部类直接注册为 Bean。
@Import(UserService.class)
@Configuration
public class AppConfig {}
@Import
会将指定得类注册为 Spring 容器中得 Bean。其内部使用 ImportBeanDefinitionRegistrar 将类转换为 BeanDefinition。
@Import + ImportSelector 方式
ImportSelector
是一个接口,它支持动态选择要注册的类。使用它有两点好处:
- 把某个功能的相关类放到一起,方面管理和维护
- 重写
selectImports()
时,我们可以根据条件判断某些类是否需要被实例化,或者某个条件实例化这些 Bean,其他的条件实例化那些 Bean 等,我们能够非常灵活的定制化 Bean 的实例化。
public class SkServiceImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata metadata) {
return new String[]{"com.skjava.UserService","com.skjava.OrderService","com.skjava.ProductService"};
}
}
然后使用 @Import
:
@Import(SkServiceImportSelector.class)
@Configuration
public class AppConfig {}
@Import + ImportBeanDefinitionRegistrar 方式
这种方式我们需要实现 ImportBeanDefinitionRegistrar 接口,并重写 registerBeanDefinitions()
,然后定义我们需要注册的 Bean 的定义信息,然后调用 registry.registerBeanDefinition()
注册即可。这种方式比使用 ImportSelector 更加灵活,除了可以注册 Bean 外,还可以自定义 Bean 的名称、作用域等很多参数。
public class SkServiceRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
RootBeanDefinition userServiceBeanDefinition = new RootBeanDefinition(UserService.class)
userServiceBeanDefinition.setScope(BeanDefinition.SCOPE_SINGLETON);
registry.registerBeanDefinition("userService", userServiceBeanDefinition );
}
}
然后使用 @Import
:
@Import(SkServiceRegistrar.class)
@Configuration
public class AppConfig {}