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*/@Beanpublic 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 {@Nullabledefault Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {return bean;}@Nullabledefault 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;@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {// TODO Auto-generated method stubSystem.out.println("传入的ioc:"+applicationContext);this.applicationContext = applicationContext;}@Overridepublic void setBeanName(String name) {// TODO Auto-generated method stubSystem.out.println("当前bean的名字:"+name);}@Overridepublic void setEmbeddedValueResolver(StringValueResolver resolver) {// TODO Auto-generated method stubString 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());}@Nullablepublic 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")