文章目录
大家好,我是入错行的bug猫。(http://blog.csdn.net/qq_41399429,谢绝转载)
每天进步一点,今日再接再励~
动态代理在Java中有着广泛的应用,比如Spring AOP、Mybatis数据查询、RPC远程调用、性能监控,甚至事务处理等。
代理模式,根据代码的生成时机,分为两种:
- 静态代理:代码块在源码阶段已存在,经过编译之后生成在class文件中;
- 动态代理:代码块在运行过程中,根据运行环境参数决定代码如何生成,自动生成class字节码,然后再加载到JVM中;
所谓静态,也就是在程序运行前,就已经存在代理类的字节码文件,代理类和被代理类的关系在运行前就确定了。
而动态代理的源码,是在程序运行期间由JVM根据反射等机制动态的生成,所以在运行前并不存在代理类的字节码文件。
而根据动态生成字节码的技术手段,又分两种:
- Jdk动态代理
- cglib动态代理
静态代理
先了解静态代理,然后理解静态代理的优缺点缺点,再来学习动态代理;
编写一个接口IUserService
,以及该接口的一个实现类UserService
java
interface IUserService {
void findById(String uid);
void update(Object user, String uid);
}
class UserService implements IUserService {
public void findById(String uid) {
System.out.println("查询 findById");
}
public void update(Object user, String uid) {
System.out.println("更新 update");
}
}
通过静态代理对IUserService
已存在的实例,进行功能增强;在调用findById
和update
之前记录一些日志,模拟开启事务。
写一个代理类UserServiceProxy
,代理类需要实现IUserService
;整体结构有些类似装饰模式;
java
class UserServiceProxy implements IUserService {
private final IUserService target; // 被代理的对象
public UserServiceProxy() {
this.target = new UserService(); //被代理对象,是在代理类中生成
}
@Override
public void findById(String uid) {
try {
before();
target.findById(uid); // 这里才实际调用真实对象的方法
after();
} catch ( Exception ex ) {
exception(ex);
throw ex;
}
}
@Override
public void update(Object user, String uid) {
try {
before();
target.update(user, uid); // 这里才实际调用真实对象的方法
after();
} catch ( Exception ex ) {
exception(ex);
throw ex;
}
}
private void before() { // 在执行方法之前执行
System.out.println(String.format("log start time [%s] ", new Date()));
System.out.println("开启事务");
}
private void after() { // 在执行方法之后执行
System.out.println(String.format("log end time [%s] ", new Date()));
System.out.println("提交事务");
}
private void exception(Exception ex){
System.out.println(String.format("log error [%s] ", ex.getMessage()));
System.out.println("提交回滚");
}
}
客户端测试:
java
@Test
public void demo1(){
IUserService proxy = new UserServiceProxy();
proxy.findById("1");
System.out.println("");
proxy.update(new Object(), "4");
}
输出:
markdown
log start time [Sat Jun 17 09:34:05 CST 2023]
开启事务
查询 selectById
log end time [Sat Jun 17 09:34:05 CST 2023]
提交事务
log start time [Sat Jun 17 09:34:05 CST 2023]
开启事务
更新 update
log end time [Sat Jun 17 09:34:05 CST 2023]
提交事务
通过静态代理,我们达到了功能增强的目的,而且没有侵入原代码,这是静态代理的一个优点。
静态代理实现简单,且不侵入原代码,但是,当场景稍微复杂一些的时候,静态代理的缺点也会暴露出来:
-
当需要代理多个类的时候,由于代理对象要实现与目标对象一致的接口,有两种方式:
1.1 只维护一个代理类,由这个代理类实现多个接口,但是这样就导致代理类过于庞大;
1.2 新建多个代理类,每个目标对象对应一个代理类,但是这样会产生过多的代理类
-
当接口需要增加、删除、修改方法的时候,目标对象与代理类都要同时修改,不易维护。
如何改进?
可以发现,代理类的代码块流程绝大部分是相似的:在执行真实方法之前执行before
,在执行真实方法成功之后执行after
,发生异常执行exception
;
类似固定的模板,就可以使用程序来自动编写代码,用程序自动写程序,也就是动态代理。
哪些类可以动态生成代码?Enhancer
、InterfaceMaker
、BeanGenerator
有机会再单独写一篇
实现动态代理的思考方向:
为了让生成的代理类与目标对象保持一致性,从现在开始将介绍以下两种最常见的方式:
通过实现接口的方式:JDK动态代理
通过继承类的方式:CGLIB动态代理
Jdk动态代理
JDK动态代理主要涉及两个类:java.lang.reflect.Proxy
和java.lang.reflect.InvocationHandler
编写一个调用逻辑处理器JdkProxyHandler
类,提供日志增强功能,并实现InvocationHandler
接口;
在JdkProxyHandler
中维护一个目标对象,这个对象是被代理的对象;在invoke
方法中编写方法调用的逻辑处理;
java
class JdkProxyHandler implements InvocationHandler {
private final Object target; // 被代理的对象,实际的方法执行者
public JdkProxyHandler(Object target) {
this.target = target;
}
/**
* @param proxy 代理对象实例 todo ①
* @param method 被代理类的Interface中的方法; todo ②
* @param args
* */
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
before();
//反射调用 target 的 method 方法。
//proxy是代理对象实例,因此在反射调用的时候,需要替换成被代理类target对象;
Object result = method.invoke(target, args);
after();
return result; // 返回方法的执行结果
} catch ( Exception ex ) {
exception(ex);
throw ex;
}
}
private void before() { // 在执行方法之前执行
System.out.println(String.format("log start time [%s] ", new Date()));
System.out.println("开启事务");
}
private void after() { // 在执行方法之后执行
System.out.println(String.format("log end time [%s] ", new Date()));
System.out.println("提交事务");
}
private void exception(Exception ex){
System.out.println(String.format("log error [%s] ", ex.getMessage()));
System.out.println("提交回滚");
}
}
编写客户端,获取动态生成的代理类的对象须借助java.lang.reflect.Proxy
类的newProxyInstance
方法:
java
@Test
public void demo2(){
// 设置变量可以保存动态代理类,默认名称以 $Proxy0 格式命名
System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
// 1. 创建被代理的对象。实际代码中,可以使用到对象工厂
IUserService userService = new UserService();
// 2. 代理类请求处理器:拦截处理代理对象上的所有的方法调用。
// 和demo1中的UserServiceProxy类相似。
// 由于JdkProxyHandler可以复用,被代理类(userService)可以使多例,所以JdkProxyHandler也应该是多例,被代理类应该显示传入。
// 主要JdkProxyHandler构造器使用Interface作为入参,因此JdkProxyHandler可以代理一切Interface的实现类。
InvocationHandler proxyHandler = new JdkProxyHandler(userService);
// 3. 获取对应的 ClassLoader。类加载机制,如果搞错ClassLoader,可能会导致动态生成的代理类,无法被加载:提示 ClassNotFoundException;保持和被代理类在一个ClassLoader中
ClassLoader classLoader = userService.getClass().getClassLoader();
// 4. 获取所有接口的Class,这里的UserService只实现了一个接口IUserService,
Class[] interfaces = userService.getClass().getInterfaces();
/*
5.根据上面提供的信息,创建代理对象 在这个过程中,
a.JDK会通过根据传入的参数信息动态地在内存中创建和.class 文件等相关字节码;
b.然后根据相应的字节码转换成对应的class加载到JVM中;
c.然后调用newInstance()创建代理实例;
*/
IUserService proxy = (IUserService) Proxy.newProxyInstance(classLoader, interfaces, proxyHandler);
//输出动态代理之后的class代码
//generateClassFile(proxy.getClass(), "UserServiceProxy"); //在代码在文章最后
// 调用代理的方法
proxy.findById("3");
System.out.println("");
proxy.update(new Object(), "4");
//JDK动态代理,底层本质是使用方法反射;性能瓶颈受Jdk版本影响
}
执行之后可以查看动态生成的代理类:
java
//Jdk动态生成的类片段
public final class UserServiceProxy extends Proxy implements IUserService {
private static Method m3;
private static Method m4;
//注意这个入参,就是JdkProxyHandler
public UserServiceProxy(InvocationHandler var1) throws {
super(var1);
}
public final void findById(String var1) throws {
try {
//注意第一个参数this,即代表JdkProxyHandler.invoke的第一个入参,是代理类本身; todo ①
//第二个参数m4,是从cn.bugcat.code.IUserService中获取 todo ②
super.h.invoke(this, m4, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final void update(Object var1, String var2) throws {
try {
super.h.invoke(this, m3, new Object[]{var1, var2});
} catch (RuntimeException | Error var4) {
throw var4;
} catch (Throwable var5) {
throw new UndeclaredThrowableException(var5);
}
}
static {
try {
//findById、update方法,都是从Interface(IUserService)中获取 todo ②
m4 = Class.forName("cn.bugcat.code.IUserService").getMethod("findById", Class.forName("java.lang.String"));
m3 = Class.forName("cn.bugcat.code.IUserService").getMethod("update", Class.forName("java.lang.Object"), Class.forName("java.lang.String"));
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
UserServiceProxy
继承了Proxy 类,并且实现了被代理的所有接口,以及equals 、hashCode 、toString等方法;- 由于
UserServiceProxy
继承了Proxy
类,所以每个代理类都会关联一个InvocationHandler
方法调用处理器; - 类和所有方法都被
public final
修饰,所以代理类只可被使用,不可以再被继承; - 每个方法都有一个Method 对象来描述,Method 对象在
static
静态代码块中创建,以m + 数字
的格式命名; - 调用方法的时候通过
super.h.invoke(this, m1, new Object[]{});
调用,其中的super.h.invoke
实际上是在创建代理的时候传递给Proxy.newProxyInstance
的 JdkProxyHandler 对象,它继承InvocationHandler
类,负责实际的调用处理逻辑; - 而
JdkProxyHandler
的invoke 方法接收到method 、args 等参数后,进行一些处理,然后通过反射让被代理的对象target执行方法;
仔细观察JdkProxyHandler
,构造器中入参使用Object 类型接受,也就是意味JdkProxyHandler
类不光可以传入UserService
实现类,也可以传入其他任何对象实例;
Proxy.newProxyInstance
是根据第二个参数Interfaces 动态生成方法,而这些方法恰好UserService
也实现了,代理类和具体类,通过interfaces
联系到一起;
最终执行JdkProxyHandler
构造器 入参对象的所有方法 ,都会统一执行JdkProxyHandler.invoke
代码。
cglib动态代理
spring 框架中内置了cglib
相关Jar包内容,spring项目可以直接使用:
被代理类 :原对象;
代理子类:由cglib自动根据原对象生成的子类;
java
class CglibProxyHandler implements MethodInterceptor {
/**
* @param target 代理子类对象;
* @param method 被代理类的方法;
* @param args
* @param methodProxy 被代理的句柄方法 todo ③
* */
@Override
public Object intercept(Object target, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
try {
before();
//注意这里是调用invokeSuper而不是invoke,否则死循环。 todo ③
//method是被代理的方法,但是由于tagert是代理子类,执行method.invoke,实际上是表示执行代理子类的方法,代理子类又会继续执行MethodInterceptor.intercept方法,导致又回到此处代码,造成死循环;
//此处应该直接执行invokeSuper,表示直接调用被代理类的句柄方法;
Object result = methodProxy.invokeSuper(target, args);
after();
return result; // 返回方法的执行结果
} catch ( Exception ex ) {
exception(ex);
throw ex;
}
}
private void before() { // 在执行方法之前执行
System.out.println(String.format("log start time [%s] ", new Date()));
System.out.println("开启事务");
}
private void after() { // 在执行方法之后执行
System.out.println(String.format("log end time [%s] ", new Date()));
System.out.println("提交事务");
}
private void exception(Exception ex){
System.out.println(String.format("log error [%s] ", ex.getMessage()));
System.out.println("提交回滚");
}
}
调用方式:
java
@Test
public void demo3(){
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, paths);
MethodInterceptor proxyHandler = new CglibProxyHandler();
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserService.class); // 设置超类,cglib是通过继承来实现的
enhancer.setCallback(proxyHandler);
IUserService userService = (IUserService)enhancer.create(); // 创建代理类
userService.findById("4");
userService.update(new Object(), "4");
}
动态生成类片段:
java
public class UserService$$EnhancerByCGLIB$$a1b35990 extends UserService implements Factory {
final void CGLIB$update$0(Object var1, String var2) {
super.update(var1, var2); //调用父类方法 => UserService.update
}
public final void update(Object var1, String var2) { //代理类暴露的方法,等价于重写了父类UserService的update方法
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0; // 实际上是我们自定义的CglibProxyHandler对象
if (var10000 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
if (var10000 != null) {
// 第一个参数,代理类对象 todo ③
// CGLIB$update$0$Method 父类对应的方法
// CGLIB$update$0$Proxy当前代理的 CGLIB$update$0方法,该方法会继续调用父类的对应方法
var10000.intercept(this, CGLIB$update$0$Method, new Object[]{var1, var2}, CGLIB$update$0$Proxy);
} else {
super.update(var1, var2);
}
}
}
Jdk动态代理与cglib动态代理对比
- Jdk动态代理:基于Java反射机制实现,必须要实现了接口的类,才能用这种办法生成代理对象。
- cglib动态代理:基于ASM机制实现,通过操作字节码生成子类,代理代码块集成在子类上,实际调用被代理类的方法时,和原生调用效率一样;
- cglib由于使用继承被代理实现,因此如果是类、方法被
final
修饰,则无法使用!被cglib代理的类,无法再次被代理! - Jdk不受
final
关键词影响;被代理之后,仍然可以被Jdk再次代理; - cglib在创建代理子类的时候,可以通过
CallbackFilter
,可以为每个方法创建单独的MethodInterceptor
;后续调用时,方法与方法之间,代码在物理层面隔离,互相不影响; - Jdk只能在
InvocationHandler
实现类中,需要自行做分支处理;
使用案例
低配Mybatis
java
interface UserDao {
String findById(String uid);
String update(Object user, String uid);
}
class MapperProxyHandler implements MethodInterceptor {
private static Map<String, Map<String, String>> mapperMap = new HashMap<>();
static {
//初始化,模拟Mapper.xml文件
Map<String, String> mapper = new HashMap<>();
//Map的key,及为UserDao中的方法名,以及Mapper.xml的标签id
mapper.put("findById", "select * from user where id = ?");
mapper.put("update", "update user set name = ? where id = ?");
//UserDao.class.getName(),对应到Mapper.xml的namespace
mapperMap.put(UserDao.class.getName(), mapper);
}
@Override
public Object intercept(Object target, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
String methodName = method.getName();
Class<?> mapperClass = method.getDeclaringClass();
Map<String, String> mapper = mapperMap.get(mapperClass.getName());
if( mapper != null ){
return mapper.get(methodName);
}
return null;
}
}
@Test
public void demo4(){
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, paths);
MethodInterceptor proxyHandler = new MapperProxyHandler();
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Object.class); // 设置超类;此时UserDao只是一个Interface,没有任何具体的实现类,因此超类设置为默认的Object;
enhancer.setInterfaces(new Class[]{UserDao.class});
enhancer.setCallback(proxyHandler);
UserDao userDao = (UserDao)enhancer.create(); // 创建代理类
String findById = userDao.findById("4");
System.out.println(findById); //打印sql
String update = userDao.update(new Object(), "4");
System.out.println(update);
}
低配Feign
java
interface MyFeign {
@RequestMapping(value = "http://127.0.0.1:8080/aq", method = RequestMethod.POST)
ResponseEntity<Void> aq();
@RequestMapping(value = "https://www.baidu.com/s", method = RequestMethod.GET)
String baidu(@RequestParam("wd") String keyword);
}
class FeignProxyHandler implements MethodInterceptor {
/**
* target:代理子类
* method:被代理类方法
*/
@Override
public Object intercept(Object target, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
//method是被代理类中的方法,因此可以通过method获取到:方法上注解;入参列表;入参上的注解;方法的返回类型
RequestMapping requestMapping = AnnotationUtils.findAnnotation(method, RequestMapping.class);
RequestMethod[] requestMethods = requestMapping.method();
String[] paths = requestMapping.value();
Type returnType = method.getGenericReturnType();
Parameter[] parameters = method.getParameters();
Map<String, Integer> argsIndex = new HashMap<>();
for ( int idx = 0; idx < parameters.length; idx++ ) {
Parameter parameter = parameters[idx];
RequestParam requestParam = AnnotationUtils.findAnnotation(parameter, RequestParam.class);
argsIndex.put(requestParam.name(), idx);
}
Map<String, Object> reqMap = new HashMap<>();
argsIndex.forEach((pname, idx) -> {
reqMap.put(pname, args[idx]);
});
if( requestMethods[0] == RequestMethod.GET ){
String resp = HttpFactory.mutate().doGet().send(paths[0], reqMap);
return resultType(resp, returnType);
} else if (requestMethods[0] == RequestMethod.POST ){
String resp = HttpFactory.mutate().doPost().send(paths[0], reqMap);
return resultType(resp,returnType);
}
return null;
}
private <R> R resultType(String resp, Type type){
if( resp == null ){
return null;
}
if( type instanceof Class ){
if( String.class.isAssignableFrom((Class) type) ){
return (R) resp;
}
}
return JSONObject.parseObject(resp, type);
}
}
@Test
public void demo5(){
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, paths);
MethodInterceptor proxyHandler = new FeignProxyHandler();
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Object.class); // 设置超类;此时MyFeign只是一个Interface,没有任何具体的实现类,因此超类设置为默认的Object;
enhancer.setInterfaces(new Class[]{MyFeign.class});
enhancer.setCallback(proxyHandler);
MyFeign feign = (MyFeign)enhancer.create(); // 创建代理类
ResponseEntity<Void> aq = feign.aq();
System.out.println(JSONObject.toJSONString(aq));
String baidu = feign.baidu("csdn");
System.out.println(baidu);
}
拦截器
拦截器,不属于sevlet
、filter
这一类物理层面存在的组件,而是通过代码逻辑出来的一种代码结构;通过对动态代理学习,我们可以在Object result = method.invoke(target, args);
Object result = methodProxy.invokeSuper(target, args);
代码的前后,添加自定义的代码,增强被代理类方法;
但是,这部分属于核心代码JdkProxyHandler CglibProxyHandler ,一般编写完之后,会统一存放在公共位置。如果业务模块想添加功能,势必需要修改到这部分核心代码;违背了支持扩展、封闭修改原则;
因此在此基础之上,逻辑出一套拦截器模式,对业务模块开放修改;
java
//切入点对象
class Point {
private final Object target;
private final Method method;
private final Object[] args;
private final MethodProxy methodProxy;
public Point(Object target, Method method, Object[] args, MethodProxy methodProxy) {
this.target = target;
this.method = method;
this.args = args;
this.methodProxy = methodProxy;
}
public Object postHandle() throws Throwable{
Object result = methodProxy.invokeSuper(target, args);
return result;
}
}
//拦截器接口
interface MyInterceptor {
default Object doInterceptor(Point point) throws Throwable {
return point.postHandle();
}
}
class MyProxyHandler implements MethodInterceptor {
private final MyInterceptor interceptor;
public MyProxyHandler(MyInterceptor interceptor) {
this.interceptor = interceptor;
}
@Override
public Object intercept(Object target, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
//注意,本应该在此处执行的「Object result = methodProxy.invokeSuper(target, args);」,被转移到Point#postHandle方法内了 todo ⑤
Point point = new Point(target, method, args, methodProxy);
return interceptor.doInterceptor(point);
}
}
@Test
public void demo6() {
MyInterceptor interceptor = new MyInterceptor(){
@Override
public Object doInterceptor(Point point ) throws Throwable {
try {
System.out.println("befor");
Object resp = point.postHandle(); //在此处,才真正执行被代理类的方法 todo ⑤;
//本应该在MethodInterceptor子类中执行的代码,被转移到MyInterceptor子类了;MethodInterceptor子类只能有一个,但是MyInterceptor子类,结合责任链设计模式,却可以有很多个!
System.out.println("success");
return resp;
} catch ( Throwable e ) {
System.out.println("error");
throw e;
} finally {
System.out.println("finally");
}
}
};
MethodInterceptor proxyHandler = new MyProxyHandler(interceptor);
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserService.class); // 设置超类,cglib是通过继承来实现的
enhancer.setCallback(proxyHandler);
IUserService userService = (IUserService)enhancer.create(); // 创建代理类
userService.findById("4");
userService.update(new Object(), "4");
}
可用来动态生成代码的有很多,这里推荐cglib的Enhancer
、InterfaceMaker
、BeanGenerator
、ClassWriter
类;
毕竟现在基本上都是spring全家桶,而且cglib也被spring收编,只要是spring项目就可以直接使用;
动态代理也是面试必考:涉及到AOP、拦截器;而AOP的运用又是不胜枚举,拦截器结合责任链模式,也是在各种场所都用运用;
以上,就酱~
~the end~
附录代码
java?linenums
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.io.FileUtils;
import org.junit.Test;
import org.springframework.cglib.core.DebuggingClassWriter;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.client.RestTemplate;
import sun.misc.ProxyGenerator;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.lang.reflect.Proxy;
import java.lang.reflect.Type;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class ProxyDemo {
private static String paths = "E:\\tmp\\proxy\\";
/**
* 将根据类信息动态生成的二进制字节码保存到硬盘中,默认的是clazz目录下
* params: clazz 需要生成动态代理类的类
* proxyName: 为动态生成的代理类的名称
*/
public static void generateClassFile(Class clazz, String proxyName) {
try {
File file = new File(paths);
FileUtils.deleteDirectory(new File(paths));
file.mkdirs();
} catch ( IOException e ) {
e.printStackTrace();
}
// 根据类信息和提供的代理类名称,生成字节码
byte[] classFile = ProxyGenerator.generateProxyClass(proxyName, clazz.getInterfaces());
try (FileOutputStream out = new FileOutputStream(paths + proxyName + ".class")){
out.write(classFile);
out.flush();
} catch (Exception e) {
e.printStackTrace();
}
}
@Test
public void demo1(){
IUserService proxy = new UserServiceProxy();
proxy.findById("1");
System.out.println("");
proxy.update(new Object(), "4");
}
@Test
public void demo2(){
// 设置变量可以保存动态代理类,默认名称以 $Proxy0 格式命名
System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
// 1. 创建被代理的对象。实际代码中,可以使用到对象工厂
IUserService userService = new UserService();
// 2. 代理类请求处理器:拦截处理代理对象上的所有的方法调用。
// 和demo1中的UserServiceProxy类相似。
// 由于JdkProxyHandler可以复用,被代理类(userService)可以使多例,所以JdkProxyHandler也应该是多例,被代理类应该显示传入。
// 主要JdkProxyHandler构造器使用Interface作为入参,因此JdkProxyHandler可以代理一切Interface的实现类。
InvocationHandler proxyHandler = new JdkProxyHandler(userService);
// 3. 获取对应的 ClassLoader。类加载机制,如果搞错ClassLoader,可能会导致动态生成的代理类,无法被加载:提示 ClassNotFoundException;保持和被代理类在一个ClassLoader中
ClassLoader classLoader = userService.getClass().getClassLoader();
// 4. 获取所有接口的Class,这里的UserService只实现了一个接口IUserService,
Class[] interfaces = userService.getClass().getInterfaces();
/*
5.根据上面提供的信息,创建代理对象 在这个过程中,
a.JDK会通过根据传入的参数信息动态地在内存中创建和.class 文件等相关字节码;
b.然后根据相应的字节码转换成对应的class加载到JVM中;
c.然后调用newInstance()创建代理实例;
*/
IUserService proxy = (IUserService) Proxy.newProxyInstance(classLoader, interfaces, proxyHandler);
//输出动态代理之后的class代码
//generateClassFile(proxy.getClass(), "UserServiceProxy"); //在代码在文章最后
// 调用代理的方法
proxy.findById("3");
System.out.println("");
proxy.update(new Object(), "4");
//JDK动态代理,底层本质是使用方法反射;性能瓶颈受Jdk版本影响
}
@Test
public void demo3(){
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, paths);
MethodInterceptor proxyHandler = new CglibProxyHandler();
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserService.class); // 设置超类,cglib是通过继承来实现的
enhancer.setCallback(proxyHandler);
IUserService userService = (IUserService)enhancer.create(); // 创建代理类
userService.findById("4");
userService.update(new Object(), "4");
}
@Test
public void demo4(){
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, paths);
MethodInterceptor proxyHandler = new MapperProxyHandler();
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Object.class); // 设置超类;此时UserDao只是一个Interface,没有任何具体的实现类,因此超类设置为默认的Object;
enhancer.setInterfaces(new Class[]{UserDao.class});
enhancer.setCallback(proxyHandler);
UserDao userDao = (UserDao)enhancer.create(); // 创建代理类
String findById = userDao.findById("4");
System.out.println(findById); //打印sql
String update = userDao.update(new Object(), "4");
System.out.println(update);
}
@Test
public void demo5(){
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, paths);
MethodInterceptor proxyHandler = new FeignProxyHandler();
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Object.class); // 设置超类;此时MyFeign只是一个Interface,没有任何具体的实现类,因此超类设置为默认的Object;
enhancer.setInterfaces(new Class[]{MyFeign.class});
enhancer.setCallback(proxyHandler);
MyFeign feign = (MyFeign)enhancer.create(); // 创建代理类
ResponseEntity<Void> aq = feign.aq();
System.out.println(JSONObject.toJSONString(aq));
String baidu = feign.baidu("csdn");
System.out.println(baidu);
}
@Test
public void demo6() {
MyInterceptor interceptor = new MyInterceptor(){
@Override
public Object doInterceptor(Point point ) throws Throwable {
try {
System.out.println("befor");
Object resp = point.postHandle(); //在此处,才真正执行被代理类的方法 todo ⑤;
//本应该在MethodInterceptor子类中执行的代码,被转移到MyInterceptor子类了;MethodInterceptor子类只能有一个,但是MyInterceptor子类,结合责任链设计模式,却可以有很多个!
System.out.println("success");
return resp;
} catch ( Throwable e ) {
System.out.println("error");
throw e;
} finally {
System.out.println("finally");
}
}
};
MethodInterceptor proxyHandler = new MyProxyHandler(interceptor);
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserService.class); // 设置超类,cglib是通过继承来实现的
enhancer.setCallback(proxyHandler);
IUserService userService = (IUserService)enhancer.create(); // 创建代理类
userService.findById("4");
userService.update(new Object(), "4");
}
}
interface IUserService {
void findById(String uid);
void update(Object user, String uid);
}
class UserService implements IUserService {
public void findById(String uid) {
System.out.println("查询 findById");
}
public void update(Object user, String uid) {
System.out.println("更新 update");
}
}
class UserServiceProxy implements IUserService {
private final IUserService target; // 被代理的对象
public UserServiceProxy() {
this.target = new UserService(); //被代理对象,是在代理类中生成
}
@Override
public void findById(String uid) {
try {
before();
target.findById(uid); // 这里才实际调用真实对象的方法
after();
} catch ( Exception ex ) {
exception(ex);
throw ex;
}
}
@Override
public void update(Object user, String uid) {
try {
before();
target.update(user, uid); // 这里才实际调用真实对象的方法
after();
} catch ( Exception ex ) {
exception(ex);
throw ex;
}
}
private void before() { // 在执行方法之前执行
System.out.println(String.format("log start time [%s] ", new Date()));
System.out.println("开启事务");
}
private void after() { // 在执行方法之后执行
System.out.println(String.format("log end time [%s] ", new Date()));
System.out.println("提交事务");
}
private void exception(Exception ex){
System.out.println(String.format("log error [%s] ", ex.getMessage()));
System.out.println("提交回滚");
}
}
class JdkProxyHandler implements InvocationHandler {
private final Object target; // 被代理的对象,实际的方法执行者
public JdkProxyHandler(Object target) {
this.target = target;
}
/**
* @param proxy 代理对象实例 todo ①
* @param method 被代理类的Interface中的方法; todo ②
* @param args
* */
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
before();
//反射调用 target 的 method 方法。
//proxy是代理对象实例,因此在反射调用的时候,需要替换成被代理类target对象;
Object result = method.invoke(target, args);
after();
return result; // 返回方法的执行结果
} catch ( Exception ex ) {
exception(ex);
throw ex;
}
}
private void before() { // 在执行方法之前执行
System.out.println(String.format("log start time [%s] ", new Date()));
System.out.println("开启事务");
}
private void after() { // 在执行方法之后执行
System.out.println(String.format("log end time [%s] ", new Date()));
System.out.println("提交事务");
}
private void exception(Exception ex){
System.out.println(String.format("log error [%s] ", ex.getMessage()));
System.out.println("提交回滚");
}
}
class CglibProxyHandler implements MethodInterceptor {
/**
* @param target 代理子类对象;
* @param method 被代理类的方法;
* @param args
* @param methodProxy 被代理的句柄方法 todo ③
* */
@Override
public Object intercept(Object target, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
try {
before();
//注意这里是调用invokeSuper而不是invoke,否则死循环。 todo ③
//method是被代理的方法,但是由于tagert是代理子类,执行method.invoke,实际上是表示执行代理子类的方法,代理子类又会继续执行MethodInterceptor.intercept方法,导致又回到此处代码,造成死循环;
//此处应该直接执行invokeSuper,表示直接调用被代理类的句柄方法;
Object result = methodProxy.invokeSuper(target, args);
after();
return result; // 返回方法的执行结果
} catch ( Exception ex ) {
exception(ex);
throw ex;
}
}
private void before() { // 在执行方法之前执行
System.out.println(String.format("log start time [%s] ", new Date()));
System.out.println("开启事务");
}
private void after() { // 在执行方法之后执行
System.out.println(String.format("log end time [%s] ", new Date()));
System.out.println("提交事务");
}
private void exception(Exception ex){
System.out.println(String.format("log error [%s] ", ex.getMessage()));
System.out.println("提交回滚");
}
}
interface MyProxyInterceptor {
boolean preHandler();
void before();
void after();
void exception(Exception ex);
}
class CglibProxyHandler1 implements MethodInterceptor {
private final List<MyProxyInterceptor> interceptors; // 100
public CglibProxyHandler1(List<MyProxyInterceptor> interceptors) {
this.interceptors = interceptors;
}
@Override
public Object intercept(Object target, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
//2
List<MyProxyInterceptor> interceptors = this.interceptors.stream().filter(interceptor -> interceptor.preHandler()).collect(Collectors.toList());
try {
interceptors.forEach(intercept -> {
intercept.before();
});
Object result = methodProxy.invokeSuper(target, args);
interceptors.forEach(intercept -> {
intercept.after();
});
return result; // 返回方法的执行结果
} catch ( Exception ex ) {
interceptors.forEach(intercept -> {
intercept.exception(ex);
});
throw ex;
}
}
}
interface UserDao {
String findById(String uid);
String update(Object user, String uid);
}
class MapperProxyHandler implements MethodInterceptor {
private static Map<String, Map<String, String>> mapperMap = new HashMap<>();
static {
//初始化,模拟Mapper.xml文件
Map<String, String> mapper = new HashMap<>();
//Map的key,及为UserDao中的方法名,以及Mapper.xml的标签id
mapper.put("findById", "select * from user where id = ?");
mapper.put("update", "update user set name = ? where id = ?");
//UserDao.class.getName(),对应到Mapper.xml的namespace
mapperMap.put(UserDao.class.getName(), mapper);
}
@Override
public Object intercept(Object target, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
String methodName = method.getName();
Class<?> mapperClass = method.getDeclaringClass();
Map<String, String> mapper = mapperMap.get(mapperClass.getName());
if( mapper != null ){
return mapper.get(methodName);
}
return null;
}
}
interface MyFeign {
@RequestMapping(value = "http://127.0.0.1:8080/aq", method = RequestMethod.POST)
ResponseEntity<Void> aq();
@RequestMapping(value = "https://www.baidu.com/s", method = RequestMethod.GET)
String baidu(@RequestParam("wd") String keyword);
}
class FeignProxyHandler implements MethodInterceptor {
/**
* target:代理子类
* method:被代理类方法
*/
@Override
public Object intercept(Object target, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
//method是被代理类中的方法,因此可以通过method获取到:方法上注解;入参列表;入参上的注解;方法的返回类型
RequestMapping requestMapping = AnnotationUtils.findAnnotation(method, RequestMapping.class);
RequestMethod[] requestMethods = requestMapping.method();
String[] paths = requestMapping.value();
Type returnType = method.getGenericReturnType();
Parameter[] parameters = method.getParameters();
Map<String, Integer> argsIndex = new HashMap<>();
for ( int idx = 0; idx < parameters.length; idx++ ) {
Parameter parameter = parameters[idx];
RequestParam requestParam = AnnotationUtils.findAnnotation(parameter, RequestParam.class);
argsIndex.put(requestParam.name(), idx);
}
Map<String, Object> reqMap = new HashMap<>();
argsIndex.forEach((pname, idx) -> {
reqMap.put(pname, args[idx]);
});
RestTemplate rest = new RestTemplate();
if( requestMethods[0] == RequestMethod.GET ){
String resp = rest.getForObject(paths[0], String.class, reqMap);
return resultType(resp, returnType);
} else if (requestMethods[0] == RequestMethod.POST ){
String resp = rest.postForObject(paths[0], reqMap, String.class);
return resultType(resp,returnType);
}
return null;
}
private <R> R resultType(String resp, Type type){
if( resp == null ){
return null;
}
if( type instanceof Class ){
if( String.class.isAssignableFrom((Class) type) ){
return (R) resp;
}
}
return JSONObject.parseObject(resp, type);
}
}
class Point {
private final Object target;
private final Method method;
private final Object[] args;
private final MethodProxy methodProxy;
public Point(Object target, Method method, Object[] args, MethodProxy methodProxy) {
this.target = target;
this.method = method;
this.args = args;
this.methodProxy = methodProxy;
}
public Object postHandle() throws Throwable{
Object result = methodProxy.invokeSuper(target, args);
return result;
}
}
//拦截器接口
interface MyInterceptor {
default Object doInterceptor(Point point) throws Throwable {
return point.postHandle();
}
}
class MyProxyHandler implements MethodInterceptor {
private final MyInterceptor interceptor;
public MyProxyHandler(MyInterceptor interceptor) {
this.interceptor = interceptor;
}
@Override
public Object intercept(Object target, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
//注意,本应该在此处执行的[Object result = methodProxy.invokeSuper(target, args);],被转移到Point#postHandle方法内了 todo ⑤
Point point = new Point(target, method, args, methodProxy);
return interceptor.doInterceptor(point);
}
}