重學Java 設計模式:實戰代理模式「模擬mybatis-spring中定義DAO...

2020-12-11 小傅哥架構師

作者:小傅哥博客:https://bugstack.cn

沉澱、分享、成長,讓自己和他人都能有所收穫!

目錄

一、前言二、開發環境三、代理模式介紹四、案例場景模擬五、代理類模式實現過程1. 工程結構2. 代碼實現3. 測試驗證六、總結

一、前言

難以跨越的瓶頸期,把你拿捏滴死死的!

編程開發學習過程中遇到的瓶頸期,往往是由於看不到前進的方向。這個時候你特別希望能有人告訴你,你還欠缺些什麼朝著哪個方向努力。而導致這一問題的主要原因是由於日常的業務開發太過於複製過去,日復一日的重複。沒有太多的挑戰,也沒參與過較大體量的業務場景,除了這些開發場景因素外,還有缺少組內的技術氛圍和技術分享,沒有人做傳播和布道者,也缺少自己對各項技術學習的熱情,從而導致一直遊蕩在瓶頸之下,難以提升。

小公司與大公司,選擇哪個?

刨除掉薪資以外你會選擇什麼,是不有人建議小公司,因為可以接觸到各個環境,也有人建議大公司,因為正規體量大可以學習到更多。有些時候你的技術成長緩慢也是因為你的不同選擇而導致的,小公司確實要接觸各個環境,但往往如果你所做的業務體量不高,那麼你會用到的技術棧就會相對較少,同時也會技術棧研究的深度也會較淺。大公司中確實有時候你不需要去關心一個集群的部署和維護、一個中間件的開發、全套服務監控等等,但如果你願意了解這些技術在內部都是公開的,你會擁有無限的技術營養可以補充。而這最主要的是提升視野和事業。

除了業務中的CRUD開發,有些技術你真的很難接觸到!

可能很多小夥伴認為技術開發就是承接下產品需求,寫寫CRUD,不會的百度一下,就完事了,總覺得別人問的東西像再造火箭一樣。但在高體量、高並發的業務場景下,每一次的壓測優化,性能提升,都像在研究一道數學題一樣,反覆的錘鍊,壓榨性能。不斷的深究,找到最合適的設計。除了這些優化提升外,還有那麼廣闊的技術體系棧,都可能因為你只是注重CRUD而被忽略;字節碼編程、領域驅動設計架構、代理模式中間件開發、JVM虛擬機實現原理等等。

二、開發環境

JDK 1.8Idea + MavenSpring 4.3.24.RELEASE涉及工程三個,可以通過關注「公眾號」:bugstack蟲洞棧,回復源碼下載獲取(打開獲取的連結,找到序號18)

三、代理模式介紹

代理模式,圖片來自 refactoringguru.cn代理模式有點像老大和小弟,也有點像分銷商。主要解決的是問題是為某些資源的訪問、對象的類的易用操作上提供方便使用的代理服務。而這種設計思想的模式經常會出現在我們的系統中,或者你用到過的組件中,它們都提供給你一種非常簡單易用的方式控制原本你需要編寫很多代碼的進行使用的服務類。

類似這樣的場景可以想到;

你的資料庫訪問層面經常會提供一個較為基礎的應用,以此來減少應用服務擴容時不至於資料庫連接數暴增。使用過的一些中間件例如;RPC框架,在拿到jar包對接口的描述後,中間件會在服務啟動的時候生成對應的代理類,當調用接口的時候,實際是通過代理類發出的socket信息進行通過。另外像我們常用的MyBatis,基本是定義接口但是不需要寫實現類,就可以對xml或者自定義註解裡的sql語句進行增刪改查操作。四、案例場景模擬

場景模擬;實現mybatis-spring中代理類生成部分「在本案例中我們模擬實現mybatis-spring中代理類生成部分」

對於Mybatis的使用中只需要定義接口不需要寫實現類就可以完成增刪改查操作,有疑問的小夥伴,在本章節中就可以學習到這部分知識。解析下來我們會通過實現一個這樣的代理類交給spring管理的核心過程,來講述代理類模式。

