SpringBoot如何使用注解装配Bean

时间:2022-07-24
本文章向大家介绍SpringBoot如何使用注解装配Bean,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

在日常开发中,项目中大量的Bean的装配。今天就来聊聊如何使用注解装配Bean。这里与其说是SpringBoot装配Bean还是不如说是Spring注解来装配Bean。

装配Bean的方式主要有以下两种方式:

  1. 通过Java配置文件@Bean的方式来定义Bean
  2. 通过注解扫描的方式@Component@ComponentScan

一、使用@Bean的方式

首先写一个非常普通的实体类MyBean

public class MyBean {
    public String getName() {
        return "使用@Bean方式";
    }
}

然后定义一个MyBeanConfig类,在类上添加注解@Configuration

@Configuration该注解代表是一个 Java 配置文件 , Spring会根据它来生成 IoC 容器去装配 Bean。

@Bean 代表将 configBean方法返回的 POJO 装配到 IoC 容器中, name为Bean 的名称,如果没有配置它,则会将方法名称作为 Bean 的名称保存到 Spring IoC 容器中 。这里方法名称myBean就是MyBean保存在Spring IoC 容器中饿名称,如果在使用名称注入的时候就得使用myBean。否则会报错。

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MyBeanConfig {
    @Bean
    public MyBean configBean() {
        return new MyBean();
    }
}

看看@Bean源码会发现,其实除了上面说的方法名称作为Bean在Spring IOC 容器中的Bean名称外,还可以使用@Beam("configBean")的方式。

@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Bean {
    @AliasFor("name")
    String[] value() default {};

    @AliasFor("value")
    String[] name() default {};
    //...省略
}

下面我们创建一个测试controller。

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController
public class MyController {
    @Resource(name = "configBean")
    private MyBean configBean;

    @GetMapping("/test/bean")
    public String test(){
        return configBean.getName();
    }
}

启动类

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

启动项目

测试工具使用IDEA的工具类里

访问:http://localhost:8080/test/bean

成功搞定。

用上面说的另外一种方式定义Beande 名称

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MyBeanConfig {
    @Bean("helloBean")
    public MyBean configBean() {
        return new MyBean();
    }
}

其他不动,再次启动启动类,就会报异常

所以得注入时候使用Bean名称也得跟着改(按照类型注入得另说,这里是演示Bean在Spring IOC中的名称)。

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController
public class MyController {
    //这里把bean在Spring IOC中的名称修改成上面定义的helloBean名称
    @Resource(name = "helloBean")
    private MyBean helloBean;

    @GetMapping("/test/bean")
    public String test(){
        return helloBean.getName();
    }
}

正常启动

访问也正常

ok,使用@Bean的方式就这样轻松搞定。

二、使用@Component方式

创建一个类MyComponentBean

import org.springframework.stereotype.Component;

@Component
public class MyComponentBean {

    public String getName(){
        return "使用@Component注解方式";
    }
}

一样的,写一个controller类来测试,MyComponentController

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController
public class MyComponentController {

    @Resource
    private MyComponentBean myComponentBean;

    @GetMapping("/test/component")
    public String test(){
        return myComponentBean.getName();
    }
}

启动启动类main方法(和上面一样)。然后访问返回

成功。注解@Component 表明这个类将被Spring IoC容器扫描装配,bean的名称为componentBean。如果不配置这个值 ,那IoC 容器就会把类名第一个字母作为小写,其他的不变作为 Bean 名称放入到 IoC 容器中。

在工作中,controller里我们通常会注入各种service类。比如:UserInfoService、OrderService等。这些service上基本上都会使用@Service,在service层又会注入各种Repository、Mapper、Dao等,然后这些类上基本都添加了注解@Repository来装配Bean。

进入org.springframework.stereotype目录下会发现

其实@Controllert、@Service、@Repository注解上都添加了注解@Component

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Controller {
    @AliasFor(
        annotation = Component.class
    )
    String value() default "";
}

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Service {
    @AliasFor(
        annotation = Component.class
    )
    String value() default "";
}

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Repository {
    @AliasFor(
        annotation = Component.class
    )
    String value() default "";
}

也就是说,除了@Component注解以外,还可以使用上面这三个注解装配Bean。

  • @Controller 控制器(注入服务)
  • @Service 服务(注入dao)
  • @Repository dao(实现dao访问)
  • @Component (把普通pojo实例化到spring容器中,相当于配置文件中的 )

OK,使用@Component的方式就这样轻松搞定。

三、使用@ComponentScan方式

@ComponentScan是Spring的包扫描组件,作用在配置类上。使用方式为:

@ComponentScan(value="com.example.demo")

其常用属性介绍:

value:代表需要扫描的包,扫描包下的有注解@Controller、@Service、@Repository、@Component注解标注的类,将其注入IOC容器。

excludeFilters :是一个 Filter[]数组,作用是排除不需要扫描的包或者类。Filter[]数组里面需要@Filter指定过滤规则,@Filter的type属性表示过滤的规则;@Filter的classes属性是个数组,里面包含需要过滤的类。

includeFilters :是一个 Filter[]数组,作用是指定扫描的时候只需要包含哪些组件。用法与excludeFilters相同

useDefaultFilters:默认过滤规则选项,false时,可以自定义过滤规则

使用场景很多,比如说上文提到的启动类中的SpringBootApplication注解中就使用到了。

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
//使用@ComponentScan
@ComponentScan(excludeFilters = {
@Filter(type = FilterType.CUSTOM, classes = {TypeExcludeFilter.class}), 
@Filter(type = FilterType.CUSTOM, classes = {AutoConfigurationExcludeFilter.class})}
)
public @interface SpringBootApplication {
    @AliasFor(
        annotation = EnableAutoConfiguration.class
    )
    Class<?>[] exclude() default {};

    @AliasFor(
        annotation = EnableAutoConfiguration.class
    )
    String[] excludeName() default {};

    @AliasFor(
        annotation = ComponentScan.class,
        attribute = "basePackages"
    )
    String[] scanBasePackages() default {};

    @AliasFor(
        annotation = ComponentScan.class,
        attribute = "basePackageClasses"
    )
    Class<?>[] scanBasePackageClasses() default {};
}

ok,今天就分析到这里。