IoC容器的初始化包括BeanDefinition的Resource定位、載入和註冊這三個基本的過程。java
1、Resource定位。BeanDefinition的資源定位有resourceLoader經過統一的Resource接口來完成,這個Resource對各類形式的BeanDefinition的使用提供了統一接口。對於這些BeanDefinition的存在形式,相信不陌生,如:spring
FileSystemResource、ClassPathResource。這個過程相似於容器尋找數據的過程,就像用水桶裝水要把水找到同樣。apache
2、第二個關鍵的部分是BeanDefinition的載入,該載入過程把用戶定義好的Bean表示成IoC容器內部的數據結構,而這個容器內部的數據結構就是BeanDefinition。簡單說,BeanDefinition其實是POJO對象在IoC容器中的抽象,這個BeanDefinition定義了一系列的數據來使得IoC容器可以方便地對POJO對象也就是Spring的Bean進行管理。即BeanDefinition就是Spring的領域對象。數據結構
3、第三個過程是向IoC容器註冊這些BeanDefinition的過程。這個過程是經過調用BeanDefinitionRegistry接口的實現來完成的,這個註冊過程把載入過程當中解析獲得的BeanDefinition向IoC容器進行註冊。能夠看到,在IoC容器內部,是經過使用一個HashMap來持有BeanDefinition數據的。app
總結:框架
此處對於BeanPostProcessor接口的調用應該屬於高級應用了,該思路經常使用來解決擴展或集成Spring框架,其核心的思路能夠分爲如下幾步:ide
一、自定義實現類路徑掃描類,決定哪些類應該被注入進Spring容器。post
二、採用Java動態代理來動態實現對於聲明接口類的注入。測試
三、實現BeanDefinitionRegistryPostProcessor,在Spring初始化初期將須要掃描導入Spring容器的類進行注入。ui
四、經過代碼動態建立
咱們經過getBean來得到對象,但這些對象都是事先定義好的,咱們有時候要在程序中動態的加入對象.由於若是採用配置文件或者註解,咱們要加入對象的話,還要重啓服務,若是咱們想要避免這一狀況就得采用動態處理bean,包括:動態注入,動態刪除。
接下來咱們看下具體的內容:
(1)動態注入bean思路;
在具體進行代碼實現的時候,咱們要知道,Spring管理bean的對象是BeanFactory,具體的是DefaultListableBeanFactory,在這個類當中有一個注入bean的方法:registerBeanDefinition,在調用registerBeanDefinition方法時,須要BeanDefinition參數,那麼這個參數怎麼獲取呢?Spring提供了BeanDefinitionBuilder能夠構建一個BeanDefinition,那麼咱們的問題就是如何獲取BeanFactory了,這個就很簡單了,只要獲取到ApplicationContext對象便可獲取到BeanFacory了。
(2)動態注入實現代碼;
綜上所述,若是咱們要編寫一個簡單裏的例子的話,那麼分以個幾個步驟進行編碼便可進行動態注入了:
很明顯咱們須要先定義個類進行測試,好比TestService代碼以下:
package com.dxz.test; public class TestService { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } public void print() { System.out.println("動態載入bean,name=" + name); } }
注意:這裏沒有使用@Service和配置文件進行注入TestService。
那麼下面咱們的目標就是動態注入TestService了,根據以上的分析,咱們進行編碼,具體代碼以下:
package com.dxz.test; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ApplicationContext; @SpringBootApplication public class Application { public static void main(String[] args) { ApplicationContext ctx = SpringApplication.run(Application.class, args); // 獲取BeanFactory DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) ctx .getAutowireCapableBeanFactory(); // 建立bean信息. BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(TestService.class); beanDefinitionBuilder.addPropertyValue("name", "張三"); // 動態註冊bean. defaultListableBeanFactory.registerBeanDefinition("testService", beanDefinitionBuilder.getBeanDefinition()); // 獲取動態註冊的bean. TestService testService = ctx.getBean(TestService.class); testService.print(); } }
或者
@SpringBootApplication public class Application { public static void main(String[] args) { ClassPathResource res = new ClassPathResource("beans.xml"); // 獲取BeanFactory DefaultListableBeanFactory factory= new DefaultListableBeanFactory(); // 建立bean信息. XmlBeanDefinitionReader beanDefinitionBuilder = new XmlBeanDefinitionReader(factory); beanDefinitionBuilder.loadBeanDefinitions(res); //... } }
執行代碼咱們會在控制檯看到以下打印信息:
動態載入bean,name=張三
到這裏,就證實咱們的代碼很成功了。
(3)屢次注入同一個bean的狀況;
屢次注入同一個bean的,若是beanName不同的話,那麼會產生兩個Bean;若是beanName同樣的話,後面注入的會覆蓋前面的。
第一種狀況:beanName同樣的代碼:
beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(TestService.class); beanDefinitionBuilder.addPropertyValue("name","李四"); defaultListableBeanFactory.registerBeanDefinition("testService", beanDefinitionBuilder.getBeanDefinition());
運行看控制檯:
動態載入bean,name=李四
第二種狀況:beanName不同的代碼:
beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(TestService.class); beanDefinitionBuilder.addPropertyValue("name","李四"); defaultListableBeanFactory.registerBeanDefinition("testService1",beanDefinitionBuilder.getBeanDefinition());
此時若是沒有更改別的代碼直接運行的話,是會報以下錯誤的:
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [com.kfit.demo.service.TestService] is defined: expected single matching bean but found 2: testService1,testService
大致意思就是在getBean的時候,找到了兩個bean,這時候就不知道要獲取哪一個了,因此在獲取的時候,咱們就要指定咱們是要獲取的testService仍是testService1,只須要修改一句代碼:
將代碼:
TestService testService =ctx.getBean(TestService.class); 修改成: TestService testService =ctx.getBean("testService");
(4)動態刪除;
相對於動態注入,動態刪除就很簡單了,直接奉上代碼:
//刪除bean. defaultListableBeanFactory.removeBeanDefinition("testService");
"對於Spring框架,現實公司使用的很是普遍,可是因爲業務的複雜程度不一樣,瞭解到不少小夥伴們利用Spring開發僅僅是利用了Spring的IOC,即便是AOP也不多用,可是目前的Spring是一個你們族,造成了一個很大的生態,覆蓋了咱們平時開發的方方面面,拋開特殊的苛刻要求以外,Spring的生態其實已經很全面了,因此在此開個系列來研究下Spring提供給咱們的一些平時不太卻又很實用的內容。"
上一篇咱們分析了BeanPostProcessor的基本使用,接下來咱們分析下如何使用該類實現動態的接口注入,示例說明:在BeetlSQL框架中,在使用自動掃描注入時,咱們一般只須要配置上要掃描的包路徑,而後在該路徑下聲明對應的Dao接口類,這些接口類都默認繼承BaseMapper接口類,而後咱們在使用這些Dao類的時候,直接根據類型注入(@Autowired)便可使用,這個其實和Mybatis的那一套類似,也和Spring自身的Spring-data框架也相似。這個常常用於框架的開發,那麼我就該部分的實現作相應的解釋,這三個框架具體實現可能有差距,感興趣的小夥伴自行去查看源碼,我會以一個很簡單的例子來說解大概的實現邏輯。
問題描述:
繼承Spring框架,實現聲明某個自定義接口(UserMapper),改接口繼承通用接口BaseMapper,(通用接口BaseMapper有默認的實現類),實現經過類型注入UserMapper類,而後經過Spring框架的上下文類(ApplicationContext實現類)的getBean()方法拿到UserMapper類來調用內部提供的方法。
一、聲明BaseMapper接口類
package com.dxz.test; public interface BaseMapper { public void add(String value); public void remove(String key); } public class CustomBaseMapper implements BaseMapper { private final Logger logger = Logger.getLogger(this.getClass().getName()); private List<String> dataList = new CopyOnWriteArrayList<>(); @Override public void add(String value) { logger.info("添加數據:" + value); dataList.add(value); } @Override public void remove(String key) { if (dataList.isEmpty()) throw new IllegalArgumentException("Can't remove because the list is Empty!"); } }
接下來是繼承Spring的核心代碼
三、首先咱們要先定義一個掃描某路徑下的類,該類繼承ClassPathBeanDefinitionScanner,自定義掃描類:DefaultClassPathScanner
package com.dxz.test; import java.io.IOException; import java.util.Arrays; import java.util.Set; import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition; import org.springframework.beans.factory.config.BeanDefinitionHolder; import org.springframework.beans.factory.config.RuntimeBeanReference; import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.GenericBeanDefinition; import org.springframework.context.annotation.ClassPathBeanDefinitionScanner; import org.springframework.core.type.classreading.MetadataReader; import org.springframework.core.type.classreading.MetadataReaderFactory; import org.springframework.core.type.filter.TypeFilter; public class DefaultClassPathScanner extends ClassPathBeanDefinitionScanner { private final String DEFAULT_MAPPER_SUFFIX = "Mapper"; public DefaultClassPathScanner(BeanDefinitionRegistry registry) { super(registry, false); } private String mapperManagerFactoryBean; /** * 掃描包下的類-完成自定義的Bean定義類 * * @param basePackages * @return */ @Override protected Set<BeanDefinitionHolder> doScan(String... basePackages) { Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages); // 若是指定的基礎包路徑中不存在任何類對象,則提示 if (beanDefinitions.isEmpty()) { logger.warn("系統沒有在 '" + Arrays.toString(basePackages) + "' 包中找到任何Mapper,請檢查配置"); } else { processBeanDefinitions(beanDefinitions); } return beanDefinitions; } /** * 註冊過濾器-保證正確的類被掃描注入 */ protected void registerFilters() { addIncludeFilter(new TypeFilter() { @Override public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException { String className = metadataReader.getClassMetadata().getClassName(); // TODO 這裏設置包含條件-此處是個擴展點,能夠根據自定義的類後綴過濾出須要的類 return className.endsWith(DEFAULT_MAPPER_SUFFIX); } }); addExcludeFilter(new TypeFilter() { @Override public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException { String className = metadataReader.getClassMetadata().getClassName(); return className.endsWith("package-info"); } }); } /** * 重寫父類的判斷是否可以實例化的組件-該方法是在確認是否真的是isCandidateComponent 原方法解釋: * 肯定給定的bean定義是否有資格成爲候選人。 默認實現檢查類是否不是接口,也不依賴於封閉類。 以在子類中重寫。 * * @param beanDefinition * @return */ protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) { // 原方法這裏是判斷是否爲頂級類和是不是依賴類(即接口會被排除掉-因爲咱們須要將接口加進來,因此須要覆蓋該方法) return beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().isIndependent(); } /** * 擴展方法-對掃描到的含有BeetlSqlFactoryBean的Bean描述信息進行遍歷 * * @param beanDefinitions */ void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) { GenericBeanDefinition definition; for (BeanDefinitionHolder holder : beanDefinitions) { definition = (GenericBeanDefinition) holder.getBeanDefinition(); String mapperClassName = definition.getBeanClassName(); // 必須在這裏加入泛型限定,要否則在spring下會有循環引用的問題 definition.getConstructorArgumentValues().addGenericArgumentValue(mapperClassName); // 依賴注入 definition.getPropertyValues().add("mapperInterface", mapperClassName); // 根據工廠的名稱建立出默認的BaseMapper實現 definition.getPropertyValues().add("mapperManagerFactoryBean", new RuntimeBeanReference(this.mapperManagerFactoryBean)); definition.setBeanClass(BaseMapperFactoryBean.class); // 設置Mapper按照接口組裝 definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE); logger.info("已開啓自動按照類型注入 '" + holder.getBeanName() + "'."); } } public void setMapperManagerFactoryBean(String mapperManagerFactoryBean) { this.mapperManagerFactoryBean = mapperManagerFactoryBean; } }
四、核心的接口實現類:BaseMapperFactoryBean
package com.dxz.test; import java.lang.reflect.Proxy; import org.springframework.beans.BeansException; import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.InitializingBean; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.context.ApplicationEvent; import org.springframework.context.ApplicationListener; public class BaseMapperFactoryBean<T> implements FactoryBean<T>, InitializingBean, ApplicationListener<ApplicationEvent>, ApplicationContextAware { /** * 要注入的接口類定義 */ private Class<T> mapperInterface; /** * Spring上下文 */ private ApplicationContext applicationContext; // 也因該走工廠方法注入得來 private BaseMapper mapperManagerFactoryBean; public BaseMapperFactoryBean(Class<T> mapperInterface) { this.mapperInterface = mapperInterface; } @Override public T getObject() throws Exception { // 採用動態代理生成接口實現類,核心實現 return (T) Proxy.newProxyInstance(applicationContext.getClassLoader(), new Class[] { mapperInterface }, new MapperJavaProxy(mapperManagerFactoryBean, mapperInterface)); } @Override public Class<?> getObjectType() { return this.mapperInterface; } @Override public boolean isSingleton() { return true; } @Override public void afterPropertiesSet() throws Exception { // TODO 判斷屬性的注入是否正確-如mapperInterface判空 if (null == mapperInterface) throw new IllegalArgumentException("Mapper Interface Can't Be Null!!"); } /** * Handle an application event. * * @param event * the event to respond to */ @Override public void onApplicationEvent(ApplicationEvent event) { // TODO 可依據事件進行擴展 } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } public void setMapperInterface(Class<T> mapperInterface) { this.mapperInterface = mapperInterface; } public void setMapperManagerFactoryBean(BaseMapper mapperManagerFactoryBean) { this.mapperManagerFactoryBean = mapperManagerFactoryBean; } }
五、定義默認的BaseMapper的FactoryBean-MapperManagerFactoryBean
package com.dxz.test; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class MapperJavaProxy implements InvocationHandler { private BaseMapper baseMapper; private Class<?> interfaceClass; public MapperJavaProxy(BaseMapper baseMapper, Class<?> interfaceClass) { this.baseMapper = baseMapper; this.interfaceClass = interfaceClass; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (!interfaceClass.isInterface()) { throw new IllegalArgumentException("mapperInterface is not interface."); } if (baseMapper == null) { baseMapper = new CustomBaseMapper(); } return method.invoke(baseMapper, args); } }
七、調用時的核心配置類:DefaultClassRegistryBeanFactory
package com.dxz.test; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanNameAware; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.context.ConfigurableApplicationContext; public class DefaultClassRegistryBeanFactory implements ApplicationContextAware, BeanDefinitionRegistryPostProcessor, BeanNameAware { private String scanPackage; private String beanName; private String mapperManagerFactoryBean; private ApplicationContext applicationContext; public String getScanPackage() { return scanPackage; } public void setScanPackage(String scanPackage) { this.scanPackage = scanPackage; } public String getMapperManagerFactoryBean() { return mapperManagerFactoryBean; } public void setMapperManagerFactoryBean(String mapperManagerFactoryBean) { this.mapperManagerFactoryBean = mapperManagerFactoryBean; } @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException { if (StringUtils.isEmpty(this.scanPackage)) { throw new IllegalArgumentException("scanPackage can't be null"); } String basePackage2 = this.applicationContext.getEnvironment().resolvePlaceholders(this.scanPackage); String[] packages = StringUtils.tokenizeToStringArray(basePackage2, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS); DefaultClassPathScanner defaultClassPathScanner = new DefaultClassPathScanner(beanDefinitionRegistry); defaultClassPathScanner.setMapperManagerFactoryBean(mapperManagerFactoryBean); defaultClassPathScanner.registerFilters(); defaultClassPathScanner.doScan(packages); } @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException { } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } @Override public void setBeanName(String name) { this.beanName = name; } }
八、調用測試
8.一、假設你在包目錄:colin.spring.basic.advanced.inject.dao下聲明自定義的類UserMapper
public interface UserMapper extends BaseMapper { }
8.二、聲明配置類:ClassRegistryBeanScannerConfig
package com.dxz.test; @Configuration public class ClassRegistryBeanScannerConfig { @Bean(name = "mapperManagerFactoryBean") public MapperManagerFactoryBean configMapperManagerFactoryBean() { MapperManagerFactoryBean mapperManagerFactoryBean = new MapperManagerFactoryBean(); return mapperManagerFactoryBean; } @Bean public DefaultClassRegistryBeanFactory configDefaultClassRegistryBeanFactory() { DefaultClassRegistryBeanFactory defaultClassRegistryBeanFactory = new DefaultClassRegistryBeanFactory(); defaultClassRegistryBeanFactory.setScanPackage("colin.spring.basic.advanced.inject.dao"); defaultClassRegistryBeanFactory.setMapperManagerFactoryBean("mapperManagerFactoryBean"); return defaultClassRegistryBeanFactory; } }
8.三、測試調用
package com.dxz.test; public class Snippet { public static void main(String[] args) { AnnotationConfigApplicationContext acApplicationCOntext = new AnnotationConfigApplicationContext("colin.spring.basic.advanced.inject"); UserMapper userMapper = acApplicationCOntext.getBean(UserMapper.class); userMapper.add("lalaldsf"); acApplicationCOntext.stop(); } }