首頁 > 軟體

Spring通過工具類實現獲取容器中的Bean

2022-06-23 18:01:59

1. Aware 介面

小夥伴們知道,Spring 容器最大的特點在於所有的 Bean 對於 Spring 容器的存在是沒有意識的,因此我們常說理論上你可以無縫將 Spring 容器切換為其他容器(然而在現實世界中,我們其實沒有這樣的選擇,除了 Spring 容器,難道還有更好用的?)。

當然這只是一個理論,在實際開發中,我們往往要用到 Spring 容器為我們提供的諸多資源,例如想要獲取到容器中的設定、獲取到容器中的 Bean 等等。在這種情況下,就需要 Spring 容器中的 Bean 真正的意識到 Spring 容器的存在,才能要到這些東西,那麼如何讓一個 Bean 意識到 Spring 容器的存在呢?

這就依賴於 Spring 容器給我們提供的各種 Aware 介面了。

/**
 * A marker superinterface indicating that a bean is eligible to be notified by the
 * Spring container of a particular framework object through a callback-style method.
 * The actual method signature is determined by individual subinterfaces but should
 * typically consist of just one void-returning method that accepts a single argument.
 *
 * <p>Note that merely implementing {@link Aware} provides no default functionality.
 * Rather, processing must be done explicitly, for example in a
 * {@link org.springframework.beans.factory.config.BeanPostProcessor}.
 * Refer to {@link org.springframework.context.support.ApplicationContextAwareProcessor}
 * for an example of processing specific {@code *Aware} interface callbacks.
 *
 * @author Chris Beams
 * @author Juergen Hoeller
 * @since 3.1
 */
public interface Aware {

}

從這個介面的註釋中,我們也能大概看出來,這個介面的子類,主要是提供了一些只有一個引數的 set 方法,通過這些方法可以讓 Spring 容器感知到某一件事情。

Aware 的實現有很多,大的方向來說主要有如下一些:

每一個 Aware 的作用如下:

  • ApplicationEventPublisherAware:實現該介面的物件可以獲取事件釋出的能力。
  • ServletContextAware:實現該介面的物件可以獲取到 ServletContext 物件。
  • MessageSourceAware:實現該介面的物件可以獲取到 MessageSource 物件,MessageSource 支援多訊息源,主要用於主要用於國際化。
  • ResourceLoaderAware:實現該介面的物件可以獲取到一個 ResourceLoader,Spring ResourceLoader 則為我們提供了一個統一的 getResource() 方法來通過資源路徑檢索外部資源,例如文字檔案、XML 檔案、屬性檔案或影象檔案等。
  • ApplicationStartupAware:實現該介面的物件可以獲取到一個 ApplicationStartup 物件,這個比較新,是 Spring 5.3 中新推出的,通過 ApplicationStartup 可以標記應用程式啟動期間的步驟,並收集有關執行上下文或其處理時間的資料。
  • NotificationPublisherAware:實現該接的物件可以獲取到一個 NotificationPublisher 物件,通過該物件可以實現通知的傳送。
  • EnvironmentAware:實現該介面的物件可以獲取到一個 Environment 物件,通過 Environment 可以獲取到容器的環境資訊。
  • BeanFactoryAware:實現該介面的物件可以獲取到一個 BeanFactory 物件,通過 BeanFactory 可以完成 Bean 的查詢等操作。
  • ImportAware:實現該介面的物件可以獲取到一個 AnnotationMetadata 物件,ImportAware 介面是需要和 @Import 註解一起使用的。在 @Import 作為元註解使用時,通過 @Import 匯入的設定類如果實現了 ImportAware 介面就可以獲取到匯入該設定類介面的資料設定。
  • EmbeddedValueResolverAware:實現該介面的物件可以獲取到一個 StringValueResolver 物件,通過 StringValueResolver 物件,可以讀取到 Spring 容器中的 properties 設定的值(YAML 設定也可以)。
  • ServletConfigAware:實現該介面的物件可以獲取到一個 ServletConfig 物件,不過這個似乎沒什麼用,我們很少自己去設定 ServletConfig。
  • LoadTimeWeaverAware:實現該介面的物件可以獲取到一個 LoadTimeWeaver 物件,通過該物件可以獲取載入 Spring Bean 時織入的第三方模組,如 AspectJ 等。
  • BeanClassLoaderAware:實現該介面的物件可以獲取到一個 ClassLoader 物件,ClassLoader 能幹嘛不需要我多說了吧。
  • BeanNameAware:實現該介面的物件可以獲取到一個當前 Bean 的名稱。
  • ApplicationContextAware:實現該介面的物件可以獲取到一個 ApplicationContext 物件,通過 ApplicationContext 可以獲取容器中的 Bean、環境等資訊。

