探究MyBatis插件原理以及自定義插件實現(xiàn)
插件簡介
?般情況下,開源框架都會提供插件或其他形式的拓展點,供開發(fā)者??拓展。這樣的好處是顯?易?的,?是增加了框架的靈活性。?是開發(fā)者可以結合實際需求,對框架進?拓展,使其能夠更好的?作。以MyBatis為例,我們可基于MyBati s插件機制實現(xiàn)分?、分表,監(jiān)控等功能。由于插件和業(yè)務?關,業(yè)務也?法感知插件的存在。因此可以?感植?插件,在?形中增強功能。
Mybatis插件介紹
Mybati s作為?個應??泛的優(yōu)秀的ORM開源框架,這個框架具有強?的靈活性,在四?組件(Executor、StatementHandler、ParameterHandler、ResultSetHandler)處提供了簡單易?的插 件擴展機制。Mybatis對持久層的操作就是借助于四?核?對象。MyBatis?持?插件對四?核?對象進?攔截,對mybatis來說插件就是攔截器,?來增強核?對象的功能,增強功能本質(zhì)上是借助于底層的 動態(tài)代理實現(xiàn)的,換句話說,MyBatis中的四?對象都是代理對象。
MyBatis所允許攔截的?法如下:
- 執(zhí)?器Executor (update、query、commit、rollback等?法);
- SQL語法構建器StatementHandler(prepare、parameterize、batch、updates query等? 法);
- 參數(shù)處理器ParameterHandler (getParameterObject、setParameters?法);
- 結果集處理器ResultSetHandler (handleResultSets、handleOutputParameters等?法);
Mybatis插件原理
在四?對象創(chuàng)建的時候
- 每個創(chuàng)建出來的對象不是直接返回的,?是
interceptorChain.pluginAll(parameterHandler)
; - 獲取到所有的Interceptor (攔截器)(插件需要實現(xiàn)的接?);調(diào)?
interceptor.plugin(target)
;返回 target 包裝后的對象 - 插件機制,我們可以使?插件為?標對象創(chuàng)建?個代理對象;AOP (?向切?)我們的插件可以為四?對象創(chuàng)建出代理對象,代理對象就可以攔截到四?對象的每?個執(zhí)?;
攔截
插件具體是如何攔截并附加額外的功能的呢?以ParameterHandler來說。
public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object object, BoundSql sql, InterceptorChain interceptorChain){ ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement,object,sql); parameterHandler = (ParameterHandler)interceptorChain.pluginAll(parameterHandler); return parameterHandler; } public Object pluginAll(Object target) { for (Interceptor interceptor : interceptors) { target = interceptor.plugin(target); } return target; }
interceptorChain保存了所有的攔截器(interceptors),是mybatis初始化的時候創(chuàng)建的。調(diào)?攔截器鏈中的攔截器依次的對?標進?攔截或增強。interceptor.plugin(target)中的target就可以理解為mybatis中的四?對象。返回的target是被重重代理后的對象
如果我們想要攔截Executor的query?法,那么可以這樣定義插件:
@Intercepts({ @Signature( type = Executor.class, method = "query", args={MappedStatement.class,Object.class,RowBounds.class,ResultHandler.class} ) }) public class ExeunplePlugin implements Interceptor { //省略邏輯 }
除此之外,我們還需將插件配置到sqlMapConfig.xml中。
<plugins> <plugin interceptor="com.zjq.plugin.ExamplePlugin"></plugin> </plugins>
這樣MyBatis在啟動時可以加載插件,并保存插件實例到相關對象(InterceptorChain,攔截器鏈) 中。待準備?作做完后,MyBatis處于就緒狀態(tài)。我們在執(zhí)?SQL時,需要先通過DefaultSqlSessionFactory創(chuàng)建 SqlSession。Executor 實例會在創(chuàng)建 SqlSession 的過程中被創(chuàng)建, Executor實例創(chuàng)建完畢后,MyBatis會通過JDK動態(tài)代理為實例?成代理類。這樣,插件邏輯即可在 Executor相關?法被調(diào)?前執(zhí)?。
以上就是MyBatis插件機制的基本原理。
?定義插件
插件接口
Mybatis 插件接?-Interceptor
- Intercept?法,插件的核??法
- plugin?法,?成target的代理對象
- setProperties?法,傳遞插件所需參數(shù)
?定義插件
設計實現(xiàn)?個?定義插件
@Intercepts({//注意看這個?花括號,也就這說這?可以定義多個@Signature對多個地?攔截,都?這個攔截器 @Signature(type = StatementHandler.class , //這是指攔截哪個接? method = "prepare", //這個接?內(nèi)的哪個?法名,不要拼錯了 args = { Connection.class, Integer .class}), //這是攔截的?法的?參,按順序?qū)懙竭@,不要多也不要少,如果?法重載,可是要通過?法名和?參來確定唯?的 }) public class MyPlugin implements Interceptor { // //這?是每次執(zhí)?操作的時候,都會進?這個攔截器的?法內(nèi) @Override public Object intercept(Invocation invocation) throws Throwable { //增強邏輯 System.out.println("對?法進?了增強...."); return invocation.proceed(); //執(zhí)?原?法 } /** * //主要是為了把這個攔截器?成?個代理放到攔截器鏈中 * ^Description包裝?標對象 為?標對象創(chuàng)建代理對象 * @Param target為要攔截的對象 * @Return代理對象 */ @Override public Object plugin(Object target) { System.out.println("將要包裝的?標對象:"+target); return Plugin.wrap(target,this); } /**獲取配置?件的屬性**/ //插件初始化的時候調(diào)?,也只調(diào)??次,插件配置的屬性從這?設置進來 @Override public void setProperties(Properties properties) { System.out.println("插件配置的初始化參數(shù):"+properties ); } }
sqlMapConfig.xml
mapper接?
mapper.xml
<mapper namespace="com.zjq.mapper.UserMapper"> <!--sql語句抽取--> <sql id="selectUser"> select * from user </sql> <select id="findByCondition" parameterType="user" resultType="user"> <include refid="selectUser"></include> <where> <if test="id!=0"> and id=#{id} </if> <if test="username!=null and username!=''"> and username=#{username} </if> <if test="password!=null and password!=''"> and password=#{password} </if> </where> </select> </mapper>
測試類
public class PluginTest { @Test public void test() throws IOException { InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml"); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream); SqlSession sqlSession = sqlSessionFactory.openSession(); UserMapper userMapper = sqlSession.getMapper(UserMapper.class); User condition = new User(); //condition.setId(1); condition.setUsername("zjq"); List<User> byPaging = userMapper.findByCondition(condition); for (User user : byPaging) { System.out.println(user); } } }
源碼分析
執(zhí)?插件邏輯Plugin實現(xiàn)了 InvocationHandler
接?,因此它的invoke?法會攔截所有的?法調(diào)?。invoke?法會 對所攔截的?法進?檢測,以決定是否執(zhí)?插件邏輯。該?法的邏輯如下:
// -Plugin public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { try { /* *獲取被攔截?法列表,?如: * signatureMap.get(Executor.class), 可能返回 [query, update, commit] */ Set<Method> methods = signatureMap.get(method.getDeclaringClass()); //檢測?法列表是否包含被攔截的?法 if (methods != null && methods.contains(method)) { //執(zhí)?插件邏輯 return interceptor.intercept(new Invocation(target, method, args)); //執(zhí)?被攔截的?法 return method.invoke(target, args); } catch(Exception e){ } }
invoke?法的代碼?較少,邏輯不難理解。?先,invoke?法會檢測被攔截?法是否配置在插件的@Signature注解中,若是,則執(zhí)?插件邏輯,否則執(zhí)?被攔截?法。插件邏輯封裝在intercept中,該?法的參數(shù)類型為Invocationo Invocation主要?于存儲?標類,?法以及?法參數(shù)列表。下?簡單看?下該類的定義
public class Invocation { private final Object target; private final Method method; private final Object[] args; public Invocation(Object targetf Method method, Object[] args) { this.target = target; this.method = method; //省略部分代碼 public Object proceed() throws InvocationTargetException, IllegalAccessException { //調(diào)?被攔截的?法
關于插件的執(zhí)?邏輯就分析結束。
pageHelper分頁插件
MyBatis可以使?第三?的插件來對功能進?擴展,分?助?PageHelper是將分?的復雜操作進?封裝,使?簡單的?式即可獲得分?的相關數(shù)據(jù)
開發(fā)步驟:
- 導?通?PageHelper坐標
<dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper</artifactId> <version>3.7.5</version> </dependency> <dependency> <groupId>com.github.jsqlparser</groupId> <artifactId>jsqlparser</artifactId> <version>0.9.1</version> </dependency>
- 在mybatis核?配置?件中配置PageHelper插件
<!--注意:分?助?的插件 配置在通?館mapper之前*-->* <plugin interceptor="com.github.pagehelper.PageHelper"> <!—指定?? —> <property name="dialect" value="mysql"/> </plugin>
- 測試分?代碼實現(xiàn)
@Test public void testPageHelper() { //設置分?參數(shù) PageHelper.startPage(1, 2); User condition = new User(); //condition.setId(1); condition.setUsername("zjq"); List<User> select = userMapper.findByCondition(condition); for (User user : select) { System.out.println(user); } }
獲得分?相關的其他參數(shù)
//其他分?的數(shù)據(jù) PageInfo<User> pageInfo = new PageInfo<User>(select); System.out.println("總條數(shù):"+pageInfo.getTotal()); System.out.println("總?數(shù):"+pageInfo. getPages ()); System.out.println("當前?:"+pageInfo. getPageNum()); System.out.println("每?顯示?度:"+pageInfo.getPageSize()); System.out.println("是否第??:"+pageInfo.isIsFirstPage()); System.out.println("是否最后??:"+pageInfo.isIsLastPage());
通? mapper
什么是通?Mapper
通?Mapper就是為了解決單表增刪改查,基于Mybatis的插件機制。開發(fā)?員不需要編寫SQL,不需要在DAO中增加?法,只要寫好實體類,就能?持相應的增刪改查?法
如何使?
- ?先在maven項?,在pom.xml中引?mapper的依賴
<dependency> <groupId>tk.mybatis</groupId> <artifactId>mapper</artifactId> <version>3.1.2</version> </dependency>
- Mybatis配置?件中完成配置
<plugins> <!--分?插件:如果有分?插件,要排在通?mapper之前--> <plugin interceptor="com.github.pagehelper.PageHelper"> <property name="dialect" value="mysql"/> </plugin> <plugin interceptor="tk.mybatis.mapper.mapperhelper.MapperInterceptor"> <!-- 通?Mapper接?,多個通?接??逗號隔開 --> <property name="mappers" value="tk.mybatis.mapper.common.Mapper"/> </plugin> </plugins>
- 實體類設置主鍵
@Table(name = "t_user") public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; private String username; }
- 定義通?mapper
import com.zjq.domain.User; import tk.mybatis.mapper.common.Mapper; public interface UserMapper extends Mapper<User> { }
- 測試
@Test public void test1() throws IOException { Inputstream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml"); SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsStream); SqlSession sqlSession = build.openSession(); UserMapper userMapper = sqlSession.getMapper(UserMapper.class); User user = new User(); user.setId(4); //(1)mapper基礎接? //select 接? //根據(jù)實體中的屬性進?查詢,只能有一個返回值 User user1 = userMapper.selectOne(user); //查詢?nèi)拷Y果 List<User> users = userMapper.select(null); //根據(jù)主鍵字段進?查詢,?法參數(shù)必須包含完整的主鍵屬性,查詢條件使?等號 userMapper.selectByPrimaryKey(1); //根據(jù)實體中的屬性查詢總數(shù),查詢條件使?等號 userMapper.selectCount(user); // insert 接? //保存?個實體,null值也會保存,不會使?數(shù)據(jù)庫默認值 int insert = userMapper.insert(user); //保存實體,null的屬性不會保存,會使?數(shù)據(jù)庫默認值 int i = userMapper.insertSelective(user); // update 接? //根據(jù)主鍵更新實體全部字段,null值會被更新 int i1 = userMapper.updateByPrimaryKey(user); // delete 接? //根據(jù)實體屬性作為條件進?刪除,查詢條件使?等號 int delete = userMapper.delete(user); //根據(jù)主鍵字段進?刪除,?法參數(shù)必須包含完整的主鍵屬性 userMapper.deleteByPrimaryKey(1); //(2)example?法 Example example = new Example(User.class); example.createCriteria().andEqualTo("id", 1); example.createCriteria().andLike("val", "1"); //?定義查詢 List<User> users1 = userMapper.selectByExample(example); }
以上就是探究MyBatis插件原理以及自定義插件實現(xiàn)的詳細內(nèi)容,更多關于MyBatis插件原理的資料請關注腳本之家其它相關文章!
相關文章
Java IO中字節(jié)流復制圖片實現(xiàn)代碼
這篇文章主要介紹了Java IO中字節(jié)流復制圖片實現(xiàn)代碼的相關資料,需要的朋友可以參考下2017-04-04JavaScript中的isTrusted屬性及其應用場景詳解
在現(xiàn)代 Web 開發(fā)中,JavaScript 是構建交互式應用的核心語言,隨著前端技術的不斷發(fā)展,開發(fā)者需要處理越來越多的復雜場景,例如事件處理、數(shù)據(jù)傳遞和狀態(tài)管理等,本文將通過一個實際案例,深入探討 isTrusted 屬性的來源、作用,需要的朋友可以參考下2025-01-01java實現(xiàn)python session功能代碼實例
這篇文章主要介紹了java實現(xiàn)python session功能代碼實例,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2019-11-11IntelliJ?IDEA?2021.3永久最新激活至2099年(親測有效)
最新版idea2021.3已出來,很多網(wǎng)友迫不及待的要升級idea2021最新版,今天小編抽空給大家整理了一篇教程關于idea2021.3最新激活教程,本文以idea2021.2.3為例通過圖文并茂的形式給大家分享激活詳細過程,感興趣的朋友參考下吧2020-12-12使用java + selenium + OpenCV破解網(wǎng)易易盾滑動驗證碼的示例
這篇文章主要介紹了使用java + selenium + OpenCV破解網(wǎng)易易盾滑動驗證碼,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-02-02