SpringIOC注解

1、Spring概述

  • Spring是一个IOC(DI)和AOP容器框架。
  • Spring的优良特性:
    • 非侵入式:基于Spring开发的应用中的对象可以不依赖于Spring的API。
    • 依赖注入:DI——Dependency Injection,反转控制(IOC)最经典的实现。
    • 面向切面编程:Aspect Oriented Programming——AOP。
    • 容器:Spring是一个容器,因为它包含并且管理应用对象的生命周期。
    • 组件化:Spring实现了使用简单的组件配置组合成一个复杂的应用。在 Spring 中可以使用XML和Java注解组合这些对象。
    • 一站式:在IOC和AOP的基础上可以整合各种企业应用的开源框架和优秀的第三方类库(实际上Spring 自身也提供了表述层的SpringMVC和持久层的Spring JDBC)。

创建maven项目测试基本spring功能需要导入以下依赖:

1
2
3
4
5
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>

2、@Configuration和@Bean注解

  • @Configuration:从Spring3.0,@Configuration用于定义配置类,可替换xml配置文件,被注解的类内部包含有一个或多个被@Bean注解的方法,这些方法将会被AnnotationConfigApplicationContext或AnnotationConfigWebApplicationContext类进行扫描,并用于构建bean定义,初始化Spring容器。
  • @Bean:产生一个Bean对象,然后这个Bean对象交给Spring管理。产生这个Bean对象的方法Spring只会调用一次,随后这个Spring将会将这个Bean对象放在自己的IOC容器中。

例子:

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
31
32
33
//以下注解需要导入lombok相关依赖
@Data
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class Person {
private String name;

private Integer age;
}

//配置类==配置文件
@Configuration
public class MainConfig {
//给容器中注册一个Bean;类型为返回值的类型,id默认是用方法名作为id
@Bean("person")
public Person person(){
return new Person("李四", 20);
}
}

public class MainTest {
public static void main( String[] args ) {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
Person person = applicationContext.getBean(Person.class);
System.out.println(person);//Person(name=李四, age=20)
//获取bean的名字
String[] namesForType = applicationContext.getBeanNamesForType(Person.class);
for (String name : namesForType) {
System.out.println(name);//person
}
}
}
  • 也可通过工厂bean注册组件:
    • FactoryBean是一个工厂Bean,可以生成某一个类型Bean实例,它最大的一个作用是:可以让我们自定义Bean的创建过程。BeanFactory是Spring容器中的一个基本类也是很重要的一个类,在BeanFactory中可以创建和管理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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
public class Color {
}

//创建一个Spring定义的FactoryBean
public class ColorFactoryBean implements FactoryBean<Color> {
//返回一个Color对象,这个对象会添加到容器中
public Color getObject() throws Exception {
System.out.println("ColorFactoryBean...getObject...");
return new Color();
}

public Class<?> getObjectType() {
return Color.class;
}

//true:这个bean是单实例,在容器中保存一份
//false:多实例,每次获取都会创建一个新的bean;
public boolean isSingleton() {
return true;
}
}

@Configuration
public class MainConfig {
@Bean
public ColorFactoryBean colorFactoryBean(){
return new ColorFactoryBean();
}
}

public class MainTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
String[] definitionNames = applicationContext.getBeanDefinitionNames();
for (String name : definitionNames) {
System.out.println(name);
}
System.out.println("****************");
//工厂Bean获取的是调用getObject创建的对象
Object bean = applicationContext.getBean( "colorFactoryBean");
System.out.println(bean.getClass());
//获取工厂bean本身
Object bean2 = applicationContext.getBean("&colorFactoryBean");
System.out.println(bean2.getClass());
//打印如下:
//org.springframework.context.annotation.internalConfigurationAnnotationProcessor
//org.springframework.context.annotation.internalAutowiredAnnotationProcessor
//org.springframework.context.annotation.internalCommonAnnotationProcessor
//org.springframework.context.event.internalEventListenerProcessor
//org.springframework.context.event.internalEventListenerFactory
//mainConfig
//colorFactoryBean
//****************
//ColorFactoryBean...getObject...
//class com.example.bean.Color
//class com.example.bean.ColorFactoryBean
}
}
  • 特别地,如果用@Bean注解创建对象时,方法参数的值默认从ioc容器中获取。
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
@Data
@ToString
@NoArgsConstructor
@AllArgsConstructor
@Repository
//此bean的id默认为类名首字母小写,即userDao
public class UserDao {
private String name = "小红";
}

