@TOC# Spring系列
記錄在程式走的每一步___auth:huf
該篇章講解架構整合的思路; 講解該篇章主要是為了將之前的知識點進行一個串聯; 有很多介面 我僅僅是介紹了它們的使用;并沒有實戰場景;所以有很多同學想知道 這些介面 到底在什么場景下使用; 怎么使用; 能達到什么樣的效果;例如我們整合Mybatis的時候,怎么去整合的? 如果有疑問的同學;可私信我; 回復不一定及時;請各位同學諒解;
我們學習了Spring 那么多功能 主要圍繞是圍繞 Bean 做一些事情; 創建Bean 初始化Bean 運行Bean 銷毀Bean. 我們Java程式 主要圍繞著 物件 構成一個一個的模塊 最后組合成龐大的系統; 做一些計算機的處理. 我們架構整合的時候 主要是整合 物件 , 物件的創建; 物件集合的管理; 如果將一個不相關的框架 跟Spring整合; 主要整合其內部的功能集; 也就是物件; 例如 我們要整合Mybatis; 我們應該如何進行整合? 也許 在別的 大神文章里面 我們也能看到 別人是 怎么整合的 復制粘貼之后 我們也能行; 但是這樣能給我們技術帶來實際上的提升嗎? 這篇文章 我就帶大家一起手寫一個超簡易版本 mybatis-spring.jar
1:我們Mybatis 使用流程 大致如下
創建一個流 把組態檔傳入
InputStream is = Resources.getResourceAsStream("MybatisConfig.xml");
創建SqlSessionBeanFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
利用sqlSessionBeanFactory 創建SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
通過SqlSession 拿到相對應的Mapper
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
最后執行我們的sql陳述句;
List list = mapper.selectAll();
2 :我們大致準備一個架子結構;
簡簡單單的 一個掃描的Config 一個main啟動 類 一個Service 一個Mapper

注意這個Mapper 是一個介面



這就是我們平時使用Mapper的時候 最常見的方式; 直接注入Mapper 然后直接呼叫即可;
# 我們的Mapper 是一個介面 此時 在我們Service 注入了這個介面; 并且進行sql執行; 在Mybatis中 它會使用代理 進行介面實作; 并且最終把該物件的實作類 注入到這里來; 我們現在這里并沒做這個類的實作; 現在執行肯定是報錯的; 我們該如何把這個介面變成Bean呢? 答:使用FactoryBean 進行物件的創建. 我們知道 Spring內部使用了Cglib代理 所以不能代理介面;
1:我們利用FactoryBean 間接的創建Bean:
/**
* auth :huf
*/
@Component
public class HufFatoryBean implements FactoryBean {
@Override
public Object getObject() throws Exception {
Object object = Proxy.newProxyInstance(HufFatoryBean.class.getClassLoader(), new Class[]{StudentMapper.class}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(method.getName());
return null;
}
});
return object;
}
@Override
public Class<?> getObjectType() {
return StudentMapper.class;
}
}
這樣我們就可以通過FactoryBean 再利用 JDK 代理; 把介面變成物件 并且注入到容器中;

