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 {@Beanpublic 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> {@Overridepublic Person getObject() throws Exception {return new Person();}@Overridepublic Class<?> getObjectType() {return Person.class;}@Overridepublic 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,并不是FactoryBeanvar bean1 = context.getBean("&personFactoryBean");//这个是FactoryBean本身的Beanif (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 {@Overridepublic 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 {@Overridepublic 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 {@Overridepublic void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {registry.registerBeanDefinition("person", new RootBeanDefinition(Person.class));}@Overridepublic 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 {@Overridepublic 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");}}