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")