由于上一节我们使用了Mybatis的插件(如果忘记了可以看一下历史文章Mybatis拦截器那一节),他是基于代理模式实现的,因此我们在这一节,讲一下代理模式的原理以及实现.
代理模式又分为静态代理和动态代理,动态代理又有两种,一种是jdk动态代理,一种是CGLIB,我们这节只讲其中一种JDK动态代理,先说一下代理模式的作用.
一个类代表另一个类做事情,并且可以做一些额外的事情,在我们工作中经常使用代理模式,比如在不影响现有功能的查询中,我们在查询功能上做一些其他工作,打印执行时间,打印日志等等,代理模式大体意思就是代理对象在发现被监控的对象要执行被监控的行为要执行时,告知通知类,通知类就会拦截被监控对象的行为做一些额外的操作并执行被监控的行为.
照样看我们的是如何使用的,看代码之前介绍几个概念.
这个例子的场景就是,当洁癖老师想要找一个学生时,洁癖老师没有直接去找学生,而是拜托一个班长代理去找,但是班长没有立马去找而是去吃了个饭,吃完之后,再去找学生。
第一步:接口角色(要监控的行为)
/**
* 要监控的行为 找学生
*/
public interface BaseExecutorService{
List<Student> query();
}
第二步:接口的实现类
/**
* 接口的实现类-->被监控的对象 StudentExecutorServiceImp
*/
public class StudentExecutorServiceImp implements BaseExecutorService {
@Override
public List<Student> query() {
System.out.println("被监控的行为->找学生");
List list = Arrays.asList(new Student(1,"洁癖",true,"深圳"),new Student(1,"洁癖",true,"深圳"));
return list;
}
}
第三步:通知类
package com.jiepi.adviser;
import com.jiepi.service.BaseExecutorService;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* 通知类-->通知被监控的对象要执行的行为做一些其他的业务逻辑
*/
public class Invocation implements InvocationHandler {
private BaseExecutorService obj; //被监控的对象
public Invocation(BaseExecutorService obj) {
this.obj = obj;
}
/**
* 被监控的对象要执行的时候,会被Jvm拦截,被监控的行为query和行为query的参数传入invoke
* 然后通知类告知JVM,拦截的行为要做些什么样的操作,
*
* @param proxy 代理对象
* @param method 被监控的行为也就是query
* @param args 被监控的行为的参数 由于行为query没有参数 因此这里为 null
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
String name = method.getName();
//根据不同的监控行为做不同的处理
if (name.equals("query")) { //保证被监控的对象是在找学生,
eat(); //如果是查询,班长先去吃饭
result = method.invoke(this.obj, args);//然后再去找学生
}
return result;//班长找的学生
}
private void eat() {
System.out.println("被监控的对象执行query的时候(找学生的时候),班长就先吃饭");
}
}
第四步:代理类
package com.jiepi.util;
import com.jiepi.adviser.Invocation;
import com.jiepi.service.BaseExecutorService;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
/**
* 代理工厂类
*/
public class ProxyFactory {
public static BaseExecutorService builder(Class className) throws Exception {
//被监控的对象
BaseExecutorService obj = (BaseExecutorService) className.newInstance();
//通知对象 --绑定要通知那个被监控的对象
InvocationHandler adviser = new Invocation(obj);
/**
* 想Jvm申请监控对象 也就是代理对象
* load 被监控对象的内存真实地址
* interfaces 被监控对象的行为的实现接口
* h 当被监控的对象执行的时候, 要通知哪一个通对象
*/
BaseExecutorService proxy = (BaseExecutorService) Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), adviser);
return proxy;
}
}
第五步:运行结果
@Test
public void query() throws Exception { //proxy其实就是代理,也就是班长
BaseExecutorService proxy = ProxyFactory.builder(StudentExecutorServiceImp.class);
List list = proxy.query();//班长去找学生之前,先去吃饭
list.stream().map(it -> it.toString()).forEach(System.out::println);
}
运行结果:
被监控的对象执行query的时候(找学生的时候),班长就先吃饭
被监控的行为->找学生
id: 1 name: 洁癖 pass: trueaddress: 深圳
id: 1 name: 洁癖 pass: trueaddress: 深圳
这样我们就把一个简单的代理模式实现了,这就和Mybatsi使用插件进行拦截查询的道理一样,把主要业务和次要业务进行的松耦合,在不影响主要业务的功能进行额外的操作。在Mybatis的mapper接口没有实现类,那他是如何执行对象的sql语句呢?其实他也是利用java的动态代理,去执行相应的sql语句.