Spring之所以能打敗其他所有同類(lèi)型Java開(kāi)發(fā)框架屹立不倒的重要原因之一就是提供很多擴(kuò)展點(diǎn),讓其他組件和框架很容易就整合到Spring框架里,所以也就誕生很多基于Spring的二次開(kāi)發(fā)項(xiàng)目步淹,接下來(lái)我們一起聊聊Spring提供哪些擴(kuò)展點(diǎn),這篇文章只是簡(jiǎn)單說(shuō)明擴(kuò)展點(diǎn)但不深入诚撵,有興趣的伙伴可以后續(xù)一起學(xué)習(xí)交流缭裆,本篇最后我們?cè)龠M(jìn)行一個(gè)Mybatis和Spring整合工程簡(jiǎn)易開(kāi)發(fā)示例
Spring加載上下文方式
Spring加載容器上下文主要提供以下三大類(lèi)方式,第一種基于配置類(lèi)為常用的使用方式寿烟,
AnnotationConfigApplicationContext傳入?yún)?shù)是一個(gè)Class數(shù)組也即是支持多個(gè)配置類(lèi)
publicclassApplicationDemo{publicstaticvoidmain(String[] args){//基于配置類(lèi)加載Spring上下文AnnotationConfigApplicationContext applicationContext =newAnnotationConfigApplicationContext(MyConfig.class);? ? ? ? Student student = applicationContext.getBean(Student.class);? ? ? ? System.out.println(student);//基于項(xiàng)目路徑xml加載Spring上下文ClassPathXmlApplicationContext applicationContextXml =newClassPathXmlApplicationContext("spring.xml");? ? ? ? student = applicationContextXml.getBean(Student.class);? ? ? ? System.out.println(student);//基于文件系統(tǒng)絕對(duì)路徑xml加載Spring上下文FileSystemXmlApplicationContext applicationContextFileXml =newFileSystemXmlApplicationContext("E://spring.xml");? ? ? ? student = applicationContextXml.getBean(Student.class);? ? ? ? System.out.println(student);? ? }}@Configuration@ComponentScan({"com.itxs.pojo","com.itxs.extend"})publicclassMyConfig{}
publicAnnotationConfigApplicationContext(Class<?>... componentClasses){this();register(componentClasses);? ? refresh();}
擴(kuò)展點(diǎn)
ApplicationContextInitializer
這個(gè)是Spring Boot提供的擴(kuò)展點(diǎn),在整個(gè) spring 容器在刷新之前初始化
ConfigurableApplicationContext 的回調(diào)接口澈驼,簡(jiǎn)單來(lái)說(shuō),就是在容器刷新之前調(diào)用此類(lèi)的 initialize 方法筛武。這個(gè)點(diǎn)允許用戶(hù)自己擴(kuò)展缝其。用戶(hù)可以在整個(gè) spring 容器還沒(méi)被初始化之前做過(guò)一些事情∨橇可以想到的場(chǎng)景可能為内边,在最開(kāi)始激活一些配置,或者利用這時(shí)候 class 還沒(méi)被類(lèi)加載器加載的時(shí)機(jī)待锈,進(jìn)行動(dòng)態(tài)字節(jié)碼注入等操作
publicclassMyApplicationContextInitializerimplementsApplicationContextInitializer{@Overridepublicvoidinitialize(ConfigurableApplicationContext configurableApplicationContext){? ? ? ? System.out.println("-----------------------MyApplicationContextInitializer initialize");? ? }}
@SpringBootApplicationpublicclassMyApplication{publicstaticvoidmain(String[] args){//SpringApplication Main函數(shù)添加初始化器方式SpringApplication springApplication =newSpringApplication(MyApplication.class);? ? ? ? springApplication.addInitializers(newMyApplicationContextInitializer());? ? ? ? springApplication.run(args);? ? }}
第二種配置文件中的配置
context:initializer:classes:com.itxs.extend.MyApplicationContextInitializer
第三種SpringBoot的SPI擴(kuò)展
---META-INF/spring.factories中配置
org.springframework.context.ApplicationContextInitializer=com.itxs.extend.MyApplicationContextInitializer
BeanDefinitionRegistryPostProcessor-bean定義注冊(cè)后置處理器
BeanDefinitionRegistryPostProcessor是BeanFactoryPostProcessor的子接口漠其,BeanFactoryPostProcessor的作用是在bean的定義信息已經(jīng)加載但還沒(méi)有進(jìn)行初始化的時(shí)候執(zhí)行postProcessBeanFactory()的方法,BeanDefinitionRegistryPostProcessor是在BeanFactoryPostProcessor的前面執(zhí)行竿音,在bean定義之后提供的擴(kuò)展點(diǎn)和屎,比如可以在這里動(dòng)態(tài)注冊(cè)自己的 beanDefinition,加載 classpath 之外的 bean信息春瞬。
@ComponentpublicclassMyBeanDefinitionRegistryPostProcessorimplementsBeanDefinitionRegistryPostProcessor{@OverridepublicvoidpostProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry)throwsBeansException{? ? ? ? System.out.println("BeanDefinitionRegistryPostProcessor-postProcessBeanDefinitionRegistry");? ? ? ? System.out.println("BeanDefinitionCount:"+beanDefinitionRegistry.getBeanDefinitionCount());? ? ? ? String[] beanDefinitionNames = beanDefinitionRegistry.getBeanDefinitionNames();for(String beanDefinitionName : beanDefinitionNames) {? ? ? ? ? ? System.out.println("beanDefinitionName:"+beanDefinitionName);? ? ? ? }? ? }@OverridepublicvoidpostProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory)throwsBeansException{? ? ? ? System.out.println("BeanDefinitionRegistryPostProcessor-postProcessBeanFactory");? ? }}
BeanFactoryPostProcessor-Bean工廠后置處理器
BeanFactoryPostProcessor 接口是 Spring 初始化 BeanFactory 的擴(kuò)展接口柴信,在 Spring 在讀取 beanDefinition 信息之后實(shí)例化 bean 之前讀取 bean 定義并可以修改它。在這個(gè)時(shí)機(jī)宽气,用戶(hù)可以通過(guò)實(shí)現(xiàn)這個(gè)擴(kuò)展接口來(lái)自行處理一些東西随常,比如修改已經(jīng)注冊(cè)的 beanDefinition 的元信息。下面將student bean類(lèi)型修改為teacher bean類(lèi)型
@ComponentpublicclassMyBeanFactoryPostProcessorimplementsBeanFactoryPostProcessor{@OverridepublicvoidpostProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory)throwsBeansException{? ? ? ? System.out.println("BeanFactoryPostProcessor-postProcessBeanFactory");? ? ? ? BeanDefinition student = configurableListableBeanFactory.getBeanDefinition("student");? ? ? ? student.setBeanClassName(Teacher.class.getName());? ? }}
BeanPostProcessor-Bean后置處理器
該接口也可稱(chēng)為后置處理器抹竹,而我們Spring提供很多種bean后置處理器(比如在Spring源碼中九大后置處理器九次調(diào)用后置處理器地方的說(shuō)法)线罕,其作用是在Bean對(duì)象實(shí)例化和依賴(lài)注入完畢后,在顯示調(diào)用初始化方法的前后添加我們自己的業(yè)務(wù)邏輯窃判;注意是Bean實(shí)例化完畢后及依賴(lài)注入完成后觸發(fā)的钞楼;在這個(gè)擴(kuò)展點(diǎn)我們可以修改bean的屬性,可以給bean生成一個(gè)動(dòng)態(tài)代理實(shí)例等等袄琳。Spring AOP的底層處理主要也是通過(guò)實(shí)現(xiàn)BeanPostProcessor來(lái)執(zhí)行代理包裝邏輯询件。方法中輸入是一個(gè)個(gè)的bean,返回值則是bean修改的對(duì)象燃乍,默認(rèn)為null則是不修改;bean后置處理器可以有多個(gè)宛琅,可以通過(guò)實(shí)現(xiàn)Ordered接口或者標(biāo)記@Order注解來(lái)決定其處理順序刻蟹。
·
postProcessBeforeInitialization:初始化 bean 之前,相當(dāng)于把 bean 注入 spring 上下文之前
·
postProcessAfterInitialization:初始化 bean 之后嘿辟,相當(dāng)于把 bean 注入 spring 上下文之后
@ComponentpublicclassMyBeanPostProcessorimplementsBeanPostProcessor{@OverridepublicObjectpostProcessBeforeInitialization(Object bean, String beanName)throwsBeansException{? ? ? ? System.out.println("postProcessBeforeInitialization beanName:"+beanName);returnnull;? ? }@OverridepublicObjectpostProcessAfterInitialization(Object bean, String beanName)throwsBeansException{? ? ? ? System.out.println("postProcessAfterInitialization beanName:"+beanName);if(bean.getClass().isAssignableFrom(Student.class)){returnTeacher.class;? ? ? ? }returnnull;? ? }}
InstantiationAwareBeanPostProcessor
該接口也是 BeanPostProcessor的子接口舆瘪,而B(niǎo)eanPostProcessor接口只在 bean 的初始化階段進(jìn)行擴(kuò)展(Bean實(shí)例化完畢后及依賴(lài)注入完成后觸發(fā)的),而
InstantiationAwareBeanPostProcessor 接口在此基礎(chǔ)上增加了 3 個(gè)方法红伦,把可擴(kuò)展的范圍增加了實(shí)例化階段和屬性注入階段,該類(lèi)主要的擴(kuò)展點(diǎn)有以下 5 個(gè)方法英古,主要在 bean 生命周期的兩大階段:實(shí)例化階段和初始化階段**,而初始化階段兩個(gè)方法也即是上一節(jié)BeanPostProcessor提供的兩個(gè)方法**
·
postProcessBeforeInstantiation:實(shí)例化 bean 之前昙读,相當(dāng)于 new 這個(gè) bean 之前
·
postProcessAfterInstantiation:實(shí)例化 bean 之后召调,相當(dāng)于 new 這個(gè) bean 之后
· postProcessPropertyValues:bean 已經(jīng)實(shí)例化完成,在屬性注入時(shí)階段觸發(fā)蛮浑,@Autowired,@Resource 等注解原理基于此方法實(shí)現(xiàn)
使用場(chǎng)景:這個(gè)擴(kuò)展點(diǎn)非常有用 唠叛,無(wú)論是寫(xiě)中間件還是業(yè)務(wù)中,都能利用這個(gè)特性沮稚;比如實(shí)現(xiàn)了某一類(lèi)接口的 bean 在各個(gè)生命期間進(jìn)行收集艺沼,或者對(duì)某個(gè)類(lèi)型的 bean 進(jìn)行統(tǒng)一的設(shè)置等等。
@ComponentpublicclassMyInstantiationAwareBeanPostProcessorimplementsInstantiationAwareBeanPostProcessor{@OverridepublicObjectpostProcessBeforeInstantiation(Class<?> beanClass, String beanName)throwsBeansException{? ? ? ? System.out.println("postProcessBeforeInstantiation beanName"+ beanName);returnnull;? ? }@OverridepublicbooleanpostProcessAfterInstantiation(Object bean, String beanName)throwsBeansException{? ? ? ? System.out.println("postProcessAfterInstantiation beanName"+ beanName);returnfalse;? ? }@OverridepublicPropertyValuespostProcessProperties(PropertyValues pvs, Object bean, String beanName)throwsBeansException{? ? ? ? System.out.println("postProcessProperties beanName"+ beanName);returnnull;? ? }}
**### **
該接口也是
InstantiationAwareBeanPostProcessor 的子接口
· predictBeanType:該觸發(fā)點(diǎn)發(fā)生在
postProcessBeforeInstantiation 之前壮虫,這個(gè)方法用于預(yù)測(cè) Bean 的類(lèi)型澳厢,返回第一個(gè)預(yù)測(cè)成功的地方 Class 類(lèi)型,如果不能預(yù)測(cè)返回 null囚似;當(dāng)你調(diào)用 BeanFactory.getType(name) 適當(dāng)通過(guò) bean 的名字無(wú)法得到 bean 類(lèi)型信息時(shí)就調(diào)用該回調(diào)的方法來(lái)決定類(lèi)型信息。
·
determineCandidateConstructors:該觸發(fā)點(diǎn)發(fā)生在?postProcessBeforeInstantiation 之后线得,用于確定該 bean 構(gòu)造函數(shù)之用饶唤,返回的是該 bean 的所有構(gòu)造函數(shù)列表。用戶(hù)可以擴(kuò)展這個(gè)點(diǎn)贯钩,來(lái)自定義選擇相應(yīng)的構(gòu)造器來(lái)實(shí)現(xiàn)這個(gè) bean募狂。
· getEarlyBeanReference:該觸發(fā)點(diǎn)發(fā)生在
postProcessAfterInstantiation 之后,當(dāng)有循環(huán)依賴(lài)的場(chǎng)景角雷,當(dāng) bean 實(shí)例化好之后祸穷,為了防止有循環(huán)依賴(lài),Spring主要解決是的屬性的循環(huán)依賴(lài)勺三,會(huì)提前暴露回調(diào)方法雷滚,用于 bean 實(shí)例化的后置處理,這個(gè)方法就是在提前暴露的回調(diào)方法中觸發(fā)吗坚。
@ComponentpublicclassMySmartInstantiationAwareBeanPostProcessorimplementsSmartInstantiationAwareBeanPostProcessor{@OverridepublicClass predictBeanType(Class beanClass, String beanName)throwsBeansException {? ? ? ? System.out.println("predictBeanType beanName:"+beanName);returnnull;? ? }@OverridepublicConstructor[] determineCandidateConstructors(Class beanClass, String beanName)throwsBeansException {if(!beanClass.isAssignableFrom(Student.class)){? ? ? ? ? ? System.out.println("determineCandidateConstructors beanName:"+beanName);? ? ? ? }returnnull;? ? }@OverridepublicObjectgetEarlyBeanReference(Object bean, String beanName)throwsBeansException{? ? ? ? System.out.println("getEarlyBeanReference beanName:"+beanName);returnnull;? ? }}
ApplicationContextAwareProcessor
這個(gè)是一個(gè)實(shí)現(xiàn)了BeanPostProcessor的實(shí)現(xiàn)類(lèi)祈远,該類(lèi)本身并沒(méi)有擴(kuò)展點(diǎn)呆万,但是該類(lèi)內(nèi)部卻有 多個(gè)擴(kuò)展點(diǎn)可供實(shí)現(xiàn) ,這些類(lèi)觸發(fā)的時(shí)機(jī)在 bean 實(shí)例化之后车份,初始化之前,可以看到谋减,該類(lèi)用于執(zhí)行各種驅(qū)動(dòng)接口,在 bean 實(shí)例化之后扫沼,屬性填充之后出爹,通過(guò)執(zhí)行以上紅框標(biāo)出的擴(kuò)展接口,來(lái)獲取對(duì)應(yīng)容器的變量缎除。所以這里應(yīng)該來(lái)說(shuō)是有 6 個(gè)擴(kuò)展點(diǎn)严就,這里就放一起來(lái)說(shuō)了
· EnvironmentAware:用于獲取 EnviromentAware 的一個(gè)擴(kuò)展類(lèi),這個(gè)變量非常有用伴找, 可以獲得系統(tǒng)內(nèi)的所有參數(shù)盈蛮。當(dāng)然個(gè)人認(rèn)為這個(gè) Aware 沒(méi)必要去擴(kuò)展,因?yàn)?spring 內(nèi)部都可以通過(guò)注入的方式來(lái)直接獲得技矮。
·
EmbeddedValueResolverAware:用于獲取 StringValueResolver 的一個(gè)擴(kuò)展類(lèi)抖誉, StringValueResolver 用于獲取基于 String 類(lèi)型的 properties 的變量,一般我們都用 @Value 的方式去獲取衰倦,如果實(shí)現(xiàn)了這個(gè) Aware 接口袒炉,把 StringValueResolver 緩存起來(lái),通過(guò)這個(gè)類(lèi)去獲取 String 類(lèi)型的變量樊零,效果是一樣的我磁。
· ResourceLoaderAware:用于獲取 ResourceLoader 的一個(gè)擴(kuò)展類(lèi),ResourceLoader 可以用于獲取 classpath 內(nèi)所有的資源對(duì)象驻襟,可以擴(kuò)展此類(lèi)來(lái)拿到 ResourceLoader 對(duì)象夺艰。
·
ApplicationEventPublisherAware:用于獲取 ApplicationEventPublisher 的一個(gè)擴(kuò)展類(lèi),ApplicationEventPublisher 可以用來(lái)發(fā)布事件沉衣,結(jié)合 ApplicationListener 來(lái)共同使用郁副,下文在介紹 ApplicationListener 時(shí)會(huì)詳細(xì)提到。這個(gè)對(duì)象也可以通過(guò) spring 注入的方式來(lái)獲得豌习。
· MessageSourceAware:用于獲取 MessageSource 的一個(gè)擴(kuò)展類(lèi)存谎,MessageSource 主要用來(lái)做國(guó)際化。
· ApplicationContextAware:用來(lái)獲取 ApplicationContext 的一個(gè)擴(kuò)展類(lèi)肥隆,ApplicationContext 應(yīng)該是很多人非常熟悉的一個(gè)類(lèi)了既荚,就是 spring 上下文管理器,可以手動(dòng)的獲取任何在 spring 上下文注冊(cè)的 bean栋艳,我們經(jīng)常擴(kuò)展這個(gè)接口來(lái)緩存 spring 上下文恰聘,包裝成靜態(tài)方法。同時(shí) ApplicationContext 也實(shí)現(xiàn)了 BeanFactory,MessageSource憨琳,ApplicationEventPublisher 等接口诫钓,也可以用來(lái)做相關(guān)接口的事情。
privatevoidinvokeAwareInterfaces(Object bean){if(beaninstanceofEnvironmentAware) {? ? ? ((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());? }if(beaninstanceofEmbeddedValueResolverAware) {? ? ? ((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver);? }if(beaninstanceofResourceLoaderAware) {? ? ? ((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);? }if(beaninstanceofApplicationEventPublisherAware) {? ? ? ((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);? }if(beaninstanceofMessageSourceAware) {? ? ? ((MessageSourceAware) bean).setMessageSource(this.applicationContext);? }if(beaninstanceofApplicationStartupAware) {? ? ? ((ApplicationStartupAware) bean).setApplicationStartup(this.applicationContext.getApplicationStartup());? }if(beaninstanceofApplicationContextAware) {? ? ? ((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);? }}
SmartInitializingSingleton
這個(gè)接口中只有一個(gè)方法
afterSingletonsInstantiated篙螟,其作用是 在 spring 容器管理的所有單例對(duì)象(非懶加載對(duì)象)初始化完成之后調(diào)用的回調(diào)接口菌湃。其觸發(fā)時(shí)機(jī)為?postProcessAfterInitialization 之后。使用場(chǎng)景:用戶(hù)可以擴(kuò)展此接口在對(duì)所有單例對(duì)象初始化完畢后遍略,做一些后置的業(yè)務(wù)處理惧所。
@ComponentpublicclassMySmartInitializingSingletonimplementsSmartInitializingSingleton{@OverridepublicvoidafterSingletonsInstantiated(){? ? ? ? System.out.println("afterSingletonsInstantiated");? ? }}
FactoryBean
Spring為此提供了一個(gè)
org.springframework.bean.factory.FactoryBean的工廠類(lèi)接口,用戶(hù)可以通過(guò)實(shí)現(xiàn)該接口定制實(shí)例化Bean的邏輯绪杏。FactoryBean接口對(duì)于Spring框架來(lái)說(shuō)占用重要的地位下愈,Spring自身就提供了70多個(gè)FactoryBean的實(shí)現(xiàn);FactoryBean是一個(gè)接口蕾久,當(dāng)在IOC容器中的Bean實(shí)現(xiàn)了FactoryBean后势似,通過(guò)getBean(String BeanName)獲取到的Bean對(duì)象并不是FactoryBean的實(shí)現(xiàn)類(lèi)對(duì)象,而是這個(gè)實(shí)現(xiàn)類(lèi)中的getObject()方法返回的對(duì)象僧著。要想獲取FactoryBean的實(shí)現(xiàn)類(lèi)履因,就要getBean(&BeanName),在BeanName之前加上&盹愚;
@ComponentpublicclassMyFactoryBeanimplementsFactoryBean{@OverridepublicTeachergetObject()throwsException{returnnewTeacher();? ? }@OverridepublicClass getObjectType() {returnTeacher.class;? ? }}
publicclassApplicationExtend{publicstaticvoidmain(String[] args){? ? ? ? AnnotationConfigApplicationContext applicationContext =newAnnotationConfigApplicationContext(MyConfig.class);//Student student = applicationContext.getBean("student", Student.class); //這里是原來(lái)獲取Student的類(lèi)型栅迄,擴(kuò)展點(diǎn)修改后需要注釋掉不然會(huì)類(lèi)型轉(zhuǎn)換錯(cuò)誤Object student = applicationContext.getBean("student");? ? ? ? System.out.println("object:"+student);? ? ? ? System.out.println("factoryBeanReturn:"+applicationContext.getBean("myFactoryBean"));? ? ? ? System.out.println("factoryBeanSelf:"+applicationContext.getBean("&myFactoryBean"));? ? }}
CommandLineRunner
這個(gè)是Spring Boot提供擴(kuò)展接口,這個(gè)接口也只有一個(gè)方法:run(String... args)皆怕,觸發(fā)時(shí)機(jī)為整個(gè)項(xiàng)目啟動(dòng)完畢后毅舆,自動(dòng)執(zhí)行。如果有多個(gè) CommandLineRunner愈腾,可以利用 @Order注解 來(lái)進(jìn)行排序,值越小越優(yōu)先執(zhí)行憋活。使用場(chǎng)景:用戶(hù)擴(kuò)展此接口,進(jìn)行啟動(dòng)項(xiàng)目之后一些業(yè)務(wù)的預(yù)處理虱黄。
@Component@Order(1)public class MyCommandLineRunner implements CommandLineRunner {@Overridepublic void run(String... args) throws Exception {System.out.println("MyCommandLineRunner run args:"+args);? ? }}
@Component@Order(0)public class MyTwoCommandLineRunner implements CommandLineRunner {@Overridepublic void run(String... args) throws Exception {System.out.println("MyTwoCommandLineRunner run args:"+args);? ? }}
ApplicationListener
ApplicationListener 可以監(jiān)聽(tīng)某個(gè)事件的 event余掖,觸發(fā)時(shí)機(jī)可以穿插在業(yè)務(wù)方法執(zhí)行過(guò)程中,用戶(hù)可以自定義某個(gè)業(yè)務(wù)事件;但是 spring 內(nèi)部也有一些內(nèi)置事件礁鲁,這種事件,可以穿插在啟動(dòng)調(diào)用中赁豆。我們也可以利用這個(gè)特性仅醇,來(lái)自己做一些內(nèi)置事件的監(jiān)聽(tīng)器來(lái)達(dá)到和前面一些觸發(fā)點(diǎn)大致相同的事情。接下來(lái)羅列下 spring 主要的內(nèi)置事件:
· ContextRefreshedEvent
· ApplicationContext 被初始化或刷新時(shí)魔种,該事件被發(fā)布析二。這也可以在
ConfigurableApplicationContext 接口中使用 refresh() 方法來(lái)發(fā)生。此處的初始化是指:所有的 Bean 被成功裝載,后處理 Bean 被檢測(cè)并激活叶摄,所有 Singleton Bean 被預(yù)實(shí)例化属韧,ApplicationContext 容器已就緒可用。
· ContextStartedEvent
· 當(dāng)使用
ConfigurableApplicationContext (ApplicationContext 子接口)接口中的 start() 方法啟動(dòng) ApplicationContext 時(shí)蛤吓,該事件被發(fā)布宵喂。你可以調(diào)查你的數(shù)據(jù)庫(kù),或者你可以在接受到這個(gè)事件后重啟任何停止的應(yīng)用程序会傲。
· ContextStoppedEvent
· 當(dāng)使用
ConfigurableApplicationContext 接口中的 stop() 停止 ApplicationContext 時(shí)锅棕,發(fā)布這個(gè)事件。你可以在接受到這個(gè)事件后做必要的清理的工作
· ContextClosedEvent
· 當(dāng)使用
ConfigurableApplicationContext 接口中的 close() 方法關(guān)閉 ApplicationContext 時(shí)淌山,該事件被發(fā)布裸燎。一個(gè)已關(guān)閉的上下文到達(dá)生命周期末端;它不能被刷新或重啟
· RequestHandledEvent
· 這是一個(gè) web-specific 事件泼疑,告訴所有 bean HTTP 請(qǐng)求已經(jīng)被服務(wù)德绿。只能應(yīng)用于使用 DispatcherServlet 的 Web 應(yīng)用。在使用 Spring 作為前端的 MVC 控制器時(shí)退渗,當(dāng) Spring 處理用戶(hù)請(qǐng)求結(jié)束后移稳,系統(tǒng)會(huì)自動(dòng)觸發(fā)該事件
@ConfigurationpublicclassMyApplicationListenerimplementsApplicationListener{@OverridepublicvoidonApplicationEvent(ApplicationEvent applicationEvent){? ? ? ? System.out.println("MyApplicationListener onApplicationEvent");? ? }}
BeanNameAware
BeanNameAware也是 Aware 接口擴(kuò)展的子接口,觸發(fā)點(diǎn)在 bean 的初始化之前氓辣,也就是
postProcessBeforeInitialization 之前
如果bean實(shí)現(xiàn)了BeanNameAware接口秒裕,spring將bean的id傳給setBeanName()方法;這個(gè)類(lèi)的觸發(fā)點(diǎn)方法只有一個(gè):setBeanName使用場(chǎng)景為:用戶(hù)可以擴(kuò)展這個(gè)點(diǎn)钞啸,在初始化 bean 之前拿到 spring 容器中注冊(cè)的的 beanName几蜻,來(lái)自行修改這個(gè) ;
@ComponentpublicclassMyBeanNameAwareimplementsBeanNameAware{@OverridepublicvoidsetBeanName(String s){? ? ? ? System.out.println("setBeanName:"+s);? ? }}
BeanFactoryAware
BeanFactoryAware也是 Aware 接口擴(kuò)展的子接口体斩,只有一個(gè)觸發(fā)點(diǎn)梭稚,發(fā)生在 bean 的實(shí)例化之后,注入屬性之前絮吵,也就是 Setter 之前弧烤。這個(gè)類(lèi)的擴(kuò)展點(diǎn)方法為 setBeanFactory,可以拿到 BeanFactory 這個(gè)屬性蹬敲。使用場(chǎng)景為暇昂,你可以在 bean 實(shí)例化之后,但還未初始化之前伴嗡,拿到 BeanFactory急波,在這個(gè)時(shí)候,可以對(duì)每個(gè) bean 作特殊化的定制瘪校。也或者可以把 BeanFactory 拿到進(jìn)行緩存澄暮,日后使用名段,如果bean實(shí)現(xiàn)了BeanFactoryAware接口,spring將調(diào)用setBeanFactory方法泣懊,將BeanFactory實(shí)例傳進(jìn)來(lái)伸辟;
@ComponentpublicclassMyBeanFactoryAwareimplementsBeanFactoryAware{@OverridepublicvoidsetBeanFactory(BeanFactory beanFactory)throwsBeansException{? ? ? ? System.out.println("MyBeanFactoryAware setBeanFactory:"+beanFactory.getBean("myBeanFactoryAware").getClass().getSimpleName());? ? }}
Bean初始化及銷(xiāo)毀回調(diào)方法
InitializingBean是用來(lái)初始化 bean 的。InitializingBean 接口為 bean 提供了初始化方法的方式馍刮,它只包括 afterPropertiesSet 方法信夫,凡是繼承該接口的類(lèi),在初始化 bean 的時(shí)候都會(huì)執(zhí)行該方法渠退。這個(gè)擴(kuò)展點(diǎn)的觸發(fā)時(shí)機(jī)在 postProcessAfterInitialization 之前忙迁。
DisposableBean這個(gè)擴(kuò)展點(diǎn)也只有一個(gè)方法:destroy(),其觸發(fā)時(shí)機(jī)為當(dāng)此對(duì)象銷(xiāo)毀時(shí)碎乃,會(huì)自動(dòng)執(zhí)行這個(gè)方法姊扔。比如說(shuō)運(yùn)行 applicationContext.registerShutdownHook 時(shí),就會(huì)觸發(fā)這個(gè)方法梅誓。
@PostConstruct這個(gè)并不算一個(gè)擴(kuò)展點(diǎn)恰梢,其實(shí)就是一個(gè)標(biāo)注。其作用是在 bean 的初始化階段梗掰,如果對(duì)一個(gè)方法標(biāo)注了 @PostConstruct嵌言,會(huì)先調(diào)用這個(gè)方法。這里重點(diǎn)是要關(guān)注下這個(gè)標(biāo)準(zhǔn)的觸發(fā)點(diǎn)及穗,這個(gè)觸發(fā)點(diǎn)是在 postProcessBeforeInitialization 之后摧茴,InitializingBean.afterPropertiesSet 之前。使用場(chǎng)景:用戶(hù)可以對(duì)某一方法進(jìn)行標(biāo)注埂陆,來(lái)進(jìn)行初始化某一個(gè)屬性
@PreDestroy修飾的方法會(huì)在服務(wù)器關(guān)閉Spring容器的時(shí)候運(yùn)行苛白,并且只會(huì)調(diào)用一次
使用場(chǎng)景:用戶(hù)實(shí)現(xiàn)此接口,來(lái)進(jìn)行系統(tǒng)啟動(dòng)的時(shí)候一些業(yè)務(wù)指標(biāo)的初始化工作焚虱。
@RepositorypublicclassStudentimplementsInitializingBean,DisposableBean{@PostConstructpublicvoidinit()throwsException{? ? ? ? System.out.println("Student init");? ? }@PreDestroypublicvoidclose()throwsException{? ? ? ? System.out.println("Student close");? ? }@Overridepublicvoiddestroy()throwsException{? ? ? ? System.out.println("Student destroy");? ? }@OverridepublicvoidafterPropertiesSet()throwsException{? ? ? ? System.out.println("Student afterPropertiesSet");? ? }}
整合示例
mybatis-spring gitee源碼地址
https://gitee.com/yongzhebuju/mybatis-spring
我們從這些 Spring&Spring Boot 的擴(kuò)展點(diǎn)當(dāng)中购裙,大致可以窺視到整個(gè) bean 的生命周期。在業(yè)務(wù)開(kāi)發(fā)或者寫(xiě)中間件業(yè)務(wù)的時(shí)候鹃栽,可以合理利用 Spring 提供給我們的擴(kuò)展點(diǎn)躏率,在 Spring 啟動(dòng)的各個(gè)階段內(nèi)做一些事情,以達(dá)到自定義初始化的目的民鼓。接下來(lái)我們一起學(xué)習(xí)一個(gè)常見(jiàn)Mybatis和Spring整合開(kāi)發(fā)簡(jiǎn)易示例,上面內(nèi)容我們初步對(duì)于Spring擴(kuò)展點(diǎn)有了一些理解薇芝,由于本人閱讀過(guò)部分Mybatic與Spring整合源碼,思路也是來(lái)源于此丰嘉,由于目前是簡(jiǎn)易示例恩掷,不在工程加入Mybatis依賴(lài),著重在整合部分
思考問(wèn)題
Spring的Bean是如何生成的供嚎?
Spring提供哪些擴(kuò)展點(diǎn)來(lái)整合第三方框架?
Spring是如何來(lái)整理Mybatis的?Mybatis代理對(duì)象都是接口克滴,不能直接通過(guò)New方式將Mapper Bean注冊(cè)Spring容器里
主體思路
首先可以明確一點(diǎn)逼争,我們需要利用到Jdk動(dòng)態(tài)代理及反射機(jī)制
借助FactoryBean特性,F(xiàn)actoryBean可以返回動(dòng)態(tài)代理的對(duì)象及類(lèi)型
通過(guò)ImportBeanDefinitionRegistrar通過(guò)Import注解將FactoryBean通過(guò)BeanDefinition注冊(cè)到BeanDefinitionRegistry通過(guò)后續(xù)Bean的生命周期最終放到Spring的容器里
Mybatic快速入門(mén)
從官網(wǎng)的就可以直接找到Java編程快速入門(mén)** ,這里只是大概說(shuō)明一下**
Stringresource ="org/mybatis/example/mybatis-config.xml";InputStreaminputStream = Resources.getResourceAsStream(resource);SqlSessionFactorysqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);try(SqlSession session = sqlSessionFactory.openSession()) {BlogMappermapper = session.getMapper(BlogMapper.class);Blogblog = mapper.selectBlog(101);}
整合示例實(shí)現(xiàn)
pom文件
<?xml version="1.0" encoding="UTF-8"?>4.0.0com.itxsmybatis-spring1.0-SNAPSHOT88org.springframeworkspring-context5.3.9
注解接口,通過(guò)@Import同時(shí)將
ItxsImportBeanDefinitionRegistrar導(dǎo)入到Spring容器里
@Retention(RetentionPolicy.RUNTIME)@Import({ItxsImportBeanDefinitionRegistrar.class})public@interfaceItxsScan {? ? String value()default"";}
配置類(lèi),@ItxsScan為我們自定義的注解劝赔,主要標(biāo)記掃描mapper路徑
@Configuration@ComponentScan({"com.itxs"})@ItxsScan("com.itxs.dao")public class AppConfig {}
mapper接口誓焦,@ItxsSelect為我們自定義的注解,主要標(biāo)記mapper接口方法sql語(yǔ)句
packagecom.itxs.dao;importcom.itxs.annotation.ItxsSelect;importcom.itxs.pojo.User;publicinterfaceUserMapper{@ItxsSelect("select * from user where user_id = #{userId}")User selectByUserId(Integer userId);}
service實(shí)現(xiàn)類(lèi)
@ComponentpublicclassUserService{@AutowiredUserMapper userMapper;@AutowiredOrderMapper orderMapper;publicvoidgetUser(){? ? ? ? userMapper.selectByUserId(1);? ? ? ? orderMapper.selectByOrderId(1);? ? }}
FactoryBean實(shí)現(xiàn),這里通過(guò)構(gòu)造函數(shù)傳入接口的Class類(lèi)型着帽,將類(lèi)型通過(guò)Jdk動(dòng)態(tài)代理生成并返回對(duì)象杂伟,當(dāng)調(diào)用目標(biāo)對(duì)象后會(huì)執(zhí)行代理對(duì)象invoke方法,從invoke方法通過(guò)反射與注解獲取到sql語(yǔ)句仍翰,后續(xù)流程就可以利用Mybatis提供操作數(shù)據(jù)庫(kù)流程赫粥,這里就不繼續(xù)深入了
packagecom.itxs.utils;importcom.itxs.annotation.ItxsSelect;importorg.springframework.beans.factory.FactoryBean;importjava.lang.reflect.InvocationHandler;importjava.lang.reflect.Method;importjava.lang.reflect.Proxy;publicclassItxsFactoryBeanimplementsFactoryBean{privateClass mapper;publicItxsFactoryBean(Class mapper) {this.mapper = mapper;? ? }@OverridepublicObject getObject() throws Exception {//使用動(dòng)態(tài)代理機(jī)制Object o = Proxy.newProxyInstance(ItxsFactoryBean.class.getClassLoader(), new Class[]{mapper}, new InvocationHandler() {@OverridepublicObject invoke(Object proxy, Method method, Object[] args) throws Throwable {if(Object.class.equals(method.getDeclaringClass())){returnmethod.invoke(this,args);? ? ? ? ? ? ? ? }? ? ? ? ? ? ? ? ItxsSelectannotation= method.getAnnotation(ItxsSelect.class);System.out.println("調(diào)用方法名稱(chēng)是"+method.getName()+",sql語(yǔ)句為"+annotation.value());//后面可執(zhí)行Mybatic操作數(shù)據(jù)庫(kù)的相關(guān)操作returnnull;? ? ? ? ? ? }? ? ? ? });returno;? ? }@OverridepublicClass getObjectType() {returnmapper;? ? }}
ImportBeanDefinitionRegistrar的實(shí)現(xiàn),通過(guò)獲取@import上的注解找到mapper的掃描路徑予借,通過(guò)classLoader加載磁盤(pán)下Class文件生成BeanDefinition并設(shè)置構(gòu)造函數(shù)mapper類(lèi)型參數(shù)越平,最終將BeanDefinition注冊(cè)到BeanDefinitionRegistry
package com.itxs.utils;importcom.itxs.annotation.ItxsScan;importcom.itxs.dao.OrderMapper;importcom.itxs.dao.UserMapper;importorg.springframework.beans.factory.support.AbstractBeanDefinition;importorg.springframework.beans.factory.support.BeanDefinitionBuilder;importorg.springframework.beans.factory.support.BeanDefinitionRegistry;importorg.springframework.beans.factory.support.BeanNameGenerator;importorg.springframework.context.annotation.ImportBeanDefinitionRegistrar;importorg.springframework.core.type.AnnotationMetadata;importjava.io.File;importjava.net.URL;importjava.util.ArrayList;importjava.util.List;importjava.util.Map;publicclassItxsImportBeanDefinitionRegistrarimplementsImportBeanDefinitionRegistrar{? ? @Overridepublicvoid registerBeanDefinitions(AnnotationMetadataimportingClassMetadata,BeanDefinitionRegistryregistry,BeanNameGeneratorimportBeanNameGenerator) {//獲取注解ItxsScan對(duì)象,并取出value值作為掃描的路徑Map annotationAttributes = importingClassMetadata.getAnnotationAttributes(ItxsScan.class.getName());StringmapperPath=annotationAttributes.get("value").toString();Listmappers=scan(mapperPath);for(Classmapper:mappers){BeanDefinitionBuilderbuilder =BeanDefinitionBuilder.genericBeanDefinition();AbstractBeanDefinitionbeanDefinition = builder.getBeanDefinition();? ? ? ? ? ? beanDefinition.setBeanClass(ItxsFactoryBean.class);beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(mapper);registry.registerBeanDefinition(StringUtils.toLowerCaseFirstOne(mapper.getSimpleName()),beanDefinition);? ? ? ? }? ? }privateListscan(Stringpath){List classList = newArrayList();? ? ? ? path = path.replace(".","/");ClassLoaderclassLoader =ItxsImportBeanDefinitionRegistrar.class.getClassLoader();URLurl=classLoader.getResource(path);Filefile=newFile(url.getFile());if(file.isDirectory()){File[] files = file.listFiles();for(int i =0; i < files.length; i++) {StringabsolutePath = files[i].getAbsolutePath();? ? ? ? ? ? ? ? absolutePath = absolutePath.substring(absolutePath.indexOf("com"), absolutePath.indexOf(".class"));? ? ? ? ? ? ? ? absolutePath = absolutePath.replace("\\",".");try{Class aClass = classLoader.loadClass(absolutePath);? ? ? ? ? ? ? ? ? ? classList.add(aClass);? ? ? ? ? ? ? ? }catch(ClassNotFoundExceptione) {? ? ? ? ? ? ? ? ? ? e.printStackTrace();? ? ? ? ? ? ? ? }? ? ? ? ? ? }? ? ? ? }returnclassList;? ? }}
Main程序
packagecom.itxs;importcom.itxs.config.AppConfig;importcom.itxs.service.UserService;importorg.springframework.context.annotation.AnnotationConfigApplicationContext;publicclassMybatisApplication{publicstaticvoidmain(String[] args){? ? ? ? AnnotationConfigApplicationContext applicationContext =newAnnotationConfigApplicationContext(AppConfig.class);? ? ? ? UserService userService = applicationContext.getBean("userService", UserService.class);? ? ? ? userService.getUser();? ? }}
運(yùn)行結(jié)果
Spring之所以能打敗其他所有同類(lèi)型Java開(kāi)發(fā)框架屹立不倒的重要原因之一就是提供很多擴(kuò)展點(diǎn)灵迫,讓其他組件和框架很容易就整合到Spring框架里,所以也就誕生很多基于Spring的二次開(kāi)發(fā)項(xiàng)目秦叛,接下來(lái)我們一起聊聊Spring提供哪些擴(kuò)展點(diǎn),這篇文章只是簡(jiǎn)單說(shuō)明擴(kuò)展點(diǎn)但不深入瀑粥,有興趣的伙伴可以后續(xù)一起學(xué)習(xí)交流挣跋,本篇最后我們?cè)龠M(jìn)行一個(gè)Mybatis和Spring整合工程簡(jiǎn)易開(kāi)發(fā)示例
Spring加載上下文方式
Spring加載容器上下文主要提供以下三大類(lèi)方式,第一種基于配置類(lèi)為常用的使用方式狞换,
AnnotationConfigApplicationContext傳入?yún)?shù)是一個(gè)Class數(shù)組也即是支持多個(gè)配置類(lèi)
publicclassApplicationDemo{publicstaticvoidmain(String[] args){//基于配置類(lèi)加載Spring上下文AnnotationConfigApplicationContext applicationContext =newAnnotationConfigApplicationContext(MyConfig.class);? ? ? ? Student student = applicationContext.getBean(Student.class);? ? ? ? System.out.println(student);//基于項(xiàng)目路徑xml加載Spring上下文ClassPathXmlApplicationContext applicationContextXml =newClassPathXmlApplicationContext("spring.xml");? ? ? ? student = applicationContextXml.getBean(Student.class);? ? ? ? System.out.println(student);//基于文件系統(tǒng)絕對(duì)路徑xml加載Spring上下文FileSystemXmlApplicationContext applicationContextFileXml =newFileSystemXmlApplicationContext("E://spring.xml");? ? ? ? student = applicationContextXml.getBean(Student.class);? ? ? ? System.out.println(student);? ? }}@Configuration@ComponentScan({"com.itxs.pojo","com.itxs.extend"})publicclassMyConfig{}
publicAnnotationConfigApplicationContext(Class<?>... componentClasses){this();register(componentClasses);? ? refresh();}
擴(kuò)展點(diǎn)
ApplicationContextInitializer
這個(gè)是Spring Boot提供的擴(kuò)展點(diǎn),在整個(gè) spring 容器在刷新之前初始化
ConfigurableApplicationContext 的回調(diào)接口避咆,簡(jiǎn)單來(lái)說(shuō),就是在容器刷新之前調(diào)用此類(lèi)的 initialize 方法哀澈。這個(gè)點(diǎn)允許用戶(hù)自己擴(kuò)展牌借。用戶(hù)可以在整個(gè) spring 容器還沒(méi)被初始化之前做過(guò)一些事情「畎矗可以想到的場(chǎng)景可能為膨报,在最開(kāi)始激活一些配置,或者利用這時(shí)候 class 還沒(méi)被類(lèi)加載器加載的時(shí)機(jī)适荣,進(jìn)行動(dòng)態(tài)字節(jié)碼注入等操作
publicclassMyApplicationContextInitializerimplementsApplicationContextInitializer{@Overridepublicvoidinitialize(ConfigurableApplicationContext configurableApplicationContext){? ? ? ? System.out.println("-----------------------MyApplicationContextInitializer initialize");? ? }}
@SpringBootApplicationpublicclassMyApplication{publicstaticvoidmain(String[] args){//SpringApplication Main函數(shù)添加初始化器方式SpringApplication springApplication =newSpringApplication(MyApplication.class);? ? ? ? springApplication.addInitializers(newMyApplicationContextInitializer());? ? ? ? springApplication.run(args);? ? }}
第二種配置文件中的配置
context:initializer:classes:com.itxs.extend.MyApplicationContextInitializer
第三種SpringBoot的SPI擴(kuò)展
---META-INF/spring.factories中配置
org.springframework.context.ApplicationContextInitializer=com.itxs.extend.MyApplicationContextInitializer
BeanDefinitionRegistryPostProcessor-bean定義注冊(cè)后置處理器
BeanDefinitionRegistryPostProcessor是BeanFactoryPostProcessor的子接口现柠,BeanFactoryPostProcessor的作用是在bean的定義信息已經(jīng)加載但還沒(méi)有進(jìn)行初始化的時(shí)候執(zhí)行postProcessBeanFactory()的方法,BeanDefinitionRegistryPostProcessor是在BeanFactoryPostProcessor的前面執(zhí)行弛矛,在bean定義之后提供的擴(kuò)展點(diǎn)够吩,比如可以在這里動(dòng)態(tài)注冊(cè)自己的 beanDefinition,加載 classpath 之外的 bean信息丈氓。
@ComponentpublicclassMyBeanDefinitionRegistryPostProcessorimplementsBeanDefinitionRegistryPostProcessor{@OverridepublicvoidpostProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry)throwsBeansException{? ? ? ? System.out.println("BeanDefinitionRegistryPostProcessor-postProcessBeanDefinitionRegistry");? ? ? ? System.out.println("BeanDefinitionCount:"+beanDefinitionRegistry.getBeanDefinitionCount());? ? ? ? String[] beanDefinitionNames = beanDefinitionRegistry.getBeanDefinitionNames();for(String beanDefinitionName : beanDefinitionNames) {? ? ? ? ? ? System.out.println("beanDefinitionName:"+beanDefinitionName);? ? ? ? }? ? }@OverridepublicvoidpostProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory)throwsBeansException{? ? ? ? System.out.println("BeanDefinitionRegistryPostProcessor-postProcessBeanFactory");? ? }}
BeanFactoryPostProcessor-Bean工廠后置處理器
BeanFactoryPostProcessor 接口是 Spring 初始化 BeanFactory 的擴(kuò)展接口周循,在 Spring 在讀取 beanDefinition 信息之后實(shí)例化 bean 之前讀取 bean 定義并可以修改它强法。在這個(gè)時(shí)機(jī),用戶(hù)可以通過(guò)實(shí)現(xiàn)這個(gè)擴(kuò)展接口來(lái)自行處理一些東西湾笛,比如修改已經(jīng)注冊(cè)的 beanDefinition 的元信息饮怯。下面將student bean類(lèi)型修改為teacher bean類(lèi)型
@ComponentpublicclassMyBeanFactoryPostProcessorimplementsBeanFactoryPostProcessor{@OverridepublicvoidpostProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory)throwsBeansException{? ? ? ? System.out.println("BeanFactoryPostProcessor-postProcessBeanFactory");? ? ? ? BeanDefinition student = configurableListableBeanFactory.getBeanDefinition("student");? ? ? ? student.setBeanClassName(Teacher.class.getName());? ? }}
BeanPostProcessor-Bean后置處理器
該接口也可稱(chēng)為后置處理器,而我們Spring提供很多種bean后置處理器(比如在Spring源碼中九大后置處理器九次調(diào)用后置處理器地方的說(shuō)法)嚎研,其作用是在Bean對(duì)象實(shí)例化和依賴(lài)注入完畢后蓖墅,在顯示調(diào)用初始化方法的前后添加我們自己的業(yè)務(wù)邏輯;注意是Bean實(shí)例化完畢后及依賴(lài)注入完成后觸發(fā)的临扮;在這個(gè)擴(kuò)展點(diǎn)我們可以修改bean的屬性论矾,可以給bean生成一個(gè)動(dòng)態(tài)代理實(shí)例等等。Spring AOP的底層處理主要也是通過(guò)實(shí)現(xiàn)BeanPostProcessor來(lái)執(zhí)行代理包裝邏輯杆勇。方法中輸入是一個(gè)個(gè)的bean,返回值則是bean修改的對(duì)象贪壳,默認(rèn)為null則是不修改;bean后置處理器可以有多個(gè)靶橱,可以通過(guò)實(shí)現(xiàn)Ordered接口或者標(biāo)記@Order注解來(lái)決定其處理順序寥袭。
·
postProcessBeforeInitialization:初始化 bean 之前,相當(dāng)于把 bean 注入 spring 上下文之前
·
postProcessAfterInitialization:初始化 bean 之后关霸,相當(dāng)于把 bean 注入 spring 上下文之后
@ComponentpublicclassMyBeanPostProcessorimplementsBeanPostProcessor{@OverridepublicObjectpostProcessBeforeInitialization(Object bean, String beanName)throwsBeansException{? ? ? ? System.out.println("postProcessBeforeInitialization beanName:"+beanName);returnnull;? ? }@OverridepublicObjectpostProcessAfterInitialization(Object bean, String beanName)throwsBeansException{? ? ? ? System.out.println("postProcessAfterInitialization beanName:"+beanName);if(bean.getClass().isAssignableFrom(Student.class)){returnTeacher.class;? ? ? ? }returnnull;? ? }}
InstantiationAwareBeanPostProcessor
該接口也是 BeanPostProcessor的子接口传黄,而B(niǎo)eanPostProcessor接口只在 bean 的初始化階段進(jìn)行擴(kuò)展(Bean實(shí)例化完畢后及依賴(lài)注入完成后觸發(fā)的),而
InstantiationAwareBeanPostProcessor 接口在此基礎(chǔ)上增加了 3 個(gè)方法队寇,把可擴(kuò)展的范圍增加了實(shí)例化階段和屬性注入階段,該類(lèi)主要的擴(kuò)展點(diǎn)有以下 5 個(gè)方法膘掰,主要在 bean 生命周期的兩大階段:實(shí)例化階段和初始化階段**,而初始化階段兩個(gè)方法也即是上一節(jié)BeanPostProcessor提供的兩個(gè)方法**
·
postProcessBeforeInstantiation:實(shí)例化 bean 之前佳遣,相當(dāng)于 new 這個(gè) bean 之前
·
postProcessAfterInstantiation:實(shí)例化 bean 之后识埋,相當(dāng)于 new 這個(gè) bean 之后
· postProcessPropertyValues:bean 已經(jīng)實(shí)例化完成,在屬性注入時(shí)階段觸發(fā)零渐,@Autowired,@Resource 等注解原理基于此方法實(shí)現(xiàn)
使用場(chǎng)景:這個(gè)擴(kuò)展點(diǎn)非常有用 窒舟,無(wú)論是寫(xiě)中間件還是業(yè)務(wù)中,都能利用這個(gè)特性诵盼;比如實(shí)現(xiàn)了某一類(lèi)接口的 bean 在各個(gè)生命期間進(jìn)行收集惠豺,或者對(duì)某個(gè)類(lèi)型的 bean 進(jìn)行統(tǒng)一的設(shè)置等等。
@ComponentpublicclassMyInstantiationAwareBeanPostProcessorimplementsInstantiationAwareBeanPostProcessor{@OverridepublicObjectpostProcessBeforeInstantiation(Class<?> beanClass, String beanName)throwsBeansException{? ? ? ? System.out.println("postProcessBeforeInstantiation beanName"+ beanName);returnnull;? ? }@OverridepublicbooleanpostProcessAfterInstantiation(Object bean, String beanName)throwsBeansException{? ? ? ? System.out.println("postProcessAfterInstantiation beanName"+ beanName);returnfalse;? ? }@OverridepublicPropertyValuespostProcessProperties(PropertyValues pvs, Object bean, String beanName)throwsBeansException{? ? ? ? System.out.println("postProcessProperties beanName"+ beanName);returnnull;? ? }}
**### **
該接口也是
InstantiationAwareBeanPostProcessor 的子接口
· predictBeanType:該觸發(fā)點(diǎn)發(fā)生在
postProcessBeforeInstantiation 之前风宁,這個(gè)方法用于預(yù)測(cè) Bean 的類(lèi)型洁墙,返回第一個(gè)預(yù)測(cè)成功的地方 Class 類(lèi)型依沮,如果不能預(yù)測(cè)返回 null响迂;當(dāng)你調(diào)用 BeanFactory.getType(name) 適當(dāng)通過(guò) bean 的名字無(wú)法得到 bean 類(lèi)型信息時(shí)就調(diào)用該回調(diào)的方法來(lái)決定類(lèi)型信息。
·
determineCandidateConstructors:該觸發(fā)點(diǎn)發(fā)生在?postProcessBeforeInstantiation 之后殃饿,用于確定該 bean 構(gòu)造函數(shù)之用饮寞,返回的是該 bean 的所有構(gòu)造函數(shù)列表孝扛。用戶(hù)可以擴(kuò)展這個(gè)點(diǎn)列吼,來(lái)自定義選擇相應(yīng)的構(gòu)造器來(lái)實(shí)現(xiàn)這個(gè) bean。
· getEarlyBeanReference:該觸發(fā)點(diǎn)發(fā)生在
postProcessAfterInstantiation 之后疗琉,當(dāng)有循環(huán)依賴(lài)的場(chǎng)景冈欢,當(dāng) bean 實(shí)例化好之后,為了防止有循環(huán)依賴(lài)盈简,Spring主要解決是的屬性的循環(huán)依賴(lài),會(huì)提前暴露回調(diào)方法太示,用于 bean 實(shí)例化的后置處理柠贤,這個(gè)方法就是在提前暴露的回調(diào)方法中觸發(fā)。
@ComponentpublicclassMySmartInstantiationAwareBeanPostProcessorimplementsSmartInstantiationAwareBeanPostProcessor{@OverridepublicClass predictBeanType(Class beanClass, String beanName)throwsBeansException {? ? ? ? System.out.println("predictBeanType beanName:"+beanName);returnnull;? ? }@OverridepublicConstructor[] determineCandidateConstructors(Class beanClass, String beanName)throwsBeansException {if(!beanClass.isAssignableFrom(Student.class)){? ? ? ? ? ? System.out.println("determineCandidateConstructors beanName:"+beanName);? ? ? ? }returnnull;? ? }@OverridepublicObjectgetEarlyBeanReference(Object bean, String beanName)throwsBeansException{? ? ? ? System.out.println("getEarlyBeanReference beanName:"+beanName);returnnull;? ? }}
ApplicationContextAwareProcessor
這個(gè)是一個(gè)實(shí)現(xiàn)了BeanPostProcessor的實(shí)現(xiàn)類(lèi)类缤,該類(lèi)本身并沒(méi)有擴(kuò)展點(diǎn)臼勉,但是該類(lèi)內(nèi)部卻有 多個(gè)擴(kuò)展點(diǎn)可供實(shí)現(xiàn) ,這些類(lèi)觸發(fā)的時(shí)機(jī)在 bean 實(shí)例化之后餐弱,初始化之前,可以看到宴霸,該類(lèi)用于執(zhí)行各種驅(qū)動(dòng)接口,在 bean 實(shí)例化之后膏蚓,屬性填充之后瓢谢,通過(guò)執(zhí)行以上紅框標(biāo)出的擴(kuò)展接口,來(lái)獲取對(duì)應(yīng)容器的變量驮瞧。所以這里應(yīng)該來(lái)說(shuō)是有 6 個(gè)擴(kuò)展點(diǎn)氓扛,這里就放一起來(lái)說(shuō)了
· EnvironmentAware:用于獲取 EnviromentAware 的一個(gè)擴(kuò)展類(lèi),這個(gè)變量非常有用论笔, 可以獲得系統(tǒng)內(nèi)的所有參數(shù)采郎。當(dāng)然個(gè)人認(rèn)為這個(gè) Aware 沒(méi)必要去擴(kuò)展,因?yàn)?spring 內(nèi)部都可以通過(guò)注入的方式來(lái)直接獲得狂魔。
·
EmbeddedValueResolverAware:用于獲取 StringValueResolver 的一個(gè)擴(kuò)展類(lèi)蒜埋, StringValueResolver 用于獲取基于 String 類(lèi)型的 properties 的變量,一般我們都用 @Value 的方式去獲取最楷,如果實(shí)現(xiàn)了這個(gè) Aware 接口整份,把 StringValueResolver 緩存起來(lái),通過(guò)這個(gè)類(lèi)去獲取 String 類(lèi)型的變量管嬉,效果是一樣的皂林。
· ResourceLoaderAware:用于獲取 ResourceLoader 的一個(gè)擴(kuò)展類(lèi),ResourceLoader 可以用于獲取 classpath 內(nèi)所有的資源對(duì)象蚯撩,可以擴(kuò)展此類(lèi)來(lái)拿到 ResourceLoader 對(duì)象础倍。
·
ApplicationEventPublisherAware:用于獲取 ApplicationEventPublisher 的一個(gè)擴(kuò)展類(lèi),ApplicationEventPublisher 可以用來(lái)發(fā)布事件胎挎,結(jié)合 ApplicationListener 來(lái)共同使用沟启,下文在介紹 ApplicationListener 時(shí)會(huì)詳細(xì)提到忆家。這個(gè)對(duì)象也可以通過(guò) spring 注入的方式來(lái)獲得。
· MessageSourceAware:用于獲取 MessageSource 的一個(gè)擴(kuò)展類(lèi)德迹,MessageSource 主要用來(lái)做國(guó)際化芽卿。
· ApplicationContextAware:用來(lái)獲取 ApplicationContext 的一個(gè)擴(kuò)展類(lèi),ApplicationContext 應(yīng)該是很多人非常熟悉的一個(gè)類(lèi)了胳搞,就是 spring 上下文管理器卸例,可以手動(dòng)的獲取任何在 spring 上下文注冊(cè)的 bean,我們經(jīng)常擴(kuò)展這個(gè)接口來(lái)緩存 spring 上下文肌毅,包裝成靜態(tài)方法筷转。同時(shí) ApplicationContext 也實(shí)現(xiàn)了 BeanFactory,MessageSource悬而,ApplicationEventPublisher 等接口呜舒,也可以用來(lái)做相關(guān)接口的事情。
privatevoidinvokeAwareInterfaces(Object bean){if(beaninstanceofEnvironmentAware) {? ? ? ((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());? }if(beaninstanceofEmbeddedValueResolverAware) {? ? ? ((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver);? }if(beaninstanceofResourceLoaderAware) {? ? ? ((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);? }if(beaninstanceofApplicationEventPublisherAware) {? ? ? ((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);? }if(beaninstanceofMessageSourceAware) {? ? ? ((MessageSourceAware) bean).setMessageSource(this.applicationContext);? }if(beaninstanceofApplicationStartupAware) {? ? ? ((ApplicationStartupAware) bean).setApplicationStartup(this.applicationContext.getApplicationStartup());? }if(beaninstanceofApplicationContextAware) {? ? ? ((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);? }}
SmartInitializingSingleton
這個(gè)接口中只有一個(gè)方法
afterSingletonsInstantiated笨奠,其作用是 在 spring 容器管理的所有單例對(duì)象(非懶加載對(duì)象)初始化完成之后調(diào)用的回調(diào)接口袭蝗。其觸發(fā)時(shí)機(jī)為?postProcessAfterInitialization 之后。使用場(chǎng)景:用戶(hù)可以擴(kuò)展此接口在對(duì)所有單例對(duì)象初始化完畢后般婆,做一些后置的業(yè)務(wù)處理到腥。
@ComponentpublicclassMySmartInitializingSingletonimplementsSmartInitializingSingleton{@OverridepublicvoidafterSingletonsInstantiated(){? ? ? ? System.out.println("afterSingletonsInstantiated");? ? }}
FactoryBean
Spring為此提供了一個(gè)
org.springframework.bean.factory.FactoryBean的工廠類(lèi)接口,用戶(hù)可以通過(guò)實(shí)現(xiàn)該接口定制實(shí)例化Bean的邏輯腺兴。FactoryBean接口對(duì)于Spring框架來(lái)說(shuō)占用重要的地位左电,Spring自身就提供了70多個(gè)FactoryBean的實(shí)現(xiàn);FactoryBean是一個(gè)接口页响,當(dāng)在IOC容器中的Bean實(shí)現(xiàn)了FactoryBean后篓足,通過(guò)getBean(String BeanName)獲取到的Bean對(duì)象并不是FactoryBean的實(shí)現(xiàn)類(lèi)對(duì)象,而是這個(gè)實(shí)現(xiàn)類(lèi)中的getObject()方法返回的對(duì)象闰蚕。要想獲取FactoryBean的實(shí)現(xiàn)類(lèi)栈拖,就要getBean(&BeanName),在BeanName之前加上&没陡;
@ComponentpublicclassMyFactoryBeanimplementsFactoryBean{@OverridepublicTeachergetObject()throwsException{returnnewTeacher();? ? }@OverridepublicClass getObjectType() {returnTeacher.class;? ? }}
publicclassApplicationExtend{publicstaticvoidmain(String[] args){? ? ? ? AnnotationConfigApplicationContext applicationContext =newAnnotationConfigApplicationContext(MyConfig.class);//Student student = applicationContext.getBean("student", Student.class); //這里是原來(lái)獲取Student的類(lèi)型涩哟,擴(kuò)展點(diǎn)修改后需要注釋掉不然會(huì)類(lèi)型轉(zhuǎn)換錯(cuò)誤Object student = applicationContext.getBean("student");? ? ? ? System.out.println("object:"+student);? ? ? ? System.out.println("factoryBeanReturn:"+applicationContext.getBean("myFactoryBean"));? ? ? ? System.out.println("factoryBeanSelf:"+applicationContext.getBean("&myFactoryBean"));? ? }}
CommandLineRunner
這個(gè)是Spring Boot提供擴(kuò)展接口,這個(gè)接口也只有一個(gè)方法:run(String... args)盼玄,觸發(fā)時(shí)機(jī)為整個(gè)項(xiàng)目啟動(dòng)完畢后贴彼,自動(dòng)執(zhí)行。如果有多個(gè) CommandLineRunner埃儿,可以利用 @Order注解 來(lái)進(jìn)行排序,值越小越優(yōu)先執(zhí)行器仗。使用場(chǎng)景:用戶(hù)擴(kuò)展此接口,進(jìn)行啟動(dòng)項(xiàng)目之后一些業(yè)務(wù)的預(yù)處理。
@Component@Order(1)public class MyCommandLineRunner implements CommandLineRunner {@Overridepublic void run(String... args) throws Exception {System.out.println("MyCommandLineRunner run args:"+args);? ? }}
@Component@Order(0)public class MyTwoCommandLineRunner implements CommandLineRunner {@Overridepublic void run(String... args) throws Exception {System.out.println("MyTwoCommandLineRunner run args:"+args);? ? }}
ApplicationListener
ApplicationListener 可以監(jiān)聽(tīng)某個(gè)事件的 event精钮,觸發(fā)時(shí)機(jī)可以穿插在業(yè)務(wù)方法執(zhí)行過(guò)程中威鹿,用戶(hù)可以自定義某個(gè)業(yè)務(wù)事件;但是 spring 內(nèi)部也有一些內(nèi)置事件,這種事件轨香,可以穿插在啟動(dòng)調(diào)用中忽你。我們也可以利用這個(gè)特性,來(lái)自己做一些內(nèi)置事件的監(jiān)聽(tīng)器來(lái)達(dá)到和前面一些觸發(fā)點(diǎn)大致相同的事情臂容。接下來(lái)羅列下 spring 主要的內(nèi)置事件:
· ContextRefreshedEvent
· ApplicationContext 被初始化或刷新時(shí)科雳,該事件被發(fā)布。這也可以在
ConfigurableApplicationContext 接口中使用 refresh() 方法來(lái)發(fā)生脓杉。此處的初始化是指:所有的 Bean 被成功裝載炸渡,后處理 Bean 被檢測(cè)并激活,所有 Singleton Bean 被預(yù)實(shí)例化丽已,ApplicationContext 容器已就緒可用。
· ContextStartedEvent
· 當(dāng)使用
ConfigurableApplicationContext (ApplicationContext 子接口)接口中的 start() 方法啟動(dòng) ApplicationContext 時(shí)买决,該事件被發(fā)布沛婴。你可以調(diào)查你的數(shù)據(jù)庫(kù),或者你可以在接受到這個(gè)事件后重啟任何停止的應(yīng)用程序督赤。
· ContextStoppedEvent
· 當(dāng)使用
ConfigurableApplicationContext 接口中的 stop() 停止 ApplicationContext 時(shí)嘁灯,發(fā)布這個(gè)事件。你可以在接受到這個(gè)事件后做必要的清理的工作
· ContextClosedEvent
· 當(dāng)使用
ConfigurableApplicationContext 接口中的 close() 方法關(guān)閉 ApplicationContext 時(shí)躲舌,該事件被發(fā)布丑婿。一個(gè)已關(guān)閉的上下文到達(dá)生命周期末端;它不能被刷新或重啟
· RequestHandledEvent
· 這是一個(gè) web-specific 事件没卸,告訴所有 bean HTTP 請(qǐng)求已經(jīng)被服務(wù)羹奉。只能應(yīng)用于使用 DispatcherServlet 的 Web 應(yīng)用。在使用 Spring 作為前端的 MVC 控制器時(shí)约计,當(dāng) Spring 處理用戶(hù)請(qǐng)求結(jié)束后诀拭,系統(tǒng)會(huì)自動(dòng)觸發(fā)該事件
@ConfigurationpublicclassMyApplicationListenerimplementsApplicationListener{@OverridepublicvoidonApplicationEvent(ApplicationEvent applicationEvent){? ? ? ? System.out.println("MyApplicationListener onApplicationEvent");? ? }}
BeanNameAware
BeanNameAware也是 Aware 接口擴(kuò)展的子接口,觸發(fā)點(diǎn)在 bean 的初始化之前煤蚌,也就是
postProcessBeforeInitialization 之前
如果bean實(shí)現(xiàn)了BeanNameAware接口耕挨,spring將bean的id傳給setBeanName()方法;這個(gè)類(lèi)的觸發(fā)點(diǎn)方法只有一個(gè):setBeanName使用場(chǎng)景為:用戶(hù)可以擴(kuò)展這個(gè)點(diǎn)尉桩,在初始化 bean 之前拿到 spring 容器中注冊(cè)的的 beanName筒占,來(lái)自行修改這個(gè) ;
@ComponentpublicclassMyBeanNameAwareimplementsBeanNameAware{@OverridepublicvoidsetBeanName(String s){? ? ? ? System.out.println("setBeanName:"+s);? ? }}
BeanFactoryAware
BeanFactoryAware也是 Aware 接口擴(kuò)展的子接口蜘犁,只有一個(gè)觸發(fā)點(diǎn)翰苫,發(fā)生在 bean 的實(shí)例化之后,注入屬性之前沽瘦,也就是 Setter 之前革骨。這個(gè)類(lèi)的擴(kuò)展點(diǎn)方法為 setBeanFactory农尖,可以拿到 BeanFactory 這個(gè)屬性。使用場(chǎng)景為良哲,你可以在 bean 實(shí)例化之后盛卡,但還未初始化之前,拿到 BeanFactory筑凫,在這個(gè)時(shí)候滑沧,可以對(duì)每個(gè) bean 作特殊化的定制。也或者可以把 BeanFactory 拿到進(jìn)行緩存巍实,日后使用滓技,如果bean實(shí)現(xiàn)了BeanFactoryAware接口,spring將調(diào)用setBeanFactory方法棚潦,將BeanFactory實(shí)例傳進(jìn)來(lái)令漂;
@ComponentpublicclassMyBeanFactoryAwareimplementsBeanFactoryAware{@OverridepublicvoidsetBeanFactory(BeanFactory beanFactory)throwsBeansException{? ? ? ? System.out.println("MyBeanFactoryAware setBeanFactory:"+beanFactory.getBean("myBeanFactoryAware").getClass().getSimpleName());? ? }}
Bean初始化及銷(xiāo)毀回調(diào)方法
InitializingBean是用來(lái)初始化 bean 的。InitializingBean 接口為 bean 提供了初始化方法的方式丸边,它只包括 afterPropertiesSet 方法叠必,凡是繼承該接口的類(lèi),在初始化 bean 的時(shí)候都會(huì)執(zhí)行該方法妹窖。這個(gè)擴(kuò)展點(diǎn)的觸發(fā)時(shí)機(jī)在 postProcessAfterInitialization 之前纬朝。
DisposableBean這個(gè)擴(kuò)展點(diǎn)也只有一個(gè)方法:destroy(),其觸發(fā)時(shí)機(jī)為當(dāng)此對(duì)象銷(xiāo)毀時(shí)骄呼,會(huì)自動(dòng)執(zhí)行這個(gè)方法共苛。比如說(shuō)運(yùn)行 applicationContext.registerShutdownHook 時(shí),就會(huì)觸發(fā)這個(gè)方法蜓萄。
@PostConstruct這個(gè)并不算一個(gè)擴(kuò)展點(diǎn)隅茎,其實(shí)就是一個(gè)標(biāo)注。其作用是在 bean 的初始化階段绕德,如果對(duì)一個(gè)方法標(biāo)注了 @PostConstruct患膛,會(huì)先調(diào)用這個(gè)方法。這里重點(diǎn)是要關(guān)注下這個(gè)標(biāo)準(zhǔn)的觸發(fā)點(diǎn)耻蛇,這個(gè)觸發(fā)點(diǎn)是在 postProcessBeforeInitialization 之后踪蹬,InitializingBean.afterPropertiesSet 之前。使用場(chǎng)景:用戶(hù)可以對(duì)某一方法進(jìn)行標(biāo)注臣咖,來(lái)進(jìn)行初始化某一個(gè)屬性
@PreDestroy修飾的方法會(huì)在服務(wù)器關(guān)閉Spring容器的時(shí)候運(yùn)行跃捣,并且只會(huì)調(diào)用一次
使用場(chǎng)景:用戶(hù)實(shí)現(xiàn)此接口,來(lái)進(jìn)行系統(tǒng)啟動(dòng)的時(shí)候一些業(yè)務(wù)指標(biāo)的初始化工作夺蛇。
@RepositorypublicclassStudentimplementsInitializingBean,DisposableBean{@PostConstructpublicvoidinit()throwsException{? ? ? ? System.out.println("Student init");? ? }@PreDestroypublicvoidclose()throwsException{? ? ? ? System.out.println("Student close");? ? }@Overridepublicvoiddestroy()throwsException{? ? ? ? System.out.println("Student destroy");? ? }@OverridepublicvoidafterPropertiesSet()throwsException{? ? ? ? System.out.println("Student afterPropertiesSet");? ? }}
整合示例
mybatis-spring gitee源碼地址
https://gitee.com/yongzhebuju/mybatis-spring
我們從這些 Spring&Spring Boot 的擴(kuò)展點(diǎn)當(dāng)中疚漆,大致可以窺視到整個(gè) bean 的生命周期。在業(yè)務(wù)開(kāi)發(fā)或者寫(xiě)中間件業(yè)務(wù)的時(shí)候,可以合理利用 Spring 提供給我們的擴(kuò)展點(diǎn)娶聘,在 Spring 啟動(dòng)的各個(gè)階段內(nèi)做一些事情闻镶,以達(dá)到自定義初始化的目的。接下來(lái)我們一起學(xué)習(xí)一個(gè)常見(jiàn)Mybatis和Spring整合開(kāi)發(fā)簡(jiǎn)易示例,上面內(nèi)容我們初步對(duì)于Spring擴(kuò)展點(diǎn)有了一些理解丸升,由于本人閱讀過(guò)部分Mybatic與Spring整合源碼铆农,思路也是來(lái)源于此,由于目前是簡(jiǎn)易示例狡耻,不在工程加入Mybatis依賴(lài)墩剖,著重在整合部分
思考問(wèn)題
Spring的Bean是如何生成的?
Spring提供哪些擴(kuò)展點(diǎn)來(lái)整合第三方框架夷狰?
Spring是如何來(lái)整理Mybatis的岭皂?Mybatis代理對(duì)象都是接口,不能直接通過(guò)New方式將Mapper Bean注冊(cè)Spring容器里
主體思路
首先可以明確一點(diǎn)沼头,我們需要利用到Jdk動(dòng)態(tài)代理及反射機(jī)制
借助FactoryBean特性爷绘,F(xiàn)actoryBean可以返回動(dòng)態(tài)代理的對(duì)象及類(lèi)型
通過(guò)ImportBeanDefinitionRegistrar通過(guò)Import注解將FactoryBean通過(guò)BeanDefinition注冊(cè)到BeanDefinitionRegistry通過(guò)后續(xù)Bean的生命周期最終放到Spring的容器里
Mybatic快速入門(mén)
從官網(wǎng)的就可以直接找到Java編程快速入門(mén)** ,這里只是大概說(shuō)明一下**
Stringresource ="org/mybatis/example/mybatis-config.xml";InputStreaminputStream = Resources.getResourceAsStream(resource);SqlSessionFactorysqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);try(SqlSession session = sqlSessionFactory.openSession()) {BlogMappermapper = session.getMapper(BlogMapper.class);Blogblog = mapper.selectBlog(101);}
整合示例實(shí)現(xiàn)
pom文件
<?xml version="1.0" encoding="UTF-8"?>4.0.0com.itxsmybatis-spring1.0-SNAPSHOT88org.springframeworkspring-context5.3.9
注解接口,通過(guò)@Import同時(shí)將
ItxsImportBeanDefinitionRegistrar導(dǎo)入到Spring容器里
@Retention(RetentionPolicy.RUNTIME)@Import({ItxsImportBeanDefinitionRegistrar.class})public@interfaceItxsScan {? ? String value()default"";}
配置類(lèi),@ItxsScan為我們自定義的注解,主要標(biāo)記掃描mapper路徑
@Configuration@ComponentScan({"com.itxs"})@ItxsScan("com.itxs.dao")public class AppConfig {}
mapper接口进倍,@ItxsSelect為我們自定義的注解揉阎,主要標(biāo)記mapper接口方法sql語(yǔ)句
packagecom.itxs.dao;importcom.itxs.annotation.ItxsSelect;importcom.itxs.pojo.User;publicinterfaceUserMapper{@ItxsSelect("select * from user where user_id = #{userId}")User selectByUserId(Integer userId);}
service實(shí)現(xiàn)類(lèi)
@ComponentpublicclassUserService{@AutowiredUserMapper userMapper;@AutowiredOrderMapper orderMapper;publicvoidgetUser(){? ? ? ? userMapper.selectByUserId(1);? ? ? ? orderMapper.selectByOrderId(1);? ? }}
FactoryBean實(shí)現(xiàn),這里通過(guò)構(gòu)造函數(shù)傳入接口的Class類(lèi)型,將類(lèi)型通過(guò)Jdk動(dòng)態(tài)代理生成并返回對(duì)象背捌,當(dāng)調(diào)用目標(biāo)對(duì)象后會(huì)執(zhí)行代理對(duì)象invoke方法,從invoke方法通過(guò)反射與注解獲取到sql語(yǔ)句洞斯,后續(xù)流程就可以利用Mybatis提供操作數(shù)據(jù)庫(kù)流程毡庆,這里就不繼續(xù)深入了
packagecom.itxs.utils;importcom.itxs.annotation.ItxsSelect;importorg.springframework.beans.factory.FactoryBean;importjava.lang.reflect.InvocationHandler;importjava.lang.reflect.Method;importjava.lang.reflect.Proxy;publicclassItxsFactoryBeanimplementsFactoryBean{privateClass mapper;publicItxsFactoryBean(Class mapper) {this.mapper = mapper;? ? }@OverridepublicObject getObject() throws Exception {//使用動(dòng)態(tài)代理機(jī)制Object o = Proxy.newProxyInstance(ItxsFactoryBean.class.getClassLoader(), new Class[]{mapper}, new InvocationHandler() {@OverridepublicObject invoke(Object proxy, Method method, Object[] args) throws Throwable {if(Object.class.equals(method.getDeclaringClass())){returnmethod.invoke(this,args);? ? ? ? ? ? ? ? }? ? ? ? ? ? ? ? ItxsSelectannotation= method.getAnnotation(ItxsSelect.class);System.out.println("調(diào)用方法名稱(chēng)是"+method.getName()+",sql語(yǔ)句為"+annotation.value());//后面可執(zhí)行Mybatic操作數(shù)據(jù)庫(kù)的相關(guān)操作returnnull;? ? ? ? ? ? }? ? ? ? });returno;? ? }@OverridepublicClass getObjectType() {returnmapper;? ? }}
ImportBeanDefinitionRegistrar的實(shí)現(xiàn),通過(guò)獲取@import上的注解找到mapper的掃描路徑烙如,通過(guò)classLoader加載磁盤(pán)下Class文件生成BeanDefinition并設(shè)置構(gòu)造函數(shù)mapper類(lèi)型參數(shù)么抗,最終將BeanDefinition注冊(cè)到BeanDefinitionRegistry
package com.itxs.utils;importcom.itxs.annotation.ItxsScan;importcom.itxs.dao.OrderMapper;importcom.itxs.dao.UserMapper;importorg.springframework.beans.factory.support.AbstractBeanDefinition;importorg.springframework.beans.factory.support.BeanDefinitionBuilder;importorg.springframework.beans.factory.support.BeanDefinitionRegistry;importorg.springframework.beans.factory.support.BeanNameGenerator;importorg.springframework.context.annotation.ImportBeanDefinitionRegistrar;importorg.springframework.core.type.AnnotationMetadata;importjava.io.File;importjava.net.URL;importjava.util.ArrayList;importjava.util.List;importjava.util.Map;publicclassItxsImportBeanDefinitionRegistrarimplementsImportBeanDefinitionRegistrar{? ? @Overridepublicvoid registerBeanDefinitions(AnnotationMetadataimportingClassMetadata,BeanDefinitionRegistryregistry,BeanNameGeneratorimportBeanNameGenerator) {//獲取注解ItxsScan對(duì)象,并取出value值作為掃描的路徑Map annotationAttributes = importingClassMetadata.getAnnotationAttributes(ItxsScan.class.getName());StringmapperPath=annotationAttributes.get("value").toString();Listmappers=scan(mapperPath);for(Classmapper:mappers){BeanDefinitionBuilderbuilder =BeanDefinitionBuilder.genericBeanDefinition();AbstractBeanDefinitionbeanDefinition = builder.getBeanDefinition();? ? ? ? ? ? beanDefinition.setBeanClass(ItxsFactoryBean.class);beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(mapper);registry.registerBeanDefinition(StringUtils.toLowerCaseFirstOne(mapper.getSimpleName()),beanDefinition);? ? ? ? }? ? }privateListscan(Stringpath){List classList = newArrayList();? ? ? ? path = path.replace(".","/");ClassLoaderclassLoader =ItxsImportBeanDefinitionRegistrar.class.getClassLoader();URLurl=classLoader.getResource(path);Filefile=newFile(url.getFile());if(file.isDirectory()){File[] files = file.listFiles();for(int i =0; i < files.length; i++) {StringabsolutePath = files[i].getAbsolutePath();? ? ? ? ? ? ? ? absolutePath = absolutePath.substring(absolutePath.indexOf("com"), absolutePath.indexOf(".class"));? ? ? ? ? ? ? ? absolutePath = absolutePath.replace("\\",".");try{Class aClass = classLoader.loadClass(absolutePath);? ? ? ? ? ? ? ? ? ? classList.add(aClass);? ? ? ? ? ? ? ? }catch(ClassNotFoundExceptione) {? ? ? ? ? ? ? ? ? ? e.printStackTrace();? ? ? ? ? ? ? ? }? ? ? ? ? ? }? ? ? ? }returnclassList;? ? }}
Main程序
packagecom.itxs;importcom.itxs.config.AppConfig;importcom.itxs.service.UserService;importorg.springframework.context.annotation.AnnotationConfigApplicationContext;publicclassMybatisApplication{publicstaticvoidmain(String[] args){? ? ? ? AnnotationConfigApplicationContext applicationContext =newAnnotationConfigApplicationContext(AppConfig.class);? ? ? ? UserService userService = applicationContext.getBean("userService", UserService.class);? ? ? ? userService.getUser();? ? }}
運(yùn)行結(jié)果