springBoot @Bean 注册组件

springBoot 添加组件,不需要再编写spring.xml配置文件。
@Configuration 指明当前类是一个配置类,替代spring配置文件。
@Bean 对应xml配置文件中的<bean></bean>标签。将方法的返回值添加到容器中。容器中的这个组件的id就是方法名。
示例代码:

@Configuration
public class MyConfig {
    @Bean
    public HelloService helloService(){
        System.out.println("添加helloService 组件");
        return new HelloService();
    }
}

@RestController
@RequestMapping("/home")
public class HomeController {
    @Autowired
    private HelloService helloService;
    @Autowired
    private ApplicationContext ioc;
    @GetMapping("hello")
    public Map<String,Object> hello(){
       boolean  isHas = ioc.containsBean("helloService");
       String result = helloService.sayHello();
       Map<String,Object> map = new HashMap<>();
       map.put("isHas",isHas);
       map.put("result",result);
       return map;
    }
}

{
    "result": "hello",
    "isHas": true
}

@Configuration

从Spring3.0,@Configuration用于定义配置类,可替换xml配置文件,被注解的类内部包含有一个或多个被@Bean注解的方法,这些方法将会被AnnotationConfigApplicationContext或AnnotationConfigWebApplicationContext类进行扫描,并用于构建bean定义,初始化Spring容器。

注意:@Configuration注解的配置类有如下要求:

     @Configuration不可以是final类型;
     @Configuration不可以是匿名类;
     嵌套的configuration必须是静态类。

一、@Configuation加载Spring方法

前言

@Configuration用于定义配置类,可替换XML配置文件,被注解的类内部包含一个或多个@Bean注解方法。可以被AnnotationConfigApplicationContext或者AnnotationConfigWebApplicationContext进行扫描。用于构建bean定义以及初始化Spring容器。

实例

@Configuration 加载Spring方法

Car.java

public class Car {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

}
定义Config类
@Configuration
public class Config {
    public Config() {
        System.out.println("TestConfig容器初始化...");
    }

    @Bean(name = "getMyCar")
    public Car getCar() {
        Car c = new Car();
        c.setName("dankun");
        return c;
    }
}

实例化
public void testConfig() {
        ApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
        Car car = (Car)context.getBean("car");
        System.out.println(car.getName());
    }
// 输出
// TestConfig容器初始化...
// dankun

@Configuration + @Component

@Configuration也附带了@Component的功能。所以理论上也可以使用@Autowared功能。上述代码可以改成下面形式
Car.java

@Component
public class Car {
    @Value("dankun")
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

}
Config.java
@Configuration
@ComponentScan("com.wuyue.annotation")
public class Config {
    public Config() {
        System.out.println("TestConfig容器初始化...");
    }
测试主入口
public class TestConfig {
    @Test
    public void testConfig() {
        ApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
        Car car = (Car)context.getBean("car");
        System.out.println(car.getName());
    }
}
// 输出
// TestConfig容器初始化...
// dankun

总结

  • @Configuation等价于<Beans></Beans>
  • @Bean 等价于<Bean></Bean>
  • @ComponentScan等价于<context:component-scan base-package="com.dxz.demo"/>
  • @Component 等价于<Bean></Bean>

@Bean VS @Component

  • 两个注解的结果是相同的,bean都会被添加到Spring上下文中。
  • @Component 标注的是类,允许通过自动扫描发现。@Bean需要在配置类@Configuation中使用。
  • @Component类使用的方法或字段时不会使用CGLIB增强。而在@Configuration类中使用方法或字段时则使用CGLIB创造协作对象

假设我们需要将一些第三方的库组件装配到应用中或者 我们有一个在多个应用程序***享的模块,它包含一些服务。并非所有应用都需要它们。

如果在这些服务类上使用@Component并在应用程序中使用组件扫描,我们最终可能会检测到超过必要的bean。导致应用程序无法启动
但是我们可以使用@Bean来加载

因此,基本上,使用@Bean将第三方类添加到上下文中。和@Component,如果它只在你的单个应用程序中


Spring什么时候实例化bean,首先要分2种情况
第一:如果你使用BeanFactory作为Spring Bean的工厂类,则所有的bean都是在第一次使用该Bean的时候实例化
第二:如果你使用ApplicationContext作为Spring Bean的工厂类,则又分为以下几种情况:
(1):如果bean的scope是singleton的,并且lazy-init为false(默认是false,所以可以不用设置),则ApplicationContext启动的时候就实例化该Bean,并且将实例化的Bean放在一个map结构的缓存中,下次再使用该Bean的时候,直接从这个缓存中取
(2):如果bean的scope是singleton的,并且lazy-init为true,则该Bean的实例化是在第一次使用该Bean的时候进行实例化
(3):如果bean的scope是prototype的,则该Bean的实例化是在第一次使用该Bean的时候进行实例化

Spring, Spring Boot中的@Component 和@ComponentScan注解用法介绍

@ComponentScan

如果你理解了ComponentScan,你就理解了Spring.

Spring是一个依赖注入(dependency injection)框架。所有的内容都是关于bean的定义及其依赖关系。

定义Spring Beans的第一步是使用正确的注解-@Component或@Service或@Repository.

但是,Spring不知道你定义了某个bean除非它知道从哪里可以找到这个bean.

ComponentScan做的事情就是告诉Spring从哪里找到bean

由你来定义哪些包需要被扫描。一旦你指定了,Spring将会将在被指定的包及其下级的包(sub packages)中寻找bean

下面分别介绍在Spring Boot项目和非Spring Boot项目(如简单的JSP/Servlet或者Spring MVC应用)中如何定义Component Scan

Spring Boot项目

总结:

