今天和一個朋友聊天,他告訴我說:“今天面試,被問到了一個問題,如何將我們創建出來的物件交給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關於這個類可能使用過的同學比較少,平時我們其實是很常見這個類的,大家可能很多時候都沒有注意過。