這樣的案例場景在實際的業務開發中其實不多,因為這是將這種思想運用在中間件開發上,而很多小夥伴經常是做業務開發,所以對Spring的bean定義以及註冊和對代理以及反射調用的知識了解的相對較少。但可以通過本章節作為一個入門學習,逐步了解。

五、代理類模式實現過程

接下來會使用代理類模式來模擬實現一個Mybatis中對類的代理過程,也就是只需要定義接口,就可以關聯到方法註解中的sql語句完成對資料庫的操作。

這裡需要注意一些知識點;

BeanDefinitionRegistryPostProcessor,spring的接口類用於處理對bean的定義註冊。GenericBeanDefinition,定義bean的信息,在mybatis-spring中使用到的是;ScannedGenericBeanDefinition 略有不同。FactoryBean,用於處理bean工廠的類,這個類非常見。1. 工程結構

itstack-demo-design-12-00└── src ├── main │ ├── java │ │ └── org.itstack.demo.design │ │ ├── agent │ │ │ ├── MapperFactoryBean.java │ │ │ ├── RegisterBeanFactory.java │ │ │ └── Select.java │ │ └── IUserDao.java │ └── resources │ └── spring-config.xml └── test └── java └── org.itstack.demo.test └── ApiTest.java「代理模式中間件模型結構」

代理模式中間件模型結構此模型中涉及的類並不多,但都是抽離出來的核心處理類。主要的事情就是對類的代理和註冊到spring中。上圖中最上面是關於中間件的實現部分,下面對應的是功能的使用。2. 代碼實現

2.1 自定義註解

@Documented@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.METHOD})public@interface Select {String value()default ""; // sql語句}這裡我們定義了一個模擬mybatis-spring中的自定義註解,用於使用在方法層面。2.2 Dao層接口

publicinterfaceIUserDao{@Select("select userName from user where id = #{uId}")String queryUserInfo(String uId);}這裡定義一個Dao層接口,並把自定義註解添加上。這與你使用的mybatis組件是一樣的。2.1和2.2是我們的準備工作,後面開始實現中間件功能部分。2.3 代理類定義

publicclassMapperFactoryBean<T> implementsFactoryBean<T> {private Logger logger = LoggerFactory.getLogger(MapperFactoryBean.class);private Class<T> mapperInterface;publicMapperFactoryBean(Class<T> mapperInterface){this.mapperInterface = mapperInterface; }@Overridepublic T getObject()throws Exception { InvocationHandler handler = (proxy, method, args) -> { Select select = method.getAnnotation(Select.class); logger.info("SQL:{}", select.value().replace("#{uId}", args[0].toString()));return args[0] + ",小傅哥,bugstack.cn - 沉澱、分享、成長,讓自己和他人都能有所收穫!"; };return (T) Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class[]{mapperInterface}, handler); }@Overridepublic Class<?> getObjectType() {return mapperInterface; }@OverridepublicbooleanisSingleton(){returntrue; }}如果你有閱讀過mybatis源碼,是可以看到這樣的一個類;MapperFactoryBean,這裡我們也模擬一個這樣的類,在裡面實現我們對代理類的定義。通過繼承FactoryBean,提供bean對象,也就是方法;T getObject()。在方法getObject()中提供類的代理以及模擬對sql語句的處理,這裡包含了用戶調用dao層方法時候的處理邏輯。還有最上面我們提供構造函數來透傳需要被代理類,Class<T> mapperInterface,在mybatis中也是使用這樣的方式進行透傳。另外getObjectType()提供對象類型反饋,以及isSingleton()返回類是單例的。2.4 將Bean定義註冊到Spring容器

