net.oschina.bilbodai.common.beanutil.property.PropertyUtil Maven / Gradle / Ivy
/*
* Copyright (c) 2014 Qunar.com. All Rights Reserved.
*/
package net.oschina.bilbodai.common.beanutil.property;
import net.oschina.bilbodai.common.beanutil.core.ASMCodeGenerator;
import net.oschina.bilbodai.common.beanutil.property.asm.lexer.code.CodeAppliers;
import net.oschina.bilbodai.common.beanutil.property.asm.lexer.parse.ITokenIterator;
import net.oschina.bilbodai.common.beanutil.property.asm.lexer.parse.StringTokenizerIterator;
import net.oschina.bilbodai.common.beanutil.property.asm.lexer.parse.Token;
import net.oschina.bilbodai.common.beanutil.runtime.IRuntimeClassLoader;
import net.oschina.bilbodai.common.beanutil.runtime.RuntimeClassLoader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import java.io.Closeable;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.FutureTask;
import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
import static org.objectweb.asm.Opcodes.ALOAD;
import static org.objectweb.asm.Opcodes.ARETURN;
/**
* 每个独立的属性路径都会生成一个class,比如 x.y.z 和 x.y.k
* 同时对于list和array,每个获取的位置也会产生一个,比如 x.y[0] 和 x.y[1]
* 对map来说,每个key也会, 比如 x.key1 和 x.key2
* 所以这是一个很明显的缺点,会增加class的内存开销
* 但这个问题可以通过缓存和失效来控制
* 如果需要控制加载累的数量{@link #cacheProperties}
*
* {@link StringTokenizerIterator} {@code ->} {@link Token} {@code ->} {@link CodeAppliers}
*
*
* @author qt-maodai Date: 15-8-8 Time: 下午1:12
* @version $Id$
*/
public class PropertyUtil implements Closeable {
private static IRuntimeClassLoader classLoader;
private static PrintWriter printWriter;
static {
classLoader = AccessController.doPrivileged(new PrivilegedAction() {
public IRuntimeClassLoader run() {
return new RuntimeClassLoader(PropertyUtil.class.getClassLoader());
}
});
}
private final static ConcurrentHashMap> cacheProperties =
new ConcurrentHashMap>();
/**
* 根据实例所在的属性路径字符串获取实例中属性值
*
* @param propertyPath 属性路径,类似 user.books[0].name
* @param bean 实例,如 user
* @return user第一本书的名字,注意如果中途某个属性为null,将抛出空指针异常,另外可能会抛出数组越界异常
*/
public static Object getProperty(final String propertyPath, final Object bean) {
if (propertyPath == null || propertyPath.trim().length() == 0) {
throw new IllegalArgumentException("propertyPath can't be null");
}
final String cacheKey = bean.getClass().getName() + "." + propertyPath;
FutureTask task = cacheProperties.get(cacheKey);
if (task != null) {
return getCachedProperty(cacheKey, task).get(bean);
}
task = new FutureTask<>(new Callable() {
public Property call() throws Exception {
return make(propertyPath, bean.getClass());
}
});
FutureTask existedTask = cacheProperties.putIfAbsent(cacheKey, task);
if (existedTask == null) {
existedTask = task;
existedTask.run();
}
return getCachedProperty(cacheKey, existedTask).get(bean);
}
public static Property make(String propertyPath, final Class> beanType) throws Exception {
StringTokenizerIterator stringTokenizerIterator = new StringTokenizerIterator(propertyPath);
ASMCodeGenerator asmCodeGenerator = new ASMCodeGenerator(Opcodes.V1_6, classLoader, printWriter, Object.class, Property.class) {
@Override
public void onVisitorMethods(ClassVisitor cw, Object... args) throws Exception {
ITokenIterator iterator = (ITokenIterator) args[0];
Class> rootBeanType = (Class>) args[1];
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "get", "(Ljava/lang/Object;)Ljava/lang/Object;", null, null);
mv.visitCode();
mv.visitVarInsn(ALOAD, 1);
new Token().apply(mv, iterator, rootBeanType);
mv.visitInsn(ARETURN);
mv.visitMaxs(0, 0);
mv.visitEnd();
}
};
Class> generateClass = asmCodeGenerator.getLoadedClass(stringTokenizerIterator, beanType);
return (Property) generateClass.newInstance();
}
private static Property getCachedProperty(final String cacheKey, FutureTask task) {
try {
return task.get();
} catch (Exception e) {
cacheProperties.remove(cacheKey);
throw new IllegalStateException("获取路径为" + cacheKey + "的属性值出错", e);
}
}
/**
* 如果要打印class生成的日志,可以设置相应的输出流
* @param out 输出流
*/
public static void setTrace(OutputStream out) {
printWriter = new PrintWriter(new OutputStreamWriter(out));
}
public void close() throws IOException {
if (printWriter != null) {
printWriter.close();
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy