SpringBoot注解之@Configuration、@Bean、@Component

目录
  1. 1. @Component:通用的注解!
  2. 2. @Bean
    1. 2.1. 引入第三方的类
    2. 2.2. @Bean精准创建
  3. 3. @Configuration

首先,我们先简单描述一下这三个的作用:

  • @Component注解表明一个类会作为组件类,并告知Spring要为这个类创建bean。
  • @Bean注解告诉Spring这个方法将会返回一个对象,这个对象要注册为Spring应用上下文中的bean。通常方法体中包含了最终产生bean实例的逻辑。用于显式声明单个bean,而不是让Spring像上面那样自动执行它。它将bean的声明与类定义分离,并允许您精确地创建和配置bean。另外@Bean注解的方法返回值是对象,可以在方法中为对象设置属性。
  • @Configuration即用来代替Spring配置文件的,它就是一个@Component组件,接收一个value值也就是bean的名字,value可以不填。

但是上面的讲解都只是冰冷的概念,决定重新捡起来,好好研究这些配置,因为虽说平时自己写的时候都是机械性的,甚至是试探性的,但是还是要更深入一点。

为什么要先讲这三个概念,是因为他们之间的关系,有些你中有我我中有你,最好先有一个大概的了解。

@Component:通用的注解!

通俗的讲:@Compent就是说,只要你想将你写的类交给Spring容器来管理,那就可以用它!

1、@controller:controller控制器层(注入服务)

2、@service:service服务层(注入dao)

3、@repository:dao持久层(实现dao访问)

为什么摆出上面这三个,因为他们其实就是@Compent,例如我们进入到@Controller里看一下,发现在Controller上就有一个@Component,剩下的两个同理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package org.springframework.stereotype;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.annotation.AliasFor;

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

一步一步来,先看最简单的功能

先写一个类,用@Compent注解上

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.newcrud.learn;

import lombok.Data;
import org.springframework.stereotype.Component;

@Data
@Component
public class KangShiFuTwo {
   String name;
   Integer age;
   public void getKangShiFuTwo(){
       System.out.println("getKangShiFuTwo");
  }
}

再写一个测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.newcrud.learn;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;
import org.testng.annotations.Test;

@SpringBootTest

public class KangShiFuTwoTest extends AbstractTestNGSpringContextTests {
   @Autowired
   KangShiFuTwo kangShiFuTwo;
   @Test
   public void testGetKangShiFuTwo() {
       kangShiFuTwo.getKangShiFuTwo();
  }

}

执行结果

1
getKangShiFuTwo

可以用@Autowired获取到说明已经在Spring容器里了

@Bean

我们先看一下@Bean的源码

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
package org.springframework.context.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.beans.factory.annotation.Autowire;
import org.springframework.core.annotation.AliasFor;

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

   @AliasFor("value")
   String[] name() default {};

   /** @deprecated */
   @Deprecated
   Autowire autowire() default Autowire.NO;

   boolean autowireCandidate() default true;

   String initMethod() default "";

   String destroyMethod() default "(inferred)";
}

emmm,他没有@Compent

引入第三方的类

那,我们首先创建一个没有@Compent的类

1
2
3
4
5
6
7
8
9
10
11
12
package com.newcrud.learn;

import lombok.Data;

@Data
public class KangShiFuOne {
   String name;
   Integer age;
   public void getKangShiFuOne(){
       System.out.println("getKangShiFuOne");
  }
}

再来写一个配置类

1
2
3
4
5
6
7
8
9
10
11
12
package com.newcrud.learn;

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

@Configuration
public class MyConfiguration {
   @Bean
   public KangShiFuOne getKangShiFuOneConfig(){
       return new KangShiFuOne();
  }
}

再来写一个测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.newcrud.learn;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;
import org.testng.annotations.Test;
@SpringBootTest
public class MyConfigurationTest extends AbstractTestNGSpringContextTests {
   @Autowired
   KangShiFuOne kangShiFuOne;

   @Test
   public void testGetKangShiFuTwoConfig() {
       kangShiFuOne.getKangShiFuOne();
  }
}

结果如下

1
getKangShiFuOne

能被@Autowired获取到,是因为@Configuration的作用,那@Bean的作用是什么呢,因为他有一个@Compent没有的功能,那就是他可以将第三方包里的类放到Spring容器里,比如说你在pom文件中引入的工具包,你并不能把@Compent放到工具包里面去对不对,那你就可以通过@Bean的方式来获取。

这个涉及到springboot的自动装载的概念,当你的bean不在你jar包的扫描目录下时,是没法实例化的给spring管理的。

好吧,上面的例子好像并不能展示出引用第三方库中的类需要装配到Spring容器时,则只能通过@Bean来实现的,也不能展示出@Bean的精准创建的功能,但是引用第三方库中的类,也实在懒得写了,我们就来再看一下精准创建的功能

@Bean精准创建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.newcrud.learn;

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

@Configuration
public class MyBean {
@Bean("getKangShiFuOneHello")
public KangShiFuOne getKangShiFuOne(){
KangShiFuOne kangShiFuOne=new KangShiFuOne();
kangShiFuOne.setAge(19);
kangShiFuOne.setName("zhangsan");
kangShiFuOne.getKangShiFuOne();
return kangShiFuOne;
}
}

再写一个测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.newcrud.learn;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ApplicationContext;
import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;
import org.testng.annotations.Test;