@Data
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class UserService {

private UserDao userDao;

private void print() {
System.out.println(userDao);
}
}

@Configuration
@ComponentScan({"com.example.service","com.example.dao",
"com.example.controller","com.example.bean"})
public class MainConfig {

//@Bean标注的方法创建对象时,方法参数的值从容器中获取(@Autowired可省略)
@Bean
public UserService userService(UserDao userDao){
UserService userService = new UserService();
userService.setUserDao(userDao);
return userService;
}
}

public class MainTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
UserService bean = applicationContext.getBean(UserService.class);
System.out.println(bean);//UserService(userDao=UserDao(name=小红))
}
}

3、@ComponentScan注解

  • ComponentScan注解:根据指定的配置自动扫描package,将符合条件的组件加入到IOC容器中,该注解默认会扫描该类所在的包下所有的配置类。
    • Filter[] includeFilters():指定扫描的时候只需要包含哪些组件。
    • Filter[] excludeFilters():指定扫描的时候按照什么规则排除那些组件。
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
//以下注解需要导入lombok相关依赖
@Data
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class Person {
private String name;

private Integer age;
}

@Configuration
@ComponentScan(value = "com.example")//扫描com.example包下所有组件
public class MainConfig {
//给容器中注册一个Bean;类型为返回值的类型,id默认是用方法名作为id
@Bean("person")
public Person person(){
return new Person("李四", 20);
}
}

@Controller
public class UserController {
}

@Service
public class UserService {
}

@Repository
public class UserDao {
}

@Data
@ToString
@NoArgsConstructor
@AllArgsConstructor
@Component
public class Person {
private String name;

private Integer age;
}

public class MainTest {
public static void main( String[] args ) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
String[] definitionNames = applicationContext.getBeanDefinitionNames();
for (String name : definitionNames) {
System.out.println(name);
}
}
}

类及其所在包的目录如下:

打印如下:

1
2
3
4
5
6
7
8
9
10
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
mainConfig
person
userController
userDao
userService
  • 如果配置类改成以下代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Configuration
//排除被@Controller注解标注的组件
//FilterType.ANNOTATION:按照注解
//filtertype.ASSIGNABLE_TYPE:按照给定的类型;
//filtertype.ASPECTJ:使用ASPECTJ表达式
//FilterType.REGEX:使用正则指定
//FilterType.CUSTOM:使用自定义规则
@ComponentScan(value = "com.example",excludeFilters = {
@ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class})
})
public class MainConfig {
//给容器中注册一个Bean;类型为返回值的类型,id默认是用方法名作为id
@Bean("person")
public Person person(){
return new Person("李四", 20);
}
}

则打印如下:

1
2
3
4
5
6
7
8
9
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
mainConfig
person
userDao
userService
  • 如果配置类改成以下代码:
1
2
3
4
5
6
7
8
9
10
11
12
@Configuration
//只扫描被@Controller注解标注的组件
@ComponentScan(value = "com.example",includeFilters = {
@ComponentScan.Filter(type = FilterType.ANNOTATION,classes = Controller.class)
},useDefaultFilters = false)
public class MainConfig {
//给容器中注册一个Bean;类型为返回值的类型,id默认是用方法名作为id
@Bean("person")
public Person person(){
return new Person("李四", 20);
}
}

则打印如下:

