在学习Mybatis的时候,发现使用了动态代理。
- Mybatis利用动态代理技术生成了mapper接口的代理类,所以我们可以调用mapper接口中定义的抽象方法进行增删改查操作。
- Mybatis的插件也是用动态代理和责任链来实现的。
什么是代理模式?
代理模式又叫委托模式,是为某个对象提供一个代理对象,并且由代理对象控制对原对象的访问。代理模式通俗来讲就是我们生活中常见的中介。
在不改变目标对象方法的情况下,对方法进行增强。
按照代理创建的时期来进行分类的话, 可以分为两种:静态代理、动态代理。如果我们在代码编译时就确定了被代理的类是哪一个,那么就可以直接使用静态代理;如果不能确定,那么可以使用类的动态加载机制,在代码运行期间加载被代理的类。
静态代理
要求被代理类和代理类同时实现相应的一套接口,通过代理类调用重写接口的方法,实际上调用的是原始对象的同样的方法。
- 顶层接口
/**
* 顶层接口
*
* @author zhangyu
* @date 2020-01-17
*/
public interface Animal {
void action();
}
- 被代理类
/**
* 被代理类
*
* @author zhangyu
* @date 2020-01-17
*/
public class Mouse implements Animal {
@Override
public void action() {
System.out.println("一只老鼠---------");
}
}
- 代理类
/**
* 代理类
*
* @author zhangyu
* @date 2020-01-17
*/
public class MouseProxy implements Animal{
private Animal animal;
public MouseProxy(Animal animal) {
this.animal = animal;
}
@Override
public void action() {
System.out.println("代理类-----start");
// 调用被代理类对象的方法
animal.action();
System.out.println("代理类-----end");
}
}
- 启动方法
public static void main(String[] args) {
Mouse mouse = new Mouse();
MouseProxy proxy = new MouseProxy(mouse);
// 调用代理类对象的方法
proxy.action();
}
- 输出结果
代理类-----start
一只老鼠---------
代理类-----end
缺点:
如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。
代理对象只服务于一种类型的对象(一个接口对应一个代理类),如果要服务多类型的对象,势必要为每一种对象都进行代理。如上的代码是只为Animal接口的访问提供了代理,但是如果还要为其他接口提供代理的话,就需要我们再次添加相应的代理类。
动态代理
解释:在程序运行时根据需要动态创建目标类的代理对象,对目标类的方法进行增强,而不是预先定义。
根据如上对静态代理的介绍,每个代理类只能为一个接口服务,这样在程序中就会产生很多个代理类,相比静态代理,动态代理一个非常显著的优点是可以通过一个代理类完成全部的代理功能。
而动态代理有一个缺点,那就是只能代理基于接口的类,而无法代理没有接口的委托类。
在Java中要想实现动态代理机制,需要java.lang.reflect.InvocationHandler接口和 java.lang.reflect.Proxy 类的支持。
- 代理类
/**
* 代理类
*
* @author zhangyu
* @date 2020-01-17
*/
public class MouseProxy implements InvocationHandler {
/**
* 被代理对象
*/
private Object object;
public MouseProxy(Object object) {
this.object = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("代理类-----start");
Object o = method.invoke(object, args);
System.out.println("代理类-----end");
return o;
}
}
- 启动方法
public static void main(String[] args) {
Mouse mouse = new Mouse();
MouseProxy proxy = new MouseProxy(mouse);
// 生成代理对象
Animal animal = (Animal)Proxy.newProxyInstance(
mouse.getClass().getClassLoader(),
mouse.getClass().getInterfaces(), proxy);
animal.action();
}
newProxyInstance()方法的三个参数依次为:
1. loader: 用哪个类加载器去加载代理对象,被代理类的类加载器
2. interfaces:动态代理类需要实现的接口,增强的方法
3. h:自定义的代理类,动态代理方法在执行时,会先调用h里面的invoke方法去执行
所以在执行Mouse类中的action方法时,会先去执行代理类MouseProxy中重写的invoke()方法。