Google Guava 反射机制工具类
Posted tuacy
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Google Guava 反射机制工具类相关的知识,希望对你有一定的参考价值。
一 TypeToken
Guava TypeToken类是用来帮我们解决java运行时泛型类型被擦除的问题的。
这里用一个具体的实例来解释下什么是类型檫除,特别是使用泛型的时候容易出现类型檫除。
ArrayList<String> stringList = Lists.newArrayList();
ArrayList<Integer> intList = Lists.newArrayList();
System.out.println("intList type is " + intList.getClass());
System.out.println("stringList type is " + stringList.getClass());
上述代码虽然我们定义的是ArrayList、ArrayList类型的对象。但是两者的打印都是java.util.ArrayList。没办法去获取到String、Integer了。他们的类型被檫除了,在想通过类型来判断是哪个对象就做不到了。
鉴于类似这样的情况发生Guava给我们提供了TypeToken类。怎么用,
// 认为stringList和intList的类型是一样的。这就是所谓的泛型类型擦除, 泛型String和Integer被檫除了。
System.out.println(stringList.getClass().isAssignableFrom(intList.getClass()));
// 定义了一个空的匿名类
TypeToken<ArrayList<String>> typeToken = new TypeToken<ArrayList<String>>()
;
// TypeToken解析出了泛型参数的具体类型
TypeToken<?> genericTypeToken = typeToken.resolveType(ArrayList.class.getTypeParameters()[0]);
System.out.println(genericTypeToken.getType());
通过TypeToken我们就可以获取到ArrayList里面的class java.lang.String。
TypeToken常用方法如下:
方法 | 描述 |
---|---|
getType() | 获得包装的java.lang.reflect.Type. |
getRawType() | 返回大家熟知的运行时类 |
getSubtype(Class<?>) | 返回那些有特定原始类的子类型。举个例子,如果这有一个Iterable并且参数是List.class,那么返回将是List。 |
getSupertype(Class<?>) | 产生这个类型的超类,这个超类是指定的原始类型。举个例子,如果这是一个Set并且参数是Iterable.class,结果将会是Iterable。 |
isAssignableFrom(type) | 如果这个类型是 assignable from 指定的类型,并且考虑泛型参数,返回true。List<? extends Number>是assignable from List,但List没有. |
getTypes() | 返回一个Set,包含了这个所有接口,子类和类是这个类型的类。返回的Set同样提供了classes()和interfaces()方法允许你只浏览超类和接口类。 |
isArray() | 检查某个类型是不是数组,甚至是<? extends A[]>。 |
getComponentType() | 返回组件类型数组。 |
二 Invokable
Guava的Invokable是对java.lang.reflect.Method和java.lang.reflect.Constructor的流式包装。它简化了常见的反射代码的使用。
2.1 Invokable常用方法
/**
* 根据指定的Method返回Invokable对象(MethodInvokable)
*/
public static Invokable<?, Object> from(Method method);
/**
* 根据指定的Constructor返回Invokable对象(ConstructorInvokable)
*/
public static <T> Invokable<T, T> from(Constructor<T> constructor)
return new ConstructorInvokable<T>(constructor);
/**
* 判断当前方法是否可以重写的
* Constructors, private, static or final methods, or methods declared by final classes
* 都是不能被重写的
*/
public abstract boolean isOverridable();
/**
* 当前方法对应的参数是否是可变的参数
*/
public abstract boolean isVarArgs();
/**
* 执行对应的方法
*/
// All subclasses are owned by us and we'll make sure to get the R type right.
@SuppressWarnings("unchecked")
@CanIgnoreReturnValue
public final R invoke(@Nullable T receiver, Object... args)
throws InvocationTargetException, IllegalAccessException
return (R) invokeInternal(receiver, checkNotNull(args));
/**
* 获取当前方法的返回值
*/
// All subclasses are owned by us and we'll make sure to get the R type right.
@SuppressWarnings("unchecked")
public final TypeToken<? extends R> getReturnType();
/**
* 获取当前方法的参数
*/
public final ImmutableList<Parameter> getParameters();
/**
* 获取当前方法的异常类型
*/
public final ImmutableList<TypeToken<? extends Throwable>> getExceptionTypes();
/**
* 显式指定此Invokable的返回类型.可以作为一个过滤作用处理
*
* 例如:
*
* Method factoryMethod = Person.class.getMethod("create");
* Invokable<?, Person> factory = Invokable.of(getNameMethod).returning(Person.class);
*
*/
public final <R1 extends R> Invokable<T, R1> returning(Class<R1> returnType);
/**
* 显式指定此Invokable的返回类型
*/
public final <R1 extends R> Invokable<T, R1> returning(TypeToken<R1> returnType);
/**
* 返回表示声明由此Invokable对象表示的方法的类的Class对象,父类中申明的就会返回父类class
*/
@SuppressWarnings("unchecked") // The declaring class is T's raw class, or one of its supertypes.
@Override
public final Class<? super T> getDeclaringClass();
/**
* 方法返回表示声明由此Method对象表示的方法的类的Class对象,Class包装成了TypeToken而已
*/
// Overridden in TypeToken#method() and TypeToken#constructor()
@SuppressWarnings("unchecked") // The declaring class is T.
@Override
public TypeToken<T> getOwnerType();
/**
* 返回一个AnnotatedType对象,把该方法的返回类型包装成了一个AnnotatedType类型
*/
public abstract AnnotatedType getAnnotatedReturnType();
2.2 Invokable简单使用
public class InvokableTest
@Test
public void invokableTest()
// 对象
InvokableInstance object = new InvokableInstance(10);
// 获去对象对应的类
Class clazz = object.getClass();
Method[] invokableSourceList = clazz.getMethods();
// Constructor[] invokableSourceList = clazz.getConstructors();
if (invokableSourceList.length > 0)
for (Method item : invokableSourceList)
System.out.println("========================================");
// 方法名字
System.out.println("方法名字:" + item.getName());
// 把Method包装成Invokable
Invokable methodInvokable = Invokable.from(item);
// getDeclaringClass() 获取定义该方法的类
System.out.println("定义该方法的类:" + methodInvokable.getDeclaringClass());
// getOwnerType() 获取定义该方法的class的包装对象Type
System.out.println("定义该方法的类:" + methodInvokable.getOwnerType().getType());
// isOverridable() 该方法是否可以重写
System.out.println("是否可以重写:" + methodInvokable.isOverridable());
// isVarArgs() 该方法是否可变参数
System.out.println("是否可变参数:" + methodInvokable.isVarArgs());
// getReturnType() 该方法返回值类型
System.out.println("返回值类型:" + methodInvokable.getReturnType().getType());
// getParameters() 获取参数
ImmutableList<Parameter> parameterList = methodInvokable.getParameters();
for (int index = 0; index < parameterList.size(); index++)
System.out.println("方法参数" + index + ": " + parameterList.get(index).getType());
// getExceptionTypes() 获取异常类
ImmutableList<TypeToken> exceptionList = methodInvokable.getExceptionTypes();
for (int index = 0; index < exceptionList.size(); index++)
System.out.println("异常类" + index + ": " + exceptionList.get(index).getType());
// getAnnotatedReturnType()
AnnotatedType annotatedType = methodInvokable.getAnnotatedReturnType();
System.out.println("annotatedType: " + annotatedType.getType());
/**
* 一个测试用类
*/
class InvokableInstance
/**
* 构造函数
*/
public InvokableInstance(int a)
/**
* 仅仅是用来测试的一个方法
*/
@CanIgnoreReturnValue
public void sum(int a, int b) throws NullPointerException
// return a + b;
三 动态代理(Dynamic Proxies)
Guava为了方便大家很好的处理动态代理,给大家做了两件事:简化生成代理对象的生成、提供了AbstractInvocationHandler了。
3.1 动态代理对象的生成
对于单一的接口类型需要被代理的时候。Guava简化了代理对象的生成。我们用一个具体的实例来说明
定义一个很简单的接口AddService
public interface AddService
int add(int a, int b);
实现AddService
public class AddServiceImpl implements AddService
@Override
public int add(int a, int b)
return a + b;
原来JDK生成代理对象是通过Proxy.newProxyInstance()方法生成,这个方法需要三个参数:第一个参数指定产生代理对象的类加载器,需要将其指定为和目标对象同一个类加载器、第二个参数要实现和目标对象一样的接口,所以只需要拿到目标对象的实现接口、第三个参数表明这些被拦截的方法在被拦截时需要执行哪个InvocationHandler的invoke方法。我们还是直接用代码来说明问题。
实现InvocationHandler接口(代理方法处理接口)。通过实现类里面的getProxy()
拿到代理对象。
public class JdkInvocationHandlerImpl<T> implements InvocationHandler
/**
* 目标对象
*/
private T targetObject;
public JdkInvocationHandlerImpl(T target)
this.targetObject = target;
/**
* 绑定关系,也就是关联到哪个接口(与具体的实现类绑定)的哪些方法将被调用时,执行invoke方法。
*/
public AddService getProxy()
//该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例
//第一个参数指定产生代理对象的类加载器,需要将其指定为和目标对象同一个类加载器
//第二个参数要实现和目标对象一样的接口,所以只需要拿到目标对象的实现接口
//第三个参数表明这些被拦截的方法在被拦截时需要执行哪个InvocationHandler的invoke方法
//根据传入的目标返回一个代理对象
return (AddService) Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),
targetObject.getClass().getInterfaces(), this);
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
// 打印参数信息列表
for (Object arg : args)
System.out.println(arg);
Object ret;
try
/*原对象方法调用前处理日志信息*/
System.out.println("方法开始执行-->>");
//调用目标方法
ret = method.invoke(targetObject, args);
/*原对象方法调用后处理日志信息*/
System.out.println("方法执行成功-->>");
catch (Exception e)
// 执行方法过程中出现异常
e.printStackTrace();
System.out.println("方法执行失败-->>");
throw e;
return ret;
使用
@Test
public void jdkNewProxyTest()
AddServiceImpl service = new AddServiceImpl();
JdkInvocationHandlerImpl<AddService> addServiceHandler = new JdkInvocationHandlerImpl<>(service);
AddService proxy = addServiceHandler.getProxy();
Assert.assertEquals(3, proxy.add(1, 2));
Guava生成代理对象。直接调用Reflection.newProxy()函数,这个函数也是需要两个参数:第一个参数接口,要实现和目标对象的某一样的接口(那个接口里面的方法需要代理)、第二个参数表明这些被拦截的方法在被拦截时需要执行哪个InvocationHandler的invoke方法。具体使用实例如下:
实现InvocationHandler接口(代理方法处理接口)。通过实现类里面的Reflection.newProxy()拿到代理对象。
public class GuavaInvocationHandlerImpl<T> implements InvocationHandler
/**
* 目标对象对应的接口(因为一个对象可以实现多个接口,我们不知道是那个接口,所以传递进来)
*/
private Class<T> targetInterface;
/**
* 目标对象
*/
private T targetObject;
public GuavaInvocationHandlerImpl(Class<T> targetInterface, T targetObject)
/*参数判断*/
// targetInterfaced一定要是一个接口
checkArgument(targetInterface.isInterface(), "%s 不是一个接口类", targetInterface);
// targetObject一定是targetInterface接口的实现类。
boolean valid = false;
Class<?>[] targetInterfaceList = targetObject.getClass().getInterfaces();
if (targetInterfaceList != null && targetInterfaceList.length > 0)
for (Class<?> item : targetInterfaceList)
if (targetInterface.getName().equals(item.getName()))
valid = true;
break;
checkArgument(valid, "%s 必须实现 %s", targetObject.getClass().getName(), targetInterface.getName());
this.targetInterface = targetInterface;
this.targetObject = targetObject;
/**
* 获取都代理对象
*/
public T getProxy()
// guava里面Reflection帮助类提供的 Reflection.newProxy() 实现
// 第一个参数接口,要实现和目标对象的某一样的接口(那个接口里面的方法需要代理)
// 第二个参数表明这些被拦截的方法在被拦截时需要执行哪个InvocationHandler的invoke方法
return Reflection.newProxy(targetInterface, this);
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
// 打印参数信息列表
for (Object arg : args)
System.out.println(arg);
Object ret;
try
/*原对象方法调用前处理日志信息*/
System.out.println("方法开始执行-->>");
//调用目标方法
ret = method.invoke(targetObject, args);
/*原对象方法调用后处理日志信息*/
System.out.println("方法执行成功-->>");
catch (Exception e)
// 执行方法过程中出现异常
e.printStackTrace();
System.out.println("方法执行失败-->>");
throw e;
return ret;
使用
@Test
public void guavaNewProxyTest()
AddServiceImpl service = new AddServiceImpl();
GuavaInvocationHandlerImpl<AddService> addServiceHandler = new GuavaInvocationHandlerImpl<>(AddService.class, service);
AddService proxy = addServiceHandler.getProxy();
Assert.assertEquals(3, proxy.add(1, 2));
3.2 AbstractInvocationHandler
有时候你可能想动态代理能够更直观的支持equals(),hashCode()和toString()。AbstractInvocationHandler能帮我们做到三件事:
- 一个代理实例equal另外一个代理实例,只要他们有同样的接口类型和equal的invocation handlers。
- 一个代理实例的toString()会被代理到invocation handler的toString(),这样更容易自定义toString()。
- AbstractInvocationHandler确保传递给handleInvocation(Object, Method, Object[]))的参数数组永远不会空,从而减少了空指针异常的机会。
如果对上面讲的几点不是很明白的话,强烈建议大家看下AbstractInvocationHandler的源码部分。
public class GuavaAbstractInvocationHandlerImpl<T> extends AbstractInvocationHandler
/**
* 目标对象对应的接口(因为一个对象可以实现多个接口,我们不知道是那个接口,所以传递进来)
*/
private Class<T> targetInterface;
/**
* 目标对象
*/
private T targetObject;
public GuavaAbstractInvocationHandlerImpl(Class<T> targetInterface, T targetObject)
/*参数判断*/
// targetInterfaced一定要是一个接口
checkArgument(targetInterface.isInterface(), "%s 不是一个接口类", targetInterface);
// targetObject一定是targetInterface接口的实现类。
boolean valid = false;
Class<?>[] targetInterfaceList = targetObject.getClass().getInterfaces();
if (targetInterfaceList != null && targetInterfaceList.length > 0)
for (Class<?> item : targetInterfaceList)
if (targetInterface.getName().equals(item.getName()))
valid = true;
break;
checkArgument(valid, "%s 必须实现 %s", targetObject.getClass().getName(), targetInterface.getName());
this.targetInterface = targetInterface;
this.targetObject = targetObject;
/**
* 获取都代理对象
*/
public T getProxy()
// guava里面Reflection帮助类提供的 Reflection.newProxy() 实现
return Reflection.newProxy(targetInterface, this);
@Override
protected Object handleInvocation(Object proxy, Method method, Object[] args) throws Throwable
// 打印参数信息列表
for (Object arg : args)
System.out.println(arg);
Object ret;
try
/*原对象方法调用前处理日志信息*/
System.out.println("方法开始执行-->>");
//调用目标方法
ret = method.invoke(targetObject, args);
/*原对象方法调用后处理日志信息*/
System.out.println("方法执行成功-->>");
catch (Exception e)
// 执行方法过程中出现异常
e.printStackTrace();
System.out.println("方法执行失败-->>");
throw e;
return ret;
四 ClassPath
严格来讲,Java没有平台无关的方式来浏览类和类资源。不过一定的包或者工程下,还是能够实现的,比方说,去检查某个特定的工程的惯例或者某种一直遵从的约束。
ClassPath是一种实用工具,它提供尽最大努力的类路径扫描。
值得注意的是,ClassPath是一个尽力而为的工具。它只扫描jar文件中或者某个文件目录下的class文件。也不能扫描非URLClassLoader的自定义class loader管理的class,所以不要将它用于关键任务生产任务。
4.1 ClassPath常用方法
/**
* 创建一个ClassPath对象
*/
public static ClassPath from(ClassLoader classloader) throws IOException;
/**
* 返回从当前类路径可加载的所有资源,包括所有可加载类的类文件,但不包括“META-INF / MANIFEST.MF”文件
*/
public ImmutableSet<ResourceInfo> getResources();
/**
* 返回可加载的所有类
*/
public ImmutableSet<ClassInfo> getAllClasses();
/**
* 返回所有的顶级类(不包括内部类)
*/
public ImmutableSet<ClassInfo> getTopLevelClasses();
/**
* 返回指定package下的所有顶级类
*/
public ImmutableSet<ClassInfo> getTopLevelClasses(String packageName);
/**
* 返回所有指定package已经子package下所有的顶级类
*/
public ImmutableSet<ClassInfo> getTopLevelClassesRecursive(String packageName);
4.2 ClassPath简单使用
@Test
public void classPathTest()
try
ClassPath classpath = ClassPath.from(ClassPathBase.class.getClassLoader());
// getTopLevelClassesRecursive
ImmutableSet<ClassPath.ClassInfo> topClassList = classpath.getTopLevelClassesRecursive("com.tuacy.guava.study");
if (topClassList != null && !topClassList.isEmpty())
for (ClassPath.ClassInfo classInfoItem : topClassList)
System.out.println(classInfoItem.toString());
// getResources
ImmutableSet<ClassPath.ResourceInfo> resourceList = classpath.getResources();
if (resourceList != null && !resourceList.isEmpty())
for (ClassPath.ResourceInfo resourceItem : resourceList)
System.out.println(resourceItem.url().toString());
catch (IOException e)
e.printStackTrace();
static class ClassPathBase
关于Guava反射机制工具类我们就暂时先提这些,上文中涉及到的代码可以在https://github.com/tuacy/google-guava-study里面找到(在单元测试代码里面)。
以上是关于Google Guava 反射机制工具类的主要内容,如果未能解决你的问题,请参考以下文章
别再造轮子了,Google 开源的 Guava 工具库真心强大!