目录
3、ApplicationContextAwareProcessor注入源码
一、spring规范的bean自动注入
1、使用@Autowired自动注入
(1)自动扫描包
@Configuration
@ComponentScan({"com.xiang.spring.dao", "com.xiang.spring.controller", "com.xiang.spring.service"})
public class MainConfigAutowired {
}
(2)定义bean
package com.xiang.spring.service;
import com.xiang.spring.dao.BookDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
// 名字默认是类名首字母小写
@Service
public class BookService {
@Autowired(required = false)
private BookDao bookDao;
public void print() {
System.out.println(bookDao);
}
@Override
public String toString() {
return "BookService{" +
"bookDao=" + bookDao +
'}';
}
}
package com.xiang.spring.dao;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Repository;
// 名字默认是类名首字母小写
@Repository
public class BookDao {
}
(3)测试类
@Test
public void test01() {
// 创建ioc容器,容器创建时,默认会将单例的bean都创建出来
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigAutowired.class);
BookService bean = applicationContext.getBean(BookService.class);
System.out.println(bean);
BookDao bean1 = applicationContext.getBean(BookDao.class);
System.out.println(bean1);
}
(4)@Autowired自动装配顺序
① 优先按照class类型查找对应的组件,如果找到就赋值。
private BookDao bookDao; - 先按照类型查找
② 如果找到多个相同类型的组件,再将属性的名称作为组件的id去容器中查找。
private BookDao bookDao; - 如果多个,按照bookDao这个属性名查找
2、使用@Qualifier指定需要装配的组件
(1)装配时,添加@Qualifier注解
// 使用@Qualifier指定要装配的组件的id,而不是使用属性名
@Qualifier("bookDao")
@Autowired
private BookDao bookDao;
3、使用@Autowired装配null对象
不加@Autowired(required = false)的话,如果需要装配的bean没有找到,程序会报错。
加了@Autowired(required = false)的话,如果需要装配的bean没有找到,会装配null。
// 使用@Qualifier指定要装配的组件的id,而不是使用属性名
@Qualifier("bookDao")
@Autowired(required = false)
private BookDao bookDao;
4、使用@Primary装配首选bean
有多个相同类型的bean时,@Primary注释的bean可以被优先注入,而不是使用属性名查找注入。
(1)在bean上添加@Primary注解
@Primary
@Repository
public class BookDao {
}
5、总结
自动装配
spring利用依赖注入(DI),完成对IOC容器中各个组件的依赖关系赋值
(1) @Autowired自动注入,一个组件想要使用另一个组件
① 优先按照class类型查找对应的组件,如果找到就赋值。
private BookDao bookDao; - 先按照类型查找
② 如果找到多个相同类型的组件,再将属性的名称作为组件的id去容器中查找。
private BookDao bookDao; - 如果多个,按照bookDao这个属性名查找
③ @Qualifier("bookDao"):使用@Qualifier指定需要装配的组件的id,而不是使用属性名。
④ 自动装配默认一定要将属性赋值好,没有找到组件就会报错。
可以使用@Autowired(required = false) ,没有该组件不会报错,是null。
⑤ @Primary:让spring进行自动装配的时候,默认使用首选的bean。
直接在bean上面加@Primary,多个同类型的bean会优先使用@Primary修饰的。
也可以使用@Qualifier指定,@Qualifier优先级更强。
6、自动装配原理
AutowiredAnnotationBeanPostProcessor 实现自动装配
二、java规范的bean自动注入
1、@Resource自动装配
@Resource:
可以和@Autowired一样实现自动装配功能。默认是按照组件名称进行装配的。
可以指定装配名称:@Resource(name = "bookDao")
不支持@Qualifier、@Primary、required = false
(1)在bean上加@Resource
// 使用@Resource自动注入
@Resource(name = "bookDao")
private BookDao bookDao;
2、@Inject自动装配
@Inject:
需要额外导入javax.inject包依赖。
和@Autowired的功能一样,支持@Qualifier、@Primary,不支持required = false
(1)pom文件导入依赖
<!--@Inject依赖-->
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
(2)使用@Inject注入
// 使用@Inject自动注入
@Inject
private BookDao bookDao;
3、总结
spring还支持使用@Resource(JSR250)和@Inject(JSR330)【java规范的注解】
@Resource:
可以和@Autowired一样实现自动装配功能。默认是按照组件名称进行装配的。
可以指定装配名称:@Resource(name = "bookDao")
不支持@Qualifier、@Primary、required = false
@Inject:
需要额外导入javax.inject包依赖。
和@Autowired的功能一样,支持@Qualifier、@Primary,不支持required = false
@Autowired是spring的,@Resource和@Inject是java的规范。
AutowiredAnnotationBeanPostProcessor实现自动装配
三、方法、构造器位置的自动装配
1、@Autowired标注在方法上
(1)标注在set方法上
/**
* 标注在方法上,spring容器创建当前对象时,就会调用方法完成赋值。
* 方法使用的参数,自定义类型的值,从ioc容器中进行获取。
* setCar(Car car) 中的参数,就会从ioc容器中获取。
*/
@Autowired
public void setCar(Car car) {
this.car = car;
}
2、@Autowired标注在构造器上
(1)标注在构造器上
/**
* 写一个有参构造器
* @Autowired标注在构造器上,构造器用的参数也会从ioc容器中取
* 如果只有一个有参构造器,@Autowired是可以省略的,参数位置的组件也会自动注入。
*/
@Autowired // 如果只有一个有参构造器,可以省略
private Boos(Car car) {
this.car = car;
System.out.println("boos 的有参构造器");
}
3、@Autowired标注在参数上
(1)标注在构造器参数上
private Boos(@Autowired Car car) {
this.car = car;
System.out.println("boos 的有参构造器");
}
(2)标注在方法参数上
public void setCar(@Autowired Car car) {
this.car = car;
}
4、@Bean的自动装配
(1)@Bean自动装配
/**
* @Bean标注的注解创建对象的时候,方法参数的值从容器中获取
*/
@Bean
public Color color(Car car) {
System.out.println("color bean " + car);
Color color = new Color();
color.setCar(car);
return color;
}
5、总结
@Autowired:可以标注 构造器、参数、方法、属性。
① 标注在方法上:
标注在方法上,spring容器创建当前对象时,就会调用方法完成赋值。
方法使用的参数,自定义类型的值,从ioc容器中进行获取。
@Bean标注的注解创建对象的时候,方法参数的值从容器中获取。默认不写@Autowired
② 标注在构造器上:
@Autowired标注在构造器上,构造器用的参数也会从ioc容器中取
如果只有一个有参构造器,@Autowired是可以省略的,参数位置的组件也会自动注入。
③ 标注在参数上:
在方法、构造器中的参数前标注@Autowired,也会从ioc容器中获取参数组件值。
四、自定义组件使用spring容器底层组件
1、基本使用
(1)定义bean
package com.xiang.spring.bean;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.EmbeddedValueResolverAware;
import org.springframework.stereotype.Component;
import org.springframework.util.StringValueResolver;
@Component
public class Red implements ApplicationContextAware, BeanNameAware, EmbeddedValueResolverAware {
private ApplicationContext applicationContext;
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
System.out.println("传入的ioc:" + applicationContext);
this.applicationContext = applicationContext;
}
public void setBeanName(String name) {
System.out.println("当前bean的名字:" + name);
}
public void setEmbeddedValueResolver(StringValueResolver resolver) {
String s = resolver.resolveStringValue("你好${os.name} 我是#{20*18}");
System.out.println("解析的字符串:" + s);
}
}
(2)测试类查看执行结果
@Test
public void test01() {
// 创建ioc容器,容器创建时,默认会将单例的bean都创建出来
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigAutowired.class);
}
// 查看Red组件执行结果
当前bean的名字:red
解析的字符串:你好Windows 10 我是360
传入的ioc:org.springframework.context.annotation.AnnotationConfigApplicationContext@4141d797
2、解释
自定义组件想要使用spring容器底层的一些组件(ApplicationContext、BeanFactory、等等)
只需让自定义组件实现XxxxAware,在创建对象时,会调用接口规定的方法,注入相关的组件。
把spring底层的一些组件注入到自定义的Bean中。
这些XxxxAware的功能,都是使用XxxxProcessor实现的:
ApplicationContextAware -> ApplicationContextAwareProcessor
Aware接口有以下子接口:
3、ApplicationContextAwareProcessor注入源码
@Override
public Object postProcessBeforeInitialization(final Object bean, String beanName) throws BeansException {
AccessControlContext acc = null;
if (System.getSecurityManager() != null &&
(bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware ||
bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware ||
bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware)) {
acc = this.applicationContext.getBeanFactory().getAccessControlContext();
}
if (acc != null) {
AccessController.doPrivileged(new PrivilegedAction<Object>() {
@Override
public Object run() {
invokeAwareInterfaces(bean);
return null;
}
}, acc);
}
else {
invokeAwareInterfaces(bean);
}
return bean;
}
private void invokeAwareInterfaces(Object bean) {
if (bean instanceof Aware) {
if (bean instanceof EnvironmentAware) {
((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
}
if (bean instanceof EmbeddedValueResolverAware) {
((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver);
}
if (bean instanceof ResourceLoaderAware) {
((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
}
if (bean instanceof ApplicationEventPublisherAware) {
((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);
}
if (bean instanceof MessageSourceAware) {
((MessageSourceAware) bean).setMessageSource(this.applicationContext);
}
if (bean instanceof ApplicationContextAware) {
((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
}
}
}
五、使用@Profile根据环境注入组件
1、使用@Profile
(1)pom引入依赖
<!--c3p0数据源-->
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.1</version>
</dependency>
<!--mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.44</version>
</dependency>
(2)在resources目录下新建配置文件db.properties
db.user=root
db.password=123
db.driverClass=com.mysql.jdbc.Driver
(3)配置类
package com.xiang.spring.config;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.EmbeddedValueResolverAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.context.annotation.PropertySource;
import org.springframework.util.StringValueResolver;
import javax.sql.DataSource;
/**
* profile:
* Spring为我们提供的 可以根据当前环境,动态的激活和切换一系列组件的功能。
* 指定组件在哪个环境下才能被注册到环境中。
* (1)加了@Profile的bean,只有这个环境被激活的时候才能注册到ioc容器中。
* 默认是default环境
* (2)@Profile 写在配置类上,只有是指定的环境的时候,整个配置类里面的所有配置才能生效。
* (3)没有标注@Profile的bean在任何环境下都是会加载的。
*
*
* 例如:数据源:开发环境(dev)、测试环境(uat)、生产环境(prod)
*
*/
//@Profile("prod")
@PropertySource("classpath:/db.properties")
@Configuration
public class MainConfigOfProfile implements EmbeddedValueResolverAware {
// 三种方式获取配置文件数据
@Value("${db.user}")
private String user;
private String driverClass;
@Profile("prod")
@Bean
public DataSource dataSourceProd(@Value("${db.password}") String password) throws Exception {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser(user);
dataSource.setPassword(password);
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/prod");
dataSource.setDriverClass(driverClass);
return dataSource;
}
@Profile("uat")
@Bean
public DataSource dataSourceUat(@Value("${db.password}") String password) throws Exception {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser(user);
dataSource.setPassword(password);
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/uat");
dataSource.setDriverClass(driverClass);
return dataSource;
}
@Profile("default")
@Bean
public DataSource dataSourceDefault(@Value("${db.password}") String password) throws Exception {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser(user);
dataSource.setPassword(password);
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/default");
dataSource.setDriverClass(driverClass);
return dataSource;
}
@Profile("dev")
@Bean
public DataSource dataSourceDev(@Value("${db.password}") String password) throws Exception {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser(user);
dataSource.setPassword(password);
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/dev");
dataSource.setDriverClass(driverClass);
return dataSource;
}
public void setEmbeddedValueResolver(StringValueResolver resolver) {
this.driverClass = resolver.resolveStringValue("${db.driverClass}");
}
}
(4)测试类查看执行结果
package com.xiang.spring.test;
import com.xiang.spring.config.MainConfigOfProfile;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import javax.sql.DataSource;
public class IOCTest_Profile {
/**
* 切换环境
* (1)使用命令行参数
* run-runconfiguration -> 参数写上:-Dspring.profiles.active=prod
* (2)使用代码的方式激活某个环境
*/
@Test
public void test01() {
// 创建ioc容器,容器创建时,默认会将单例的bean都创建出来
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfProfile.class);
String[] dataSources = applicationContext.getBeanNamesForType(DataSource.class);
for (String dataSource : dataSources) {
System.out.println(dataSource);
}
}
@Test
public void test02() {
// 使用代码方式 设置环境
// 1.创建一个applicationContext
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
// 2.设置激活的环境,环境可设置多个
applicationContext.getEnvironment().setActiveProfiles("prod", "uat");
// 3.注册配置类
applicationContext.register(MainConfigOfProfile.class);
// 4.启动刷新容器
applicationContext.refresh();
String[] dataSources = applicationContext.getBeanNamesForType(DataSource.class);
for (String dataSource : dataSources) {
System.out.println(dataSource);
}
}
}