• SpringBoot学习day6


    spring中注入bean的几种方式

    1. 通过xml来进行注入,首先我们需要通过创建一个配置文件applicationContext.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="cat" class="com.demo.domain.Cat">bean>
          <bean class="com.demo.domain.Dog">bean>
          <bean class="com.demo.domain.Dog">bean>
      beans>
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11

      至此我们已经实现了注入bean,如果我们要获取bean时候,我们需要通过读取spring的核心配置文件,然后通过ApplicationContext对象调用getBean方法来获取对应的bean对象,其中getBean方法可以有几种方式:

      • getBean(对应的Bean的字节码对象):如果通过这一种方式的话,那么就会返回对应的Bean对象,但是这时候如果在Spring容器中有多个这种类型的bean对象的时候,那么调用这个方法的时候就会发生报错,提示这种bean不唯一的错误,因为没有办法知道要拿的是哪个bean。
      • getBean(String beanName):通过这一种方式,就会获取对应的beanName名称的bean对象,但是因为没有明确bean是什么类型,所以最后返回的对象是Object对象,所以这时候我们需要进行类型转换。
      • getBean(String beanName,Class clazz):通过这种方式,就会获取对应的beanName名称的bean对象,并且应为有传递了这个bean对象的字节码对象,所以可以明确知道这个bean是什么类型,所以最后返回的对象就是我们需要的bean类型,因此不再需要类型转换了。
      public class App1 {
          public static void main(String[] args) {
              ApplicationContext application = new ClassPathXmlApplicationContext("applicationContext.xml");
              String[] names = application.getBeanDefinitionNames();//获取定义的bean的名字
              for(String name : names){
                  System.out.println(name);
              }
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9

      对应的输出为:

      cat
      com.demo.domain.Dog#0
      com.demo.domain.Dog#1
      
      • 1
      • 2
      • 3

      如果需要注入第三方技术的bean的时候,通过xml进行注入的时候,也是这样的道理,只是需要我们导入对应的依赖之后才可以。

    2. 通过注解来注入bean对象:

      如果需要通过注解来注入bean对象的时候,我们可以利用一下几个注解:@Component , @Controller, @Service

      , @Repository,只是@Component可以在任意层中添加到bean容器中,@Controller则是将controller层中的类添加到bean容器中,@Service,@Repository则是将service层,dao层中的类添加到bean容器中。

      只是在使用这些注解的同时,我们还需要在配置文件中利用context命名空间进行组件扫描,只有添加这个语句,才可以将利用上面注解修饰的类添加到bean容器中,否则那些被上面注解修饰的类是没有办法添加到spring容器中的。

      对应的applicationContext文件:

      
      <beans xmlns="http://www.springframework.org/schema/beans"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xmlns:context="http://www.springframework.org/schema/context"
             xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                                 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
      
          
          <context:component-scan base-package="com.demo.domain,com.demo.config">context:component-scan>
      beans>
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      @Component("杰瑞") //将cat这个类添加到spring容器中,bean的名称为为杰瑞,如果没有设置,那么就是类名,并且第一个字母小写
      public class Cat {
      }
      
      @Component
      public class Dog {
      }
      
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8

      利用applicationContext调用getBeanDefinitionNames的时候,就可以获取所有的bean对象的名称,对应的代码为:

      public class App1 {
          public static void main(String[] args) {
              ApplicationContext application = new ClassPathXmlApplicationContext("applicationContext.xml");
              String[] names = application.getBeanDefinitionNames();//获取定义的bean的名字
              for(String name : names){
                  System.out.println(name);
              }
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9

      输出为:

      杰瑞
      dog
      org.springframework.context.annotation.internalConfigurationAnnotationProcessor
      org.springframework.context.annotation.internalAutowiredAnnotationProcessor
      org.springframework.context.annotation.internalCommonAnnotationProcessor
      org.springframework.context.event.internalEventListenerProcessor
      org.springframework.context.event.internalEventListenerFactory
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7

      但是这时候我们如果将第三方技术注入到spring容器中呢?这时候我们需要利用注解@Bean,这样他就可以将方法的返回值添加到spring容器中了。但是这个方法所在的类必须也是在spring容器中,否则如果这个类是一个普通类,那么即使方法中添加了注解@Bean,spring也不会扫描这个普通类,也就不会进入到这个类的内部了。这时候我们可以利用注解@Configuration来说明这个类是一个配置类,而在@Configuration注解中,也有利用到了注解@Component,那么这时候利用注解@Configuration,也可以将类添加到spring容器中了。

      对应的代码为:

      //这里使用@Component也是可以的
      @Configuration
      public class DbConfig {
          @Bean
          public DataSource dataSource(){
              System.out.println("dataSource is init....");
              return new ComboPooledDataSource();
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9

      这时候对应的输出结果为:

      dataSource is init....
      八月 30, 2022 4:43:56 下午 com.mchange.v2.log.MLog 
      信息: MLog clients using java 1.4+ standard logging.
      八月 30, 2022 4:43:57 下午 com.mchange.v2.c3p0.C3P0Registry 
      信息: Initializing c3p0-0.9.5.2 [built 08-December-2015 22:06:04 -0800; debug? true; trace: 10]
      杰瑞
      dog
      dbConfig
      org.springframework.context.annotation.internalConfigurationAnnotationProcessor
      org.springframework.context.annotation.internalAutowiredAnnotationProcessor
      org.springframework.context.annotation.internalCommonAnnotationProcessor
      org.springframework.context.event.internalEventListenerProcessor
      org.springframework.context.event.internalEventListenerFactory
      dataSource
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14

      但是这时候既然@Component和@Configuration都可以将配置类添加到spring容器中,那么为什么还需要使用注解@Configuration,换句话说,为什么还需要@Configuration呢?

      这时候我们的测试代码为:

      public class App1 {
          public static void main(String[] args) {
              ApplicationContext application = new ClassPathXmlApplicationContext("applicationContext.xml");
              DbConfig dbConfig = application.getBean("dbConfig", DbConfig.class);
              
              System.out.println(dbConfig.dog2());
              System.out.println(dbConfig.dog2());
              System.out.println(dbConfig.dog2());
      
          }
      }
      
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12

      如果在DbConfig这个类上面使用的是注解@Configuration,那么对应的结果为:

      dataSource is init....
      八月 30, 2022 5:15:00 下午 com.mchange.v2.log.MLog 
      信息: MLog clients using java 1.4+ standard logging.
      八月 30, 2022 5:15:00 下午 com.mchange.v2.c3p0.C3P0Registry 
      信息: Initializing c3p0-0.9.5.2 [built 08-December-2015 22:06:04 -0800; debug? true; trace: 10]
      dog2 is init......
      com.demo.domain.Dog@35e2d654
      com.demo.domain.Dog@35e2d654
      com.demo.domain.Dog@35e2d654
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9

      如果在DbConfig类上面使用的是注解@Component,那么对应的结果为:

      dataSource is init....
      八月 30, 2022 5:12:55 下午 com.mchange.v2.log.MLog 
      信息: MLog clients using java 1.4+ standard logging.
      八月 30, 2022 5:12:56 下午 com.mchange.v2.c3p0.C3P0Registry 
      信息: Initializing c3p0-0.9.5.2 [built 08-December-2015 22:06:04 -0800; debug? true; trace: 10]
      dog2 is init......
      dog2 is init......
      com.demo.domain.Dog@51e5fc98
      dog2 is init......
      com.demo.domain.Dog@7c469c48
      dog2 is init......
      com.demo.domain.Dog@12e61fe6
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12

      为什么有这样的区别呢?这是因为注解@Configuration中存在一个属性proxyMethodBean,默认值为true,这样他会将我们生成一个cglib代理对象,也即第一次调用对应的方法(这个方法需要被@Bean修饰)的时候,他会将返回值添加到spring容器中,然后下次再调用的时候,那么就会直接从spring容器中获取,而不会再次进入到这个方法内部了,如果proxyMethodBean的值为false,那么这时候它的结果和@Component的结果一样,每次调用方法的时候,都需要进入到方法内部执行。而@Component则是每次调用方法的时候,都会进入到方法内部,所以这时候得到的对象是不一样的。可以参考这个文章:@Component和@Configuration

    3. 使用注解之后,发现还需要applicationContext.xml这个配置文件中,利用context命名空间进行组件扫描,那么能不能指利用注解,而不再需要这个配置文件呢?答案是肯定的,我们需要利用配置类,然后再配置类的上方使用注解@ComponentScan来进行组件扫描即可。这时候因为没有了配置文件,所以在获取ApplicationContext对象的时候,我们不可以在通过ClassPathXmlApplicationContext来获取了,而是通过AnnotationConfigApplicationContext来获取,对应的代码为:

      @Configuration
      @ComponentScan(basePackages = {"com.demo.domain","com.demo.config"})
      public class SpringConfig {
          @Bean
          public DogFactoryBean factoryBean(){
              return new DogFactoryBean();
          }
      
          @Bean
          public Cat cat(){
              System.out.println("Cat is init..........");
              return new Cat();
          }
      }
      
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15

      测试代码为:

      public class App3 {
          public static void main(String[] args) {
              ApplicationContext app = new AnnotationConfigApplicationContext(SpringConfig.class);
              /*
              在注解@Configuration中,有设置属性proxyMethodBean,默认值为true,意思是
              当在配置类中创造bean对象的时候,方法返回的是一个新建的新建的对象,这时候
              如果它的值为true,那么第一次调用这个方法的时候,将它的返回值添加到spring容器
              中,当下次在调用这个方法的时候,不再进入方法内部执行,而是直接从spring容器中取出的,否则
              如果它的值为false,那么每次都需要进入到方法内部执行代码,然后返回的对象都是新建的,
              所以每次调用得到的对象都是不同的
               */
              SpringConfig springConfig = app.getBean("springConfig", SpringConfig.class);
              System.out.println(springConfig.cat());
              System.out.println(springConfig.cat());
              System.out.println(springConfig.cat());
          }
      }
      
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18

      对应的结果为:

      dataSource is init....
      八月 30, 2022 5:25:57 下午 com.mchange.v2.log.MLog 
      信息: MLog clients using java 1.4+ standard logging.
      八月 30, 2022 5:25:58 下午 com.mchange.v2.c3p0.C3P0Registry 
      信息: Initializing c3p0-0.9.5.2 [built 08-December-2015 22:06:04 -0800; debug? true; trace: 10]
      Cat is init..........
      com.demo.domain.Cat@62150f9e
      com.demo.domain.Cat@62150f9e
      com.demo.domain.Cat@62150f9e
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
    4. 手动注册bean对象:当我们获取到了AnnotationConfigApplicationContext对象之后,我们通过调用register或者registerBean方法来注册bean对象:

      • register(Class clazz): 注册对应的字节码对象为bean对象,这时候bean对象的名字就是它的类名,并且第一个字母为小写。这时候对应的类可以不使用注解来将其添加到bean也可以,因为register会将其注册的,但是如果这个类已经使用对应的注解,将其添加到spring容器中,那么调用这个方法的时候,并没有生效的。
      • registerBean(String beanName,Class clazz,):注册对应的bean对象,并且bean对象的名字是beanName
      • registerBean(String beanName,Class clazz,constructionArags):注册对应的bean对象,并且bean对象的名字为beanName,并且有传递可变参数constructionArags,这时候,同一个beanName中多次调用这个方法,并且传递的参数不一样,那么将会以最后一个参数的为主,因为之前的已经被覆盖了。
      @Configuration
      @ComponentScan({"com.demo.domain"})
      public class SpringConfig2 {
      }
      
      • 1
      • 2
      • 3
      • 4

      对应的实体类代码:

      @Component("杰瑞")
      public class Cat {
      }
      
      @Component
      public class Dog {
          public Dog() {
          }
          int age;
      
          public Dog(int age) {
              this.age = age;
          }
      
          @Override
          public String toString() {
              return "Dog{" +
                      "age=" + age +
                      '}';
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21

      测试代码:

      public class App4 {
          public static void main(String[] args) {
              AnnotationConfigApplicationContext app = new AnnotationConfigApplicationContext(SpringConfig2.class);
              //手动注册bean对象
              app.register(Cat.class);//注册Cat类型的bean对象,并且bean的名字为全路径
              app.registerBean("yellow", Dog.class,1);
              app.registerBean("yellow",Dog.class,2);
              app.registerBean("yellow",Dog.class,3);
              String[] names = app.getBeanDefinitionNames();
              for(String name : names){
                  System.out.println(name);
              }
              System.out.println(app.getBean("yellow",Dog.class));
          }
      }
      
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16

      输出结果为:

      org.springframework.context.annotation.internalConfigurationAnnotationProcessor
      org.springframework.context.annotation.internalAutowiredAnnotationProcessor
      org.springframework.context.annotation.internalCommonAnnotationProcessor
      org.springframework.context.event.internalEventListenerProcessor
      org.springframework.context.event.internalEventListenerFactory
      springConfig2
      杰瑞
      dog
      yellow
      Dog{age=3}
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
    5. 定义一个类,使得这个类实现了ImportSelector接口,然后重写它的方法selectImport,方法的返回值就是要添加的bean对象。然后需要在核心配置类中利用@Import注解来引入这个类,然后就可以进行测试了,对应的代码为:

      public class MyImportSelector implements ImportSelector {
          public String[] selectImports(AnnotationMetadata annotationMetadata) {
              /*
              将字符串数组中的元素添加到spring容器中,对应的元素就是对应的类的全路径,
              这时候获取到的bean对象的名字就是它的全路径。但是如果这个类上面使用了
              @Component等注解,并且设置它的名字,那么这时候对应的bean对象的名字就是自己设置的
              否则,如果没有设置它的名字,或者根本没有使用@Component等注解,那么对应的bean对象的名字
              就是它的全路径
              */
              return new String[]{"com.demo.domain.Dog","com.demo.domain.Cat"};
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12

      实体类:

      @Component("杰瑞")
      public class Cat {
      }
      
      public class Dog {
          public Dog() {
          }
          int age;
      
          public Dog(int age) {
              this.age = age;
          }
      
          @Override
          public String toString() {
              return "Dog{" +
                      "age=" + age +
                      '}';
          }
      }
      
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21

      核心配置类:

      @Configuration
      @Import(MyImportSelector.class) //利用注解@Import,这样就可以导入对应的类
      public class SpringConfig3 {
      }
      
      • 1
      • 2
      • 3
      • 4

      测试代码:

      public class App5 {
          public static void main(String[] args) {
              AnnotationConfigApplicationContext app = new AnnotationConfigApplicationContext(SpringConfig3.class);
              String[] names = app.getBeanDefinitionNames();
              for(String name : names){
                  System.out.println(name);
              }
          }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8

      测试结果:

      org.springframework.context.annotation.internalConfigurationAnnotationProcessor
      org.springframework.context.annotation.internalAutowiredAnnotationProcessor
      org.springframework.context.annotation.internalCommonAnnotationProcessor
      org.springframework.context.event.internalEventListenerProcessor
      org.springframework.context.event.internalEventListenerFactory
      springConfig3
      com.demo.domain.Dog
      杰瑞
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
    6. 定义一个类,让这个类实现接口ImportBeanDefinitionRegistrar,重写它的对应方法,从而注册bean

      对应的代码为:

      public class MyRegistrar implements ImportBeanDefinitionRegistrar {
          public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
              BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(Cat.class).getBeanDefinition();
              registry.registerBeanDefinition("Tom猫",beanDefinition);
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6

      配置类代码:

      @Configuration
      @Import(MyRegistrar.class)
      public class SpringConfig4 {
      }
      
      • 1
      • 2
      • 3
      • 4

      测试类代码:

      public class App6 {
          public static void main(String[] args) {
              AnnotationConfigApplicationContext app = new AnnotationConfigApplicationContext(SpringConfig4.class);
              String[] names = app.getBeanDefinitionNames();
              for(String name : names){
                  System.out.println(name);
              }
          }
      }
      
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10

      运行结果为:

      org.springframework.context.annotation.internalConfigurationAnnotationProcessor
      org.springframework.context.annotation.internalAutowiredAnnotationProcessor
      org.springframework.context.annotation.internalCommonAnnotationProcessor
      org.springframework.context.event.internalEventListenerProcessor
      org.springframework.context.event.internalEventListenerFactory
      springConfig4
      Tom猫
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
    7. 定义一个类,让这个类实现接口BeanDefinitionRegistryPostProcessor接口,然后重写方法,从而将对应的bean注册。值得注意的是,因为是BeanDefinitionRegistryPostProcessor接口,根据它的名称可以知道,它是在bean定义之后,然后再处理的,所以如果这个bean之前已经存在了,那么再这个接口中重新注入相同名字的bean的时候,就会覆盖之前的。对应的代码为:

      public class MyPostProcessor implements BeanDefinitionRegistryPostProcessor {
          public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
              //注册bean,只是他是在bean定义之后才进行的,
              BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(BookServiceImpl4.class).getBeanDefinition();
              registry.registerBeanDefinition("bookService",beanDefinition);
      
          }
      
          public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
      
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12

      同时为了证明这种方式将会覆盖之前的bean,所以需要再次通过实现接口BeanDefinitionRegistrar来注入bean:

      public class MyRegistrar1 implements ImportBeanDefinitionRegistrar {
          public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
              BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(BookServiceImpl2.class).getBeanDefinition();
              registry.registerBeanDefinition("bookService",beanDefinition);
          }
      }
      
      
      public class MyRegistrar2 implements ImportBeanDefinitionRegistrar {
          public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
              BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(BookServiceImpl3.class).getBeanDefinition();
              registry.registerBeanDefinition("bookService",beanDefinition);
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14

      对应的实体类:

      public interface BookService {
          void check();
      }
      
      @Service("bookService")
      public class BookServiceImpl1 implements BookService {
          public void check() {
              System.out.println("bookService 1 .. ");
          }
      }
      
      
      public class BookServiceImpl2 implements BookService {
          public void check() {
              System.out.println("bookService 2.... ");
          }
      }
      
      
      public class BookServiceImpl3 implements BookService {
          public void check() {
              System.out.println("bookService 3 ....... ");
          }
      }
      
      
      public class BookServiceImpl4 implements BookService {
          public void check() {
              System.out.println("bookService 4......... ");
          }
      }
      
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32

      对应的配置类:

      @Configuration
      @Import({BookServiceImpl1.class, MyRegistrar2.class,MyRegistrar1.class, MyPostProcessor.class})
      public class SpringConfig5 {
      }
      
      • 1
      • 2
      • 3
      • 4

      测试类的代码:

      public class App7 {
          public static void main(String[] args) {
              ApplicationContext app = new AnnotationConfigApplicationContext(SpringConfig5.class);
              BookService bookService = app.getBean("bookService", BookService.class);
              bookService.check();
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7

      运行结果是根据上面配置类中注解@Import的值来设置的:

      • @Import({BookServiceImpl1.class}):那么输出的是bookService 1…
      • @Import({BookServiceImpl1.class,MyRegistar1.class})或者@Import({MyRegistar1.class,BookServiceImpl1.class}),都是输出bookService 2…,这说明了优先级是通过实现接口BeanDefinitionRegistrar注入bean这种方式的优先级更加高,如果是@Import({BookServiceImpl1.class,MyRegistar1.class,MyRegistar2.class}),那么输出的是bookService 3…,那么说明同一优先级的时候,那么是根据@Import中的顺序来配置的
      • @Import({BookServiceImpl1.class,MyRegistar1.class,MyRegistar1.class,MyPostProcessor.class})还是@Import({MyPostProcessor.class,BookServiceImpl1.class,MyRegistar1.class,MyRegistar1.class}),都是输出的是bookService 4…,说明通过实现接口BeanDefinitionRegistryPostProcessor来注入bean的优先级最高.
    8. 在配置类的上方使用注解@Import,然后属性classes的值是我们需要导入的字节码对象,这样就可以将对应的字节码对象添加到bean中,这样就不需要在对应的类上方使用@Component等注解来注入bean了,从而降低了解耦。如果导入的是一个配置类,那么在将这个配置类导入bean的同时,这个配置类内部中使用@Bean注解修饰的方法也会注入到bean中。

    bean的注入控制

    如果我们需要控制bean的注入,那么上面的几种注入bean的方式中,只有以下方式可以实现bean的注入控制:

    • 通过实现ImportSelector接口,然后在配置类中利用注解@Import来导入实现类。
    • 通过实现BeanDefinitionRegistrar接口,然后在配置类中利用注解@Import来导入对应的实现类
    • 通过实现BeanDefinitionRegistrarPostProcessor接口,然后在配置类中利用注解@Import来导入对应的实现类。
    • 通过AnnotationConfigApplicationContext对象调用register方法来注册bean的时候,可以进行bean的注入控制。

    所以可以拿第一种方式为例,如果能够在项目中找到mysql驱动,那么就可以注入druid数据源为bean,否则不可以注入,而判断这个类是否存在于项目中,则可以通过class.forName(xxxx)来寻找,如果返回值为null,说明不存在,否则存在.对应的代码为:

    public class MyImportSelector2 implements ImportSelector {
        public String[] selectImports(AnnotationMetadata annotationMetadata) {
            try {
               //如果能够找到数据库驱动,那么就需要注入数据源为bean,否则不需要
                Class<?> clazz = Class.forName("com.mysql.cj.jdbc.Driver");
                if(clazz != null){
                    return new String[]{"com.alibaba.druid.pool.DruidDataSource"};
                }
            } catch (ClassNotFoundException e) {
                //如果clazz没有找到,那么就会发生报错,此时直接返回,不需要注入数据源
                return new String[0];
            }
            return null;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    配置类代码为:

    @Configuration
    @Import(MyImportSelector2.class)
    public class SpringConfig6 {
    }
    
    • 1
    • 2
    • 3
    • 4

    测试类:

    public class App8 {
        public static void main(String[] args) {
            AnnotationConfigApplicationContext app = new AnnotationConfigApplicationContext(SpringConfig6.class);
            String[] names = app.getBeanDefinitionNames();
            for(String name : names){
                System.out.println(name);
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    如果我们只导入了druid的依赖,而没有导入mysql-connector-java的依赖,那么这时候运行结果为:

    org.springframework.context.annotation.internalConfigurationAnnotationProcessor
    org.springframework.context.annotation.internalAutowiredAnnotationProcessor
    org.springframework.context.annotation.internalCommonAnnotationProcessor
    org.springframework.context.event.internalEventListenerProcessor
    org.springframework.context.event.internalEventListenerFactory
    springConfig6
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    并没有注入了数据源,否则,如果我们在导入druid以来的同时,也导入了mysql-connector-java依赖,那么这时候运行结果为:

    org.springframework.context.annotation.internalConfigurationAnnotationProcessor
    org.springframework.context.annotation.internalAutowiredAnnotationProcessor
    org.springframework.context.annotation.internalCommonAnnotationProcessor
    org.springframework.context.event.internalEventListenerProcessor
    org.springframework.context.event.internalEventListenerFactory
    springConfig6
    com.alibaba.druid.pool.DruidDataSource
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    但是显然,上面方式实现bean的注入控制显然有限繁琐,所以这时候我们需要通过注解来实现bean的注入控制,主要是通过注解@Conditional,但是这个注解的代码为:

    public @interface Conditional {
        Class<? extends Condition>[] value();
    }
    
    
    • 1
    • 2
    • 3
    • 4

    也即它的属性值要求是继承了Condition的类,而Condition这个接口的代码为:

    @FunctionalInterface
    public interface Condition {
        boolean matches(ConditionContext var1, AnnotatedTypeMetadata var2);
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5

    所以通过自定义类实现了Condition接口,然后重写方法matches,那么就可以控制注入bean,如果重写的方法中返回的是false,那么不可以注入,否则可以注入。因此我们自定义一个类MyCondition,重写matches方法从而控制druid数据源的注入.

    public class MyCondition implements Condition {
        public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
           //如果返回的是false,那么使用注解@Conditional(MyConditional.class)
            //修饰的类或者方法,不会注入到spring容器中,否则返回是true的时候,就可以注入
            try {
                Class<?> clazz = Class.forName("com.mysql.cj.jdbc.Driver");
                if(clazz != null){
                    return true;
                }
            } catch (ClassNotFoundException e) {
            }
            return false;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    对应的配置类为:

    @Configuration
    public class SpringConfig7 {
        @Bean
        @Conditional(MyCondition.class)
        public DataSource druidDataSource(){
            return new DruidDataSource();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    这时候测试的时候,如果我们已经导入了mysql-connector-java依赖,那么就可以将数据源注入,否则不可以。

    但是通过实现Condition接口来控制bean的注入依旧很麻烦,所以这时候可以使用注解@ConditionalOnxxx来控制bean的注入。但是在使用@ConditionalOnxxxx这个注解的时候,首先我们需要先导入spring-boot-starter依赖,然后就饿可以使用这些注解了。所以上面的配置类的代码可以修改为:

    @Configuration
    public class SpringConfig7 {
        @Bean
        //如果能够找到com.mysql.cj.jdbc.Driver这个类,那么就可以注入这个bean,否则不可以
        @ConditionalOnClass(name = "com.mysql.cj.jdbc.Driver")
        public DataSource druidDataSource(){
            return new DruidDataSource();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    bean的依赖配置

    如果我们需要绑定bean的属性的时候,可以使用注解@ConfigurationProperties,从而可以使得这个bean绑定到了配置文件中的某一个属性了。但是使用@ConfigurationProperties注解的前提时,这个类是一个bean对象,否则就会提示错误。并且这个类属性要含有get/set方法才可以,并且类的属性成员名要和配置文件中的属性名相同,否则是没有办法进行属性绑定的

    对应的代码如下所示:

    public class Cat {
        private String name;
        private Integer age;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public Integer getAge() {
            return age;
        }
    
        public void setAge(Integer age) {
            this.age = age;
        }
    
        @Override
        public String toString() {
            return "Cat{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    '}';
        }
    }
    
    
    public class Mouse {
        private String name;
        private Integer age;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public Integer getAge() {
            return age;
        }
    
        public void setAge(Integer age) {
            this.age = age;
        }
    
        @Override
        public String toString() {
            return "Mouse{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    '}';
        }
    }
    
    @Component
    @ConfigurationProperties(prefix = "cartoon")
    public class CartoonCatAndMouse {
        private Cat cat;
        private Mouse mouse;
    
        public Cat getCat() {
            return cat;
        }
    
        public void setCat(Cat cat) {
            this.cat = cat;
        }
    
        public Mouse getMouse() {
            return mouse;
        }
    
        public void setMouse(Mouse mouse) {
            this.mouse = mouse;
        }
    
        public void play(){
            System.out.println("cat: " + cat.getName() + ", cat.age = " + cat.getAge() +
                             " mouse: name = " + mouse.getName() + ", age =  "+ mouse.getAge());
        }
    }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88

    对应的配置文件为:

    cartoon:
      cat:
        name: tom
        age: 5
      mouse:
        name: jerry
        age: 3
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    这时候的CartoonCatAndMouse中的属性成员就可以绑定到了配置文件中的cartoon的属性了。但是这时候会存在一个问题,如果我们的配置文件中并没有存在cat这个属性或者不存在cartoon属性,那么这时候CartoonCatAndMouse就没有办法绑定到了配置文件中的属性,从而导致cat,mouse是null,这时候调用方法play的时候,就会发生空指针异常。这是我们并不想看到的结果,所以这时候我们需要再定义一个类CartoonCatAndMousePropertites,在这个类中同样存在属性cat,mouse,并且将这个类和配置文件中的属性cartoon绑定,这样CartoonCatAndMouse就可以不需要和配置文件中进行绑定了,如果需要将CartoonCatAndMouse中cat,mouse和配置文件中的属性绑定的时候,那么这时候我们只需要注入CartoonCatAndMouseProperties,通过CartoonCatAndMouseProperties调用对应的方法来给cat,mouse赋值即可。

    对应的代码为:

    @Component
    @ConfigurationProperties(prefix = "cartoon")
    public class CartoonCatAndMouseProperties {
        private Cat cat;
        private Mouse mouse;
    
        public Cat getCat() {
            return cat;
        }
    
        public void setCat(Cat cat) {
            this.cat = cat;
        }
    
        public Mouse getMouse() {
            return mouse;
        }
    
        public void setMouse(Mouse mouse) {
            this.mouse = mouse;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    对应的CartoonCatAndMouse代码为:

    @Component
    public class CartoonCatAndMouse {
        private Cat cat;
        private Mouse mouse;
    
        private CartoonCatAndMouseProperties properties;
    
        public CartoonCatAndMouse(CartoonCatAndMouseProperties properties){
            this.properties = properties;
            cat = new Cat();
            cat.setName(properties.getCat() != null
                    && !StringUtils.isEmpty(properties.getCat().getName()) ? properties.getCat().getName() : "tt");
            cat.setAge(properties.getCat() != null && properties.getCat().getAge() != null ? properties.getCat().getAge() : 4);
            mouse = new Mouse();
            mouse.setName(properties.getMouse() != null
                    && !StringUtils.isEmpty(properties.getMouse().getName()) ? properties.getMouse().getName() : "mouseTT");
            mouse.setAge(properties.getMouse() != null && properties.getMouse().getAge() != null ? properties.getMouse().getAge() : 5);
        }
    
        public Cat getCat() {
            return cat;
        }
    
        public void setCat(Cat cat) {
            this.cat = cat;
        }
    
        public Mouse getMouse() {
            return mouse;
        }
    
        public void setMouse(Mouse mouse) {
            this.mouse = mouse;
        }
    
        public void play(){
            System.out.println("cat: " + cat.getName() + ", cat.age = " + cat.getAge() +
                             " mouse: name = " + mouse.getName() + ", age =  "+ mouse.getAge());
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    但是这时候会有一个缺陷,那就是当CartoonCatAndMouse不需要绑定配置文件的属性的时候,那么这时候是并不需要CartoonCatAndMouseProperties,但是实际上不管是否需要,都已经将CartoonCatAndMouseProperties注入到bean中,这时候我们如何解决这个问题,也即当加载CartoonCatAndMouse的时候,自动地将CartoonCatAndMouseProperties注入到spring容器中,当不需要加载的时候,就不会将其注入呢?这时候我们只需要在CartoonCatAndMouseProperties上面的@Component注解去掉,并且在CartoonCatAndMouse类的上面添加注解@EnableConfigurationProperties(CartoonCatAndMouseProperties.class),这样就可以在需要加载CartoonCatAndMouse的时候,自动将CartoonCatAndMouseProperties加载到spring容器中了,不需要的时候就不会添加。

    自动配置原理

    我们启动spring boot项目的时候,它是要进入到启动类的,那么这时候,这个启动类中含有注解@SpringBootApplication,这个注解的代码如下所示:

    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    @SpringBootConfiguration
    @EnableAutoConfiguration
    @ComponentScan(
        excludeFilters = {@Filter(
        type = FilterType.CUSTOM,
        classes = {TypeExcludeFilter.class}
    ), @Filter(
        type = FilterType.CUSTOM,
        classes = {AutoConfigurationExcludeFilter.class}
    )}
    )
    public @interface SpringBootApplication
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    其中关键的注解是:@SpringBootConfiguration,@EnableAutoConfiguration以及@ComponentScan

    • @SpringBootConfiguration的代码如下所示:

      @Target({ElementType.TYPE})
      @Retention(RetentionPolicy.RUNTIME)
      @Documented
      @Configuration 
      public @interface SpringBootConfiguration
      
      • 1
      • 2
      • 3
      • 4
      • 5

      所以注解@SpringBootConfiguration主要是依赖于注解@Configuration,而@Configuration注解作用相当于@Component(通过观察源码可以知道),用来添加bean,但是却又与@Component不同,因为@Configuration注解中含有属性proxyMethodBean,默认值是true,这样的话,如果我们在配置类中利用注解@Bean来将方法的返回值添加到spring容器中的时候,那么这时候在测试类中通过调用这个方法获取到的对象是和从spring容器中取出的是同一个对象,而如果是通过@Component的话,那么这时候每次调用这个方法取到的对象都是新建的。

    • @ComponentScan主要是用于组件扫描,而括号里面的值则是一个过滤器,表示哪些是不需要扫描的

    • @EnabelAutoConfiguration的代码如下所示:

      @Target({ElementType.TYPE})
      @Retention(RetentionPolicy.RUNTIME)
      @Documented
      @Inherited
      @AutoConfigurationPackage
      @Import({AutoConfigurationImportSelector.class})
      public @interface EnableAutoConfiguration{
          String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
      
          Class<?>[] exclude() default {};
      
          String[] excludeName() default {};
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13

      通过观察代码,可以知道,@EnableAutoConfiguration注解是依赖于@AutoConfigurationPackage以及@Import注解的额,并且还有两个属性值exclude,excludeName,这两个属性值主要是说明哪些类是不需要添加到spring容器中作为bean的,因为注解@SpringBootApplication依赖于这个注解,所以同样拥有这两个属性,所以也可以通过设置这两个属性值来设置哪些类不需要添加到bean中

      那么这时候我们重点来看@EnableAutoConfiguration中的两个注解@AutoConfigrationPackage以及@Import注解。

      • @AutoConfigurationPackage的代码如下所示:

        @Import({Registrar.class})
        public @interface AutoConfigurationPackage 
        
        • 1
        • 2

        说明@AutoConfigurationPackage主要依赖于注解@Import({Registrar.class}),然后我们来看Registrar这个类的源代码:

        static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
                Registrar() {
                }
        
                public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
                    AutoConfigurationPackages.register(registry, (String[])(new AutoConfigurationPackages.PackageImports(metadata)).getPackageNames().toArray(new String[0]));
                }
        
                public Set<Object> determineImports(AnnotationMetadata metadata) {
                    return Collections.singleton(new AutoConfigurationPackages.PackageImports(metadata));
                }
            }
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12

        因为这个类实现了接口ImportBeanDefinitionRegistrar,所以这个类是用来注入bean的,通过观察它的方法regiserBeanDefinitions,通过调试可以知道,(String[])(new AutoConfigurationPackages.PackageImports(metadata)).getPackageNames().toArray(new String[0])的值是我们项目的启动类所在的包名,然后将这个包及其子包下面的类进行扫描,然后添加到spring容器中。所以可以知道注解@AutoConfigurationPackage是用来扫描启动类所在的包以及子包下面的类,将其添加到spring容器中。

      • @Import({AutoConfigurationImportSelector.class})

        我们来到AutoConfigurationImportSelector.class类中,重点看它的代码:

        public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
                    Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector, () -> {
                        return String.format("Only %s implementations are supported, got %s", AutoConfigurationImportSelector.class.getSimpleName(), deferredImportSelector.getClass().getName());
                    });
                    AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector)deferredImportSelector).getAutoConfigurationEntry(annotationMetadata);
                    this.autoConfigurationEntries.add(autoConfigurationEntry);
                    Iterator var4 = autoConfigurationEntry.getConfigurations().iterator();
        
                    while(var4.hasNext()) {
                        String importClassName = (String)var4.next();
                        this.entries.putIfAbsent(importClassName, annotationMetadata);
                    }
        
        }
                
                
        protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
                if (!this.isEnabled(annotationMetadata)) {
                    return EMPTY_ENTRY;
                } else {
                    /*
                    这一步获得的之是@EnableAutoConfiguration中的属性值,也即是
                    说attributes是我们不希望添加到spring容器中的值
                    */
                    AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
                    /*
                    调用方法getCandidateConfigurations,将可以得到我们将上面attributes去掉之后的
                    类,那么这些类是可能添加到spring容器中的
                    */
                    List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
                    configurations = this.removeDuplicates(configurations);
                    Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
                    this.checkExcludedClasses(configurations, exclusions);
                    configurations.removeAll(exclusions);
                    configurations = this.getConfigurationClassFilter().filter(configurations);
                    this.fireAutoConfigurationImportEvents(configurations, exclusions);
                    return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
                }
        }  
        
        protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
            /*
            通过调用方法loadFactoryNames,那么就可以获取到可能添加到spring容器中的类的全路径名
            */
                List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
                Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.");
                return configurations;
         }
        
          public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
                String factoryTypeName = factoryType.getName();
                return (List)loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
        }
        
        
        private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
                MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
                if (result != null) {
                    return result;
                } else {
                    try {
                        /*
                        将META-INF包下面的spring.factories文件的内容,添加到spring容器中,然后返回
                        */
                        Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
                        LinkedMultiValueMap result = new LinkedMultiValueMap();
        
                        while(urls.hasMoreElements()) {
                            URL url = (URL)urls.nextElement();
                            UrlResource resource = new UrlResource(url);
                            Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                            Iterator var6 = properties.entrySet().iterator();
        
                            while(var6.hasNext()) {
                                Entry<?, ?> entry = (Entry)var6.next();
                                String factoryTypeName = ((String)entry.getKey()).trim();
                                String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
                                int var10 = var9.length;
        
                                for(int var11 = 0; var11 < var10; ++var11) {
                                    String factoryImplementationName = var9[var11];
                                    result.add(factoryTypeName, factoryImplementationName.trim());
                                }
                            }
                        }
        
                        cache.put(classLoader, result);
                        return result;
                    } catch (IOException var13) {
                        throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);
                    }
                }
            }
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
        • 14
        • 15
        • 16
        • 17
        • 18
        • 19
        • 20
        • 21
        • 22
        • 23
        • 24
        • 25
        • 26
        • 27
        • 28
        • 29
        • 30
        • 31
        • 32
        • 33
        • 34
        • 35
        • 36
        • 37
        • 38
        • 39
        • 40
        • 41
        • 42
        • 43
        • 44
        • 45
        • 46
        • 47
        • 48
        • 49
        • 50
        • 51
        • 52
        • 53
        • 54
        • 55
        • 56
        • 57
        • 58
        • 59
        • 60
        • 61
        • 62
        • 63
        • 64
        • 65
        • 66
        • 67
        • 68
        • 69
        • 70
        • 71
        • 72
        • 73
        • 74
        • 75
        • 76
        • 77
        • 78
        • 79
        • 80
        • 81
        • 82
        • 83
        • 84
        • 85
        • 86
        • 87
        • 88
        • 89
        • 90
        • 91
        • 92
        • 93

      我们的测试代码如下所示:

      @SpringBootApplication
      public class SpringbootBeanPropertiesApplication {
      
          public static void main(String[] args) {
              ConfigurableApplicationContext app = SpringApplication.run(SpringbootBeanPropertiesApplication.class, args);
              String[] names = app.getBeanDefinitionNames();
              for(String name : names){
                  System.out.println(name);
              }
          }
      
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12

      运行结果中就会多出一些我们不知道的类,原因就是上面将META-INF包下面的spring.factories文件中的内容添加到了spring容器中了。所以如果我们需要实现自动配置,那么我们同样可以在resources包下面定义META-INF/spring.factoreis文件:
      在这里插入图片描述
      然后按照对应的格式,设置org.springframework.boot.autoconfigure.EnableAutoConfiguration=\的值是我们希望自动配置的类的全路径名,这样就可以将对应的类自动配置到spring容器中了。同时我们也可以利用注解@ConditionalOnxxxx来控制bean的注入,也可以利用注解@EnableConfigurationProperties来实现它的依赖配置。例如redis就是这样设置的.

  • 相关阅读:
    Ansible-Tower web界面管理安装
    基于Stable Diffusion的AIGC服饰穿搭实践
    Python 面试必看
    基于Java web的校园滴滴代驾管理系统 毕业设计-附源码260839
    系统优化与微服务架构、分布式架构的合理性思考
    [PAT练级笔记] 16 Basic Level 1016
    使用Python进行时间序列分析的8种图
    MSF入门
    go实战(2)-hello,world与包基础(2)-模块基础
    【补充】助力工业物联网,工业大数据之AirFlow安装
  • 原文地址:https://blog.csdn.net/weixin_46544385/article/details/126648541