1. 程式人生 > >製作Mybatis外掛---針對Mybatis的四大物件

製作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。