1
2
3
4
5
6
7
8
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
mainConfig
userController
person
  • 如果配置类改成以下代码,并添加自定义过滤规则类:
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
31
32
33
34
35
36
@Configuration
//按照自定义过滤规则扫描组件
@ComponentScans(value = {
@ComponentScan(value = "com.example",includeFilters = {
@ComponentScan.Filter(type=FilterType.CUSTOM,classes={MyTypeFilter.class})
},useDefaultFilters = false)
})
public class MainConfig {
//给容器中注册一个Bean;类型为返回值的类型,id默认是用方法名作为id
@Bean("person")
public Person person(){
return new Person("李四", 20);
}
}

public class MyTypeFilter implements TypeFilter {
/**
* metadataReader:读取到的当前正在扫描的类的信息
* metadataReaderFactory:可以获取到其他任何类信息的
*/
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
//获取当前类注解的信息
AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
//获取当前正在扫描的类的类信息
ClassMetadata classMetadata = metadataReader.getClassMetadata();
//获取当前类资源(类的路径)
Resource resource = metadataReader.getResource();
String className = classMetadata.getClassName();
System.out.println("--->"+className);
//如果包名+类名含有“ter”
if(className.contains("ter")){
return true;
}
return false;
}
}

则打印如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
--->com.example.MainTest
--->com.example.bean.Person
--->com.example.config.MyTypeFilter
--->com.example.controller.UserController
--->com.example.dao.UserDao
--->com.example.service.UserService
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
mainConfig
myTypeFilter
person

4、@Scope注解

  • @Scope注解是springIoc容器中的一个作用域,在 Spring IoC 容器中具有以下几种作用域:基本作用域singleton(单例)、**prototype(多例)**,Web 作用域(reqeust、session、globalsession),自定义作用域。
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
//以下注解需要导入lombok相关依赖
@Data
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class Person {
private String name;

private Integer age;
}

@Configuration
public class MainConfig {
/**
* prototype:多实例的:ioc容器启动并不会去调用方法创建对象放在容器中。
* 每次获取的时候才会调用方法创建对象;
* singleton:单实例的(默认值):ioc容器启动会调用方法创建对象放到ioc容器中。
* 以后每次获取就是直接从容器(map.get())中拿,
* request:同一次请求创建一个实例
* session:同一个session创建一个实例
*/
//默认是单实例的
@Scope("singleton")
@Bean("person")
public Person person(){
System.out.println("给容器中添加person组件");
return new Person("李四", 20);
}
}

public class MainTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
Object person1 = applicationContext.getBean("person");
Object person2 = applicationContext.getBean("person");
System.out.println(person1 == person2);
//@Scope("singleton")时打印:
// 给容器中添加person组件
// true
//@Scope("prototype")时打印:
// 给容器中添加person组件
// 给容器中添加person组件
// false
}
}

5、@Lazy注解

  • Spring IoC (ApplicationContext) 容器一般都会在启动的时候实例化所有单实例 bean 。如果我们想要 Spring 在启动的时候延迟加载 bean,即在调用某个 bean 的时候再去初始化,那么就可以使用 @Lazy 注解。
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
31
32
33
34
//以下注解需要导入lombok相关依赖
@Data
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class Person {
private String name;

private Integer age;
}

@Configuration
public class MainConfig {
/**
* 懒加载:
* 单实例bean:默认在容器启动的时候创建对象;
* 懒加载:容器启动不创建对象。第一次使用(获取)Bean创建对象,并初始化;
*/
@Bean("person")
@Lazy //懒加载一般针对单实例
public Person person(){
System.out.println("给容器中添加person组件");
return new Person("李四", 20);
}
}

public class MainTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
//上一行代码执行完后控制台没有打印任何信息
Object person = applicationContext.getBean("person");
//控制台打印“给容器中添加person组件”
}
}

6、@Conditional注解

  • @Conditional是Spring4新提供的注解,它的作用是按照一定的条件进行判断,满足条件给容器注册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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
//以下注解需要导入lombok相关依赖
@Data
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class Person {
private String name;

private Integer age;
}

