Spring Boot 2.x实战4 - Spring 5.x基础2 - Spring Bean的配置

2 Spring Bean的配置

2.1 注解配置 - @Component

当类注解@Component@Service@Repository@Controller,Spring容器会自动扫描(通过@ComponentScan实现,Spring Boot已经做好了配置)并将他们注册成受容器管理的Bean。

@Component
public class SomeService {
  public void doSomething(){
    System.out.println("我做了一些工作");
  }
}

@Component@Service@Repository@Controller在当前示例中是完全等同的:

@Service
public class SomeService2 {
    public void doSomething(){
        System.out.println("我也做了一些工作");
    }
}

上面的@Component@Service都没有给Bean命名,Spring容器会自动命名为类名的第一个字母小写,即someServicesomeService2,我们一般没有必要去改Bean的名称,使用默认的Bean名即可,当然也可以通过@Component("SomeService")来设置。

@Service@Repository@Controller这三个注解组合了@Component注解,都是@Component语义上的特例。

  • @Component:被注解类是“组件”;
  • @Controller:被注解类是“控制器”;
  • @Service:被注解类是“服务”;
  • @Repository:被注解类是“数据仓库”。

2.2 Java配置 - @Configuration和@Bean

在类上注解了@Configuration@Component的特例,也会被容器自动扫描)作为配置类上,使用@Bean标注在方法上让方法的返回值作为Bean的实例。如我们现在有另外一个类:

@Getter //lombok注解,给属性生成get方法
@Setter //lombok注解,给属性生成set方法
public class AnotherService {
    private String person;

    public AnotherService(String person) {
        this.person = person;
    }

    public void doAnotherThing(){
        System.out.println(person + "做了另外的事情");
    }

}

我们的用Java配置的如下:

@Configuration
public class JavaConfig {

    @Bean
    public AnotherService anotherService(){
        return new AnotherService("wyf");
    }

}

同样我们也没有给Bean命名,Spring会将方法名anotherService默认成方法名,若需要修改使用@Bean(name = "AnotherService")

2.3 依赖注入

2.3.1 自动注入- @Autowired

容器已经为我们创建了SomeServiceAnotherServiceSomeService2的Bean,那其他的Bean怎么注入使用呢?

  • 注解注入

    我们有个AnnotationInjectionService要使用SomeServiceAnotherService的Bean,我们只要在AnnotationInjectionService构造器上注解@Autowired即可注入参数里需要的Bean:

    @Service
    public class AnnotationInjectionService {
        private SomeService someService;
    
        private SomeService2 someService2;
    
        @Autowired
        public AnnotationInjectionService(SomeService someService,SomeService2 someService2) {
            this.someService = someService;
            this.someService2 = someService2;
        }
    
        public void doMyThing(){
            someService.doSomething();
            someService2.doSomething();
        }
    }
    

    使用构造器注入是Spring推荐的注入方式,当然我们也可以在属性上注解@Autowired来注入Bean:

    @Service
    public class AnnotationPropertyInjectionService {
        @Autowired
        private SomeService someService;
    
        @Autowired
        private SomeService2 someService2;
    
    
        public void doMyThing(){
            someService.doSomething();
            someService2.doSomething();
        }
    }
    

    我们也可以在set方法上注解@Autowired来注入Bean:

    @Service
    public class AnnotationSetterInjectionService {
    
        private SomeService someService;
    
        private SomeService2 someService2;
    
        @Autowired
        public void setSomeService(SomeService someService) {
            this.someService = someService;
        }
    
        @Autowired
        public void setSomeService2(SomeService2 someService2) {
            this.someService2 = someService2;
        }
    
        public void doMyThing(){
            someService.doSomething();
            someService2.doSomething();
        }
    }
    
    

    如果Bean只有一个构造器的话,我们可以直接省略@Autowired注解;若有多个构造器,需注解一个构造器用来注入如:

    @Service
    public class AnnotationOneInjectionService {
        private SomeService someService;
    
        public AnnotationOneInjectionService(SomeService someService) {
            this.someService = someService;
        }
    
        public void doMyThing(){
            someService.doSomething();
        }
    }
    
  • 配置注入

    现在我们需要在Bean JavaConfigInjectService注入BeanAnotherService而使用Java配置的方式,JavaConfigInjectService定义如下:

    public class JavaConfigInjectService {
        private AnotherService anotherService;
    
        public JavaConfigInjectService(AnotherService anotherService) {
            this.anotherService = anotherService;
        }
    
        public void doMyThing(){
            anotherService.doAnotherThing();
        }
    }
    

    前面我们已经将AnotherService通过@Bean注解成Bean了,我们只需在定义JavaConfigInjectService的Bean的方法参数里注入AnotherService的Bean即可:

    @Bean
    public JavaConfigInjectService javaConfigInjectService(AnotherService anotherService){
        return new JavaConfigInjectService(anotherService);
    }
    

    在同一个配置类里,我们还可以在新建JavaConfigInjectService的构造里直接注入创建SomeService2的Bean的方法:

    @Bean
    public JavaConfigInjectService javaConfigInjectService(){
        return new JavaConfigInjectService(anotherService());
    }
    
  • 混合注入

    注解配置的Bean可以直接注入给使用Java配置的Bean,反之亦然。

    • 注解Bean注入配置Bean:

      @Service //使用注解配置的Bean
      public class MixInjectionService {
          private AnotherService anotherService;//注入Java配置的Bean
      
          public MixInjectionService(AnotherService anotherService) {
              this.anotherService = anotherService;
          }
      
          public void doMyThing(){
              anotherService.doAnotherThing();
          }
      }
      
    • 配置Bean注入注解Bean:

      被注入的BeanMixInjectionService2定义如下:

      public class MixInjectionService2 {
          private SomeService someService; //是使用@Component注解配置的Bean
      
          public MixInjectionService2(SomeService someService) {
              this.someService = someService;
          }
      
          public void doMyThing(){
              someService.doSomething();
          }
      }
      

      JavaConfig类里可以直接在参数注入:

      @Bean
      public MixInjectionService2 mixInjectionService2(SomeService someService){
          return new MixInjectionService2(someService);
      }
      
    2.3.2 @Primary

