Spring利用依赖注入(DI),完成对IOC容器中中各个组件的依赖关系赋值;依赖注入是spring ioc的具体体现,主要是通过各种注解进行属性的自动注入。
一、@Autowired:自动注入
一、注解介绍
1、默认优先按照类型去容器中找对应的组件;
2、如果找到多个相同类型的组件,再将属性的名称作为组件的id去容器中查找
3、@Qualifier("bookDao"):使用@Qualifier指定需要装配的组件的id,而不是使用属性名
4、自动装配默认一定要将属性赋值好,没有就会报错;
可以使用@Autowired(required=false);
5、@Primary:让Spring进行自动装配的时候,默认使用首选的bean;
也可以继续使用@Qualifier指定需要装配的bean的名字
BookService{
@Autowired
BookDao bookDao;
}
二、AutowiredAnnotationBeanPostProcessor
@Autowired主要是通过AutowiredAnnotationBeanPostProcessor这个类实现的
三、使用地方
1、[标注在方法位置]:@Bean+方法参数;参数从容器中获取;默认不写@Autowired效果是一样的;都能自动装配
2、[标在构造器上]:如果组件只有一个有参构造器,这个有参构造器的@Autowired可以省略,参数位置的组件还是可以自动从容器中获取
3、放在参数位置
/**
* @Bean标注的方法创建对象的时候,方法参数的值从容器中获取
* @param car
* @return
*/
@Bean
public Color color(Car car){
Color color = new Color();
color.setCar(car);
return color;
}
二、@Resource(JSR250)和@Inject(JSR330)
Spring还支持使用@Resource(JSR250)和@Inject(JSR330)[java规范的注解]
一、@Resource:
可以和@Autowired一样实现自动装配功能;默认是按照组件名称进行装配的;
不支持@Primary功能也没有@Autowired(reqiured=false)这种功能;
二、@Inject:
需要导入javax.inject的包,和Autowired的功能一样。没有required=false的功能;
注意:@Autowired:Spring定义的; @Resource、@Inject都是java规范
三、自定义组件
一、自定义组件想要使用Spring容器底层的一些组件
自定义组件想要使用Spring容器底层的一些组件,例如:想要获取底层ApplicationContext(IOC)组件,BeanFactory等等。可以自定义实现xxxAware接口,在创建对象的时候,会调用接口规定的方法注入相关组件 ;把Spring底层一些组件注入到自定义的Bean中。一般xxxAware接口功能都是使用xxxProcessor实现;
例如:ApplicationContextAware==》ApplicationContextAwareProcessor;
ApplicationContextAware接口只有一个方法
public interface ApplicationContextAware extends Aware { void setApplicationContext(ApplicationContext var1) throws BeansException;
}
而ApplicationContextAwareProcessor是实现BeanPostProcessor接口里面的方法(后置处理器)
public interface BeanPostProcessor { @Nullable default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean; } @Nullable default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { return bean; } }然后ApplicationContextAwareProcessor会在对象初始化前调用postProcessBeforeInitialization将ApplicationContext设置进实现 ApplicationContextAware接口的对象的setApplicationContext方法中,然后我们想要使用的时候,只需要将ApplicationContext对象保存在当前对象中即可,具体栗子如下:
/**
实现对应的xxxAware,然后实现对应的方法,
例如:ApplicationContextAware,当red对象生成后,ApplicationContextAwareProcessor还在初始化
前将ApplicationContext 传入到setApplicationContext这个方法中,然后red方法会保存在当前属性中,
在red对象中,写对应的业务逻辑是如果需要获取容器中的对象可以通过ApplicationContext 进行获取。
**/
@Component
public class Red implements ApplicationContextAware,BeanNameAware,EmbeddedValueResolverAware {
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
// TODO Auto-generated method stub
System.out.println("传入的ioc:"+applicationContext);
this.applicationContext = applicationContext;
}
@Override
public void setBeanName(String name) {
// TODO Auto-generated method stub
System.out.println("当前bean的名字:"+name);
}
@Override
public void setEmbeddedValueResolver(StringValueResolver resolver) {
// TODO Auto-generated method stub
String resolveStringValue = resolver.resolveStringValue("你好 ${os.name} 我是 #{20*18}");
System.out.println("解析的字符串:"+resolveStringValue);
}
}
二、ApplicationContextAware接口实现原理,源码展示
class ApplicationContextAwareProcessor implements BeanPostProcessor {
private final ConfigurableApplicationContext applicationContext;
private final StringValueResolver embeddedValueResolver;
public ApplicationContextAwareProcessor(ConfigurableApplicationContext applicationContext) {
this.applicationContext = applicationContext;
this.embeddedValueResolver = new EmbeddedValueResolver(applicationContext.getBeanFactory());
}
@Nullable
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
AccessControlContext acc = null;
// 判断red实现的是哪一个接口
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(() -> {
this.invokeAwareInterfaces(bean);
return null;
}, acc);
} else {
// 主要是调用这个方法
this.invokeAwareInterfaces(bean);
}
return bean;
}
/**主要是匹配对应实现的接口,例如:匹配到ApplicationContextAware,会调用
setApplicationContext将applicationContext设置进实现接口的对象方法中
**/
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);
}
}
}
public Object postProcessAfterInitialization(Object bean, String beanName) {
return bean;
}
}
四、Profile
Spring为我们提供的可以根据当前环境,动态的激活和切换一系列组件的功能。
@Profile:指定组件在哪个环境的情况下才能被注册到容器中,不指定,任何环境下都能注册这个组件
1、加了环境标识的bean,只有这个环境被激活的时候才能注册到容器中。默认是default环境
2、写在配置类上,只有是指定的环境的时候,整个配置类里面的所有配置才能开始生效
3、没有标注环境标识的bean在,任何环境下都是加载的;
栗子:
@Profile("prod")
@Profile("dev")