@Configuration
public class MainConfig {
/**
* @Conditional:按照一定的条件进行判断,满足条件则给容器中注册bean
* @return
*/
@Bean("bill")
@Conditional(WindowsCondition.class)
public Person person01(){
return new Person("Bill Gates",62);
}

@Bean("linus")
@Conditional(LinuxCondition.class)
public Person person02(){
return new Person("linus", 48);
}
}

public class LinuxCondition implements Condition {
/**
* ConditionContext:判断条件能使用的上下文(环境)
* AnnotatedTypeMetadata:注解注释信息
*/
public boolean matches(ConditionContext context, AnnotatedTypeMetadata annotatedTypeMetadata) {
// 是否linux系统
//1、能获取到ioc使用的beanfactory
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
//2、获取类加载器
ClassLoader classLoader = context.getClassLoader();
//3、获取当前环境信息
Environment environment = context.getEnvironment();
//4、获取到bean定义的注册类
BeanDefinitionRegistry registry = context.getRegistry();
//可以判断容器中的bean注册情况,也可以给容器中注册bean
boolean definition = registry.containsBeanDefinition("person");

String property = environment.getProperty("os.name");
if(property.contains("Linux")){
return true;
}
return false;
}
}

public class WindowsCondition implements Condition {
public boolean matches(ConditionContext context, AnnotatedTypeMetadata annotatedTypeMetadata) {
Environment environment = context.getEnvironment();
String property = environment.getProperty("os.name");
if(property.contains("Windows")){
return true;
}
return false;
}
}

//可通过设置虚拟机参数-Dos.name=Windows/Linux来更换系统信息
public class MainTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
String[] namesForType = applicationContext.getBeanNamesForType(Person.class);
for (String name : namesForType){
System.out.println(name);//linus
}
Map<String, Person> beansOfType = applicationContext.getBeansOfType(Person.class);
System.out.println(beansOfType);//{linus=Person(name=linus, age=48)}
}
}

7、@Import注解

  • @Import只能用在类上 ,@Import通过快速导入的方式实现把实例加入spring的IOC容器中。
  • 第一种方式
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
public class Blue {
}

public class Red {
}

@Configuration
//导入的组件名默认为全类名
@Import({Blue.class, Red.class})
public class MainConfig {
}

public class MainTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
String[] definitionNames = applicationContext.getBeanDefinitionNames();
for (String name : definitionNames) {
System.out.println(name);
}
//打印如下:
//org.springframework.context.annotation.internalConfigurationAnnotationProcessor
//org.springframework.context.annotation.internalAutowiredAnnotationProcessor
//org.springframework.context.annotation.internalCommonAnnotationProcessor
//org.springframework.context.event.internalEventListenerProcessor
//org.springframework.context.event.internalEventListenerFactory
//mainConfig
//com.example.bean.Blue
//com.example.bean.Red
}
}
  • 第二种方式
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
31
32
33
34
35
36
37
38
39
public class Blue {
}

public class Red {
}

@Configuration
@Import(MyImportSelector.class)
public class MainConfig {
}

//自定义逻辑返回需要导入的组件
public class MyImportSelector implements ImportSelector {
//返回值,就是到导入到容器中的组件全类名
//AnnotationMetadata:当前标注@Import注解的类的所有注解信息
public String[] selectImports(AnnotationMetadata annotationMetadata) {
//方法不要返回null值
return new String[]{"com.example.bean.Blue","com.example.bean.Red"};
}
}

public class MainTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
String[] definitionNames = applicationContext.getBeanDefinitionNames();
for (String name : definitionNames) {
System.out.println(name);
}
//打印如下:
//org.springframework.context.annotation.internalConfigurationAnnotationProcessor
//org.springframework.context.annotation.internalAutowiredAnnotationProcessor
//org.springframework.context.annotation.internalCommonAnnotationProcessor
//org.springframework.context.event.internalEventListenerProcessor
//org.springframework.context.event.internalEventListenerFactory
//mainConfig
//com.example.bean.Blue
//com.example.bean.Red
}
}
  • 第三种方式
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
public class Blue {
}