為什么是null 因為我Factory回傳出來就是Null;
我們繼續擴展; 假設我們有多個Mapper 一個StudentMapper 一個 TeacherMapper … 多個Mapper.那怎么辦?
答: 我們把FactoryBean 變成 BeanDefinition 就可以了; 以下是演進展示;
我們先把HufFatoryBean 寫活; 通過構造方法傳入即將要代理的類Class. 這樣 我們就可以只用一個FactoryBean 進行全部Mapper的代理.
/**
* auth :huf
*/
@Component
public class HufFatoryBean implements FactoryBean {
private Class clazz;
public HufFatoryBean(Class clazz) {
this.clazz = clazz;
}
@Override
public Object getObject() throws Exception {
Object object = Proxy.newProxyInstance(HufFatoryBean.class.getClassLoader(), new Class[]{clazz}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(method.getName());
return null;
}
});
return object;
}
@Override
public Class<?> getObjectType() {
return clazz;
}
}
然后 我們注冊BeanDefinition
第一種方式
/**
* Application啟動類;
* auth : huf
*/
public class ApplictionMain {
public static void main(String[] args) {
AnnotationConfigApplicationContext ap = new AnnotationConfigApplicationContext();
ap.register(AppConfig.class);
-------------
創建一個beanDefinition
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
傳入我們的FactoryBean
beanDefinition.setBeanClass(HufFatoryBean.class);
通過構造方法 把Student傳入
beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(StudentMapper.class);
注冊BeanDefinition 把創建好的 BeanDefinition 傳入進去;
ap.registerBeanDefinition("studentMapper",beanDefinition);
-------------------------以下也是一樣
AbstractBeanDefinition beanDefinition1 = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
beanDefinition1.setBeanClass(HufFatoryBean.class);
beanDefinition1.getConstructorArgumentValues().addGenericArgumentValue(TeacherMapper.class);
ap.registerBeanDefinition("teacherMapper",beanDefinition1);
啟動
ap.refresh();
StudentService studentService = (StudentService) ap.getBean("studentService");
studentService.test();
}
}
這里 同學們應該都理解了吧? 到這里 其實思路就已經出來了; 我們不可能以這種方式去加載BeanDefinition; 以下 我介紹2種加載BeanDefinition的方式; 其實以前說過 再之前的篇章里邊; 這里我寫一次;
第二種BeanDefinitionRegistryPostProcessor
/**
* auth : huf
*/
public class HufBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
創建一個beanDefinition
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
傳入我們的FactoryBean
beanDefinition.setBeanClass(HufFatoryBean.class);
通過構造方法 把Student傳入
beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(StudentMapper.class);
注冊BeanDefinition 把創建好的 BeanDefinition 傳入進去;
ap.registerBeanDefinition("studentMapper",beanDefinition);
-------------------------以下也是一樣
AbstractBeanDefinition beanDefinition1 = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
beanDefinition1.setBeanClass(HufFatoryBean.class);
beanDefinition1.getConstructorArgumentValues().addGenericArgumentValue(TeacherMapper.class);
ap.registerBeanDefinition("teacherMapper",beanDefinition1);
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
}
}
這樣是否是合理很多? 我們場景再繼續往下深入一下; 如果我們之后想加一個Mapper 我們還得再這里寫多一個BeanDefinition 是不是又突然覺得不太合理了?
答: 我們可以利用 Scan 掃描的方式 進行Mapper掃描. 凡是再某個包下面的所有介面 我都認為它是Mapper 是否OK? 我們定義一個注解;


現在我們注入了掃描 我們會發現 之前用的BeanDefinitionRegistryPostProcessor 無法拿到我們的配置;我們就引出了另外一個
第三種ImportBeanDefinitionRegistrar

這樣 我們在組態檔中 可以通過@Inport(HufImportBeanDefinitionRegistrar.class) Spring捕捉到組態檔中的Import后 就可以調度這個方法 把BeanDefinition 加入容器中去; 這樣我們繼續改造我們的HufImportBeanDefinitionRegistrar 讓他通過掃描的方式得到所有MapperClass 然后通過工廠的方式 生產BeanDefinition 注冊到容器中; 以下是改造后的代碼:
/**
*
* auth : huf
*/
public class HufImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
Map<String, Object> annotationAttributes = importingClassMetadata.getAnnotationAttributes(HufMapperScan.class.getName());
String path = MapUtils.getString(annotationAttributes,"value");
//Spring 原生掃描的方式 因為我們使用的是介面 所以我們必須繼承這個類 并且進行改造
// ClassPathBeanDefinitionScanner scan = new ClassPathBeanDefinitionScanner(registry);
// scan.scan(path);
創建自己的MapperBeanDefinitionScanner
HufMapperBeanDefinitionScanner scanner = new HufMapperBeanDefinitionScanner(registry);
該步驟是為了在掃描其中 如果有@Component 才會加入到容器 默認false 我改成true 就是 不管有沒有 都是Bean
scanner.addIncludeFilter(new TypeFilter() {
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
return true;
}
});
scanner.scan(path);
}
}
HufMapperBeanDefinitionScanner
中間的doScan方法重寫 是為了什么? 我們通過path路徑掃描到的 是Mapper本身 并不是Factory 現在我們變成Factory 所以要從寫Scan 方法 這時候 我們得到的BeanDefinition 就是我們想要的了; 這樣我們的掃描組件就寫完了

以上就是我們的掃描組件; 但是我們留了一個問題; 我們的物件 Mapper物件 并沒有實作我們 sql的執行; 沒有訪問資料庫; 我們繼續往下擴展:

我們只需要把之前我們流程所說的Mybatis Sqlsession.getMapper 物件 回傳出去; 即可;
改造后代碼為:

這樣就徹底的整合完畢;實際上 mybatis-spring 就是為我們做了這么一件事; 真正的代碼 核心就是這樣 但是因為更豐富的功能實作 以及兼容 . 實際代碼肯定是比這里更復雜;
總結:
1: Import 注解使用場景
2: FactoryBean 使用場景
3: BeanDefinitionRegistryPostProcessor使用場景;
4:ImportBeanDefinitionRegistrar使用場景
5:ClassPathBeanDefinitionScanner使用場景
6: Beandefinition 的使用場景;
See You
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/309552.html
標籤:其他
