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");
	}

}

你可能感兴趣的:(spring,java,spring,boot)