public class Red {
}

public class RainBow {
}

@Configuration
@Import({MyImportBeanDefinitionRegistrar.class, Red.class, Blue.class})
public class MainConfig {
}

public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
/**
* AnnotationMetadata:当前类的注解信息
* BeanDefinitionRegistry:BeanDefinition注册类,即可以把所有需要添加到容器中的bean,调用
* BeanDefinitionRegistry.registerBeanDefinition手工注册进来。
*/
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

boolean definition = registry.containsBeanDefinition("com.example.bean.Red");
boolean definition2 = registry.containsBeanDefinition("com.example.bean.Blue");
if(definition && definition2){
//指定Bean定义信息(Bean的类型等)
RootBeanDefinition beanDefinition = new RootBeanDefinition(RainBow.class);
//注册一个Bean,指定bean名
registry.registerBeanDefinition("rainBow", beanDefinition);
}
}
}

public class MainTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
String[] definitionNames = applicationContext.getBeanDefinitionNames();
for (String name : definitionNames) {
System.out.println(name);
}
//打印如下:
//org.springframework.context.annotation.internalConfigurationAnnotationProcessor
//org.springframework.context.annotation.internalAutowiredAnnotationProcessor
//org.springframework.context.annotation.internalCommonAnnotationProcessor
//org.springframework.context.event.internalEventListenerProcessor
//org.springframework.context.event.internalEventListenerFactory
//mainConfig
//com.example.bean.Red
//com.example.bean.Blue
//rainBow
}
}

8、@Value和@PropertySource注解

  • 使用@Value注解可以给bean的属性赋值。使用@PropertySource读取外部配置文件中的k/v保存到运行的环境变量中;加载完外部的配置文件以后使用${}取出配置文件的值。
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
31
32
33
34
35
36
37
38
39
@Data
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class Person {
//使用@Value赋值;
//1、基本数值
//2、可以写SpEL; #{}
//3、可以写${};取出配置文件【properties】中的值(在运行环境变量里面的值)
@Value("张三")
private String name;

@Value("#{20-2}")
private Integer age;

@Value("${person.nickName}")
private String nickName;
}

@Configuration
@PropertySource(value={"classpath:/person.properties"})
public class MainConfig {
@Bean
public Person person(){
return new Person();
}
}

public class MainTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
Person bean = applicationContext.getBean(Person.class);
System.out.println(bean);//Person(name=张三, age=18, nickName=小明)

ConfigurableEnvironment environment = applicationContext.getEnvironment();
String property = environment.getProperty("person.nickName");
System.out.println(property);//小明
}
}

person.properties文件:

1
person.nickName=小明

9、@Autowired、@Qualifier、@Primary、@Resource和@Inject注解

  • @Autowired:自动注入。默认一定要将属性赋值,否则会报错,但可以使用@Autowired(required=false)。
    • 默认优先按照类型去容器中找对应的组件:applicationContext.getBean(UserDao.class);找到就赋值。
    • 如果找到多个相同类型的组件,再将属性的名称作为组件的id去容器中查找:applicationContext.getBean(“userDao”)。
  • @Qualifier:使用@Qualifier指定需要装配的组件的id,而不是使用属性名。
  • @Primary:让Spring进行自动装配的时候,默认使用首选的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
31
32
33
34
35
36
37
38
39
40
41
42
43
@Data
@ToString
@NoArgsConstructor
@AllArgsConstructor
@Repository
//此bean的id默认为类名首字母小写,即userDao
public class UserDao {
private String name = "小红";
}

@Data
@ToString
@NoArgsConstructor
@AllArgsConstructor
@Service
public class UserService {

//先通过getBean(UserDao.class)找到容器中有两个相同类型的bean,所以要通过属性名getBean("userDao2")去找唯一的bean
@Autowired
private UserDao userDao2;

private void print(){
System.out.println(userDao2);
}
}

