Mybatis 插件(plugins)簡介
Mybatis 插件像
是個攔截器,本質上是JDK動態代理的封裝,返回了代理對象,起到了攔截器作用,接下來將按照從0開始實現Mybatis 插件(plugins)功能,再使用一下Mybatis插件應用一下,這裏實現並不是與Mybatis Plugin代碼一模一樣,有一些簡化和優化,更加方便理解
簡單版攔截器功能 v0.1
定義攔截器
public interface Interceptor {
Object intercept(Object target, Method method, Object[] args) throws Throwable;
}
通用動態代理實現
public class Plugin implements InvocationHandler {
private final Object target;
private final Interceptor interceptor;
public Plugin(Object target, Interceptor interceptor) {
this.target = target;
this.interceptor = interceptor;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return interceptor.intercept(target, method, args);
}
}
封裝生成代理類工具類
public class PluginUtils {
public static <T> T wrap(T target, Interceptor interceptor) {
return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),
new Plugin(target, interceptor));
}
}
編寫業務測試類
public interface IPerson {
String sayHello(String name);
void test();
}
public class PersonImpl implements IPerson {
@Override
public String sayHello(String name) {
String msg = "hello, " + name;
System.out.println(msg);
return msg;
}
@Override
public void test() {
System.out.println("test");
}
}
測試類
實現自定義攔截器
public class MyInterceptor implements Interceptor {
@Override
public Object intercept(Object target, Method method, Object[] args) throws Throwable {
System.out.println("before.");
Object invoke = method.invoke(target, args);
System.out.println("after.");
return invoke;
}
}
測試類
public class MyTest {
public static void main(String[] args) {
Interceptor interceptor = new MyInterceptor();
IPerson person = new PersonImpl();
IPerson personProxy = PluginUtils.wrap(person, interceptor);
String message = personProxy.sayHello("zhangsan");
System.out.println("return: " + message);
}
}
測試結果,攔截成功
before.
hello, zhangsan
after.
return: hello, zhangsan
實現註解功能 v0.2
攔截器註解@Intercepts
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Intercepts {
Signature[] value();
}
標記攔截哪些類註解@Signature
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({})
public @interface Signature {
Class<?> type();
SignatureMethod[] method();
}
標記攔截哪些方法註解@SignatureMethod
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({})
public @interface SignatureMethod {
String method();
Class<?>[] args();
}
優化代理生成工具類,添加解析註解信息並傳入給Plugin類
public class PluginUtils {
public static <T> T wrap(T target, Interceptor interceptor) {
Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),
new Plugin(target, interceptor, signatureMap));
}
// 解析註解,獲取對應的類和方法
private static Map<Class<?>, Set<Method>> getSignatureMap(Interceptor interceptor) {
Intercepts interceptsAnnotation = interceptor.getClass().getAnnotation(Intercepts.class);
if (interceptsAnnotation == null) {
throw new RuntimeException(
"No @Intercepts annotation was found in interceptor " + interceptor.getClass().getName());
}
Signature[] sigs = interceptsAnnotation.value();
Map<Class<?>, Set<Method>> signatureMap = new HashMap<Class<?>, Set<Method>>();
for (Signature sig : sigs) {
Set<Method> methods = signatureMap.get(sig.type());
if (methods == null) {
methods = new HashSet<Method>();
signatureMap.put(sig.type(), methods);
}
SignatureMethod[] signatureMethod = sig.method();
for (int i = 0; i < signatureMethod.length; i++) {
try {
Method method = sig.type().getMethod(signatureMethod[i].method(), signatureMethod[i].args());
methods.add(method);
} catch (NoSuchMethodException e) {
throw new RuntimeException(
"Could not find method on " + sig.type() + " named " + sig.method() + ". Cause: " + e, e);
}
}
}
return signatureMap;
}
}
優化自定義攔截器,對方法進行控制,只攔截註解對應的方法
public class Plugin implements InvocationHandler {
private final Object target;
private final Interceptor interceptor;
private final Map<Class<?>, Set<Method>> signatureMap;
public Plugin(Object target, Interceptor interceptor, Map<Class<?>, Set<Method>> signatureMap) {
this.target = target;
this.interceptor = interceptor;
this.signatureMap = signatureMap;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Set<Method> methods = signatureMap.get(method.getDeclaringClass());
if (methods != null && methods.contains(method)) {
return interceptor.intercept(target, method, args);
}
return method.invoke(target, args);
}
}
測試類
實現自定義攔截器
@Intercepts({
@Signature(type = IPerson.class, method = {
@SignatureMethod(method = "sayHello", args = { String.class }) }) })
public class MyInterceptor3 implements Interceptor {
@Override
public Object intercept(Object target, Method method, Object[] args) throws Throwable {
System.out.println("3333.");
Object invoke = method.invoke(target, args);
return invoke;
}
}
測試類
public class MyTest {
public static void main(String[] args) {
Interceptor interceptor = new MyInterceptor3();
IPerson person = new PersonImpl();
IPerson personProxy = PluginUtils.wrap(person, interceptor);
String message = personProxy.sayHello("zhangsan");
System.out.println("return: " + message);
}
}
測試結果,攔截成功
3333.
hello, zhangsan
return: hello, zhangsan
修改攔截器自定義攔截器,改爲@SignatureMethod(method = "toString", args = {})
@Intercepts({
@Signature(type = IPerson.class, method = {
@SignatureMethod(method = "test", args = {}) }) })
public class MyInterceptor3 implements Interceptor {
@Override
public Object intercept(Object target, Method method, Object[] args) throws Throwable {
System.out.println("3333.");
Object invoke = method.invoke(target, args);
return invoke;
}
}
執行測試類
public class MyTest3 {
public static void main(String[] args) {
Interceptor interceptor = new MyInterceptor3();
IPerson person = new PersonImpl();
IPerson personProxy = PluginUtils.wrap(person, interceptor);
String message = personProxy.sayHello("zhangsan");
System.out.println("return: " + message);
personProxy.test();
}
}
測試結果,可以看出,只代理了test方法,沒有代理sayHello
hello, zhangsan
return: hello, zhangsan
3333.
test
若想代理,需要修改註解,再次執行測試類即可
@Intercepts({
@Signature(type = IPerson.class, method = {
@SignatureMethod(method = "sayHello", args = { String.class }),
@SignatureMethod(method = "test", args = { }) }) })
攔截器鏈實現 v0.3
鏈式封裝
public class InterceptorChain {
private final List<Interceptor> interceptors = new ArrayList<>();
public Object wrapAll(Object target) {
for (Interceptor interceptor : interceptors) {
target = PluginUtils.wrap(target, interceptor);
}
return target;
}
public void addInterceptor(Interceptor interceptor) {
interceptors.add(interceptor);
}
}
添加實現自定義攔截器
@Intercepts({
@Signature(type = IPerson.class, method = {
@SignatureMethod(method = "sayHello", args = { String.class }) }) })
public class MyInterceptor3 implements Interceptor {
@Override
public Object intercept(Object target, Method method, Object[] args) throws Throwable {
System.out.println("3333.");
Object invoke = method.invoke(target, args);
return invoke;
}
}
@Intercepts({
@Signature(type = IPerson.class, method = {
@SignatureMethod(method = "sayHello", args = { String.class }) }) })
public class MyInterceptor4 implements Interceptor {
@Override
public Object intercept(Object target, Method method, Object[] args) throws Throwable {
System.out.println("4444.");
Object invoke = method.invoke(target, args);
return invoke;
}
}
@Intercepts({
@Signature(type = IPerson.class, method = {
@SignatureMethod(method = "sayHello", args = { String.class }) }) })
public class MyInterceptor5 implements Interceptor {
@Override
public Object intercept(Object target, Method method, Object[] args) throws Throwable {
System.out.println("5555.");
Object invoke = method.invoke(target, args);
return invoke;
}
}
測試類
public class MyTest2 {
public static void main(String[] args) {
InterceptorChain chain = new InterceptorChain();
chain.addInterceptor(new MyInterceptor3());
chain.addInterceptor(new MyInterceptor4());
chain.addInterceptor(new MyInterceptor5());
IPerson person = new PersonImpl();
IPerson personProxy = (IPerson) chain.wrapAll(person);
String message = personProxy.sayHello("zhangsan");
System.out.println("return: " + message);
}
}
測試結果,多攔截器效果
5555.
4444.
3333.
hello, zhangsan
return: hello, zhangsan
可以思考一下爲什麼倒序輸出5555. 4444. 3333. ?
攔截器鏈實現 v0.4
封裝一下攔截器Interceptor的參數
public class Invocation {
private final Object target;
private final Method method;
private final Object[] args;
public Invocation(Object target, Method method, Object[] args) {
this.target = target;
this.method = method;
this.args = args;
}
public Object getTarget() {
return target;
}
public Method getMethod() {
return method;
}
public Object[] getArgs() {
return args;
}
public Object proceed() throws InvocationTargetException, IllegalAccessException {
return method.invoke(target, args);
}
}
修改攔截器Interceptor參數
public interface Interceptor {
Object intercept(Invocation invocation) throws Throwable;
}
插件Plugin封裝修改return interceptor.intercept(new Invocation(target, method, args));
public class Plugin implements InvocationHandler {
private final Object target;
private final Interceptor interceptor;
private final Map<Class<?>, Set<Method>> signatureMap;
public Plugin(Object target, Interceptor interceptor, Map<Class<?>, Set<Method>> signatureMap) {
this.target = target;
this.interceptor = interceptor;
this.signatureMap = signatureMap;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Set<Method> methods = signatureMap.get(method.getDeclaringClass());
if (methods != null && methods.contains(method)) {
return interceptor.intercept(new Invocation(target, method, args));
}
return method.invoke(target, args);
}
}
修改自定義攔截器實現Object invoke = invocation.proceed();
@Intercepts({
@Signature(type = IPerson.class, method = {
@SignatureMethod(method = "sayHello", args = { String.class }) }) })
public class MyInterceptor3 implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
System.out.println("3333.");
Object invoke = invocation.proceed();
return invoke;
}
}
測試類
public static void main(String[] args) {
Interceptor interceptor = new MyInterceptor3();
IPerson person = new PersonImpl();
IPerson personProxy = PluginUtils.wrap(person, interceptor);
String message = personProxy.sayHello("zhangsan");
System.out.println("return: " + message);
}
}
測試類
3333.
hello, zhangsan
return: hello, zhangsan
接下來分析一下官網的使用
Mybatis官網插件(plugins)分析
MyBatis 允許你在映射語句執行過程中的某一點進行攔截調用。默認情況下,MyBatis 允許使用插件來攔截的方法調用包括:
- Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
- ParameterHandler (getParameterObject, setParameters)
- ResultSetHandler (handleResultSets, handleOutputParameters)
- StatementHandler (prepare, parameterize, batch, update, query)
所有我們註冊的類只能是上面的四個接口,ExamplePlugin.java
攔截器例子
@Intercepts({@Signature(
type= Executor.class,
method = "update",
args = {MappedStatement.class,Object.class})})
public class ExamplePlugin implements Interceptor {
private Properties properties = new Properties();
public Object intercept(Invocation invocation) throws Throwable {
// implement pre processing if need, 攔截前
Object returnObject = invocation.proceed();
// implement post processing if need, 攔截後
return returnObject;
}
public void setProperties(Properties properties) {
this.properties = properties;
}
}
從例子可以看出使用,enjoy…
完整代碼例子
- 執行命令從github上面拉取代碼:git clone [email protected]:dengjili/mybatis-3-mybatis-3.4.6.git
- 相關代碼目錄
src/test/java priv.mybatis.example03
src/test/java priv.mybatis.example04
src/test/java priv.mybatis.example05