import static org.testng.Assert.*;
@SpringBootTest
public class MyBeanTest extends AbstractTestNGSpringContextTests {
@Autowired
ApplicationContext applicationContext;
@Test
public void testGetKangShiFuOne() {
KangShiFuOne kangShiFuOne=(KangShiFuOne) applicationContext.getBean("getKangShiFuOneHello");
System.out.println(kangShiFuOne);
}
}

结果

1
KangShiFuOne(name=zhangsan, age=19)

发现已经从上下文中获取到了我们精准定制化的getKangShiFuOneHello。

@Configuration

官方文档描述:用@Configuration注释类表明其主要目的是作为bean定义的源

我们再来看一下@Configuration的源代码,发现在上面也有一个@Compent,那就说明,它也有@Compent的功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package org.springframework.context.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.annotation.AliasFor;
import org.springframework.stereotype.Component;

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

boolean proxyBeanMethods() default true;
}

通俗的来讲,这个就是代表它所注释的类是一个配置类,但什么才是配置类呢,上面我们也看到了@Configuration好像也就是个@Compent啊,没啥大用

那我们先来编写两个类

1
2
3
4
5
6
7
8
9
10
11
12
package com.newcrud.learn;

import lombok.Data;

@Data
public class KangShiFuFour {
public KangShiFuFour(){
System.out.println("getKangShiFuFour");
}
String name;
Integer age;
}
1
2
3
4
5
6
7
8
9
10
11
12
package com.newcrud.learn;

import lombok.Data;

@Data
public class KangShiFuThree {
public KangShiFuThree(){
System.out.println("getKangShiFuThree");
}
String name;
Integer age;
}

再来写我们的@Configuration

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.newcrud.learn;

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

@Configuration
public class MyConfigurationTwo {
@Bean
public KangShiFuThree getKangShiFuThree(){
return new KangShiFuThree();
}
@Bean
public KangShiFuFour getKangShiFuFour(){
getKangShiFuThree();
return new KangShiFuFour();
}
}

再写我们的测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.newcrud.learn;

import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;
import org.testng.annotations.Test;

@SpringBootTest
public class MyConfigurationTwoTest extends AbstractTestNGSpringContextTests {

@Test
public void testGetKangShiFuThree() {
//这个只是保留一个执行的入口,没有实际意义,我们要看的是启动信息
}
}

在启动后会打印两个类的初始化函数输出

1
2
getKangShiFuThree
getKangShiFuFour

但是我们仔细想一下,我们是调用了两次getKangShiFuThree的构造函数的,但是只打印了一次,这也是他的一个功能:保持单例模式!

我们再来看一下他的另一个功能

@Configuration类允许通过调用同一类中的其他@Bean方法来定义bean之间的依赖关系

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.newcrud.learn;

import org.springframework.context.annotation.Bean;

public class MyConfigurationTwo {
@Bean
public KangShiFuThree getKangShiFuThree(){
return new KangShiFuThree();
}

@Bean
public KangShiFuFour getKangShiFuFour(){
//去掉@Configuration,这里就会报错:Method annotated with @Bean is called directly. Use dependency injection instead.
//因为未添加@Configuration注解,导致@Bean之间相互调用出错
getKangShiFuThree();
return new KangShiFuFour();
}
}

@Bean注解修饰带参数方法

1
2
3
4
5
6
@Bean
public TestBean createTestBean(Demo demo1){
TestBean testBean = new TestBean ();
testBean.setDemo(demo1);
return testBean;
}

如果Spring容器中只有一个Demo类型的bean, 则默认按照类型匹配注入Demo的bean,如果有多个Demo类型的bean,则按照名称(此处应为demo1)匹配注入对应的bean。

如果@Bean标注的方法,其参数若为List集合,则获取继承或实现该类的所有已注入的Bean,验证如下:

Person.java

1
2
3
4
5
6
7
public interface Person {
/**
* 信息
* @return String
*/
String inf();
}

Student.java

1
2
3
4
5
6
7
8
9
10
public class Student implements Person {
private String work;
public Student(String work){
this.work=this.getClass().getName()+":"+work;
}
@Override
public String inf() {
return this.work;
}
}

Worker.java

1
2
3
4
5
6
7
8
9
10
public class Worker implements Person {
private String work;
public Worker(String work){
this.work=this.getClass().getName()+":"+work;
}
@Override
public String inf() {
return this.work;
}
}

AnimalAutoConfiguration

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Configuration
public class AnimalAutoConfiguration {
@Bean
public Student getUser(){
return new Student("学生");
}
@Bean
public Worker getWorker(){
return new Worker("上班族");
}
/**
* 此处将把所有实现Persion接口的类的Bean注入list集合中
*/
@Bean
public List<Person> getAnimals(List<Person> list){
return list;
}
}

Test.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@RestController
public class Test {
@Autowired
private Student student;
@Autowired
private Worker worker;
@Autowired
private List<Person> animals;

@GetMapping(value = "/get/info")
public void test(){
System.out.println("student--->"+student.inf());;
System.out.println("worker--->"+worker.inf());;
for (Person person:animals) {
System.out.println("animals--->"+person.inf());;

}
}
}

输出:

1
2
3
4
student--->com.ioc.Student:学生
worker--->com.ioc.Worker:上班族
animals--->com.ioc.Student:学生
animals--->com.ioc.Worker:上班族