publicclassRegisterBeanFactoryimplementsBeanDefinitionRegistryPostProcessor{@OverridepublicvoidpostProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)throws BeansException { GenericBeanDefinition beanDefinition = new GenericBeanDefinition(); beanDefinition.setBeanClass(MapperFactoryBean.class); beanDefinition.setScope("singleton"); beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(IUserDao.class); BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(beanDefinition, "userDao"); BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, registry); }@OverridepublicvoidpostProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory)throws BeansException {// left intentionally blank }}這裡我們將代理的bean交給spring容器管理,也就可以非常方便讓我們可以獲取到代理的bean。這部分是spring中關於一個bean註冊過程的源碼。GenericBeanDefinition,用於定義一個bean的基本信息setBeanClass(MapperFactoryBean.class);,也包括可以透傳給構造函數信息addGenericArgumentValue(IUserDao.class);最後使用 BeanDefinitionReaderUtils.registerBeanDefinition,進行bean的註冊,也就是註冊到DefaultListableBeanFactory中。2.5 配置文件spring-config

<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"default-autowire="byName"> <bean id="userDao"class="org.itstack.demo.design.agent.RegisterBeanFactory"/></beans>接下來在配置文件中添加我們的bean配置,在mybatis的使用中一般會配置掃描的dao層包,這樣就可以減少這部分的配置。3. 測試驗證

3.1 編寫測試類

@Testpublicvoidtest_IUserDao(){ BeanFactory beanFactory = new ClassPathXmlApplicationContext("spring-config.xml"); IUserDao userDao = beanFactory.getBean("userDao", IUserDao.class); String res = userDao.queryUserInfo("100001"); logger.info("測試結果:{}", res);}測試的過程比較簡單,通過加載Bean工廠獲取我們的代理類的實例對象,之後調用方法返回結果。那麼這個過程你可以看到我們是沒有對接口先一個實現類的,而是使用代理的方式給接口生成一個實現類,並交給spring管理。3.2 測試結果

23:21:57.551 [main] DEBUG o.s.core.env.StandardEnvironment - Adding PropertySource 'systemProperties' with lowest search precedence...23:21:57.858 [main] DEBUG o.s.c.s.ClassPathXmlApplicationContext - Unable to locate LifecycleProcessor with name 'lifecycleProcessor': using default [org.springframework.context.support.DefaultLifecycleProcessor@7bc1a03d]23:21:57.859 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'lifecycleProcessor'23:21:57.860 [main] DEBUG o.s.c.e.PropertySourcesPropertyResolver - Could not find key 'spring.liveBeansView.mbeanDomain' in any property source23:21:57.861 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'userDao'23:21:57.915 [main] INFO o.i.d.design.agent.MapperFactoryBean - SQL:select userName from user where id = 10000123:21:57.915 [main] INFO org.itstack.demo.design.test.ApiTest - 測試結果:100001,小傅哥,bugstack.cn - 沉澱、分享、成長,讓自己和他人都能有所收穫!Process finished with exit code 0從測試結果可以看到,我們列印了SQL語句,這部分語句是從自定義註解中獲取的;select userName from user where id = 100001,我們做了簡單的適配。在mybatis框架中會交給SqlSession的實現類進行邏輯處理返回操作資料庫數據而這裡我們的測試結果是一個固定的,如果你願意更加深入的研究可以嘗試與資料庫操作層進行關聯,讓這個框架可以更加完善。六、總結

關於這部分代理模式的講解我們採用了開發一個關於mybatis-spring中間件中部分核心功能來體現代理模式的強大之處,所以涉及到了一些關於代理類的創建以及spring中bean的註冊這些知識點,可能在平常的業務開發中都是很少用到的,但是在中間件開發中確實非常常見的操作。代理模式除了開發中間件外還可以是對服務的包裝,物聯網組件等等,讓複雜的各項服務變為輕量級調用、緩存使用。你可以理解為你家裡的電燈開關,我們不能操作220v電線的人肉連接,但是可以使用開關,避免觸電。代理模式的設計方式可以讓代碼更加整潔、乾淨易於維護,雖然在這部分開發中額外增加了很多類也包括了自己處理bean的註冊等,但是這樣的中間件復用性極高也更加智能,可以非常方便的擴展到各個服務應用中。

bugstack蟲洞棧

沉澱、分享、成長,讓自己和他人都能有所收穫!

作者小傅哥多年從事一線網際網路Java開發,從19年開始編寫工作和學習歷程的技術匯總,旨在為大家提供一個較清晰詳細的核心技能學習文檔。如果本文能為您提供幫助,請給予支持(關注、點讚、分享)!

相關焦點

  • 重學Java 設計模式:實戰外觀模式
    四、案例場景模擬場景模擬;所有服務添加白名單校驗「在本案例中我們模擬一個將所有服務接口添加白名單的場景」在項目不斷壯大發展的路上,每一次發版上線都需要進行測試,而這部分測試驗證一般會進行白名單開量或者切量的方式進行驗證。
  • 重學Java 設計模式:實戰命令模式「模擬高檔餐廳八大菜系,小二點單...
    命令模式在我們通常的網際網路開發中相對來說用的比較少,但這樣的模式在我們的日常中卻經常使用到,那就是Ctrl+C、Ctrl+V。當然如果你開發過一些桌面應用,也會感受到這樣設計模式的應用場景。從這樣的模式感受上,可以想到這是把邏輯實現與操作請求進行分離,降低耦合方便擴展。命令模式是行為模式中的一種,以數據驅動的方式將命令對象,可以使用構造函數的方式傳遞給調用者。調用者再提供相應的實現為命令執行提供操作方法。可能會感覺這部分有一些饒,可以通過對代碼的實現進行理解,在通過實操來熟練。
  • 詳解設計模式在 Spring 中的應用
    今天,螃蟹就設計模式的內在價值做一番探討,並以spring為例進行講解,只有領略了其設計的思想理念,才能在工作學習中運用到「無形」。Spring作為業界的經典框架,無論是在架構設計方面,還是在代碼編寫方面,都堪稱行內典範。好了,話不多說,開始今天的內容。
  • Java程式設計師必會 springmvc-spring-mybatis框架整合搭建傻瓜教程
    ssm是用於將springmvc-spring-mybatis三個框架整合來進行java開發web項目。本文通過ssm三大框架整合的形式講解springmvc的使用教程,最新的框架版本主流IDE,只要按照圖中步驟能夠保證每個人的框架搭建完成並成功運行。這裡使用maven來構建項目,我們需要創建一個名為ssm的maven項目,打包方式為war包。
  • 使用Java API在Jedis中實現DAO設計模式
    在本文中,我們將學習Jedis Java客戶端中的DAO設計模式和實現。DAO模式被實現為客戶端應用程式和資料庫之間的一層。客戶端應用程式不必依賴基礎資料庫交互API(低級)。Redis資料庫中存儲的數據被建模為Domain對象(POJO類),它將僅具有getter和setter方法,客戶端應用程式僅知道域對象和高級API。
  • Mybatis_day01
    -- namespace是命名空間,用來隔離sql語句,可以隨意定義,注意:在使用代理時具有特殊的約定含義,不能隨意定義 --><mapper namespace="test"></mapper>Namespace:命名空間 用來隔離sql語句,在mybatis代理模式具有特殊的含義詳細的mapper
  • Spring Boot 2.X 實戰--SQL 資料庫(MyBatis)
    Gradle 依賴 1dependencies { 2    implementation 'org.springframework.boot:spring-boot-starter-web' 3    implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter
  • java 23種設計模式及其在spring,tomcat,jdk中的應用
    設計模式是前人(一般都是大師)對程序設計經驗的總結,學習並應用設計模式可以使我們開發的項目更加規範、便於擴展和維護,這大概是為什麼設計模式基本是面試必問的原因吧!本文主要內容有四部分:設計原則、設計模式分類、23種設計模式、設計模式應用。設計模式雖多,但重要的、主流開源框架應用較多的往往就那幾個。
  • 面試官:Mybatis 使用了哪些設計模式?
    本文轉載自【微信公眾號:java進階架構師,ID:java_jiagoushi】經微信公眾號授權轉載,如需轉載與原文作者聯繫雖然我們都知道有20多個設計模式,但是大多停留在概念層面,真實開發中很少遇到,Mybatis源碼中使用了大量的設計模式,閱讀源碼並觀察設計模式在其中的應用,能夠更深入的理解設計模式。
  • ...設計模式:實戰策略模式「模擬多種營銷類型優惠券,折扣金額計算...
    四、案例場景模擬場景模擬;商品支付使用營銷優惠券「在本案例中我們模擬在購買商品時候使用的各種類型優惠券(滿減、直減、折扣、n元購)」這個場景幾乎也是大家的一個日常購物省錢渠道,購買商品的時候都希望找一些優惠券,讓購買的商品更加實惠。
  • 五分鐘學Java:一篇文章搞懂spring和springMVC
    讓我們先看看百度百科對於spring的定義,Spring框架是由於軟體開發的複雜性而創建的。Spring使用的是基本的JavaBean來完成以前只可能由EJB完成的事情。然而,Spring的用途不僅僅限於伺服器端的開發。從簡單性、可測試性和鬆耦合性角度而言,絕大部分Java應用都可以從Spring中受益。
  • MyBatis JPA Extra,MyBatis JPA 擴展 v2.2 發布
    ;import java.text.SimpleDateFormat;import java.util.ArrayList;import java.util.Date;>import java.util.List;import org.apache.mybatis.jpa.test.dao.service.StudentsService;import org.apache.mybatis.jpa.test.domain.Students
  • spring AOP是什麼?你都拿它做什麼?
    我們知道java是一個面向對象(OOP)的語言,但它有一些弊端,比如當我們需要為多個不具有繼承關係的對象引入一個公共行為,例如日誌,權限驗證,事務等功能時,只能在在每個對象裡引用公共行為,這樣做不便於維護,而且有大量重複代碼。AOP的出現彌補了OOP的這點不足。 為了闡述清楚spring AOP,我們從將以下方面進行討論: 1.代理模式。
  • SpringBoot+Mybatis動態切換數據源
    ,單位是毫秒druid.minEvictableIdleTimeMillis=40000Mybatis配置文件(spring-dao.xml):<?-- 5.配置掃描Dao接口包,動態實現Dao接口,注入到spring容器中 --> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">  <!
  • 「原創」讓設計模式飛一會兒|⑥面試必問代理模式
    另外為了代碼看起來清晰整潔,將所有代碼中的異常處理邏輯全部拿去了哈嘍,大家好,我是高冷就是範兒,好久不見!今天我們繼續來聊設計模式這個話題。前面已經講過幾個模式,如果沒有閱讀過的朋友可以回顧一下。今天我要跟大家聊的是代理模式。
  • Java 最常見面試題
    4.final 在 java 中有什麼作用?5.java 中的 Math.round(-1.5) 等於多少?6.String 屬於基礎的數據類型嗎?7.java 中操作字符串都有哪些類?它們之間有什麼區別?8.String str="i"與 String str=new String(「i」)一樣嗎?
  • 是時候了解一下Spring中常用的設計模式了
    1.介紹設計模式是軟體開發的重要組成部分。這些解決方案不僅解決了反覆出現的問題,而且還通過識別通用模式來幫助開發人員了解框架的設計。在本教程中,我們將研究Spring框架中使用的四種最常見的設計模式:單例模式工廠方法模式代理模式
  • 1小時學會spring boot,人人都可以成為架構師「手稿」
    測試中的@TestPropertySource註解。 測試中的@SpringBootTest#properties註解特性。 命令行參數 SPRING_APPLICATION_JSON中的屬性(環境變量或系統屬性中的內聯JSON嵌入)。
  • 5w 字 | 172 圖 | 超級賽亞級 Spring Cloud 實戰
    IDEA 安裝mybatisx 插件mybatisx:mybatis plus開發的一個插件,從mapper方法快速定位到xml文件添加跨域配置passjava-gateway應用中添加配置類PassJavaCorsConfiguration.javapackage com.jackson0714.passjava.gateway.config;import org.springframework.context.annotation.Bean
  • Java 持久層框架之 MyBatis
    ; <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>1.3.1</version></dependency><dependency> <groupId>org.springframework