製作Mybatis外掛---針對Mybatis的四大物件
注:本筆記是根據尚矽谷的MyBatis視訊記錄的
一、為什麼要製作Mybatis外掛
根據自己的意圖動態修改mybatis執行流程
二、外掛原理
當有多個外掛同時攔截同一個方法時:
在四大物件建立的時候:
1、每個創建出來的物件不是直接返回的,而是
interceptorChain.pluginAll(parameterHandler);
2、獲取到所有的Interceptor(攔截器)(外掛需要實現的介面);
呼叫interceptor.plugin(target);返回target包裝後的物件
3、外掛機制,我們可以使用外掛為目標物件建立一個代理物件;AOP(面向切面)
我們的外掛可以為四大物件創建出代理物件;
代理物件就可以攔截到四大物件的每一個執行;
注:Mybatis的四大物件
三、外掛編寫步驟
- 1、編寫Interceptor的實現類
- 2、使用@Intercepts註解完成外掛簽名
- 3、將寫好的外掛註冊到全域性配置檔案中
1、編寫Interceptor的實現類,使用@Intercepts註解完成外掛簽名
注意:@Signature可以寫多個
2、將寫好的外掛註冊到全域性配置檔案中
注:Interceptor介面中的方法
注意,此處填入的property的name和value,會在自定義外掛類中的setProperties()方法射入值,並可以在當前外掛中使用;
@Override
public void setProperties(Properties properties) {
// TODO Auto-generated method stub
System.out.println("外掛配置的資訊:"+properties);
}
四、自定義外掛示例
此處攔截四大物件之一的StatementHandler物件中的引數管理的parameterize()方法;即在引數賦值之前進行一些操作;
示例:動態的改變一下sql執行的引數:以前1號員工,實際從資料庫查詢3號員工
1、自定義外掛
package com.atguigu.mybatis.dao; import java.util.Properties; import org.apache.ibatis.executor.parameter.ParameterHandler; import org.apache.ibatis.executor.statement.StatementHandler; import org.apache.ibatis.plugin.Interceptor; import org.apache.ibatis.plugin.Intercepts; import org.apache.ibatis.plugin.Invocation; import org.apache.ibatis.plugin.Plugin; import org.apache.ibatis.plugin.Signature; import org.apache.ibatis.reflection.MetaObject; import org.apache.ibatis.reflection.SystemMetaObject; /** * 完成外掛簽名: * 告訴MyBatis當前外掛用來攔截哪個物件的哪個方法 */ @Intercepts( { @Signature(type=StatementHandler.class,method="parameterize",args=java.sql.Statement.class) }) public class MyFirstPlugin implements Interceptor{ /** * intercept:攔截: * 攔截目標物件的目標方法的執行; */ @Override public Object intercept(Invocation invocation) throws Throwable { // TODO Auto-generated method stub System.out.println("MyFirstPlugin...intercept:"+invocation.getMethod()); //動態的改變一下sql執行的引數:以前1號員工,實際從資料庫查詢3號員工 Object target = invocation.getTarget(); System.out.println("當前攔截到的物件:"+target); //拿到:StatementHandler==>ParameterHandler===>parameterObject //拿到target的元資料 MetaObject metaObject = SystemMetaObject.forObject(target); Object value = metaObject.getValue("parameterHandler.parameterObject"); System.out.println("sql語句用的引數是:"+value); //修改完sql語句要用的引數 metaObject.setValue("parameterHandler.parameterObject", 11); //執行目標方法 Object proceed = invocation.proceed(); //返回執行後的返回值 return proceed; } /** * plugin: * 包裝目標物件的:包裝:為目標物件建立一個代理物件 */ @Override public Object plugin(Object target) { // TODO Auto-generated method stub //我們可以藉助Plugin的wrap方法來使用當前Interceptor包裝我們目標物件 System.out.println("MyFirstPlugin...plugin:mybatis將要包裝的物件"+target); Object wrap = Plugin.wrap(target, this); //返回為當前target建立的動態代理 return wrap; } /** * setProperties: * 將外掛註冊時 的property屬性設定進來 */ @Override public void setProperties(Properties properties) { // TODO Auto-generated method stub System.out.println("外掛配置的資訊:"+properties); } }
方法解析:
intercept():會執行目標方法,並在之前和之後加上自己的程式碼
plugin():為目標物件建立動態代理物件
在四大物件建立bean的時候,會執行plugin()方法;
如在BaseStatementHandler類中建立ParameterHandler的時候:
setProperties():將xml中配置的值設定到屬性中
2、Mybatis全域性配置檔案中外掛的設定
<plugins>標籤可以設定多個外掛
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--plugins:註冊外掛 -->
<plugins>
<plugin interceptor="com.atguigu.mybatis.dao.MyFirstPlugin">
<property name="username" value="root"/>
<property name="password" value="123456"/>
</plugin>
</plugins>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/mybatis" />
<property name="username" value="root" />
<property name="password" value="123456" />
</dataSource>
</environment>
</environments>
<!-- 將我們寫好的sql對映檔案(EmployeeMapper.xml)一定要註冊到全域性配置檔案(mybatis-config.xml)中 -->
<mappers>
<mapper resource="EmployeeMapper.xml" />
</mappers>
</configuration>
3、測試
package com.atguigu.mybatis.test;
import java.io.IOException;
import java.io.InputStream;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;
import com.atguigu.mybatis.bean.Employee;
import com.atguigu.mybatis.dao.EmployeeMapper;
public class MyBatisTest {
public SqlSessionFactory getSqlSessionFactory() throws IOException {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
return new SqlSessionFactoryBuilder().build(inputStream);
}
@Test
public void test01() throws IOException {
// 1、獲取sqlSessionFactory物件
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
// 2、獲取sqlSession物件
SqlSession openSession = sqlSessionFactory.openSession();
try {
// 3、獲取介面的實現類物件
//會為介面自動的建立一個代理物件,代理物件去執行增刪改查方法
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
Employee employee = mapper.getEmpById(1);
System.out.println(mapper);
System.out.println(employee);
} finally {
openSession.close();
}
}
}
結果:
說明:已在外掛中修改了引數。
五、當多個攔截器攔截這個方法時的執行順序
第一個代理類:外掛會產生目標物件的代理物件
第二個代理類:多個外掛就會產生多層代理
執行順序:
Mybatis中全域性檔案的配置:
<!--plugins:註冊外掛 -->
<plugins>
<plugin interceptor="com.atguigu.mybatis.dao.MyFirstPlugin">
<property name="username" value="root"/>
<property name="password" value="123456"/>
</plugin>
<plugin interceptor="com.atguigu.mybatis.dao.MySecondPlugin"></plugin>
</plugins>
則:執行 plugin()時,會按照<xml>中的配置順序包裝,即第二個外掛包裝第一個外掛的代理物件,即MySecondPlugin包裝MyFirstPlugin
則:執行intercept()方法時,就會先執行外層的代理物件,即先執行MySecondPlugin,再執行MyFirstPlugin。