jdk-proxy
Table of Contents
proxy
前言
目前动态代理的实现存在 java-proxy,cglib-proxy,byte-buddy,asm,java-assit,aspectj
asm需要对字节码了如指掌,aspectj是静态的,cglib不支持高版本jdk
byte-buddy
此框架14年产生,已经是最新的技术
使用
生成字节码
public static void main(String[] args) throws InstantiationException, IllegalAccessException, IOException {
DynamicType.Loaded<Object> dynamicType = new ByteBuddy()
.with(TypeValidation.DISABLED)
// 指定实现类
.subclass(Object.class)
// 指定方法
.method(ElementMatchers.named("toString"))
// 拦截填充值
.intercept(FixedValue.value("hello world"))
// 生成class文件
.make()
// 类加载器加载
.load(ClassLoader.getSystemClassLoader());
//保存生成的字节码
dynamicType.saveIn(new File("/home/paul/opensource/yutak-dev/out"));
System.out.println(dynamicType.getLoaded().newInstance().toString());
}
idea反编译
package com.yutak.bytebuddy.proxy;
public class String {
public java.lang.String toString() {
return "hello world";
}
public String() {
}
}
指令解释
- 动态增强模式
new ByteBuddy().subclass()
new ByteBuddy().rebasing()
new ByteBuddy().redefine()
- subclass 生成子类
- rebasing 遇到冲突直接将原方法private并重命名
- redefine 替换实现
- 类加载策略
.load(ClassLoader.getSystemClassLoader(), ClassLoadingStrategy.Default.WRAPPER);
- WRAPPER 创建新的ClassLoader
- CHILD_FIRST 创建子类优先加载Classloader,破坏双亲委派
- INJETCION 注入此类到当前的ClassLoader
jdk porxy
jdk
代理基于接口实现,所以不可直接代理实现类,如果测试会发现形如..$proxy?...
报出classCastException
- 核心方法(
args
中的ClassLoader
类加载器,Class<?>[] interfaces
接口方法,InvocationHandler
实例),此方法应证jdk proxy
基于interface
实现(调用时不要将接口.class传入)
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
- 显然代理会针对一种行为,所以基于接口代理是存在其合理性,不过
cglib
的可操作性更强,无需接口 - 实际生成实现对应接口的新类,
jvm
层面的 - 演示代码的结构是工厂类
MyDynamicFactory
,接口MyInterface
,接口实现类MyProxy
思考
- 存在不少示例,是将工厂类内部设置了
target
,并且抽象了MyInvocationHandler
然后调用时重写属性,个人认为一般代理增强的代码业务通用,非entity
,所以想法是对要代理的类加入限制<T extends InvocationHandler>
这样减少类数量,减少耦合.,其次将增强逻辑至于同一类 - 如果将代理增强逻辑独立实现,显然工厂类中需要关于不同类型的
MyIncocationHandler
的集合,加上后续存类实例的hashMap
,安全性降低,并且复杂性提高,有可能需要concurrentHashMap
实现 - 工厂内部代理集合采用
hashMap<K,V>
,其中K
类型String
,是全类名,V
采用实体类的对象,但是这样会引发调用时无关的创建对象问题,所以实现类采用单例 hashMap
线程不安全,主要是操作不原子,所以对工厂的put(String clzName
加锁,对外不暴露hashMap
的operation
- 单例里有个逻辑是防止反射破坏的(反射调用
constructor
,加个static boolean
标志查重)
代码
工厂类
@Slf4j
package com.pg.backend.designPattern.structuralPattern.proxyPattern.dynamicProxyPattern.main;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.HashMap;
/**
* JDK 代理基于接口,本实例
* @author paul 2024/3/6
*/
@Slf4j
public class MyDynamicProxyFactory {
//考虑到的想法是 ,基于 Enum ,但是考虑到类数量,基于hashMap<K,V> 全类名存储
private final HashMap<String,Object> proxiedObjs = new HashMap<>();
public Object getProxy( Object o) {
return Proxy.newProxyInstance(o.getClass().getClassLoader(), o.getClass().getInterfaces(), (InvocationHandler) o);
}
public Object getProxy(String clzName) {
if (clzName == null || clzName.isEmpty()) {
log.info("paramsError");
return null;
}
Object o = this.get(clzName);
return Proxy.newProxyInstance(o.getClass().getClassLoader(), o.getClass().getInterfaces(), (InvocationHandler) o);
}
@SneakyThrows
public synchronized <T extends InvocationHandler> void put(T o) {
proxiedObjs.put(o.getClass().getName(),o);
}
private synchronized Object get(String name) {
return proxiedObjs.get(name);
}
}
接口
package com.pg.backend.designPattern.structuralPattern.proxyPattern.dynamicProxyPattern.main;
/**
* @author paul 2024/3/6
*/
public interface MyInterface {
void test();
}
实现类
@Slf4j
package com.pg.backend.designPattern.structuralPattern.proxyPattern.dynamicProxyPattern.main;
import lombok.extern.slf4j.Slf4j;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* @author paul 2024/3/6
*/
@Slf4j
public class MyProxy implements InvocationHandler,MyInterface {
private static volatile MyProxy myProxy;
private static boolean flag = false;
private MyProxy() {
if(flag) {
throw new RuntimeException("canNotCreateMoreInstance");
}
flag = true;
}
public static MyProxy getInstance() {
if(myProxy == null) {
synchronized (MyProxy.class) {
if(myProxy == null){
myProxy = new MyProxy();
}
}
}
return myProxy;
}
public void test(){
log.info("test...");
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
log.info("beforeProxy");
return method.invoke(MyProxy.getInstance(),args);
}
}
框架
org.reflections 此框架处理非spring项目的aop或者动态增强很合适,比如类的收集,拥有注解的Field,在轮子项目里很合适 pom
<dependency>
<groupId>org.reflections</groupId>
<artifactId>reflections</artifactId>
<version>0.10.2</version>
</dependency>
工具类
public class ReflectKit {
private Reflections reflections;
public ReflectKit(String packageAddress) {
// 配置类
ConfigurationBuilder configurationBuilder = new ConfigurationBuilder();
// 多组包扫描的分包
String[] addresses = packageAddress.split(",");
Stream.of(addresses).forEach(str -> configurationBuilder.addUrls(ClasspathHelper.forPackage(str.trim())));
// 加入Scanner扫描器,如果扫描注解字段,不加Scanners.FieldsAnnotated是不会扫描到的
configurationBuilder.setScanners(Scanners.FieldsAnnotated, Scanners.MethodsAnnotated,Scanners.TypesAnnotated);
reflections = new Reflections(configurationBuilder);
}
public <T extends Annotation> Set<Class<?>> getBeans(Class<T> anno) {
return reflections.getTypesAnnotatedWith(anno);
}
public <T extends Annotation> Set<Field> getFields(Class<T> anno) {
return reflections.getFieldsAnnotatedWith(anno);
}
}