這是 Spring 中提供的一堆 Aware。

接下來鬆哥隨便寫個例子大家來看下 Aware 的用法。

2. BeanFactoryAware

實現該介面的物件可以獲取到一個 BeanFactory 物件,通過 BeanFactory 可以完成 Bean 的查詢等操作。這算是一個比較常見的 Aware 了,我們一起來看下。

這裡為了省事,我就在 Spring Boot 中來和大家演示。

首先我們來定義一個簡單的 UserService:

@Service
public class UserService {
    public void hello() {
        System.out.println("hello javaboy!");
    }
}

然後提供一個工具類:

@Component
public class BeanUtils implements BeanFactoryAware {
    private static BeanFactory beanFactory = null;

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        BeanUtils.beanFactory = beanFactory;
    }

    public static <T> T getBean(String beanName) {
        return (T) beanFactory.getBean(beanName);
    }
}

有了這個工具類,接下來我們就可以在一個非 Spring 管理的 Bean 中,隨時隨地的查詢 Bean 了,像下面這樣:

UserService userService = BeanUtils.getBean("userService");
userService.hello();

3. TienChin 專案實踐

為什麼會有今天這篇文章呢?主要是在鬆哥最近做的 TienChin 專案中,有一個地方涉及到這塊知識點了,但是有的小夥伴不熟悉,因此就拎出來和大家梳理下。

在 TienChin 專案中,在記錄紀錄檔的時候,因為紀錄檔是一個延遲任務,所以提前準備好了相關的 Bean 已經註冊到 Spring 容器中了,像下面這樣:

@Configuration
public class ThreadPoolConfig {

    /**
     * 執行週期性或定時任務
     */
    @Bean(name = "scheduledExecutorService")
    protected ScheduledExecutorService scheduledExecutorService() {
        return new ScheduledThreadPoolExecutor(corePoolSize,
                new BasicThreadFactory.Builder().namingPattern("schedule-pool-%d").daemon(true).build(),
                new ThreadPoolExecutor.CallerRunsPolicy()) {
            @Override
            protected void afterExecute(Runnable r, Throwable t) {
                super.afterExecute(r, t);
                Threads.printException(r, t);
            }
        };
    }
}
@Component
public final class SpringUtils implements BeanFactoryPostProcessor, ApplicationContextAware {
    /**
     * Spring應用上下文環境
     */
    private static ConfigurableListableBeanFactory beanFactory;

    private static ApplicationContext applicationContext;

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        SpringUtils.beanFactory = beanFactory;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        SpringUtils.applicationContext = applicationContext;
    }

    /**
     * 獲取物件
     *
     * @param name
     * @return Object 一個以所給名字註冊的bean的範例
     * @throws org.springframework.beans.BeansException
     */
    @SuppressWarnings("unchecked")
    public static <T> T getBean(String name) throws BeansException {
        return (T) beanFactory.getBean(name);
    }

    /**
     * 獲取型別為requiredType的物件
     *
     * @param clz
     * @return
     * @throws org.springframework.beans.BeansException
     */
    public static <T> T getBean(Class<T> clz) throws BeansException {
        T result = (T) beanFactory.getBean(clz);
        return result;
    }
}

而寫紀錄檔的非同步任務工具類,並非一個容器,所以要通過這個工具類獲取相應的 Bean,如下:

public class AsyncManager {
    /**
     * 操作延遲10毫秒
     */
    private final int OPERATE_DELAY_TIME = 10;

    /**
     * 非同步操作任務排程執行緒池
     */
    private ScheduledExecutorService executor = SpringUtils.getBean("scheduledExecutorService");

    /**
     * 單例模式
     */
    private AsyncManager() {
    }

    private static AsyncManager me = new AsyncManager();

    public static AsyncManager me() {
        return me;
    }

    /**
     * 執行任務
     *
     * @param task 任務
     */
    public void execute(TimerTask task) {
        executor.schedule(task, OPERATE_DELAY_TIME, TimeUnit.MILLISECONDS);
    }
}

有了 SpringUtils 我們就可以在一個非 Spring 容器所管理的 Bean 中,獲取到 Spring 容器中的 Bean 了。

到此這篇關於Spring通過工具類實現獲取容器中的Bean的文章就介紹到這了,更多相關Spring獲取Bean內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com