目錄
ApplicationContextAware 獲取 bean
BeanFactory ( IOC 容器)概述
1、org.springframework.beans.factory.BeanFactory 是一個 Factory(工廠),也就是 IOC 容器/對象工廠,Spring 中所有的 bean 都是由 BeanFactory(也就是IOC容器)來進行管理的。
2、經常會使用 @Autowired、@Resource 註解來從 Spring 容器中獲取實例,而這些實例恰恰都是 BeanFactory 管理。無論是以前用 xml 配置文件來配置 <bean>、還是後來使用 @Bean 註解,都是爲了將對象交由 Spring 容器管理。
3、BeanFactory 作爲 IOC 功能的頂級接口,提供了各種不同的實現,IOC 作爲 Spring 的核心功能之一,所以這些 API 也是很常見的。
//這兩行代碼一定不陌生
BeanFactory bf = new ClassPathXmlApplicationContext("student.xml");
Student studentBean = (Student) bf.getBean("studentBean");//BeanFactory 在 spring-beans-x.x.x.RELEASE.jar 包下
4、ApplicationContext
是 BeanFactory
的擴展,功能得到了進一步增強,比如更易與 Spring AOP 集成、消息資源處理(國際化處理)、事件傳遞及各種不同應用層的 context 實現(如針對 web 應用的 WebApplicationContext
)。
Object getBean(String name) |
返回指定名稱的 bean 實例,默認爲單例。 如果沒有具有指定名稱的bean,則引發 NoSuchBeanDefinitionException 如果無法獲取bean,則拋出 BeansException |
<T> T getBean(String name, Class<T> requiredType) |
在 get(String name) 的基礎上加上了requiredType類型限制,可以是接口或超類。 如果bean不是所需類型,則引發BeannotofRequiredTypeException |
<T> T getBean(Class<T> requiredType) |
返回唯一匹配給定對象類型的bean實例(如果有). bean 必須匹配參數RequiredType類型,可以是接口或超類 如果找到給定類型的多個bean,則引發nouniqueBeanDefinitionException |
boolean containsBean(String name) | 返回是否存在具有給定名稱的bean. |
boolean isSingleton(String name) | 返回此bean是否是單例。如果沒有具有給定名稱的bean,則引發nosuchbeanDefinitionException |
Class<?> getType(String name) |
返回bean的類型,如果不能確定,則返回 NULL。 如果沒有具有給定名稱的bean,則引發nosuchbeanDefinitionException |
BeanFactoryAware 獲取 bean
1、對於 BeanFactory 管理的 Bean,怎麼獲取它然後使用呢?使用 @Autowired、@Resource 註解來注入是最常見的操作,也是必須掌握的技能。
2、 使用 @Autowired、@Resource 註解的類自己首先必須是 Spring 組件 @Component 才能注入,而有時候想在項目中的任意位置都能獲取容器中的實例進行使用,比如想在某個 XxxUtils 中使用某個 @Service 標識的實例,這時把 XxxUtils 標識爲 @Component ,然後使用 @Resource 注入實例,這種方式看起來沒有使用代碼直接從 BeanFactory 容器中獲取實例方便。
3、顯然只要拿到了 BeanFactory 對象,就可以使用它的 getBean 方法從容器中獲取 bean 實例,而獲取 BeanFactory 實例的一個最簡單的方式就是實現 BeanFactoryAware 接口。
import org.springframework.beans.BeansException;
/**
* BeanFactoryAware 接口只要一個方法
*/
public interface BeanFactoryAware extends Aware {
/**初始化回調方法,spring 會自動將 BeanFactory 注入進行,我們接收之後即可使用 BeanFactory*/
void setBeanFactory(BeanFactory beanFactory) throws BeansException;
}
//BeanFactoryAware 在 spring-beans-x.x.x.RELEASE.jar 包下
編碼演示
1、下面使用 Jdk 8 + Spring Boot 2.1.6(Spring 5.1.8 )進行演示,代碼結構如下,詳細說明在代碼註釋中。
瀏覽器訪問 PersonController 中的方法,PersonController 中不再使用 @Autowired、@Resource 註解注入 PersonService,而是使用 BeanFactoryHelper 獲取 PersonService 實例。
2、爲了結構完整,定義一個 service 接口和它的實現類:
public interface PersonService {
void deleteAll();
}
import org.springframework.stereotype.Service;
import wmx.com.springbootredisstudy.services.PersonService;
import java.util.logging.Logger;
//@Service 註解沒有指定名稱時,默認爲實現類 類名首字母小寫的 personServiceImpl
@Service
public class PersonServiceImpl implements PersonService {
private Logger logger = Logger.getAnonymousLogger();
@Override
public void deleteAll() {
logger.info("刪除所有用戶成功");
}
}
3、自定義一個工廠類/工具類/助手類 實現 BeanFactoryAware 接口,有了它,則可以在項目中的任意位置傳入 bean 名稱或者類型來輕鬆獲取容器中的實例:
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.stereotype.Component;
/**
* 1、實現 BeanFactoryAware 接口,重寫 setBeanFactory(BeanFactory beanFactory)
* 2、本類必須是 Spring 組件,所以加上 @Component 註解
* 3、spring 容器啓動實例化本類的時候,會自動執行回調方法 setBeanFactory(BeanFactory beanFactory) 將 BeanFactory 注入
* 4、獲取了 BeanFactory 之後,則可以使用它的任意方法了,比如各種 getBean
*/
@Component
public class BeanFactoryHelper implements BeanFactoryAware {
private static BeanFactory beanFactory;
/**
* 重寫 BeanFactoryAware 接口的方法
* @param beanFactory :參數賦值給本地屬性之後即可使用 BeanFactory
* @throws BeansException
*/
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
/**
* 根據名稱獲取容器中的對象實例
* @param beanName :注入的實例必須已經存在容器中,否則拋異常:NoSuchBeanDefinitionException
* @return
*/
public static Object getBean(String beanName) {
return beanFactory.getBean(beanName);
}
/**
* 根據 class 獲取容器中的對象實例
* @param requiredType :被注入的必須已經存在容器中,否則拋異常:NoSuchBeanDefinitionException
* @param <T>
* @return
*/
public static <T> T getBean(Class<T> requiredType) {
return beanFactory.getBean(requiredType);
}
/**
* 判斷 spring 容器中是否包含指定名稱的對象
* @param beanName
* @return
*/
public static boolean containsBean(String beanName) {
return beanFactory.containsBean(beanName);
}
//其它需求皆可參考 BeanFactory 接口和它的實現類
}
4、控制層代碼如下:
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import wmx.com.springbootredisstudy.factory.BeanFactoryHelper;
import wmx.com.springbootredisstudy.services.PersonService;
import java.util.logging.Logger;
@RestController
public class PersonController {
private Logger logger = Logger.getAnonymousLogger();
/**
* 刪除所有用戶:http://localhost:8080/person/deleteAll
*/
@GetMapping("person/deleteAll")
public String deleteAll() {
logger.info("用戶準備刪除所有用戶...");
//PersonService personService = BeanFactoryHelper.getBean(PersonService.class);
//上面是根據 bean 的類型獲取實例,下面是根據 bean 的名稱獲取實例
PersonService personService = (PersonService) BeanFactoryHelper.getBean("personServiceImpl");
personService.deleteAll();
return "delete success";
}
}
ApplicationContextAware 獲取 bean
1、上面已經說過 ApplicationContext
接口是 BeanFactory
接口的擴展,功能得到了進一步增強,能使用的方法也更多。
2、BeanFactory
對應 BeanFactoryAware,ApplicationContext 對應 ApplicationContext
Aware ,使用起來完全同理。
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
/**
* 1、spring 容器啓動的時候會自動創建 ApplicationContextHelper 實例
* 2、然後調用 setApplicationContext 回調方法自動注入 ApplicationContext
* 3、有了 ApplicationContext 就可以 getBean 了
*/
@Component
public class ApplicationContextHelper implements ApplicationContextAware {
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
/**
* 根據名稱獲取容器中的對象實例
* @param beanName :注入的實例必須已經存在容器中,否則拋異常:NoSuchBeanDefinitionException
* @return
*/
public static Object getBean(String beanName) {
return applicationContext.getBean(beanName);
}
/**
* 根據 class 獲取容器中的對象實例
* @param requiredType :被注入的必須已經存在容器中,否則拋異常:NoSuchBeanDefinitionException
* @param <T>
* @return
*/
public static <T> T getBean(Class<T> requiredType) {
return applicationContext.getBean(requiredType);
}
/**
* 判斷 spring 容器中是否包含指定名稱的對象
* @param beanName
* @return
*/
public static boolean containsBean(String beanName) {
return applicationContext.containsBean(beanName);
}
}