概要:在我们使用MyBatis的时候,发现Mapper仅仅只是一个接口,而不是一个包含逻辑的实现类的,我们都知道,一个接口是没有办法去执行的,那么为什么MyBatis就可以呢?这就涉及到动态代理了。
反射技术
被代理的类:
/**
* @author: Jason
* @date: 2020-07-19 22:19
*/
public class ReflectService {
/**
* 服务方法
* @param name 姓名
*/
public void sayHello(String name) {
System.err.println("hello " + name);
}
}
通过反射创建ReflectService并调用其sayHello方法:
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* @author: Jason
* @date: 2020-07-19 22:21
*/
public class ReflectTest {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
// 通过反射创建ReflectService对象
Object reflectService = Class.forName(ReflectService.class.getName()).newInstance();
// 获取服务方法:sayHello
Method method = reflectService.getClass().getMethod("sayHello", String.class);
// 反射调用方法
Object zhangsan = method.invoke(reflectService, "zhangsan");
// void 无返回值,null
System.out.println(zhangsan);
}
}
JDK动态代理
- 编写服务类(HelloService)和接口(HelloServiceImpl),这个是真正的服务提供者,在JDK代理中接口是必须的。
- 编写代理类,实现InvocationHandler接口
- 通过bind方法绑定委托对象返回一个代理类
- 实现invoke方法,通过method调用真是对象的方法,并可以在前后做增强
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* @author: Jay Mitter
* @date: 2020-07-19 22:34
*/
public class HelloServiceProxy implements InvocationHandler {
/**
* 真实的代理对象
*/
private Object target;
/**
* 绑定委托对象返回一个代理类
* @param target
* @return
*/
public Object bind(Object target) {
this.target = target;
// target.getClass().getInterfaces() ==> JDK代理需要提供接口
// spring默认使用jdk动态代理,如果类没有接口,则使用cglib。
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
/**
*
* @param proxy 代理对象
* @param method 被调用的方法
* @param args 方法的参数
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.err.println("################### 我是JDK动态代理 ###################");
Object result = null;
// 增强
// 反射方法前调用
System.err.println("我准备说hello。");
// 执行方法,相当于调用HelloServiceImpl类的sayHello方法
result = method.invoke(target, args);
// 反射方法后调用
System.err.println("我说过了hello。");
return result;
}
}
HelloService接口:
/**
* @author: Jay Miiter
* @date: 2020-07-19 22:28
*/
public interface HelloService {
/**
* sayHello
* @param name 姓名
*/
void sayHello(String name);
}
HelloService接口实现类HelloServiceImpl:
/**
* @author: Jason
* @date: 2020-07-19 22:29
*/
public class HelloServiceImpl implements HelloService {
@Override
public void sayHello(String name) {
System.err.println("hello " + name);
}
}
JDK的代理最大的缺点是需要提供接口,而MyBatis的Mapper就是一个接口,它采用的就是JDK的动态代理。
CgLib动态代理
JDK提供的动态代理存在一个缺陷,就是你必须提供接口才可以使用,为了克服这个缺陷,我们可以使用开源框架——CGLIB,它是一种流行的动态代理。
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* @author: Jay Mitter
* @date: 2020-07-20 21:43
*/
public class HelloServiceCgLib implements MethodInterceptor {
/**
* 创建代理对象
* 根据一个类型产生代理类
* @param clazz
* @return
*/
public Object getInstance(Class<?> clazz) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(clazz);
// 回调方法
enhancer.setCallback(this);
// 创建代理对象
return enhancer.create();
}
/**
* 回调方法
*
* @param object
* @param method
* @param args
* @param methodProxy
* @return
* @throws Throwable
*/
@Override
public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.err.println("################# 我是CGLIB的动态代理 #################");
// 增强
// 反射方法前调用
System.err.println("我准备说hello。");
Object returnObj = methodProxy.invokeSuper(object, args);
// 反射方法后调用
System.err.println("我说过hello了。");
return returnObj;
}
}
这样便能够实现CGLIB的动态代理。在MyBatis中通常在延迟加载的时候才会用到CGLIB的动态代理。
无需接口的HelloServiceImplNoInterface类:
/**
* @author: Jay Mitter
* @date: 2020-07-19 22:29
* CgLib 无需实现接口
*/
public class HelloServiceImplNoInterface {
public void sayHello(String name) {
System.err.println("hello " + name);
}
}
测试
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* @author: Jay Mitter
* @date: 2020-07-20 21:31
*/
public class HelloServiceMain {
public static void main(String[] args) {
testJdkProxy();
testCgLibProxy();
}
private static void testJdkProxy() {
// 将生成的字节码保存到本地,
createProxyClassFile();
HelloServiceProxy helloServiceHandler = new HelloServiceProxy();
HelloService proxy = (HelloService) helloServiceHandler.bind(new HelloServiceImpl());
proxy.sayHello("张三");
}
private static void testCgLibProxy() {
HelloServiceCgLib helloServiceCgLib = new HelloServiceCgLib();
HelloServiceImplNoInterface instance = (HelloServiceImplNoInterface) helloServiceCgLib.getInstance(HelloServiceImplNoInterface.class);
instance.sayHello("李四");
}
private static void createProxyClassFile() {
String name = "ProxyHelloService";
byte[] data = ProxyGenerator.generateProxyClass(name, new Class[]{HelloService.class});
FileOutputStream out = null;
try {
out = new FileOutputStream(name + ".class");
System.out.println((new File("hello")).getAbsolutePath());
out.write(data);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (null != out) {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
通过字节啊生成的实现类:
/**
* @author Jay Mitter
* @date 2020-07-20 21:43
* JDK动态代理通过字节码生成的最终真正的代理类,它继承自Proxy并实现了我们定义的HelloService接口
* 在实现Subject接口方法的内部,通过反射调用了InvocationHandlerImpl的invoke方法。
*/
public final class ProxyHelloService extends Proxy implements HelloService {
private static final long serialVersionUID = 4642095113614858015L;
private static Method m1;
private static Method m3;
private static Method m2;
private static Method m0;
public ProxyHelloService(InvocationHandler var1) {
super(var1);
}
@Override
public final boolean equals(Object var1) {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
@Override
public final void sayHello(String var1) {
try {
super.h.invoke(this, m3, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
@Override
public final String toString() {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
@Override
public final int hashCode() {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m3 = Class.forName("com.pengjs.kkb.mybatis.service.HelloService").getMethod("sayHello", Class.forName("java.lang.String"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
网友评论