@Configuration
@ComponentScan({"com.example.service","com.example.dao",
"com.example.controller","com.example.bean"})
public class MainConfig {
@Bean("userDao2")
public UserDao userDao(){
return new UserDao("小明");
}
}

public class MainTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
UserService bean = applicationContext.getBean(UserService.class);
System.out.println(bean);//UserService(userDao2=UserDao(name=小明))
}
}
  • 如果UserService类使用@Qualifier,则打印UserService(userDao2=UserDao(name=小红)):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Data
@ToString
@NoArgsConstructor
@AllArgsConstructor
@Service
public class UserService {
@Autowired
@Qualifier("userDao")
private UserDao userDao2;

private void print(){
System.out.println(userDao2);
}
}
  • 如果配置类使用@Primary(要发挥效果得不使用@Qualifier),则打印UserService(userDao2=UserDao(name=小明)):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Configuration
@ComponentScan({"com.example.service","com.example.dao",
"com.example.controller","com.example.bean"})
public class MainConfig {
@Primary
@Bean("userDao2")
public UserDao userDao(){
return new UserDao("小明");
}
}

@Data
@ToString
@NoArgsConstructor
@AllArgsConstructor
@Service
public class UserService {
@Autowired
private UserDao userDao2;

private void print(){
System.out.println(userDao2);
}
}
  • 此外,@Autowired还可以标注在构造器,参数,方法上。

    • 测试标注在方法上:
    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
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    @Data
    @ToString
    @NoArgsConstructor
    @AllArgsConstructor
    @Repository
    //此bean的id默认为类名首字母小写,即userDao
    public class UserDao {
    private String name = "小红";
    }

    @Data
    @ToString
    @NoArgsConstructor
    @AllArgsConstructor
    @Service
    public class UserService {

    private UserDao userDao;

    //标注在方法上,则Spring容器创建该对象时会调用该方法完成属性的赋值,方法参数的值从ioc容器中获取。
    @Autowired
    public void setUserDao(UserDao userDao) {
    this.userDao = userDao;
    }

    private void print(){
    System.out.println(userDao);
    }
    }

    @Configuration
    @ComponentScan({"com.example.service","com.example.dao",
    "com.example.controller","com.example.bean"})
    public class MainConfig {

    }

    public class MainTest {
    public static void main(String[] args) {
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
    UserService bean = applicationContext.getBean(UserService.class);
    System.out.println(bean);//UserService(userDao=UserDao(name=小红))
    }
    }
    • 测试标注在构造器上(特别地,如果一个bean中只有一个有参构造器,则可以省略@Autowired注解):
    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
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    @Data
    @ToString
    @NoArgsConstructor
    @AllArgsConstructor
    @Repository
    //此bean的id默认为类名首字母小写,即userDao
    public class UserDao {
    private String name = "小红";
    }

    @Data
    @ToString
    @Service
    public class UserService {

    private UserDao userDao;

    //构造器参数用的组件也是从ioc容器中获取
    @Autowired
    public UserService(UserDao userDao) {
    this.userDao = userDao;
    }

    private void print() {
    System.out.println(userDao);
    }
    }

    @Configuration
    @ComponentScan({"com.example.service","com.example.dao",
    "com.example.controller","com.example.bean"})
    public class MainConfig {

    }

    public class MainTest {
    public static void main(String[] args) {
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
    UserService bean = applicationContext.getBean(UserService.class);
    System.out.println(bean);//UserService(userDao=UserDao(name=小红))
    }
    }
    • 测试标注在方法参数上
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    @Data
    @ToString
    @Service
    public class UserService {

    private UserDao userDao;

    //构造器参数用的组件也是从ioc容器中获取
    public UserService(@Autowired UserDao userDao) {
    this.userDao = userDao;
    }

    private void print() {
    System.out.println(userDao);
    }
    }
  • Spring还支持使用@Resource(JSR250)和@Inject(JSR330),@Resource、@Inject都是java规范而@Autowired是Spring定义的。

    • @Resource:

      • 可以和@Autowired一样实现自动装配功能;默认是按照组件名称进行装配的;没有能支持@Primary功能;没有支持@Autowired(reqiured=false)功能。
    • @Inject:

      • 需要导入javax.inject的包,和Autowired的功能一样,但没有required=false的功能。需要导入以下依赖:
      1
      2
      3
      4
      5
      6
      <!-- https://mvnrepository.com/artifact/javax.inject/javax.inject -->
      <dependency>
      <groupId>javax.inject</groupId>
      <artifactId>javax.inject</artifactId>
      <version>1</version>
      </dependency>

