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

猜你喜欢

转载自blog.csdn.net/jinhaijing/article/details/84313668
今日推荐