在看Mybatis发现其SQL执行使用了动态代理,这里简单做个记录
代理模式应该是最常见的设计模式之一,它很好。
![](https://img.haomeiwen.com/i15085536/93f3b1a473e884ce.png)
- 客户调用的是接口,无所谓你什么实现,完成我的功能就行
- 实际调用的是代理对象,在代理对象内部调用了实际对象的接口实现方法
- 代理是一种增强
- 代理分为动态代理和静态代理两种,区别就是代理的生成时机
- 静态代理在程序运行之前就存在,动态代理则在运行时通过反射等技术动态生成代理对象
- 动态代理分为JDK动态代理和CGLIB动态代理
- 开闭原则
其实跟代理模式很容易混淆的就是适配器模式,只需要记住代理模式是一种增强,而适配器模式是转换,而且还需要新增对外的适配接口并且所有目标接口都要继承它。
我觉得代理模式就做了两件事
- 生成代理对象
- 在代理对象中调用目标方法同时做一些增强
Base 对象
接口IService.java
public interface IService {
void doSomeThing();
}
实现IServiceImpl.java
public class IServiceImpl implements IService {
@Override
public void doSomeThing() {
System.out.println(" Hello world ");
}
}
静态代理
代理StaticProxy.java,代理的就是这个实现类IServiceImpl 了
public class StaticProxy implements IService {
public IService iService;
public StaticProxy(IService iService) {
this.iService = iService;
}
@Override
public void doSomeThing() {
beforeDo();
iService.doSomeThing();
afterDo();
}
private void beforeDo() {
System.out.println("before do this");
}
private void afterDo() {
System.out.println("after do this");
}
}
调用示例
public class StaticDemo {
public static void main(String[] args) {
// init
StaticProxy sdp = new StaticProxy(new IServiceImpl());
// invoke
sdp.doSomeThing();
}
}
静态代理的两件事:
- 以实际对象为参数构造代理对象StaticProxy
- 代理对象的方法调用时调用实际对象
关于动态代理与静态代理的区别,也就是为什么动态代理横行而静态代理门可罗雀,我讲不好。
但我想试试看。
- 动态代理可以代理一系列的接口,静态代理也可以实现一系列接口,但实际方法调用时就要一个一个去调用实际对象的同名方法
- 都说了我不会。。
动态代理 - JDK
- 需要实现指定接口InvocationHandler
- 代理对象通过反射类Proxy生成
JDKDynamicProxy.java
public class JDKDynamicProxy implements InvocationHandler {
private Object obj;
public JDKDynamicProxy(Object obj) {
this.obj = obj;
}
/**
* 实现方法
*
* @param proxy 代理对象
* @param method 代理方法
* @param args 代理方法参数
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("proxy name: " + proxy.getClass().getName());
System.out.println("method name: " + method.getName());
beforeDo();
// obj.method(args)
Object response = method.invoke(obj, args);
afterDo();
return response;
}
private void beforeDo() {
System.out.println("JDK PROXY BEFORE");
}
private void afterDo() {
System.out.println("JDK PROXY AFTER");
}
}
再看下如何生成
JDKDynamicFactory.java
public class JDKDynamicFactory {
public static IService newInstance() {
// 实际对象
Object delegate = new IServiceImpl();
// 调用处理器
InvocationHandler handler = new JDKDynamicProxy(delegate);
return newInstance(delegate, handler);
}
private static IService newInstance(Object obj, InvocationHandler invocationHandler) {
/**
* 类加载器,可传null
*
* 代理接口,可直接写 Class<?>{Xxxx.class}
*
* 当前代理对象, InvocationHandler
*/
return (IService) Proxy.newProxyInstance(
obj.getClass().getClassLoader(),
obj.getClass().getInterfaces(),
invocationHandler);
}
}
仔细分析下最后一步的方法 Proxy.newProxyInstance
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h) throws IllegalArgumentException {
Objects.requireNonNull(h);
final Class<?>[] intfs = interfaces.clone();
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
/*
* Look up or generate the designated proxy class.
*/
// 1.为接口生成代理对象
Class<?> cl = getProxyClass0(loader, intfs);
/*
* Invoke its constructor with the designated invocation handler.
*/
try {
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
// 2.使用反射拿到代理对象的构造器
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
}
// 3.构造实例返回
return cons.newInstance(new Object[]{h});
} catch (IllegalAccessException|InstantiationException e) {
throw new InternalError(e.toString(), e);
} catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new InternalError(t.toString(), t);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString(), e);
}
}
其实 做了三件事
1.为接口生成代理对象
Class<?> cl = getProxyClass0(loader, intfs);
2.使用反射拿到代理对象的构造器,以 InvocationHandler 为参数
private static final Class<?>[] constructorParams = {InvocationHandler.class};
final Constructor<?> cons = cl.getConstructor(constructorParams);
3.使用调用处理器实例构造接口实例返回
InvocationHandler h;
return cons.newInstance(new Object[]{h});
那么整个工厂额外做的一件事就是构造调用处理器实例 h 了
0.构造调用处理器
Object delegate = new IServiceImpl();
InvocationHandler handler = new JDKDynamicProxy(delegate);
JDKDynamicProxy 实现了 InvocationHandler 接口,并能实现方法调用从代理类到委托类的分派转发
其内部通常包含指向委托类实例的引用,用于真正执行分派转发过来的方法调用
再看下调用JDKDynamicDemo.java
public class JDKDynamicDemo {
public static void main(String[] args) {
IService proxy = JDKDynamicFactory.newInstance();
proxy.doSomeThing();
}
}
调用结果
proxy name: com.sun.proxy.$Proxy0
method name: doSomeThing
JDK PROXY BEFORE
Hello world
JDK PROXY AFTER
JDK动态代理的两件事
- JDKDynamicFactory.newInstance 构造代理对象
- JDKDynamicProxy.invoke 调用实际对象
动态代理 - CGLIB
相比之前的代理模式实现,CGLIB最牛逼的点应该就是可以不用实现接口,他可以直接代理类
不过同样,代理类需要实现指定接口MethodInterceptor
代理类CglibDynamicProxy
public class CglibDynamicProxy implements MethodInterceptor {
public CglibDynamicProxy() {}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
Object ot = null;
beforeDo();
ot = methodProxy.invokeSuper(o, objects);
afterDo();
return ot;
}
private void beforeDo() {
System.out.println("CGLIB PROXY BEFORE");
}
private void afterDo() {
System.out.println("CGLIB PROXY AFTER");
}
}
被代理类CglibService.java
public class CglibService {
public void a() {
System.out.println("aaaaaaaaaaaaaaa");
}
}
调用CglibDynamicDemo.java
public class CglibDynamicDemo {
public static void main(String[] args){
Enhancer enhancer = new Enhancer();
0. 设置代理目标
enhancer.setSuperclass(CglibService.class);
1. 设置CGLIB代理处理
enhancer.setCallback(new CglibDynamicProxy());
2. 创建代理对象
CglibService iService = (CglibService) enhancer.create();
iService.a();
}
}
执行结果
CGLIB PROXY BEFORE
aaaaaaaaaaaaaaa
CGLIB PROXY AFTER
CGLIB的实现包有两种,其中还有一段很乱的历史。
代理模式的经典场景
-
spring aop
底层实现就是动态代理,我隐约记得是如果有接口就用JDK没有就用CGLIB -
mybatis
的SQL执行
......
参考链接
动态代理与静态代理区别
今天一天都在为mybatis的SQL执行流程源码做准备,醉了~
网友评论