10、@Profile注解

  • @Profile:指定组件在哪个环境的情况下才能被注册到容器中,不指定则任何环境下都能注册这个组件。此注解可以标注在类和方法上。
    • 加了环境标识的bean,只有这个环境被激活的时候才能注册到容器中。默认是default环境。
    • 写在配置类上时,只有是指定的环境的时候,整个配置类里面的所有配置才能开始生效。
    • 没有标注环境标识的bean在,任何环境下都是加载的。
  • 测试根据不同环境设置切换数据源,需要导入以下依赖:
1
2
3
4
5
6
7
8
9
10
11
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.21</version>
</dependency>

<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>6.0.6</version>
</dependency>

配置文件代码如下:

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
@Configuration
@PropertySource("classpath:/dbconfig.properties")
public class MainConfig implements EmbeddedValueResolverAware {

@Value("${db.user}")
private String userName;

private String driverClassName;

private StringValueResolver valueResolver;

@Profile("test")
@Bean("testDataSource")
public DataSource dataSourceTest(@Value("${db.password}")String password) throws Exception{
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName(driverClassName);
dataSource.setUrl("jdbc:mysql://localhost:3306/test");
dataSource.setUsername(userName);
dataSource.setPassword(password);
return dataSource;
}

@Profile("dev")
@Bean("devDataSource")
public DataSource dataSourceDev(@Value("${db.password}")String password) throws Exception{
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName(driverClassName);
dataSource.setUrl("jdbc:mysql://localhost:3306/dev");
dataSource.setUsername(userName);
dataSource.setPassword(password);
return dataSource;
}

@Profile("prod")
@Bean("prodDataSource")
public DataSource dataSourceProd(@Value("${db.password}")String password) throws Exception{
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName(driverClassName);
dataSource.setUrl("jdbc:mysql://localhost:3306/prod");
dataSource.setUsername(userName);
dataSource.setPassword(password);
return dataSource;
}

public void setEmbeddedValueResolver(StringValueResolver stringValueResolver) {
this.valueResolver = stringValueResolver;
driverClassName = valueResolver.resolveStringValue("${db.driverClass}");
}
}

public class MainTest {
public static void main(String[] args) throws SQLException {
//1、使用命令行动态参数: 在虚拟机参数位置加载 -Dspring.profiles.active=test
//2、代码的方式激活某种环境。

//1、创建一个applicationContext
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
//2、设置需要激活的环境
applicationContext.getEnvironment().setActiveProfiles("test","dev");
//3、注册主配置类
applicationContext.register(MainConfig.class);
//4、启动刷新容器
applicationContext.refresh();

String[] namesForType = applicationContext.getBeanNamesForType(DataSource.class);
for (String string : namesForType) {
System.out.println(string);
}

DataSource dataSource = (DataSource) applicationContext.getBean("devDataSource");
Connection connection = dataSource.getConnection();
System.out.println(connection);
}
}

dbconfig.properties内容为:

1
2
3
4
db.user=root
db.password=<你的数据库密码>
db.driverClass=com.mysql.cj.jdbc.Driver
db.url=jdbc:mysql://localhost:3306/test

执行结果:

1
2
3
testDataSource
devDataSource
com.mysql.cj.jdbc.ConnectionImpl@123ef382