在介绍Mybatis拦截器代码之前,我们先研究下jdk自带的动态代理及优化
其实动态代理也是一种设计模式...优于静态代理,同时动态代理我知道的有两种,一种是面向接口的jdk的代理,第二种是基于第三方的非面向接口的cglib.
我们现在说的是jdk的动态代理,因为mybatis拦截器也是基于这个实现的。
简单介绍就是建立一个目标类的代理类。在执行目标类的方法前先执行代理类的方法,目标类的方法是在代理类中执行的,所以目标类方法执行前后,可以再代理类中进行其他操作。
简单版:
复制代码
1 public interface Target {
2 void work();
3 }
复制代码
复制代码
1 public class TargetImpl implements Target {
2 @Override
3 public void work() {
4 System.out.println("我就只能做这么多了");
5 }
6 }
复制代码
下面创建一个代理类
复制代码
1 public class TargetProxy implements InvocationHandler {
2
3 private Object target;
4
5 public TargetProxy(Object target){
6 this.target = target;
7 }
8
9 /**
10 * 缺点 代理需要做的事情不是很灵活。直接在这里面写死了。
11 * @param proxy
12 * @param method
13 * @param args
14 * @return
15 * @throws Throwable
16 */
17 @Override
18 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
19 System.out.println("代理在前面做事情了");
20 try {
21 return method.invoke(target, args);
22 } catch (InvocationTargetException e) {
23 throw e.getCause();
24 }
25 }
26
27 public static Object getProxyObject(Object target){
28 return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),new TargetProxy(target));
29 }
30 }
复制代码
复制代码
1 public class Client {
2
3 public static void main(String[] args) {
4 Target target = new TargetImpl();
5 target.work();
6 System.out.println("-----------------------------");
7 Target target1 = (Target) TargetProxy.getProxyObject(new TargetImpl());
8 target1.work();
9 }
10 }
复制代码
结果:
我就只能做这么多了
-----------------------------
代理在前面做事情了
我就只能做这么多了
———————————————————————————————————————————————————————————————————————————————
这样是最常见的代理了,但是有个缺点,代理类要做的事情在代理类写死了,要换就得多写一个代理类。那下面我们就把代理的事项单独拿出来。
增加拦截器接口和实现类
复制代码
1 public interface Interceptor {
2 void doOtherThings();
3 }
复制代码
复制代码
1 public class InterceptorImpl implements Interceptor {
2 @Override
3 public void doOtherThings() {
4 System.out.println("还可以灵活地做其他事情");
5 }
6 }
复制代码
代理类变一下:
复制代码
1 public class TargetProxy implements InvocationHandler {
2
3 private Object target;
4
5 private Interceptor interceptor;
6
7 public TargetProxy(Object target, Interceptor interceptor) {
8 this.interceptor = interceptor;
9 this.target = target;
10 }
11
12 @Override
13 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
14 interceptor.doOtherThings();
15 return method.invoke(target, args);
16 }
17
18 /**
19 * 获取代理对象的时候顺便把拦截逻辑对象也传过来
20 *
21 * @param interceptor
22 * @return
23 * @paramarget
24 */
25 public static Object getProxyObject(Object target, Interceptor interceptor) {
26 return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new TargetProxy(target, interceptor));
27 }
28
29
30 }
复制代码
复制代码
1 public class Client {
2
3 public static void main(String[] args) {
4 Target target = new TargetImpl();
5 target.work();
6 System.out.println("-----------------------------");
7 Interceptor interceptor = new InterceptorImpl();
8 Target target1 = (Target) TargetProxy.getProxyObject(new TargetImpl(),interceptor);
9 target1.work();
10 System.out.println("-----------------------------");
11 Interceptor interceptor1 = new Interceptor() {
12 @Override
13 public void doOtherThings() {
14 System.out.println("换个拦截方式?");
15 }
16 };
17 Target target2 = (Target) TargetProxy.getProxyObject(new TargetImpl(),interceptor1);
18 target2.work();
19 }
20 }
复制代码
结果:
我就只能做这么多了
-----------------------------
还可以灵活地做其他事情
我就只能做这么多了
-----------------------------
换个拦截方式?
我就只能做这么多了
———————————————————————————————————————————————————————————————————————————————
这样写是不是感觉挺好了。但是你是不是要拦截所有方法的呢?正常场景是不是只需要拦截method中某个某几个方法呢?那这样就把拦截器加个参数,method好了。
复制代码
1 public interface Interceptor {
2 void doOtherThings(Method method, Object[] args);
3 }
复制代码
复制代码
1 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
2 interceptor.doOtherThings(method, args);
3 return method.invoke(target, args);
4 }
复制代码
注意:java设计模式中有一个规则就迪米特法则,也叫最少知识法则,意思应该就是一个类知道的越少越好,对一个对象知道的越少越好。
那这样看反正拦截器都需要method还不如让代理类也不知道method好了,method执行需要代理类的target那就把target也传过去。
传了这么多参数,我们可以不可以把这些参数封装成一个对象传过去,暂时就叫Invocation
然后method.invoke执行就需要这三个参数,那么这三个操作就放在Invocation里面好了,要不然你还得让拦截器去获取这些属性
复制代码
1 public class Invocation {
2
3 private Object target;
4
5 private Method method;
6
7 private Object[] args;
8
9 public Invocation(Object target, Method method, Object[] args) {
10 this.target = target;
11 this.method = method;
12 this.args = args;
13 }
14
15 public Object proceed() throws InvocationTargetException, IllegalAccessException {
16 return method.invoke(target,args);
17 }
18
19 public Object getTarget() {
20 return target;
21 }
22
23 public void setTarget(Object target) {
24 this.target = target;
25 }
26
27 public Method getMethod() {
28 return method;
29 }
30
31 public void setMethod(Method method) {
32 this.method = method;
33 }
34
35 public Object[] getArgs() {
36 return args;
37 }
38
39 public void setArgs(Object[] args) {
40 this.args = args;
41 }
42 }
复制代码
下面看拦截器怎么实现
复制代码
1 public interface Interceptor {
2
3 public Object intercept(Invocation invocation) throws InvocationTargetException, IllegalAccessException;
4 }
复制代码
复制代码
1 public class InterceptorImpl implements Interceptor {
2 @Override
3 public Object intercept(Invocation invocation) throws InvocationTargetException, IllegalAccessException {
4 if(invocation.getMethod().getName().equals("work")){
5 System.out.println("真的假的");
6 return invocation.proceed();
7 }else{
8 return null;
9 }
10
11 }
复制代码
复制代码
1 public class TargetProxyTwo implements InvocationHandler {
2
3 private Object target;
4
5 private Interceptor interceptor;
6
7 public TargetProxyTwo(Object target, Interceptor interceptor) {
8 this.target = target;
9 this.interceptor = interceptor;
10 }
11
12 @Override
13 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
14 return interceptor.intercept(new Invocation(target,method,args));
15 }
16
17 public static Object getProxyObj(Object target,Interceptor interceptor){
18 return Proxy.newProxyInstance(target.getClass().getClassLoader(),
19 target.getClass().getInterfaces(),
20 new TargetProxyTwo(target, interceptor));
21 }
22 }
复制代码
复制代码
1 public class Client {
2
3 public static void main(String[] args) {
4 Target target = (Target) TargetProxyTwo.getProxyObj(new TargetImpl(),new InterceptorImpl());
5 target.work();
6 }
7 }
复制代码
结果:
真的假的
我就只能做这么多了
———————————————————————————————————————————————————————————————————————————————
迪米特法则来看,客户端现在需要知道 拦截器,和代理类。 那么能不能把代理类的注册放到拦截器里面呢?可以的。来看下
复制代码
1 public interface Interceptor {
2
3 public Object register(Object target);
4
5 public Object intercept(Invocation invocation) throws InvocationTargetException, IllegalAccessException;
6 }
复制代码
复制代码
1 public class InterceptorImpl implements Interceptor {
2
3
4 @Override
5 public Object register(Object target) {
6 return TargetProxyTwo.getProxyObj(target,this);
7 }
8
9 @Override
10 public Object intercept(Invocation invocation) throws InvocationTargetException, IllegalAccessException {
11 if(invocation.getMethod().getName().equals("work")){
12 System.out.println("真的假的");
13 return invocation.proceed();
14 }else{
15 return invocation.procedd();
16 }
17
18 }
19 }
复制代码
复制代码
1 public class Client {
2
3 public static void main(String[] args) {
4
5 Target target = (Target) new InterceptorImpl().register(new TargetImpl());
6 target.work();
7 }
8 }
复制代码
这样是不是很完美?
这样写是有问题的
复制代码
1 if(invocation.getMethod().getName().equals("work")){
2 System.out.println("真的假的");
3 return invocation.proceed();
4 }else{
5 return invocation.proceed();
6 }
复制代码
把判断方法的逻辑方法拦截方法里面,那么假如十个方法,十个拦截逻辑呢?你是不是要写大一堆if else?这样是美观的。怎么解决呢?注解啊!!!
在拦截器上添加要拦截的方法注解不就好了嘛。
来看代码:
复制代码
1 @Retention(RetentionPolicy.RUNTIME)
2 @java.lang.annotation.Target(ElementType.TYPE)
3 public @interface MethodName {
4 public String value();
5 }
复制代码
1 @MethodName("work")
2 public class InterceptorImpl implements Interceptor
复制代码
1 public class TargetProxy implements InvocationHandler {
2
3 private Object target;
4
5 private Interceptor interceptor;
6
7 public TargetProxy(Object target, Interceptor interceptor) {
8 this.interceptor = interceptor;
9 this.target = target;
10 }
11
12 @Override
13 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
14 MethodName methodName = this.interceptor.getClass().getAnnotation(MethodName.class);
15 if (methodName == null) {
16 throw new NullPointerException("拦截器注解方法名字为空");
17 }
18 String name = methodName.value();
19 if (name.equals(method.getName())) {
20 return interceptor.intercept(new Invocation(target, method, args));
21 }
22 return method.invoke(target, args);
23 }
24
25 /**
26 * 获取代理对象的时候顺便把拦截逻辑对象也传过来
27 *
28 * @param interceptor
29 * @return
30 * @paramarget
31 */
32 public static Object getProxyObject(Object target, Interceptor interceptor) {
33 return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new TargetProxy(target, interceptor));
34 }
35 }
复制代码
怎么样,这样是不是就很完美了。
先预告下: 上面的类对应Mybatis的类:
Invocation,Interceptor完全一样。TargetProxy对应Plugin类。regist方法对应wrap方法
好的下面来看Mybatis的拦截器了
MyBatis 允许你在已映射语句执行过程中的某一点进行拦截调用。默认情况下,MyBatis 允许使用插件来拦截的方法调用包括:
Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
ParameterHandler (getParameterObject, setParameters)
ResultSetHandler (handleResultSets, handleOutputParameters)
StatementHandler (prepare, parameterize, batch, update, query)
Mybatis的拦截器的使用方式是通过xml配置的
复制代码
复制代码
通过这样的配置,就有了默认的拦截器:
那么这四种拦截器分别在什么时候添加的?
XMLConfigBuilder
复制代码
1 private void pluginElement(XNode parent) throws Exception {
2 if (parent != null) {
3 for (XNode child : parent.getChildren()) {
4 String interceptor = child.getStringAttribute("interceptor");
5 Properties properties = child.getChildrenAsProperties();
6 Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();
7 interceptorInstance.setProperties(properties);
8 configuration.addInterceptor(interceptorInstance);
9 }
10 }
11 }
复制代码
Mybatis中有一个拦截器链,典型的责任链模式
那么这四种拦截器分别在什么时候开始执行拦截呢?
先介绍下责任链获取恰当的拦截器的方法,Configuration类中
复制代码
1 public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
2 ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);
3 parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
4 return parameterHandler;
5 }
6
7 public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler,
8 ResultHandler resultHandler, BoundSql boundSql) {
9 ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds);
10 resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
11 return resultSetHandler;
12 }
13
14 public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
15 StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
16 statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
17 return statementHandler;
18 }
19
20 public Executor newExecutor(Transaction transaction) {
21 return newExecutor(transaction, defaultExecutorType);
22 }
23
24 public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
25 executorType = executorType == null ? defaultExecutorType : executorType;
26 executorType = executorType == null ? ExecutorType.SIMPLE : execut