代码中使用了 Lombok 和 Slf4j,关于这两个库的具体使用,可以参考:
示例1:使用 @Bean 定义 spring bean
自动装配,默认是根据类型注入的。
demo01 项目结构:
demo01
├── build.gradle
└── src
├── main
│ ├── java
│ │ └── demo
│ │ ├── AppConfig.java
│ │ ├── Demo01Application.java
│ │ └── model
│ │ ├── Book.java
│ │ └── Person.java
│ └── resources
└── test
├── java
└── resources
Book 类:
package demo.model;
public class Book {
public String title;
}
Person 类:
package demo.model;
public class Person {
public String name;
public Book book;
}
Spring容器会去管理使用@Bean注解的函数返回的类。@Bean要和@Configuration配合使用。
AppConfig 类:
package demo;
import demo.model.Book;
import demo.model.Person;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AppConfig {
@Bean
public Book getBook() {
Book book = new Book();
book.title = "书名";
return book;
}
@Bean
public Person getPerson(Book book) { // 参数book也会自动注入
Person person = new Person();
person.name = "人名";
person.book = book;
return person;
}
}
主类 Demo01Application:
package demo;
import demo.model.Book;
import demo.model.Person;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@Slf4j // 这是 lombok 提供的一个注解,lombok 会向 Demo01Application 类中注入一个 log 变量
public class Demo01Application implements CommandLineRunner {
@Autowired
private Book theBook;
@Autowired
private Person thePerson;
@Override
public void run(String... args) throws Exception {
log.info("theBook.title: {}", theBook.title);
log.info("thePerson.name: {}", thePerson.name);
log.info("thePerson.theBook.title: {}", thePerson.book.title);
}
public static void main(String[] args) {
SpringApplication.run(Demo01Application.class, args);
}
}
执行结果:
2018-08-01 07:37:56.612 INFO 77531 --- [ main] demo.Demo01Application : theBook.title: 书名
2018-08-01 07:37:56.612 INFO 77531 --- [ main] demo.Demo01Application : thePerson.name: 人名
2018-08-01 07:37:56.612 INFO 77531 --- [ main] demo.Demo01Application : thePerson.theBook.title: 书名
注意,上面的代码中我们通过下面的方式声明一个依赖 book 的 Person 类型的 bean:
@Bean
public Person getPerson(Book book) { // 参数book也会自动注入
Person person = new Person();
person.name = "人名";
person.book = book;
return person;
}
对于这段代码,也可以这样实现:
@Bean
public Person getPerson() {
Person person = new Person();
person.name = "人名";
person.book = getBook(); // 直接调用 getBook 方法
return person;
}
@Bean 修饰的方法得到的bean默认得到的是单例;直接调用 @Bean 修饰的方法时,Spring 会做拦截,让其返回的结果保证是单例。
示例2:一个错误的示例
这个示例运行会报错。为什么?定义了多个返回同一类型的Bean,Spring 注入时,不知道该使用哪个Bean。
项目结构:
demo02
├── build.gradle
└── src
├── main
│ ├── java
│ │ └── demo
│ │ ├── AppConfig.java
│ │ ├── Demo02Application.java
│ │ └── model
│ │ ├── Book.java
│ │ └── Person.java
│ └── resources
└── test
├── java
└── resources
Book 类和 Person 类实现和示例1相同。
AppConfig 类:
package demo;
import demo.model.Book;
import demo.model.Person;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AppConfig {
// 定义了两个返回Book类型的Bean
@Bean
public Book getBook01() {
Book book = new Book();
book.title = "石头记";
return book;
}
@Bean
public Book getBook02() {
Book book = new Book();
book.title = "红楼梦";
return book;
}
// 定义了两个返回Person类型的Bean
@Bean
public Person getPerson01(Book book) {
Person person = new Person();
person.name = "曹夢阮";
person.book = book;
return person;
}
@Bean
public Person getPerson02(Book book) {
Person person = new Person();
person.name = "曹雪芹";
person.book = book;
return person;
}
}
主类 Demo02Application:
package demo;
import demo.model.Book;
import demo.model.Person;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* 会报错
*/
@SpringBootApplication
@Slf4j
public class Demo02Application implements CommandLineRunner {
@Autowired
private Book theBook;
@Autowired
private Person thePerson;
@Override
public void run(String... args) throws Exception {
log.info("theBook.title: {}", theBook.title);
log.info("thePerson.name: {}", thePerson.name);
log.info("thePerson.theBook.title: {}", thePerson.book.title);
}
public static void main(String[] args) {
SpringApplication.run(Demo02Application.class, args);
}
}
运行时会报错:
Field theBook in demo.Demo02Application required a single bean, but 2 were found:
- getBook01: defined by method 'getBook01' in class path resource [demo/AppConfig.class]
- getBook02: defined by method 'getBook02' in class path resource [demo/AppConfig.class]
Action:
Consider marking one of the beans as @Primary, updating the consumer to accept multiple beans, or using @Qualifier to identify the bean that should be consumed
它提示我们可以用 @Primary 或者 @Qualifier 来解决。@Primary
就不说了,比较简单。我们看下 @Qualifier
。
示例3:使用 @Qualifier 区分使用同类型的多个 bean
@Bean
注解可以声明一个标识。而@Qualifier
既可以声明标识,也可以指明使用哪个标识的Bean。
这个示例,用@Bean
注解声明一个标识,用@Qualifier
指明使用哪个标识的Bean。
项目结构:
demo03
├── build.gradle
└── src
├── main
│ ├── java
│ │ └── demo
│ │ ├── AppConfig.java
│ │ ├── Demo03Application.java
│ │ └── model
│ │ ├── Book.java
│ │ └── Person.java
│ └── resources
└── test
├── java
└── resources
类 AppConfig :
package demo;
import demo.model.Book;
import demo.model.Person;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AppConfig {
@Bean(name="石头记")
public Book getBook01() {
Book book = new Book();
book.title = "石头记";
return book;
}
@Bean(name="红楼梦")
public Book getBook02() {
Book book = new Book();
book.title = "红楼梦";
return book;
}
@Bean(name="曹夢阮")
public Person getPerson01(@Qualifier("石头记") Book book) {
Person person = new Person();
person.name = "曹夢阮";
person.book = book;
return person;
}
@Bean(name="曹雪芹")
public Person getPerson02(@Qualifier("红楼梦") Book book) {
Person person = new Person();
person.name = "曹雪芹";
person.book = book;
return person;
}
}
主类 Demo03Application :
package demo;
import demo.model.Book;
import demo.model.Person;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@Slf4j
public class Demo03Application implements CommandLineRunner {
@Autowired
@Qualifier("石头记")
private Book book;
@Autowired
@Qualifier("曹雪芹")
private Person person;
@Override
public void run(String... args) throws Exception {
log.info("book.title: {}", book.title);
log.info("person.name: {}", person.name);
log.info("person.book.title: {}", person.book.title);
}
public static void main(String[] args) {
SpringApplication.run(Demo03Application.class, args);
}
}
运行结果:
2018-08-01 08:37:56.032 INFO 77719 --- [ main] demo.Demo03Application : book.title: 石头记
2018-08-01 08:37:56.032 INFO 77719 --- [ main] demo.Demo03Application : person.name: 曹雪芹
2018-08-01 08:37:56.033 INFO 77719 --- [ main] demo.Demo03Application : person.book.title: 红楼梦
示例4:使用 @Qualifier 为bean声明标识
这个示例,用@Qualifier
注解声明一个标识,也用@Qualifier
指明使用哪个标识的Bean。
项目结构:
demo04
├── build.gradle
└── src
├── main
│ ├── java
│ │ └── demo
│ │ ├── AppConfig.java
│ │ ├── Demo04Application.java
│ │ └── model
│ │ ├── Book.java
│ │ └── Person.java
│ └── resources
└── test
├── java
└── resources
类 AppConfig:
package demo;
import demo.model.Book;
import demo.model.Person;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AppConfig {
@Bean
@Qualifier("石头记")
public Book getBook01() {
Book book = new Book();
book.title = "石头记";
return book;
}
@Bean
@Qualifier("红楼梦")
public Book getBook02() {
Book book = new Book();
book.title = "红楼梦";
return book;
}
@Bean
@Qualifier("曹夢阮")
public Person getPerson01(@Qualifier("石头记") Book book) {
Person person = new Person();
person.name = "曹夢阮";
person.book = book;
return person;
}
@Bean
@Qualifier("曹雪芹")
public Person getPerson02(@Qualifier("红楼梦") Book book) {
Person person = new Person();
person.name = "曹雪芹";
person.book = book;
return person;
}
}
主类 Demo04Application:
package demo;
import demo.model.Book;
import demo.model.Person;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@Slf4j
public class Demo04Application implements CommandLineRunner {
@Autowired
@Qualifier("石头记")
private Book book;
@Autowired
@Qualifier("曹雪芹")
private Person person;
@Override
public void run(String... args) throws Exception {
log.info("book.title: {}", book.title);
log.info("person.name: {}", person.name);
log.info("person.book.title: {}", person.book.title);
}
public static void main(String[] args) {
SpringApplication.run(Demo04Application.class, args);
}
}
运行结果:
2018-08-01 08:42:17.432 INFO 77730 --- [ main] demo.Demo04Application : book.title: 石头记
2018-08-01 08:42:17.432 INFO 77730 --- [ main] demo.Demo04Application : person.name: 曹雪芹
2018-08-01 08:42:17.432 INFO 77730 --- [ main] demo.Demo04Application : person.book.title: 红楼梦
示例5:根据变量名和函数名进行自动装配
上面说到,自动装配,默认是根据类型注入的。其实如果遇到定义了多个返回同一类型的Bean的情况,还会尝试将自动装配的变量名和@Bean
注解的函数名进行匹配。
demo05
├── build.gradle
└── src
├── main
│ ├── java
│ │ └── demo
│ │ ├── AppConfig.java
│ │ ├── Demo05Application.java
│ │ └── model
│ │ ├── Book.java
│ │ └── Person.java
│ └── resources
└── test
├── java
└── resources
package demo;
import demo.model.Book;
import demo.model.Person;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AppConfig {
@Bean
public Book book01() {
Book book = new Book();
book.title = "石头记";
return book;
}
@Bean
public Book book02() {
Book book = new Book();
book.title = "红楼梦";
return book;
}
@Bean
public Person person01(@Qualifier("book01") Book book) {
Person person = new Person();
person.name = "曹夢阮";
person.book = book;
return person;
}
@Bean
public Person person02(@Qualifier("book02") Book book) {
Person person = new Person();
person.name = "曹雪芹";
person.book = book;
return person;
}
}
package demo;
import demo.model.Book;
import demo.model.Person;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@Slf4j
public class Demo05Application implements CommandLineRunner {
@Autowired
private Book book01; // 和 public Book book01() 中的函数名对应
@Autowired
private Person person02; // 和 public Person person02(@Qualifier("book02") Book book) 中的函数名对应
@Override
public void run(String... args) throws Exception {
log.info("book01.title: {}", book01.title);
log.info("person02.name: {}", person02.name);
log.info("person02.book01.title: {}", person02.book.title);
}
public static void main(String[] args) {
SpringApplication.run(Demo05Application.class, args);
}
}
更进一步,我们可以将 AppConfig 类找那个的 person01 、person02 函数的参数也做修改:
@Bean
public Person person01(Book book01) {
Person person = new Person();
person.name = "曹夢阮";
person.book = book01;
return person;
}
@Bean
public Person person02(Book book02) {
Person person = new Person();
person.name = "曹雪芹";
person.book = book02;
return person;
}
不推荐这种做法。