我们上面的例子都是通过Bean的名称来自动注入的,当Bean的名称不能满足条件时候,容器会自动根据Bean的类型进行自动注入的,在全局只有一个类型的Bean的时候自动注入是没有问题的,但是当全局有多个同类型的Bean的时候报required a single bean, but n were found,我们可以通过注解@Primary来注解需要优先使用的Bean,如我们有两个Bean:

@Bean
public AnotherService anotherService(){
    return new AnotherService("wyf");
}

@Bean
@Primary
public AnotherService primaryAnotherService(){
		return new AnotherService("foo");
}

此时我们有两个Bean,名称分别为:anotherServiceprimaryAnotherService,我们在注入的地方不使用这个两个名字,这时就会使用按照类型自动绑定:

@Component
public class UsePrimaryService {
    private AnotherService service;

    public UsePrimaryService(AnotherService service) {
        this.service = service;
    }

    public void doSomething(){
        System.out.println("foo".equals(service.getPerson()));
    }
}

现在使用的service不符合按照名称自动注入,而按照类型自动注入,因为primaryAnotherService注解了@Primary,所以使用primaryAnotherService这个Bean。

2.3.3 @Qualifier

上面的例子中我们使用UsePrimaryService注入的AnotherService的Bean只会是primaryAnotherService,我们可以使用@Qualifier直接指定需要使用哪个Bean,两个Bean还是沿用上面的例子。

注入anotherService

@Component
public class UseQualifierService {
    @Autowired
    @Qualifier("anotherService") //通过@Qualifier("anotherService")指定使用anotherService
    private AnotherService service;

    public void doSomething(){
        System.out.println("wyf".equals(service.getPerson())); //2
    }
}

注入primaryAnotherService

@Component
public class UseQualifierService2 {
    private AnotherService service;

    public UseQualifierService2(@Qualifier("primaryAnotherService") AnotherService service) {
        this.service = service;
    }

    public void doSomething(){
        System.out.println("foo".equals(service.getPerson()));
    }

}

2.4 运行检验 - CommandLineRunner

Spring Boot下可以注册一个CommandLineRunner的Bean,这个Bean用来在容器启动后执行一些专门的任务,在JavaConfig里:

@Bean
CommandLineRunner configClr(AnnotationInjectionService annotationInjectionService,
                            AnnotationOneInjectionService annotationOneInjectionService,
                            AnnotationPropertyInjectionService annotationPropertyInjectionService,
                            AnnotationSetterInjectionService annotationSetterInjectionService,
                            JavaConfigInjectService javaConfigInjectService,
                            MixInjectionService mixInjectionService,
                            MixInjectionService2 mixInjectionService2,
                            UsePrimaryService usePrimaryService,
                            UseQualifierService useQualifierService,
                            UseQualifierService2 useQualifierService2) {
    return args -> {
        System.out.println(args);
        annotationInjectionService.doMyThing();
        annotationOneInjectionService.doMyThing();
        annotationPropertyInjectionService.doMyThing();
        annotationSetterInjectionService.doMyThing();
        javaConfigInjectService.doMyThing();
        mixInjectionService.doMyThing();
        usePrimaryService.doSomething();
        mixInjectionService2.doMyThing();
        useQualifierService.doSomething();
        useQualifierService2.doSomething();
    };
}
  1. 通过参数注入到当前的CommandLineRunner Bean中;

  2. CommandLineRunner是一个函数接口,输入的参数为main方法里接受的args参数,这里我们使用Lamba表达式将每个Bean的doMyThing()执行。

在这里插入图片描述

CommandLineRunner有个姊妹接口叫做ApplicationRunner,唯一的区别是ApplicationRunner使用org.springframework.boot.DefaultApplicationArguments类型的参数。如:

@Bean
ApplicationRunner configAr(){
 return  args -> System.out.println(args); 
}

CommandLineRunnerargs是不定字符串(String... args),而ApplicationRunnerargsDefaultApplicationArguments类型的对象。

新书推荐:
我的新书《从企业级开发到云原生微服务:Spring Boot 实战》已出版,内容涵盖了丰富Spring Boot开发的相关知识
购买地址:https://item.jd.com/12760084.html

在这里插入图片描述

主要包含目录有:

第一章 初识Spring Boot(快速领略Spring Boot的美丽)
第二章 开发必备工具(对常用开发工具进行介绍:包含IntelliJ IDEA、Gradle、Lombok、Docker等)
第三章 函数式编程
第四章 Spring 5.x基础(以Spring 5.2.x为基础)
第五章 深入Spring Boot(以Spring Boot 2.2.x为基础)
第六章 Spring Web MVC
第七章 数据访问(包含Spring Data JPA、Spring Data Elasticsearch和数据缓存)
第八章 安全控制(包含Spring Security和OAuth2)
第九章 响应式编程(包含Project Reactor、Spring WebFlux、Reactive NoSQL、R2DBC、Reactive Spring Security)
第十章 事件驱动(包含JMS、RabbitMQ、Kafka、Websocket、RSocket)
第11章 系统集成和屁股里(包含Spring Integration和Spring Batch)
第12章 Spring Cloud与微服务
第13章 Kubernetes与微服务(包含Kubernetes、Helm、Jenkins、Istio)
多谢大家支持。

发布了116 篇原创文章 · 获赞 10 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/wiselyman/article/details/105732460