自动装配:Spring利用依赖注入(DI),完成对IOC容器中中各个组件的依赖关系赋值
@Autowired和@Qualifier和@Primary
@Autowired
1
2
3
4
5
6
7
8
|
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
boolean required() default true;
}
|
- 默认优先按照类型去容器中找对应的组件: applicationContext.getBean(BookDao.class);找到就赋值
- 如果找到多个相同类型的组件,再将属性的名称作为组件的id去容器中查找:applicationContext.getBean(“bookDao”)
- 自动装配默认一定要将属性赋值好,没有就会报错;可以使用@Autowired(required=false);
@Qualifier
如果没有使用@Qualifier指定需要装配的组件的id,则默认使用要装配的属性名作为组件id,如果指定了组件di则不使用属性名作为组件id。配合@Autowired一起使用,可以在一个类型对应多个bean的时候使用
1
2
3
4
5
6
7
8
9
|
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Qualifier {
String value() default "";
}
|
@Primary
让Spring进行自动装配的时候,默认首选使用的bean;也可以继续使用@Qualifier指定需要装配的bean的名字
在没有使用@Qualifier明确指定的情况下,优先使用@Primary注解的bean,明确指定的情况下则使用明确指定的那个
1
2
3
4
5
6
7
|
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Primary {
}
|
注意:@Primary是用在声明Bean的时候,如下:
1
2
3
4
5
6
7
|
@Primary
@Bean("bookDao2")
public BookDao bookDao(){
BookDao bookDao = new BookDao();
bookDao.setLable("2");
return bookDao;
}
|
@Resource和@Inject
Spring还支持使用@Resource(JSR250)和@Inject(JSR330)[java规范的注解]
@Resource:
@Inject:
区别:@Autowired是Spring定义的; @Resource、@Inject都是java规范
AutowiredAnnotationBeanPostProcessor
解析完成自动装配功能

