代码中使用了 Lombok 和 Slf4j,关于这两个库的具体使用,可以参考:
示例1:使用 @Component 定义 spring bean
使用@Component
注解的类,如果是实现自某个接口,那么@Autowired
注解的变量类型,直接用接口类型即可。
demo01 项目结构:
demo01
├── build.gradle
└── src
├── main
│ ├── java
│ │ └── demo
│ │ ├── Demo01Application.java
│ │ └── model
│ │ ├── IBook.java
│ │ ├── IPerson.java
│ │ └── impl
│ │ ├── BookImpl.java
│ │ └── PersonImpl.java
│ └── resources
└── test
├── java
└── resources
build.gradle:
buildscript {
ext {
springBootVersion = '2.1.3.RELEASE'
}
repositories {
maven { url 'http://mirrors.cloud.tencent.com/nexus/repository/maven-public/' }
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
}
}
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8
repositories {
maven { url 'http://mirrors.cloud.tencent.com/nexus/repository/maven-public/' }
mavenCentral()
}
dependencies {
compile('org.springframework.boot:spring-boot-starter')
compile group: 'org.projectlombok', name: 'lombok', version: '1.18.0'
testCompile('org.springframework.boot:spring-boot-starter-test')
}
接口 IBook:
package demo.model;
public interface IBook {
public String getTitle();
}
接口 IPerson:
package demo.model;
public interface IPerson {
public String getName();
public IBook getBook();
}
实现类 BookImpl :
package demo.model.impl;
import demo.model.IBook;
import org.springframework.stereotype.Component;
@Component
public class BookImpl implements IBook {
@Override
public String getTitle() {
return "书名";
}
}
实现类 PersonImpl :
package demo.model.impl;
import demo.model.IBook;
import demo.model.IPerson;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class PersonImpl implements IPerson {
@Autowired
IBook book;
@Override
public String getName() {
return "人名";
}
@Override
public IBook getBook() {
return book;
}
}
主类 Demo01Application:
package demo;
import demo.model.IBook;
import demo.model.IPerson;
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 Demo01Application implements CommandLineRunner {
@Autowired
private IBook book;
@Autowired
private IPerson person;
@Override
public void run(String... args) throws Exception {
log.info("book.title: {}", book.getTitle());
log.info("person.name: {}", person.getName());
log.info("person.book.title: {}", person.getBook().getTitle());
}
public static void main(String[] args) {
SpringApplication.run(Demo01Application.class, args);
}
}
执行结果:
2018-08-01 09:34:40.060 INFO 79022 --- [ main] demo.Demo01Application : book.title: 书名
2018-08-01 09:34:40.061 INFO 79022 --- [ main] demo.Demo01Application : person.name: 人名
2018-08-01 09:34:40.061 INFO 79022 --- [ main] demo.Demo01Application : person.book.title: 书名
注意,@Componet 注解修饰的类不一定要实现某接口,例如:
@Component
public class Person {
@Autowired
IBook book;
@Override
public String getName() {
return "人名";
}
@Override
public IBook getBook() {
return book;
}
}
在需要用到 Person 的地方,直接用下面的方式注入即可:
@Autowired
private Person person;
示例2:一个错误的示例
如果同一个接口有多个用@Component
注解的实现类,Spring 注入时,不知道该使用哪个,会报错。
demo02 项目结构:
demo02
├── build.gradle
└── src
├── main
│ ├── java
│ │ └── demo
│ │ ├── Demo02Application.java
│ │ └── model
│ │ ├── IBook.java
│ │ ├── IPerson.java
│ │ └── impl
│ │ ├── BookImpl01.java
│ │ ├── BookImpl02.java
│ │ ├── PersonImpl01.java
│ │ └── PersonImpl02.java
│ └── resources
└── test
├── java
└── resources
实现类 BookImpl01:
package demo.model.impl;
import demo.model.IBook;
import org.springframework.stereotype.Component;
@Component
public class BookImpl01 implements IBook {
@Override
public String getTitle() {
return "石头记";
}
}
实现类 BookImpl02:
package demo.model.impl;
import demo.model.IBook;
import org.springframework.stereotype.Component;
@Component
public class BookImpl02 implements IBook {
@Override
public String getTitle() {
return "红楼梦";
}
}
实现类 PersonImpl01:
package demo.model.impl;
import demo.model.IBook;
import demo.model.IPerson;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class PersonImpl01 implements IPerson {
@Autowired
IBook book;
@Override
public String getName() {
return "曹夢阮";
}
@Override
public IBook getBook() {
return book;
}
}
实现类 PersonImpl02:
package demo.model.impl;
import demo.model.IBook;
import demo.model.IPerson;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class PersonImpl02 implements IPerson {
@Autowired
IBook book;
@Override
public String getName() {
return "曹雪芹";
}
@Override
public IBook getBook() {
return book;
}
}
主类 Demo02Application:
package demo;
import demo.model.IBook;
import demo.model.IPerson;
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 IBook book;
@Autowired
private IPerson person;
@Override
public void run(String... args) throws Exception {
log.info("book.title: {}", book.getTitle());
log.info("person.name: {}", person.getName());
log.info("person.book.title: {}", person.getBook().getTitle());
}
public static void main(String[] args) {
SpringApplication.run(Demo02Application.class, args);
}
}
运行结果:
Description:
Field book in demo.Demo02Application required a single bean, but 2 were found:
- bookImpl01: defined in file [/demo02/out/production/classes/demo/model/impl/BookImpl01.class]
- bookImpl02: defined in file [/demo02/out/production/classes/demo/model/impl/BookImpl02.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
示例3:使用@Component时声明表示,使用 @Qualifier 区分使用同类型的多个 bean
我们想办法解决示例2遇到的问题。
实际上,@Component
注解可以声明一个标识。而@Qualifier
既可以声明标识,也可以指明使用哪个标识的Bean。
这个示例,用@Component
注解声明一个标识,用@Qualifier
指明使用哪个标识的Bean。
demo03 项目结构:
demo03
├── build.gradle
└── src
├── main
│ ├── java
│ │ └── demo
│ │ ├── Demo03Application.java
│ │ └── model
│ │ ├── IBook.java
│ │ ├── IPerson.java
│ │ └── impl
│ │ ├── BookImpl01.java
│ │ ├── BookImpl02.java
│ │ ├── PersonImpl01.java
│ │ └── PersonImpl02.java
│ └── resources
└── test
├── java
└── resources
实现类 BookImpl01:
package demo.model.impl;
import demo.model.IBook;
import org.springframework.stereotype.Component;
@Component(value = "石头记")
public class BookImpl01 implements IBook {
@Override
public String getTitle() {
return "石头记";
}
}
实现类 BookImpl02:
package demo.model.impl;
import demo.model.IBook;
import org.springframework.stereotype.Component;
@Component("红楼梦")
public class BookImpl02 implements IBook {
@Override
public String getTitle() {
return "红楼梦";
}
}
实现类 PersonImpl01:
package demo.model.impl;
import demo.model.IBook;
import demo.model.IPerson;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
@Component("曹夢阮")
public class PersonImpl01 implements IPerson {
@Autowired
@Qualifier("石头记")
IBook book;
@Override
public String getName() {
return "曹夢阮";
}
@Override
public IBook getBook() {
return book;
}
}
实现类 PersonImpl02:
package demo.model.impl;
import demo.model.IBook;
import demo.model.IPerson;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
@Component("曹雪芹")
public class PersonImpl02 implements IPerson {
@Autowired
@Qualifier("红楼梦")
IBook book;
@Override
public String getName() {
return "曹雪芹";
}
@Override
public IBook getBook() {
return book;
}
}
主类 Demo03Application:
package demo;
import demo.model.IBook;
import demo.model.IPerson;
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 IBook book;
@Autowired
@Qualifier("曹雪芹")
private IPerson person;
@Override
public void run(String... args) throws Exception {
log.info("book.title: {}", book.getTitle());
log.info("person.name: {}", person.getName());
log.info("person.book.title: {}", person.getBook().getTitle());
}
public static void main(String[] args) {
SpringApplication.run(Demo03Application.class, args);
}
}
运行结果:
2018-08-01 09:48:51.364 INFO 79133 --- [ main] demo.Demo03Application : book.title: 石头记
2018-08-01 09:48:51.364 INFO 79133 --- [ main] demo.Demo03Application : person.name: 曹雪芹
2018-08-01 09:48:51.364 INFO 79133 --- [ main] demo.Demo03Application : person.book.title: 红楼梦
示例4:,使用 @Qualifier 注解为 bean 声明标识
这个示例,用@Qualifier
注解声明一个标识,同时用@Qualifier
指明使用哪个标识的Bean。
demo04 项目结构:
demo04
├── build.gradle
└── src
├── main
│ ├── java
│ │ └── demo
│ │ ├── Demo04Application.java
│ │ └── model
│ │ ├── IBook.java
│ │ ├── IPerson.java
│ │ └── impl
│ │ ├── BookImpl01.java
│ │ ├── BookImpl02.java
│ │ ├── PersonImpl01.java
│ │ └── PersonImpl02.java
│ └── resources
└── test
├── java
└── resources
实现类 BookImpl01:
package demo.model.impl;
import demo.model.IBook;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
@Component
@Qualifier("石头记")
public class BookImpl01 implements IBook {
@Override
public String getTitle() {
return "石头记";
}
}
实现类 BookImpl02:
package demo.model.impl;
import demo.model.IBook;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
@Component
@Qualifier("红楼梦")
public class BookImpl02 implements IBook {
@Override
public String getTitle() {
return "红楼梦";
}
}
实现类 PersonImpl01:
package demo.model.impl;
import demo.model.IBook;
import demo.model.IPerson;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
@Component
@Qualifier("曹夢阮")
public class PersonImpl01 implements IPerson {
@Autowired
@Qualifier("石头记")
IBook book;
@Override
public String getName() {
return "曹夢阮";
}
@Override
public IBook getBook() {
return book;
}
}
实现类 PersonImpl02:
package demo.model.impl;
import demo.model.IBook;
import demo.model.IPerson;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
@Component
@Qualifier("曹雪芹")
public class PersonImpl02 implements IPerson {
@Autowired
@Qualifier("红楼梦")
IBook book;
@Override
public String getName() {
return "曹雪芹";
}
@Override
public IBook getBook() {
return book;
}
}
示例5:根据函数名和类名进行装配
上面说到,自动装配,默认是根据类型注入的。其实如果遇到定义了实现同一接口的Bean的情况,还可以在自动装配时候,类型声明为具体的类类型,而非接口。
还有一个巧妙的方法是,仍然使用接口声明变量,但变量名保持和实现类一致(首字母小写)。
demo05 项目结构:
demo05
├── build.gradle
└── src
├── main
│ ├── java
│ │ └── demo
│ │ ├── Demo05Application.java
│ │ └── model
│ │ ├── IBook.java
│ │ ├── IPerson.java
│ │ └── impl
│ │ ├── BookImpl01.java
│ │ ├── BookImpl02.java
│ │ ├── PersonImpl01.java
│ │ └── PersonImpl02.java
│ └── resources
└── test
├── java
└── resources
主类 Demo05Application:
package demo;
import demo.model.IBook;
import demo.model.IPerson;
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 IBook bookImpl01; // 对应 BookImpl01
@Autowired
private IPerson personImpl02; // 对应 PersonImpl02
@Override
public void run(String... args) throws Exception {
log.info("bookImpl01.title: {}", bookImpl01.getTitle());
log.info("personImpl02.name: {}", personImpl02.getName());
log.info("personImpl02.book01.title: {}", personImpl02.getBook().getTitle());
}
public static void main(String[] args) {
SpringApplication.run(Demo05Application.class, args);
}
}
执行结果:
2018-08-01 09:57:01.376 INFO 79146 --- [ main] demo.Demo05Application : bookImpl01.title: 石头记
2018-08-01 09:57:01.376 INFO 79146 --- [ main] demo.Demo05Application : personImpl02.name: 曹雪芹
2018-08-01 09:57:01.376 INFO 79146 --- [ main] demo.Demo05Application : personImpl02.book01.title: 红楼梦
不推荐这种做法。