Spring加载Bean的多种方式
文章目录
- 1. XML方式定义
- 2. 使用@Component + @ComponentScan
- 3. 使用@Configuration + @Bean
- 4. 使用FactoryBean的方式加载bean
- 5. Import方式
- 6. @Import + @ImportSelector
- 7. @Import + ImportBeanDefinitionRegistrar
- 8. 实现接口BeanDefinitionRegistryPostProcessor
- 9. 实现接口BeanFactoryPostProcessor
Spring的IOC特性,使得其加载bean的方式有很多,这里记录一下加载bean的9种方式:
1. XML方式定义
这种方式在早起的项目中很常见,但是由于容易导致配置文件的爆炸式增长,致使其难以维护,现在的使用已经越来越少。
<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="person" class="com.example.Person"/>
</beans>
在Java代码中可以获取名为person的bean。
class Person {
}
public class Test {
public static void main(String[] args) {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring-config.xml");
System.out.println(applicationContext.getBean("person"));
}
这样我们可以轻松获取到bean。
2. 使用@Component + @ComponentScan
用@ComponentScan指定需要扫描的包,然后指定包下的标注了指定注解的类加载为bean。这里的注解包括但不限于:@Component,@Service, @Controller, @Repository。
这里要注意一点,springboot默认扫描启动类所在包以及其子包,所以很多时候我们看起来只是在类上加了注解就已经可以加载到Bean了。
3. 使用@Configuration + @Bean
有些Bean不是我们自己定义的,那么我们无法在这个类上添加注解,因此可以通过这个方法来加载Bean。
class Person {
}
@Configuration
public class Config {
@Bean
public Person person() {
return new Person();
}
}
这样我们就可以从context中获取到名为person的Bean。当然,也可以在@Bean注解上指定Bean的name,即**@Bean(“customPerson”)**
4. 使用FactoryBean的方式加载bean
通过实现FactoryBean接口,来加载bean,这里其实是代理的方式在创建bean。
class Person {
}
@Component
public class PersonFactoryBean implements FactoryBean<Person> {
@Override
public Person getObject() throws Exception {
return new Person();
}
@Override
public Class<?> getObjectType() {
return Person.class;
}
@Override
public boolean isSingleton() {
return FactoryBean.super.isSingleton();
}
}
实现了FactoryBean接口的类,它在spring容器中本身也是一个bean。这个bean的name与普通bean不同,将会以**&**开头,因此我们要获取这个类的时候,需要使用的bean名称是“&”+类名。这里就是“&PersonFactoryBean”。那么如何获取这个FactoryBean要产生的类呢?
我们已经获取了这个“FactoryBean”的bean,那么就可以通过其getObject()方法,获取这个person bean。
这样获取的话也是有些麻烦,所以我们可以直接获取名为“personFactoryBean”这个的这个bean,这个是由容器帮我们生成的,且这个bean就是Person类型的bean。
public static void main(String[] args) throws Exception {
var context = new AnnotationConfigApplicationContext(SpringApplication.class);
var bean = context.getBean("personFactoryBean");
if (bean instanceof PersonFactoryBean) {
System.out.println("personFactoryBean=====");
} //这里的bean类型,是Person,并不是FactoryBean
var bean1 = context.getBean("&personFactoryBean");
//这个是FactoryBean本身的Bean
if (bean1 instanceof PersonFactoryBean) {
System.out.println(Objects.requireNonNull(((PersonFactoryBean) bean1).getObject()).getName());
}
Person person = (Person) context.getBean("personFactoryBean");
System.out.println(person.getName());
//直接获取这个person Bean
}
5. Import方式
class Person {}
@SpringBootApplicatioin
@Import({Person.class})
public class Application{
public static void main(String[] args) {
var context = new AnnotationConfigApplicationContext(Sj1123Application.class);
Person person = (Person) context.getBean("com.example.sj1123.entity.Person");
}
}
这样就可以获取到Person类型的Bean。但是这里要注意的是,这个bean的名称,是类的全限定名,不是简单的person。
6. @Import + @ImportSelector
使用ImportSelector的好处:
- 1、把某个功能的相关类放到一起,方面管理和维护。
- 2、重写selectImports方法时,能够根据条件判断某些类是否需要被实例化,或者某个条件实例化这些bean,其他的条件实例化那些bean等,我们能够非常灵活的定制化bean的实例化。
这种方式我们需要实现ImportSelector接口,并重写selectImports()方法,然后将我们要导入的类的全限定名写在里面即可。
class Person {}
class PersonImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{"com.example.sj1123.entity.Person"};
}
}
@SpringBootApplicatioin
@Import({PersonImportSelector.class})
public class Application{
public static void main(String[] args) {
var context = new AnnotationConfigApplicationContext(Sj1123Application.class);
Person person = (Person) context.getBean("com.example.sj1123.entity.Person");
}
}
这里获取bean时,bean的名称仍然是全限定名。
7. @Import + ImportBeanDefinitionRegistrar
这种方式需要实现ImportBeanDefinitionRegistrar接口,并重写registerBeanDefinitions()方法,然后定义需要注册的Bean的定义信息,然后registry.registerBeanDefinition()方法注册即可。这种方式比ImportSelector更加灵活,可以自定义bean的名称、作用域等很多参数。
class Person {}
class PersonBeanDefinitionRegister implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(@NonNull AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry, BeanNameGenerator importBeanNameGenerator) {
ImportBeanDefinitionRegistrar.super.registerBeanDefinitions(importingClassMetadata, registry, importBeanNameGenerator);
registry.registerBeanDefinition("person", new RootBeanDefinition(Person.class));
}
}
@SpringBootApplicatioin
@Import({PersonBeanDefinitionRegister.class})
public class Application{
public static void main(String[] args) {
var context = new AnnotationConfigApplicationContext(Sj1123Application.class);
Person person = (Person) context.getBean("person");
}
}
8. 实现接口BeanDefinitionRegistryPostProcessor
在Spring容器启动方法refresh()方法的invokeBeanFactoryPostProcessors()方法中,会执行 BeanDefinitionRegistryPostProcessor 的 postProcessBeanDefinitionRegistry()方法,它允许对beanDefinition进行后置处理,我们可以在这个方法调整IOC容器中的beanDefinition定义信息,从而干扰到后面bean初始化的过程。
class Person {}
@Component
public class PersonBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
registry.registerBeanDefinition("person", new RootBeanDefinition(Person.class));
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
}
}
@SpringBootApplicatioin
public class Application{
public static void main(String[] args) {
var context = new AnnotationConfigApplicationContext(Sj1123Application.class);
Person person = (Person) context.getBean("person");
}
}
9. 实现接口BeanFactoryPostProcessor
BeanDefinitionRegistryPostProcessor就是继承自BeanFactoryPostProcessor,所以使用BeanFactoryPostProcessor也可以实现注册Bean的功能。它们的区别如下:
- 1、 BeanDefinitionRegistryPostProcessor:侧重于bean的注册;
- 2、 BeanFactoryPostProcessor:侧重于对已经注册的bean的属性进行。
class Person {}
@Component
public class PersonBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
((DefaultListableBeanFactory)beanFactory).registerBeanDefinition("person", new RootBeanDefinition(Person.class));
}
}
@SpringBootApplicatioin
public class Application{
public static void main(String[] args) {
var context = new AnnotationConfigApplicationContext(Sj1123Application.class);
Person person = (Person) context.getBean("person");
}
}