首頁>技術>

今天和一個朋友聊天,他告訴我說:“今天面試,被問到了一個問題,如何將我們創建出來的物件交給spring來管理”。剛開始聽到這個問題,我其實內心是懵逼的。

懵到不知所措

因為通常情況下,我們都是將物件的建立交給spring容器來做的,在需要使用物件的地方,透過DI的方式來獲取使用。

先不說問題的答案。我們下來思考下面試官問這個題的心態:考察候選人對spring掌握的牢靠程度。所以面試前一定要刷題。

後面我也仔細想了下,什麼場景下我們選用?大概總結了一下兩點:

配置物件為介面建立代理類

針對上面這個問題,我想到以下三種方式:

@Bean註解

@Bean註解,實際上是我們比較常用的一個註解,在@Configuration標註的class中,我們透過@Bean註解將一個方法的返回值,變為一個bean。例如:

@Configurationpublic class DemoConfiguration {    @Bean    public GoodEntity goodEntity() {        GoodEntity goodEntity = new GoodEntity();        goodEntity.setName("configuration");        return goodEntity;    }}
FactoryBean介面

可能有一些小夥伴,沒有聽說過這個介面,那我們先簡單地介紹下這個介面,先看看spring對該介面的描述:

 * Interface to be implemented by objects used within a {@link BeanFactory} which * are themselves factories for individual objects. If a bean implements this * interface, it is used as a factory for an object to expose, not directly as a * bean instance that will be exposed itself.

翻譯過來的意思大致是:實現該介面的bean是建立單個bean的工廠。如果一個bean實現了該介面,它通常被用作暴露其他bean,而不是直接暴露它本身。直白點就是實現了這個介面的bean,是一個可以建立bean的工廠。

接下來我們看看他的API

public interface FactoryBean<T> {	String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType";    // 獲取實際要返回的bean物件	@Nullable	T getObject() throws Exception; // 獲取時間返回bean物件的型別	@Nullable	Class<?> getObjectType();// 返回的bean是否是單例的	default boolean isSingleton() {		return true;	}}

下面我們看一個例子來了解下這個介面是如何使用的

@Configurationpublic class DemoConfiguration {    @Bean    public DemoFactoryBean goodEntity() {        return new DemoFactoryBean("factory bean");    }    public class DemoFactoryBean implements FactoryBean<GoodEntity> {        private final String name;        private Class<GoodEntity> clazz;        @Override        public GoodEntity getObject() throws Exception {            return new GoodEntity(name);        }        @Override        public Class<?> getObjectType() {            return clazz;        }        @Override        public boolean isSingleton() {            return true;        }        public DemoFactoryBean(String name) {            this.clazz = GoodEntity.class;            this.name = name;        }    }}@SpringBootApplicationpublic class CourseApplication {    public static void main(String[] args) {        ConfigurableApplicationContext run = SpringApplication.run(CourseApplication.class, args);        Object first = run.getBean("&goodEntity");        Object second = run.getBean("goodEntity");        if (first instanceof DemoConfiguration.DemoFactoryBean) {            DemoConfiguration.DemoFactoryBean bean = (DemoConfiguration.DemoFactoryBean)first;            System.out.println(bean.getClass());        }        if (second instanceof GoodEntity) {            GoodEntity bean = (GoodEntity)second;            System.out.println(bean.getClass());        }    }}

上面程式碼大致的邏輯是:@Bean註解向容器中注入了向容器中注入了DemoFactoryBean這個bean,DemoFactoryBean實現了FactoryBean。在springboot啟動完成後,分別從容器中獲取兩個名稱為“&goodEntity”和“goodEntity”。然後判斷他們是哪種型別的,分別輸出一句話。執行結果如下:

2021-01-19 19:17:03.723  INFO 12364 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'2021-01-19 19:17:03.822  INFO 12364 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path '/mall'2021-01-19 19:17:03.829  INFO 12364 --- [           main] c.i.s.s.course.CourseApplication         : Started CourseApplication in 1.223 seconds (JVM running for 2.008)class com.it.sun.springboot.course.config.DemoConfiguration$DemoFactoryBeanclass com.it.sun.springboot.course.entity.GoodEntity

透過上面的例子,我們可以看出FactoryBean確實可以將我們建立的物件注入spring,同時其本身也是一個bean,透過名稱獲取的話,需要在名稱前加上字首&

Mybatis在實際的開發中,大家都使用過,實際上mybatis與spring的結合就是透過FactoryBean來實現的,將mapper的代理物件注入spring。感興趣的同學可以參考該類org.mybatis.spring.mapper.MapperFactoryBean。

妙啊

ConfigurableApplicationContext

關於這個類可能使用過的同學比較少,平時我們其實是很常見這個類的,大家可能很多時候都沒有注意過。

12
  • BSA-TRITC(10mg/ml) TRITC-BSA 牛血清白蛋白改性標記羅丹明
  • RabbitMQ一個優秀的.NET訊息佇列框架