一 TypeToken
? ? ? ?Guava TypeToken類是用來(lái)幫我們解決java運(yùn)行時(shí)泛型類型被擦除的問(wèn)題的。
? ? ? ?這里用一個(gè)具體的實(shí)例來(lái)解釋下什么是類型檫除,特別是使用泛型的時(shí)候容易出現(xiàn)類型檫除眷细。
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<String>隶糕、ArrayList<Integer>類型的對(duì)象坝疼。但是兩者的打印都是java.util.ArrayList翠拣。沒(méi)辦法去獲取到String、Integer了虑灰。他們的類型被檫除了吨瞎,在想通過(guò)類型來(lái)判斷是哪個(gè)對(duì)象就做不到了。
? ? ? ?鑒于類似這樣的情況發(fā)生Guava給我們提供了TypeToken類穆咐。怎么用颤诀,
// 認(rèn)為stringList和intList的類型是一樣的。這就是所謂的泛型類型擦除, 泛型String和Integer被檫除了对湃。
System.out.println(stringList.getClass().isAssignableFrom(intList.getClass()));
// 定義了一個(gè)空的匿名類
TypeToken<ArrayList<String>> typeToken = new TypeToken<ArrayList<String>>() {
};
// TypeToken解析出了泛型參數(shù)的具體類型
TypeToken<?> genericTypeToken = typeToken.resolveType(ArrayList.class.getTypeParameters()[0]);
System.out.println(genericTypeToken.getType());
? ? ? ?通過(guò)TypeToken我們就可以獲取到ArrayList<String>里面的class java.lang.String崖叫。
? ? ? ?TypeToken常用方法如下:
方法 | 描述 |
---|---|
getType() | 獲得包裝的java.lang.reflect.Type. |
getRawType() | 返回大家熟知的運(yùn)行時(shí)類 |
getSubtype(Class<?>) | 返回那些有特定原始類的子類型。舉個(gè)例子拍柒,如果這有一個(gè)Iterable并且參數(shù)是List.class心傀,那么返回將是List。 |
getSupertype(Class<?>) | 產(chǎn)生這個(gè)類型的超類拆讯,這個(gè)超類是指定的原始類型脂男。舉個(gè)例子养叛,如果這是一個(gè)Set并且參數(shù)是Iterable.class,結(jié)果將會(huì)是Iterable宰翅。 |
isAssignableFrom(type) | 如果這個(gè)類型是 assignable from 指定的類型弃甥,并且考慮泛型參數(shù),返回true堕油。List<? extends Number>是assignable from List潘飘,但List沒(méi)有. |
getTypes() | 返回一個(gè)Set,包含了這個(gè)所有接口掉缺,子類和類是這個(gè)類型的類。返回的Set同樣提供了classes()和interfaces()方法允許你只瀏覽超類和接口類戈擒。 |
isArray() | 檢查某個(gè)類型是不是數(shù)組眶明,甚至是<? extends A[]>。 |
getComponentType() | 返回組件類型數(shù)組筐高。 |
二 Invokable
? ? ? ?Guava的Invokable是對(duì)java.lang.reflect.Method和java.lang.reflect.Constructor的流式包裝搜囱。它簡(jiǎn)化了常見(jiàn)的反射代碼的使用。
2.1 Invokable常用方法
/**
* 根據(jù)指定的Method返回Invokable對(duì)象(MethodInvokable)
*/
public static Invokable<?, Object> from(Method method);
/**
* 根據(jù)指定的Constructor返回Invokable對(duì)象(ConstructorInvokable)
*/
public static <T> Invokable<T, T> from(Constructor<T> constructor) {
return new ConstructorInvokable<T>(constructor);
}
/**
* 判斷當(dāng)前方法是否可以重寫(xiě)的
* Constructors, private, static or final methods, or methods declared by final classes
* 都是不能被重寫(xiě)的
*/
public abstract boolean isOverridable();
/**
* 當(dāng)前方法對(duì)應(yīng)的參數(shù)是否是可變的參數(shù)
*/
public abstract boolean isVarArgs();
/**
* 執(zhí)行對(duì)應(yīng)的方法
*/
// 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));
}
/**
* 獲取當(dāng)前方法的返回值
*/
// 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();
/**
* 獲取當(dāng)前方法的參數(shù)
*/
public final ImmutableList<Parameter> getParameters();
/**
* 獲取當(dāng)前方法的異常類型
*/
public final ImmutableList<TypeToken<? extends Throwable>> getExceptionTypes();
/**
* 顯式指定此Invokable的返回類型.可以作為一個(gè)過(guò)濾作用處理
*
* 例如:
*
* 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對(duì)象表示的方法的類的Class對(duì)象柑土,父類中申明的就會(huì)返回父類class
*/
@SuppressWarnings("unchecked") // The declaring class is T's raw class, or one of its supertypes.
@Override
public final Class<? super T> getDeclaringClass();
/**
* 方法返回表示聲明由此Method對(duì)象表示的方法的類的Class對(duì)象蜀肘,Class包裝成了TypeToken而已
*/
// Overridden in TypeToken#method() and TypeToken#constructor()
@SuppressWarnings("unchecked") // The declaring class is T.
@Override
public TypeToken<T> getOwnerType();
/**
* 返回一個(gè)AnnotatedType對(duì)象,把該方法的返回類型包裝成了一個(gè)AnnotatedType類型
*/
public abstract AnnotatedType getAnnotatedReturnType();
2.2 Invokable簡(jiǎn)單使用
public class InvokableTest {
@Test
public void invokableTest() {
// 對(duì)象
InvokableInstance object = new InvokableInstance(10);
// 獲去對(duì)象對(duì)應(yīng)的類
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的包裝對(duì)象Type
System.out.println("定義該方法的類:" + methodInvokable.getOwnerType().getType());
// isOverridable() 該方法是否可以重寫(xiě)
System.out.println("是否可以重寫(xiě):" + methodInvokable.isOverridable());
// isVarArgs() 該方法是否可變參數(shù)
System.out.println("是否可變參數(shù):" + methodInvokable.isVarArgs());
// getReturnType() 該方法返回值類型
System.out.println("返回值類型:" + methodInvokable.getReturnType().getType());
// getParameters() 獲取參數(shù)
ImmutableList<Parameter> parameterList = methodInvokable.getParameters();
for (int index = 0; index < parameterList.size(); index++) {
System.out.println("方法參數(shù)" + 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());
}
}
}
/**
* 一個(gè)測(cè)試用類
*/
class InvokableInstance {
/**
* 構(gòu)造函數(shù)
*/
public InvokableInstance(int a) {
}
/**
* 僅僅是用來(lái)測(cè)試的一個(gè)方法
*/
@CanIgnoreReturnValue
public void sum(int a, int b) throws NullPointerException {
// return a + b;
}
}
}
三 動(dòng)態(tài)代理(Dynamic Proxies)
? ? ? ?Guava為了方便大家很好的處理動(dòng)態(tài)代理稽屏,給大家做了兩件事:簡(jiǎn)化生成代理對(duì)象的生成扮宠、提供了AbstractInvocationHandler了。
3.1 動(dòng)態(tài)代理對(duì)象的生成
? ? ? ?對(duì)于單一的接口類型需要被代理的時(shí)候狐榔。Guava簡(jiǎn)化了代理對(duì)象的生成坛增。我們用一個(gè)具體的實(shí)例來(lái)說(shuō)明
定義一個(gè)很簡(jiǎn)單的接口AddService
public interface AddService {
int add(int a, int b);
}
實(shí)現(xiàn)AddService
public class AddServiceImpl implements AddService {
@Override
public int add(int a, int b) {
return a + b;
}
}
? ? ? ?原來(lái)JDK生成代理對(duì)象是通過(guò)Proxy.newProxyInstance()方法生成,這個(gè)方法需要三個(gè)參數(shù):第一個(gè)參數(shù)指定產(chǎn)生代理對(duì)象的類加載器薄腻,需要將其指定為和目標(biāo)對(duì)象同一個(gè)類加載器收捣、第二個(gè)參數(shù)要實(shí)現(xiàn)和目標(biāo)對(duì)象一樣的接口,所以只需要拿到目標(biāo)對(duì)象的實(shí)現(xiàn)接口庵楷、第三個(gè)參數(shù)表明這些被攔截的方法在被攔截時(shí)需要執(zhí)行哪個(gè)InvocationHandler的invoke方法罢艾。我們還是直接用代碼來(lái)說(shuō)明問(wèn)題。
實(shí)現(xiàn)InvocationHandler接口(代理方法處理接口)尽纽。通過(guò)實(shí)現(xiàn)類里面的getProxy()
拿到代理對(duì)象咐蚯。
public class JdkInvocationHandlerImpl<T> implements InvocationHandler {
/**
* 目標(biāo)對(duì)象
*/
private T targetObject;
public JdkInvocationHandlerImpl(T target) {
this.targetObject = target;
}
/**
* 綁定關(guān)系,也就是關(guān)聯(lián)到哪個(gè)接口(與具體的實(shí)現(xiàn)類綁定)的哪些方法將被調(diào)用時(shí)蜓斧,執(zhí)行invoke方法仓蛆。
*/
public AddService getProxy() {
//該方法用于為指定類裝載器、一組接口及調(diào)用處理器生成動(dòng)態(tài)代理類實(shí)例
//第一個(gè)參數(shù)指定產(chǎn)生代理對(duì)象的類加載器挎春,需要將其指定為和目標(biāo)對(duì)象同一個(gè)類加載器
//第二個(gè)參數(shù)要實(shí)現(xiàn)和目標(biāo)對(duì)象一樣的接口看疙,所以只需要拿到目標(biāo)對(duì)象的實(shí)現(xiàn)接口
//第三個(gè)參數(shù)表明這些被攔截的方法在被攔截時(shí)需要執(zhí)行哪個(gè)InvocationHandler的invoke方法
//根據(jù)傳入的目標(biāo)返回一個(gè)代理對(duì)象
return (AddService) Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),
targetObject.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 打印參數(shù)信息列表
for (Object arg : args) {
System.out.println(arg);
}
Object ret;
try {
/*原對(duì)象方法調(diào)用前處理日志信息*/
System.out.println("方法開(kāi)始執(zhí)行-->>");
//調(diào)用目標(biāo)方法
ret = method.invoke(targetObject, args);
/*原對(duì)象方法調(diào)用后處理日志信息*/
System.out.println("方法執(zhí)行成功-->>");
} catch (Exception e) {
// 執(zhí)行方法過(guò)程中出現(xiàn)異常
e.printStackTrace();
System.out.println("方法執(zhí)行失敗-->>");
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生成代理對(duì)象豆拨。直接調(diào)用Reflection.newProxy()函數(shù),這個(gè)函數(shù)也是需要兩個(gè)參數(shù):第一個(gè)參數(shù)接口能庆,要實(shí)現(xiàn)和目標(biāo)對(duì)象的某一樣的接口(那個(gè)接口里面的方法需要代理)施禾、第二個(gè)參數(shù)表明這些被攔截的方法在被攔截時(shí)需要執(zhí)行哪個(gè)InvocationHandler的invoke方法。具體使用實(shí)例如下:
實(shí)現(xiàn)InvocationHandler接口(代理方法處理接口)搁胆。通過(guò)實(shí)現(xiàn)類里面的Reflection.newProxy()拿到代理對(duì)象弥搞。
public class GuavaInvocationHandlerImpl<T> implements InvocationHandler {
/**
* 目標(biāo)對(duì)象對(duì)應(yīng)的接口(因?yàn)橐粋€(gè)對(duì)象可以實(shí)現(xiàn)多個(gè)接口,我們不知道是那個(gè)接口渠旁,所以傳遞進(jìn)來(lái))
*/
private Class<T> targetInterface;
/**
* 目標(biāo)對(duì)象
*/
private T targetObject;
public GuavaInvocationHandlerImpl(Class<T> targetInterface, T targetObject) {
/*參數(shù)判斷*/
// targetInterfaced一定要是一個(gè)接口
checkArgument(targetInterface.isInterface(), "%s 不是一個(gè)接口類", targetInterface);
// targetObject一定是targetInterface接口的實(shí)現(xiàn)類攀例。
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 必須實(shí)現(xiàn) %s", targetObject.getClass().getName(), targetInterface.getName());
this.targetInterface = targetInterface;
this.targetObject = targetObject;
}
/**
* 獲取都代理對(duì)象
*/
public T getProxy() {
// guava里面Reflection幫助類提供的 Reflection.newProxy() 實(shí)現(xiàn)
// 第一個(gè)參數(shù)接口,要實(shí)現(xiàn)和目標(biāo)對(duì)象的某一樣的接口(那個(gè)接口里面的方法需要代理)
// 第二個(gè)參數(shù)表明這些被攔截的方法在被攔截時(shí)需要執(zhí)行哪個(gè)InvocationHandler的invoke方法
return Reflection.newProxy(targetInterface, this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 打印參數(shù)信息列表
for (Object arg : args) {
System.out.println(arg);
}
Object ret;
try {
/*原對(duì)象方法調(diào)用前處理日志信息*/
System.out.println("方法開(kāi)始執(zhí)行-->>");
//調(diào)用目標(biāo)方法
ret = method.invoke(targetObject, args);
/*原對(duì)象方法調(diào)用后處理日志信息*/
System.out.println("方法執(zhí)行成功-->>");
} catch (Exception e) {
// 執(zhí)行方法過(guò)程中出現(xiàn)異常
e.printStackTrace();
System.out.println("方法執(zhí)行失敗-->>");
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
? ? ? ?有時(shí)候你可能想動(dòng)態(tài)代理能夠更直觀的支持equals()顾腊,hashCode()和toString()粤铭。AbstractInvocationHandler能幫我們做到三件事:
- 一個(gè)代理實(shí)例equal另外一個(gè)代理實(shí)例,只要他們有同樣的接口類型和equal的invocation handlers杂靶。
- 一個(gè)代理實(shí)例的toString()會(huì)被代理到invocation handler的toString()梆惯,這樣更容易自定義toString()。
- AbstractInvocationHandler確保傳遞給handleInvocation(Object, Method, Object[]))的參數(shù)數(shù)組永遠(yuǎn)不會(huì)空吗垮,從而減少了空指針異常的機(jī)會(huì)垛吗。
如果對(duì)上面講的幾點(diǎn)不是很明白的話,強(qiáng)烈建議大家看下AbstractInvocationHandler的源碼部分烁登。
public class GuavaAbstractInvocationHandlerImpl<T> extends AbstractInvocationHandler {
/**
* 目標(biāo)對(duì)象對(duì)應(yīng)的接口(因?yàn)橐粋€(gè)對(duì)象可以實(shí)現(xiàn)多個(gè)接口怯屉,我們不知道是那個(gè)接口,所以傳遞進(jìn)來(lái))
*/
private Class<T> targetInterface;
/**
* 目標(biāo)對(duì)象
*/
private T targetObject;
public GuavaAbstractInvocationHandlerImpl(Class<T> targetInterface, T targetObject) {
/*參數(shù)判斷*/
// targetInterfaced一定要是一個(gè)接口
checkArgument(targetInterface.isInterface(), "%s 不是一個(gè)接口類", targetInterface);
// targetObject一定是targetInterface接口的實(shí)現(xiàn)類防泵。
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 必須實(shí)現(xiàn) %s", targetObject.getClass().getName(), targetInterface.getName());
this.targetInterface = targetInterface;
this.targetObject = targetObject;
}
/**
* 獲取都代理對(duì)象
*/
public T getProxy() {
// guava里面Reflection幫助類提供的 Reflection.newProxy() 實(shí)現(xiàn)
return Reflection.newProxy(targetInterface, this);
}
@Override
protected Object handleInvocation(Object proxy, Method method, Object[] args) throws Throwable {
// 打印參數(shù)信息列表
for (Object arg : args) {
System.out.println(arg);
}
Object ret;
try {
/*原對(duì)象方法調(diào)用前處理日志信息*/
System.out.println("方法開(kāi)始執(zhí)行-->>");
//調(diào)用目標(biāo)方法
ret = method.invoke(targetObject, args);
/*原對(duì)象方法調(diào)用后處理日志信息*/
System.out.println("方法執(zhí)行成功-->>");
} catch (Exception e) {
// 執(zhí)行方法過(guò)程中出現(xiàn)異常
e.printStackTrace();
System.out.println("方法執(zhí)行失敗-->>");
throw e;
}
return ret;
}
}
四 ClassPath
? ? ? ?嚴(yán)格來(lái)講蚀之,Java沒(méi)有平臺(tái)無(wú)關(guān)的方式來(lái)瀏覽類和類資源。不過(guò)一定的包或者工程下捷泞,還是能夠?qū)崿F(xiàn)的足删,比方說(shuō),去檢查某個(gè)特定的工程的慣例或者某種一直遵從的約束锁右。
? ? ? ?ClassPath是一種實(shí)用工具失受,它提供盡最大努力的類路徑掃描。
? ? ? ?值得注意的是咏瑟,ClassPath是一個(gè)盡力而為的工具拂到。它只掃描jar文件中或者某個(gè)文件目錄下的class文件。也不能掃描非URLClassLoader的自定義class loader管理的class码泞,所以不要將它用于關(guān)鍵任務(wù)生產(chǎn)任務(wù)兄旬。
4.1 ClassPath常用方法
/**
* 創(chuàng)建一個(gè)ClassPath對(duì)象
*/
public static ClassPath from(ClassLoader classloader) throws IOException;
/**
* 返回從當(dāng)前類路徑可加載的所有資源,包括所有可加載類的類文件,但不包括“META-INF / MANIFEST.MF”文件
*/
public ImmutableSet<ResourceInfo> getResources();
/**
* 返回可加載的所有類
*/
public ImmutableSet<ClassInfo> getAllClasses();
/**
* 返回所有的頂級(jí)類(不包括內(nèi)部類)
*/
public ImmutableSet<ClassInfo> getTopLevelClasses();
/**
* 返回指定package下的所有頂級(jí)類
*/
public ImmutableSet<ClassInfo> getTopLevelClasses(String packageName);
/**
* 返回所有指定package已經(jīng)子package下所有的頂級(jí)類
*/
public ImmutableSet<ClassInfo> getTopLevelClassesRecursive(String packageName);
4.2 ClassPath簡(jiǎn)單使用
@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 {
}
? ? ? ?關(guān)于Guava反射機(jī)制工具類我們就暫時(shí)先提這些领铐,上文中涉及到的代碼可以在https://github.com/tuacy/google-guava-study里面找到(在單元測(cè)試代碼里面)悯森。