Spring BeanFactoryAware、ApplicationContextAware 獲取 IOC容器(BeanFactory) 中指定的 Bean

目錄

BeanFactory ( IOC 容器)概述

BeanFactoryAware 獲取 bean

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、ApplicationContextBeanFactory 的擴展,功能得到了進一步增強,比如更易與 Spring AOP 集成、消息資源處理(國際化處理)、事件傳遞及各種不同應用層的 context 實現(如針對 web 應用的 WebApplicationContext )。

BeanFactory 接口常用方法
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 對應 ApplicationContextAware ,使用起來完全同理。

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);
    }
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章