@Autowired在属性以外的其他位置注入属性
构造器,参数,方法,属性;都是从容器中获取参数组件的值
标注在方法位置
@Bean+方法参数;参数从容器中获取;默认不写@Autowired效果是一样的;都能自动装配,eg:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
package com.eh.bean;
import lombok.Getter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class RainBow {
@Getter
private Red red;
//@Autowired,标注在方法,Spring容器创建当前对象,就会调用方法,完成赋值;
//方法使用的参数,自定义类型的值从ioc容器中获取
@Autowired
public void setRed(Red red) {
this.red = red;
}
}
|
@Bean示例: 这种情况用的最多
1
2
3
4
5
6
7
8
9
10
11
|
/**
* @Bean标注的方法创建对象的时候,方法参数的值从容器中获取
* @param car
* @return
*/
@Bean
public Color color(Car car){
Color color = new Color();
color.setCar(car);
return color;
}
|
标在构造器上
如果组件只有一个有参构造器,这个有参构造器的@Autowired可以省略,参数位置的组件还是可以自动从容器中获取,如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
package com.eh.bean;
import lombok.Getter;
import org.springframework.stereotype.Component;
@Component
public class RainBow {
@Getter
private Red red;
// @Autowired 可以省略不写
public RainBow(Red red) {
this.red = red;
}
}
|
放在参数位置
1
2
3
4
5
6
7
8
9
10
11
12
13
|
@Component
public class RainBow {
@Getter
private Red red;
// 同上面一样,方法中的@Autowired可以省略不写
@Autowired
public void setRed(@Autowired Red red) {
this.red = red;
}
}
|
在自定义组件中注入Spring底层组件
例如ApplicationContext,BeanFactory等
在自定义组件实现xxxAware,在创建对象的时候,会调用接口规定的方法注入相关组件;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
/**
* 为某个bean提供spring底层容器的能力,通过使用函数回调的方式
* A marker superinterface indicating that a bean is eligible to be notified by the
* Spring container of a particular framework object through a callback-style method.
* The actual method signature is determined by individual subinterfaces but should
* typically consist of just one void-returning method that accepts a single argument.
*
* <p>Note that merely implementing {@link Aware} provides no default functionality.
* Rather, processing must be done explicitly, for example in a
* {@link org.springframework.beans.factory.config.BeanPostProcessor}.
* Refer to {@link org.springframework.context.support.ApplicationContextAwareProcessor}
* for an example of processing specific {@code *Aware} interface callbacks.
*
* @author Chris Beams
* @author Juergen Hoeller
* @since 3.1
*/
public interface Aware {
}
|
继承Aware接口的接口有:
-
ApplicationEventPublisherAware (org.springframework.context)
注入事件开发器
-
NotificationPublisherAware (org.springframework.jmx.export.notification)
-
MessageSourceAware (org.springframework.context)
国际化
-
BeanFactoryAware (org.springframework.beans.factory)
-
EnvironmentAware (org.springframework.context)
-
EmbeddedValueResolverAware (org.springframework.context)
值解析器,可以取出占位符表示的值
-
ResourceLoaderAware (org.springframework.context)
资源加载
-
ImportAware (org.springframework.context.annotation)
导入相关
-
LoadTimeWeaverAware (org.springframework.context.weaving)
-
BeanNameAware (org.springframework.beans.factory)
Bean名字
-
BeanClassLoaderAware (org.springframework.beans.factory)
类加载器
-
ApplicationContextAware (org.springframework.context)
注入IOC容器
具体由xxxAwareProcessor处理实现xxxAware接口类,例如ApplicationContextAware->ApplicationContextAware
演示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
@Component
public class Red implements ApplicationContextAware, BeanNameAware, EmbeddedValueResolverAware {
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
System.out.println("传入的ioc:" + applicationContext);
this.applicationContext = applicationContext;
}
@Override
public void setBeanName(String name) {
System.out.println("当前bean的名字:" + name);
}
@Override
public void setEmbeddedValueResolver(StringValueResolver resolver) {
String resolveStringValue = resolver.resolveStringValue("你好 ${os.name} 我是 #{20*18}");
System.out.println("解析的字符串:" + resolveStringValue);
}
}
|
@Profile
Spring为我们提供的可以根据当前环境,动态的激活和切换一系列组件的功能;
注意
在开发中不建议使用@Profile来区分环境注册组件,可以使用maven的profile,因为使用@Profile还得写各种环境的组件,但是使用maven的profile只需要隔离属性配置文件即可。私以为@Profile只适合在某个环境中独有某个组件时才使用,如果在各个环境都有某个组件,建议使用maven的profile,在打包的时候区分环境,写一个组件注册逻辑,读取不同的环境变量。
@Profile:指定组件在哪个环境的情况下才能被注册到容器中,不指定,任何环境下都能注册这个组件
- 加了环境标识的bean,只有这个环境被激活的时候才能注册到容器中。默认是default环境
- 写在配置类上,只有是指定的环境的时候,整个配置类里面的所有配置才能开始生效
- 没有标注环境标识的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
|
@PropertySource("classpath:/dbconfig.properties")
@Configuration
public class MainConfigOfProfile implements EmbeddedValueResolverAware {
@Value("${db.user}")
private String user;
private StringValueResolver valueResolver;
private String driverClass;
@Bean
public Yellow yellow() {
return new Yellow();
}
@Profile("test")
@Bean("testDataSource")
public DataSource dataSourceTest(@Value("${db.password}") String pwd) throws Exception {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser(user);
dataSource.setPassword(pwd);
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test");
dataSource.setDriverClass(driverClass);
return dataSource;
}
@Profile("dev")
@Bean("devDataSource")
public DataSource dataSourceDev(@Value("${db.password}") String pwd) throws Exception {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser(user);
dataSource.setPassword(pwd);
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/ssm_crud");
dataSource.setDriverClass(driverClass);
return dataSource;
}
@Profile("prod")
@Bean("prodDataSource")
public DataSource dataSourceProd(@Value("${db.password}") String pwd) throws Exception {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser(user);
dataSource.setPassword(pwd);
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/scw_0515");
dataSource.setDriverClass(driverClass);
return dataSource;
}
@Override
public void setEmbeddedValueResolver(StringValueResolver resolver) {
this.valueResolver = resolver;
driverClass = valueResolver.resolveStringValue("${db.driverClass}");
}
}
|
切换环境有两种方式:
-
使用命令行动态参数: 在虚拟机参数位置加载 -Dspring.profiles.active=test
-
代码的方式激活某种环境
1
2
3
4
5
6
7
8
9
10
|
// 这里使用无参的构造方法,有参的直接就完成初始化了。
AnnotationConfigApplicationContext applicationContext =
new AnnotationConfigApplicationContext();
//1、创建一个applicationContext
//2、设置需要激活的环境
applicationContext.getEnvironment().setActiveProfiles("dev");
//3、注册主配置类
applicationContext.register(MainConfigOfProfile.class);
//4、启动刷新容器
applicationContext.refresh();
|