  • 如果你的其他包都在使用了@SpringBootApplication注解的main app所在的包及其下级包,则你什么都不用做,SpringBoot会自动帮你把其他包都扫描了
  • 如果你有一些bean所在的包,不在main app的包及其下级包,那么你需要手动加上@ComponentScan注解并指定那个bean所在的包

关于spring-boot中的@SpringBootApplication中的@ComponentScan的basePackages的路径的设置。

@ComponentScan 如果不设置basePackage的话 默认会扫描包的所有类,所以最好还是写上basePackage ,减少加载时间。默认扫描**/*.class路径 比如这个注解在com.first.springbootproject.springboot 下面 ,那么会扫描这个包下的所有类还有子包的所有类,比如包com.first.springbootproject.springboot.helloworld的应用。

综上所述我们可以知道:

SpringBootApplication启动时会默认扫描主类当前包及子包,如果需要扫描主类当前包外的其他包或不扫描当前包下的特定包或类,可通过下列属性实现:

@ComponentScan(basePackages={"com.first.springbootproject.springboot.helloworld"})

通过上面的注释spring在启动的时候会按照相关的路径进行对@Component元素的加载。不会在进行全部的扫描进行加载。占用资源的情况。

除了上面的这种改变路劲的方式之外,我们还可以通过配置xml文件,对basePackages的路劲进行设置。

1.通配符形式
<context:component-scan base-package="com.*" />
2.全路径
<context:component-scan base-package="com.test" />
至此我们的spring-boot项目的类加载路劲的指定设置完毕。如果想要加载多个路劲可以使用逗号分隔的形式,对类进行加载。如下这种方式:

非Spring Boot项目

在非Spring Boot项目中,我们必须显式地使用@ComponentScan注解定义被扫描的包,可以通过XML文件在应用上下文中定义或在Java代码中对应用上下文定义

Java代码方式
@ComponentScan({"com.in28minutes.package1","com.in28minutes.package2"})
@Configuration
public class SpringConfiguration {

XML文件方式
<context:component-scan base-package="com.in28minutes.package1, com.in28minutes.package2" />

项目中常见关于Component Scan的报错

你是否在项目启动中遇到过类似这样的报错:

WARNING: No mapping found for HTTP request with URI [/spring-mvc/login] in DispatcherServlet with name ‘dispatcher’

WARNING: No mapping found for HTTP request with URI [/list-todos] in DispatcherServlet with name ‘dispatcher’

或者:

ERROR:No qualifying bean of type [com.in28minutes.springboot.jpa.UserRepository] found for dependency [com.in28minutes.springboot.jpa.UserRepository]: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}…

报错的根因都是bean没有被Spring找到

遇到这些错误你应该检查:

  • 你是否给类加了正确的注解@Controller,@Repository,@Service或@Component
  • 你是否在应用上下文定义了Component Scan
  • 报错类所在的包是否在Component Scan中指定的包的范围

@Component and @ComponentScan 的区别

@Component 和 @ComponentScan的使用目的不一样

  • 在某个类上使用@Component注解,表明当需要创建类时,这个被注解的类是一个候选类。就像是举手。
  • @ComponentScan 用于扫描指定包下的类。就像看都有哪些举手了。

Bean到底是什么?

我个人的理解,觉得bean就相当于定义一个组件,这个组件是用于具体实现某个功能的。这里的所定义的bean就相当于给了你一个简洁方便的方法来调用这个组件实现你要完成的功能。

1、Java面向对象,对象有方法和属性,那么就需要对象实例来调用方法和属性(即实例化);
2、凡是有方法或属性的类都需要实例化,这样才能具象化去使用这些方法和属性;
3、规律:凡是子类及带有方法或属性的类都要加上注册Bean到Spring IoC的注解;(@Component , @Repository , @ Controller , @Service , @Configration)
4、把Bean理解为类的代理或代言人(实际上确实是通过反射、代理来实现的),这样它就能代表类拥有该拥有的东西了
5、我们都在微博上@过某某,对方会优先看到这条信息,并给你反馈,那么在Spring中,你标识一个@符号,那么Spring就会来看看,并且从这里拿到一个Bean(注册)或者给出一个Bean(使用)