IOC 使用 (XML 引用)
定義一個接口和它的實現類
public interface FactoryInterface {
String getName();
}
@Setter
public class FactoryInterfaceImpl implements FactoryInterface {
private String name;
@Override
public String getName() {
return name;
}
}
在resource下新建beans.xml 文件 并且設置成員變量name值
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="user" class="cn.pencilso.study.studyioc.xmlbean.FactoryInterfaceImpl">
<property name="name" value="大白"/>
</bean>
</beans>
獲取bean對象 最后輸出 “大白”
ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("beans.xml");
FactoryInterface factoryInterface = classPathXmlApplicationContext.getBean(FactoryInterface.class);
String name = factoryInterface.getName();
System.out.println("name:" + name);
使用注解導入xml的bean方式 @ImportResource
@SpringBootApplication
@ImportResource(locations = {"classpath:beans.xml"})
public class StudyIocApplication {
public static void main(String[] args) {
ConfigurableApplicationContext run = SpringApplication.run(StudyIocApplication.class, args);
FactoryInterface factoryInterface = run.getBean(FactoryInterface.class);
String name = factoryInterface.getName();
System.out.println("name:" + name);
}
}
IOC使用 (注解+過濾器)
創建配置類, 添加注解,掃描base包為 "cn.pencilso.study" 并且導入有注解Repository 和 Service注解的Bean。
useDefaultFilters 默認過濾器關閉。設置為false 。
@ComponentScan(basePackages = {"cn.pencilso.study"}
, includeFilters = {
@ComponentScan.Filter(type = FilterType.ANNOTATION, value = {Repository.class, Service.class})
}, useDefaultFilters = false)
@Configuration
public class StudyIocConfig {
}
添加測試需要的bean
@Repository
public class UserDao {
}
@Service
public class UserService {
}
@Component
public class HttpPlugin {
}
嘗試打印加載到容器里的bean對象
AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(StudyIocConfig.class);
String[] beanDefinitionNames = annotationConfigApplicationContext.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
System.out.println(beanDefinitionName);
}
-----最后輸出 userDao userService已經在容器里了 但是 httpPlugin 并沒有在容器里。
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
studyIocConfig
userDao
userService
自定義過濾器 只要是 Component Service Repository 注解的都引入到bean容器
public class CompoentFilter implements TypeFilter {
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
ClassMetadata classMetadata = metadataReader.getClassMetadata();
String className = classMetadata.getClassName();
System.out.println("className:" + className);
Set<String> annotationTypes = metadataReader.getAnnotationMetadata().getAnnotationTypes();
if (annotationTypes.contains(Component.class.getName()) ||
annotationTypes.contains(Repository.class.getName()) ||
annotationTypes.contains(Service.class.getName())
) {
return true;
}
return false;
}
}
@ComponentScan(basePackages = {"cn.pencilso.study"}
, includeFilters = {
@ComponentScan.Filter(type = FilterType.CUSTOM, value = {CompoentFilter.class})
}, useDefaultFilters = false)
@Configuration
public class StudyIocConfig {
}
最后輸出 這次httpPlugin也出來了 。
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
studyIocConfig
httpPlugin
userDao
userService
排除bean引入,如果需要排除某些bean加載到容器里,可以用excludeFilters 這個過濾器,使用方法跟includeFilters 一致。
@ComponentScan(basePackages = {"cn.pencilso.study"}
, excludeFilters = {
@ComponentScan.Filter(type = FilterType.CUSTOM, value = {CompoentFilter.class})
}, useDefaultFilters = false)
@Configuration
public class StudyIocConfig {
}
IOC 容器單例、多例
Bean 默認是單例的,且是餓漢模式。容器啟動的時候,就會加載Bean對象。
public class UserModel {
public UserModel() {
System.out.println("user model Initialization");
}
}
@Configuration
public class StudyIocConfig {
@Bean
public UserModel userModel() {
return new UserModel();
}
}
public class StudyIocApplication {
public static void main(String[] args) {
AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(StudyIocConfig.class);
}
}
-- 輸出內容為 可以看到 并沒有去調用Bean,但是Bean也創建了。這是因為默認為餓漢單例。
user model Initialization
-- 也可以設置為懶漢模式 通過@Lazy 注解 如下
-- 通過添加Lazy注解后,該Bean將會在第一次使用的的時候才會創建對象,容器啟動的時候則不會創建對象。
@Configuration
public class StudyIocConfig {
@Lazy
@Bean
public UserModel userModel() {
return new UserModel();
}
}
接下來多次獲取Bean 并且比對hashcode
public class StudyIocApplication {
public static void main(String[] args) {
AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(StudyIocConfig.class);
UserModel user1 = annotationConfigApplicationContext.getBean(UserModel.class);
UserModel user2 = annotationConfigApplicationContext.getBean(UserModel.class);
System.out.println(user1 == user2);
}
}
--比對結果輸出為true 表明hashcode 一致
user model Initialization
true
如果說有多例的需求應該怎么做呢,可以采用 @Scope 注解
singleton: 單例的(默認)
prototype: 多例的
request: 同一次請求
session: 同一個會話級別
@Configuration
public class StudyIocConfig {
@Scope(value = "prototype")
@Bean
public UserModel userModel() {
return new UserModel();
}
}
public class StudyIocApplication {
public static void main(String[] args) {
AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(StudyIocConfig.class);
UserModel user1 = annotationConfigApplicationContext.getBean(UserModel.class);
UserModel user2 = annotationConfigApplicationContext.getBean(UserModel.class);
System.out.println(user1 == user2);
}
}
--最后輸出結果,構造方法執行了兩次,比對結果也是false,這時候已經是多例了。 每次獲取bean都是新的對象。
user model Initialization
user model Initialization
false
Conditional 通過條件控制bean是否被加載到容器
public class StudyIocConfig {
@Bean
public UserModel userModel() {
return new UserModel();
}
@Conditional(value = {CustomCondition.class})
@Bean
public UserDao userDao() {
return new UserDao();
}
}
//條件為 容器里已經加載了userModel
public class CustomCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return context.getBeanFactory().containsBean("userModel");
}
}
public class StudyIocApplication {
public static void main(String[] args) {
AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(StudyIocConfig.class);
String[] beanDefinitionNames = annotationConfigApplicationContext.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
System.out.println(beanDefinitionName);
}
}
}
--最后打印輸出 有三個自定義bean,studyIocConfig、userModel、userDao
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
studyIocConfig
userModel
userDao
--嘗試去掉 userModel的bean 后打印如下 這個時候userDao已經不會加載到容器了,因為條件是先加載userModel到容器。
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
studyIocConfig
Import 導入bean對象
/**
* 可以直接指定需要導入的bean class 比如說UserModel.class
* 其次可以通過ImportSelector 接口來進行批量裝載bean
* 也可以通過ImportBeanDefinitionRegistrar 對象進行bean定義,并且注冊。
*/
@Import(value = {UserModel.class, CustomImportSelector.class, CustomImportBeanDefinitionRegistrar.class})
public class StudyIocConfig {
}
/**
* 這些類型根據給定的選擇條件(通常是一個或多個批注屬性)確定應導入哪個類
*/
public class CustomImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{UserDao.class.getName()};
}
}
/**
* 根據需要注冊bean定義
*/
public class CustomImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
//構造bean定義
RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(HttpPlugin.class);
//注冊到bean容器
registry.registerBeanDefinition("httpPlugin", rootBeanDefinition);
}
}
public class StudyIocApplication {
public static void main(String[] args) {
AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(StudyIocConfig.class);
String[] beanDefinitionNames = annotationConfigApplicationContext.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
System.out.println(beanDefinitionName);
}
}
}
--輸出結果 UserModel、UserDao、httpPlugin 這幾個bean都已經被加載到容器當中。
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
studyIocConfig
cn.pencilso.study.studyioc.model.UserModel
cn.pencilso.study.studyioc.dao.UserDao
httpPlugin
自定義FactoryBean
public class StudyIocConfig {
@Bean
public UserFacotryBean userFacotryBean() {
return new UserFacotryBean();
}
}
/**
* 自定義Bean工廠
* 應用場景可以在初始化這個bean的時候需要初始化其他的組件或者依賴之類。
* 而且用了工廠后,默認則不是餓漢單例了,需要用到的時候才會創建。
*/
public class UserFacotryBean implements FactoryBean<UserModel> {
@Override
public UserModel getObject() throws Exception {
return new UserModel();
}
@Override
public Class<UserModel> getObjectType() {
return UserModel.class;
}
/**
* 是否為單例
*
* @return
*/
@Override
public boolean isSingleton() {
return true;
}
}
public class StudyIocApplication {
public static void main(String[] args) {
AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(StudyIocConfig.class);
Object userBean = annotationConfigApplicationContext.getBean("userFacotryBean");
//userModel
System.out.println("userBean:"+userBean);
//獲取工廠本身的bean
Object userFacotryBean = annotationConfigApplicationContext.getBean("&userFacotryBean");
System.out.println("userFacotryBean:"+userFacotryBean);
}
}
--最后輸出結果
user model Initialization
userBean:cn.pencilso.study.studyioc.model.UserModel@25359ed8
userFacotryBean:cn.pencilso.study.studyioc.facotry.UserFacotryBean@21a947fe
Bean 的生命周期
在bean的注解中,有兩個屬性,一個是initMethod,還有一個是destroyMethod。
如果指定了的話,那么在bean初始化的時候會執行initMethod,bean銷毀時會執行destroyMethod。
應用場景在于用作一些數據初始化和數據的釋放。但是在bean為多例的情況下,ioc則不會管理bean的銷毀方法。
注解方式管理生命周期
@Service
public class UserService {
public UserService(){
System.out.println("user service constructor");
}
@PostConstruct
public void init(){
System.out.println("user service init");
}
@PreDestroy
public void destroy(){
System.out.println("user service destroy");
}
}
public class StudyIocConfig {
@Bean
public UserService userService() {
return new UserService();
}
}
public class StudyIocApplication {
public static void main(String[] args) {
AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(StudyIocConfig.class);
UserService user1 = annotationConfigApplicationContext.getBean(UserService.class);
UserService user2 = annotationConfigApplicationContext.getBean(UserService.class);
annotationConfigApplicationContext.close();
}
}
--輸出結果如下,先執行構造方法,其次是PostConstruct注解所標記的初始化方法,最后銷毀的時候執行destroy 方法。
user service constructor
user service init
user service destroy
嘗試把bean修改為多例,看是否還能再執行destroy 方法。
public class StudyIocConfig {
@Scope("prototype")
@Bean
public UserService userService() {
return new UserService();
}
}
再次運行代碼后結果輸出如下。執行了兩遍構造器和初始化方法,因為是多例模式,而我調用了兩次,所以產生了兩個對象。并且并沒有調用PreDestroy所標記的銷毀方法。
user service constructor
user service init
user service constructor
user service init
通過bean注解管理生命周期
public class StudyIocConfig {
/**
* 指定初始化方法為init,指定銷毀方法為destroy
* @return
*/
@Bean(initMethod = "init", destroyMethod = "destroy")
public UserService userService() {
return new UserService();
}
}
@Service
public class UserService {
public UserService(){
System.out.println("user service constructor");
}
public void init(){
System.out.println("user service init");
}
public void destroy(){
System.out.println("user service destroy");
}
}
--最后執行結果如下,先執行的構造器,其次是初始化方法,銷毀則執行destroy。效果與注解方式一致。
user service constructor
user service init
user service destroy
Bean的后置處理器 BeanPostProcessor
public class CustomBeanPostProcessor implements BeanPostProcessor {
/**
* Bean 的初始化方法之前
*
* @param bean
* @param beanName
* @return
* @throws BeansException
*/
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println(beanName + " postProcessBeforeInitialization");
return bean;
}
/**
* Bean 的初始化方法之后
*
* @param bean
* @param beanName
* @return
* @throws BeansException
*/
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println(beanName + " postProcessAfterInitialization");
return bean;
}
}
public class StudyIocConfig {
/**
* 指定初始化方法為init,指定銷毀方法為destroy
* @return
*/
@Bean(initMethod = "init", destroyMethod = "destroy")
public UserService userService() {
return new UserService();
}
@Bean
public CustomBeanPostProcessor beanPostProcessor(){
return new CustomBeanPostProcessor();
}
}
public static void main(String[] args) {
AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(StudyIocConfig.class);
UserService userService = annotationConfigApplicationContext.getBean(UserService.class);
annotationConfigApplicationContext.close();
}
--輸出結果如下,先執行構造方法,其次是后置處理器的postProcessBeforeInitialization方法,再然后是bean的初始化方法,初始化方法執行后是,后置處理器的postProcessAfterInitialization方法,銷毀bean時執行destroy方法。
--應用場景可以在于攔截bean修改某些成員變量,不過我認為這種場景業務中還是很少遇到。
user service constructor
userService postProcessBeforeInitialization
user service init
userService postProcessAfterInitialization
user service destroy
貼一個初始化bean的源碼截圖,該代碼塊在AbstractAutowireCapableBeanFactory這個類中。
Bean工廠后置處理器 BeanFactoryPostProcessor
Bean工廠后置處理器,在bean解析,但是未初始化之前調用。代碼示例如下
/**
* 自定義Bean工廠后置處理器
*/
public class CustomBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
//獲取userService的bean定義
BeanDefinition beanDefinition = beanFactory.getBeanDefinition("userService");
//設置為多例模式
beanDefinition.setScope("prototype");
}
}
@Configuration
public class StudyIocConfig {
@Bean
public UserService userService() {
return new UserService();
}
@Bean
public CustomBeanFactoryPostProcessor customBeanFactoryPostProcessor(){
return new CustomBeanFactoryPostProcessor();
}
}
public class StudyIocApplication {
public static void main(String[] args) {
AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(StudyIocConfig.class);
UserService userService1 = annotationConfigApplicationContext.getBean(UserService.class);
UserService userService2 = annotationConfigApplicationContext.getBean(UserService.class);
System.out.println(userService1==userService2);
}
}
--輸出結果如下,表明該bean已經變為多例了。即使在@Bean注解聲明為單例后,也可以通過后置處理器修改為多例,或者懶加載等等。
false
InitializingBean接口使用
由bean實現的接口,這些bean需要在{@link BeanFactory}設置完所有屬性*后作出反應:例如,執行自定義初始化,*或僅僅檢查是否設置了所有必需屬性。(翻譯自源碼注釋)
也就是說 該接口是用來校驗屬性或者執行某些自定義初始化的,直接上代碼。
/**
* 這里定義了一個name屬性,并且在afterPropertiesSet方法中進行校驗,如果name為空的話,則拋出異常。
*/
@Data
public class TestInitializingBean implements InitializingBean {
private String name;
@Override
public void afterPropertiesSet() throws Exception {
Assert.isTrue(!StringUtils.isEmpty(name), "name can not be null");
}
}
public class StudyIocConfig {
@Bean
public TestInitializingBean initializingBean() {
return new TestInitializingBean();
}
}
public class StudyIocApplication {
public static void main(String[] args) {
AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(StudyIocConfig.class);
}
}
--執行出錯,輸出如下。
Caused by: java.lang.IllegalArgumentException: name can not be null
at org.springframework.util.Assert.isTrue(Assert.java:118)
at cn.pencilso.study.studyioc.bean.TestInitializingBean.afterPropertiesSet(TestInitializingBean.java:16)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1855)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1792)
... 11 more
對name屬性進行賦值后則正常裝載bean
@Data
public class TestInitializingBean implements InitializingBean {
@Value("大白")
private String name;
@Override
public void afterPropertiesSet() throws Exception {
Assert.isTrue(!StringUtils.isEmpty(name), "name can not be null");
}
}
PropertySource 加載配置文件
user.properties 文件
user.nikename= 大白
Java 代碼
@PropertySource(value = {"classpath:user.properties"},encoding = "UTF-8")
@Configuration
public class StudyIocConfig {
@Bean
public UserModel userModel(){
return new UserModel();
}
}
@Data
public class UserModel {
@Value("#{30-10}")
private int age;
@Value("${user.nikename}")
public String name;
}
@SpringBootApplication
public class StudyIocApplication {
public static void main(String[] args) {
AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(StudyIocConfig.class);
UserModel bean = annotationConfigApplicationContext.getBean(UserModel.class);
System.out.println("bean:"+bean);
}
}
--最后輸出結果
bean:UserModel(age=20, name=大白)
但是其實PropertySource 默認是不支持加載yaml文件的,那么如果要加載yaml文件的話,需要多一些處理。
yaml文件
user:
nikename: 大白
Java 代碼實現
/**
* 自定義yaml Factory
*/
public class YamlPropertySourceFactory extends DefaultPropertySourceFactory {
@Override
public PropertySource<?> createPropertySource(String name, EncodedResource resource) throws IOException {
List<PropertySource<?>> sources = new YamlPropertySourceLoader().load(resource.getResource().getFilename(), resource.getResource());
return sources.get(0);
}
}
@PropertySource(value = {"classpath:user.yaml"},encoding = "UTF-8",factory = YamlPropertySourceFactory.class)
@Configuration
public class StudyIocConfig {
@Bean
public UserModel userModel(){
return new UserModel();
}
}
--最后結果輸出如下
bean:UserModel(age=20, name=大白)
@Autowired
@Data
public class UserModel {
public String name;
}
@Configuration
public class StudyIocConfig {
@Bean
public UserModel userModel(){
UserModel userModel = new UserModel();
userModel.setName("小白");
return userModel;
}
@Bean
public UserService userService(){
return new UserService();
}
}
public class UserService {
@Autowired
private UserModel user;
public void printlnUser() {
System.out.println("userModel:" + user);
}
}
public class StudyIocApplication {
public static void main(String[] args) {
AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(StudyIocConfig.class);
UserService userService = annotationConfigApplicationContext.getBean(UserService.class);
userService.printlnUser();
}
}
--同一種類型,單個bean的正常獲取,輸出結果如下。
userModel:UserModel(name=小白)
嘗試同一種類型的多個bean對象,這里在config類中聲明了兩個bean對象。
如下,name屬性對應的數據不同,userModel對應的name是小白,userModel2對應的name是大白。
@Configuration
public class StudyIocConfig {
/**
* bean 名字為userModel
* @return
*/
@Bean
public UserModel userModel(){
UserModel userModel = new UserModel();
userModel.setName("小白");
return userModel;
}
/**
* bean 名字為userModel2
* @return
*/
@Bean
public UserModel userModel2(){
UserModel userModel = new UserModel();
userModel.setName("大白");
return userModel;
}
@Bean
public UserService userService(){
return new UserService();
}
}
這時候再運行的話會報錯,Spring會告訴你,你要加載的bean對象有兩個。而Spring不知道應該幫你加載哪個。
這是因為Autowired這個注解默認是按照類的class來進行匹配的,而它匹配到兩個,并且你沒有指定加載某一個bean名字,則會異常。
那么解決方案也比較簡單,有兩種,一種是直接通過變量名與bean名字一致的情況下,如下。
public class UserService {
/**
* 對應bean名字 userModel
*/
@Autowired
private UserModel userModel;
/**
* 對應bean名字 userModel2
*/
@Autowired
private UserModel userModel2;
public void printlnUser() {
System.out.println("userModel:" + userModel);
System.out.println("userModel2:" + userModel2);
}
}
輸出結果如下,符合預期。
userModel:UserModel(name=小白)
userModel2:UserModel(name=大白)
那么再講第二種方案,第二種方案的話需要配合Qualifier 注解,如下。
public class UserService {
/**
* 對應bean名字 userModel
*/
@Qualifier("userModel")
@Autowired
private UserModel userModel;
/**
* 對應bean名字 userModel
*/
@Qualifier("userModel")
@Autowired
private UserModel userModel2;
public void printlnUser() {
System.out.println("userModel:" + userModel);
System.out.println("userModel2:" + userModel2);
}
}
最后輸出結果如下,可以看到兩次輸出的name都是小白,這是因為已經固定了按照bean的名字也就是userModel來獲取bean對象。
userModel:UserModel(name=小白)
userModel2:UserModel(name=小白)
另外如果需要允許null值的話,在使用注解的時候將required設置為false 例如: @Autowired(required=false)
@Resource
Resource 這個注解是jdk所提供的,那么這個注解所帶來裝載順序。
1、優先匹配bean名字,如果沒有指定名字的話,會獲取變量名作為bean名字去匹配 ;
2、倘若bean名字匹配不到,則根據類型也就是type進行唯一性匹配 ;
3、如果同時指定了type,和bean名字,則匹配這兩個條件的bean;
示范如下
public class XiaoMingModel extends UserModel{
}
public class UserService {
/**
* 按照固定的bean名字獲取對象
*/
@Resource(name = "userModel")
private UserModel userModel;
/**
* 根據指定的class類型 進行匹配裝載
*/
@Resource(type = XiaoMingModel.class)
private UserModel xiaoMingModel;
public void printlnUser() {
System.out.println("userModel:" + userModel);
System.out.println("userModel2:" + xiaoMingModel);
}
}
@Configuration
public class StudyIocConfig {
/**
* bean 名字為userModel
*
* @return
*/
@Bean
public UserModel userModel() {
UserModel userModel = new UserModel();
userModel.setName("小白");
return userModel;
}
/**
* bean 名字為userModel2
*
* @return
*/
@Bean
public UserModel userModel2() {
UserModel userModel = new UserModel();
userModel.setName("大白");
return userModel;
}
@Bean
public XiaoMingModel xiaoming() {
XiaoMingModel xiaoMingModel = new XiaoMingModel();
xiaoMingModel.setName("小明同學");
return xiaoMingModel;
}
@Bean
public UserService userService() {
return new UserService();
}
}
最后輸出結果如下,變量userModel指定加載的bean是“userModel” ,所以這里打印小白是符合預期的。
其次,變量userModel2指定加載的bean類型是XiaoMingModel.class,所以這里打印小明同學也是符合預期的。
userModel:UserModel(name=小白)
userModel2:UserModel(name=小明同學)
@Profile
根據環境來決定是否加載ben對象,常見于開發環境、測試環境、生產環境,切換bean實現。
在配置文件中聲明當前的環境
spring:
profiles:
active: test
其次在代碼中配置bean,并且配置Profile注解環境。
@PropertySource(value = {"classpath:application.yaml"}, encoding = "UTF-8", factory = YamlPropertySourceFactory.class)
@Configuration
public class StudyIocConfig {
/**
* 環境為prod時,則裝載
* @return
*/
@Profile("prod")
@Bean
public UserModel userModelProd() {
UserModel userModel = new UserModel();
userModel.setName("大白");
return userModel;
}
/**
* 環境為dev 或者 test時 則裝載
* @return
*/
@Profile(value = {"dev", "test"})
@Bean
public UserModel userModelDev() {
UserModel userModel = new UserModel();
userModel.setName("小白");
return userModel;
}
@Bean
public UserService userService() {
return new UserService();
}
}
public class UserService {
@Autowired
private UserModel userModel;
public void printlnUser() {
System.out.println("userModel:" + userModel);
}
}
--最后運行結果輸出如下,在配置文件里設置環境為 active:test ,所以它最后裝載的bean是userModelDev 。
--輸出name==小白,是符合預期的。
userModel:UserModel(name=小白)
EmbeddedValueResolverAware
可以通過實現該接口,獲取配置文件值。
yaml配置文件如下
mysql:
jdbcUrl: jdbc:mysql://127.0.0.1:3306/xxxxxx
Java代碼如下
public class UserService implements EmbeddedValueResolverAware {
private String jdbcUrl;
/**
* 將StringValueResolver設置為用于解析嵌入的定義值
*
* @param resolver
*/
@Override
public void setEmbeddedValueResolver(StringValueResolver resolver) {
jdbcUrl = resolver.resolveStringValue("${mysql.jdbcUrl}");
}
public void printlnJdbc() {
System.out.println("jdbcUrl:" + jdbcUrl);
}
}
@PropertySource(value = {"classpath:application.yaml"}, encoding = "UTF-8", factory = YamlPropertySourceFactory.class)
@Configuration
public class StudyIocConfig {
@Bean
public UserService userService() {
return new UserService();
}
}
public class StudyIocApplication {
public static void main(String[] args) {
AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(StudyIocConfig.class);
UserService userService = annotationConfigApplicationContext.getBean(UserService.class);
userService.printlnJdbc();
}
}
最后結果輸出如下,結果是符合預期的。
jdbcUrl:jdbc:mysql://127.0.0.1:3306/xxxxxx