动态代理有很多使用的场景,比如
- springAOP切入
- spring事务、缓存
- 自定义业务场景等
本文就是一个动态代理util。为了使用起来更加的方便。之后的文章也有可能用得到。
后续用新文章来分析动态代理的原理
本文的使用场景(点我)
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110
| import org.springframework.beans.BeanUtils; import org.springframework.cglib.proxy.Enhancer; import org.springframework.cglib.proxy.MethodInterceptor; import org.springframework.cglib.proxy.MethodProxy; import org.springframework.util.ClassUtils; import org.springframework.util.ReflectionUtils;
import java.beans.PropertyDescriptor; import java.lang.reflect.Method; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream;
public class DynamicProxy<T> implements MethodInterceptor {
private final T target; private final Class<?> targetClass; private final boolean ignoreObjectMethod; private final Object[] arguments; private CallBack<T> callBack;
public DynamicProxy(T proxyTarget, Object... arguments) { this(proxyTarget, true, arguments); }
public DynamicProxy(T proxyTarget, boolean ignoreObjectMethod, Object... arguments) { this.target = proxyTarget; this.ignoreObjectMethod = ignoreObjectMethod; this.targetClass = proxyTarget.getClass(); this.arguments = arguments; }
public T getProxyWithWriteMethod(CallBack<T> callBack) { Set<Method> ignoreMethodNames = Stream.of(BeanUtils.getPropertyDescriptors(targetClass)) .map(PropertyDescriptor::getWriteMethod) .collect(Collectors.toSet());
this.callBack = (obj, proxyMethod, args, originMethod) -> { if (ignoreMethodNames.contains(originMethod)) { return callBack.call(target, proxyMethod, args, originMethod); } return proxyMethod.invoke(target, args); }; return getT();
}
public T getProxy(CallBack<T> callBack) { this.callBack = callBack; return getT(); }
private T getT() { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(targetClass); enhancer.setCallback(this);
try { return create(enhancer); } catch (Exception e) { enhancer = new Enhancer(); Class<?>[] allInterfacesForClass = ClassUtils.getAllInterfacesForClass(targetClass); enhancer.setInterfaces(allInterfacesForClass); enhancer.setCallback(this); return create(enhancer); } }
private T create(Enhancer enhancer) { if (arguments == null || arguments.length == 0) { return (T) enhancer.create(); } else { Class<?>[] classes = Stream.of(arguments) .map(Object::getClass) .toArray(Class[]::new); return (T) enhancer.create(classes, arguments); } }
@Override public Object intercept(Object obj, Method originMethod, Object[] args, MethodProxy proxyMethod) throws Throwable { if (ignoreObjectMethod && ReflectionUtils.isObjectMethod(originMethod)) { return proxyMethod.invoke(target, args); } return callBack.call(target, proxyMethod, args, originMethod); }
public interface CallBack<T> {
Object call(T target, MethodProxy proxyMethod, Object[] args, Method originMethod) throws Throwable; } }
|
使用方式之一
分页查询只想查询一个,但是每次new对象在去赋值,非常浪费时间。并且还会出遗漏的问题
所以建一个全局的对象,但是这个全局的对象,他又是多线程共享,不能保证他的安全,比如我只想保证他的变量page=1,别的线程set就会影响其他线程。
所以如果我建立一个全局的变量,大家共享,也不怕被set而影响其他的线程,那么可以用到此动态代理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| public class Pageable { private static final int DEFAULT_SIZE = 10; private static final int MAX_SIZE = 1000;
public static final Pageable ONLY_ONE = new DynamicProxy<>(newOnlyOne(), true) .getProxyWithWriteMethod((target, proxyMethod, args, originMethod) -> { throw new Throwable("禁止修改全局的类"); });
public static Pageable newOnlyOne() { Pageable onlyOne = new Pageable(); onlyOne.setSearchCount(false); onlyOne.setPage(1); onlyOne.setPageSize(1); return onlyOne; }
private long page = 1;
private long pageSize = DEFAULT_SIZE;
private boolean isSearchCount = true; }
|
总结
这个动态代理只是一个工具类,其他需要用到的地方用起来贼方便。基本上3两行代码即可搞定。
比如说这篇文章 MybatisPlus查询软删除的数据
后续源码原理什么的,等我有时间了在分析😁