基于Java回顧之反射的使用分析
反射可以幫助我們查看指定類型中的信息、創(chuàng)建類型的實(shí)例,調(diào)用類型的方法。我們平時(shí)使用框架,例如Spring、EJB、Hibernate等都大量的使用了反射技術(shù)。
反射簡(jiǎn)單示例
下面來(lái)演示反射相關(guān)的基本操作
首先是基礎(chǔ)代碼,我們定義一個(gè)接口及其實(shí)現(xiàn),作為我們反射操作的目標(biāo):
interface HelloWorldService
{
void sayHello(String name);
}
class MyHelloWorld implements HelloWorldService
{
public String name;
public void sayHello(String name)
{
System.out.println("Hello " + name + ".");
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
獲取方法及字段信息
下面的代碼會(huì)輸出給定類型中的方法和字段的聲明信息:
private static void printClassTypeInfo(String type) throws ClassNotFoundException
{
Class classType = Class.forName(type);
Method[] methods = classType.getDeclaredMethods();
System.out.println("Methods info as below:");
for(Method method : methods)
{
System.out.println(method.toGenericString());
}
Field[] fields = classType.getFields();
System.out.println("Fields info as below:");
for (Field field : fields)
{
System.out.println(field.toGenericString());
}
}
在使用反射時(shí),我們一般會(huì)使用java.lang.reflect包中的內(nèi)容。
然后我們調(diào)用下面的代碼:
printClassTypeInfo("sample.reflection.MyHelloWorld");
輸出結(jié)果如下:
Methods info as below:
public void sample.reflection.MyHelloWorld.sayHello(java.lang.String)
public java.lang.String sample.reflection.MyHelloWorld.getName()
public void sample.reflection.MyHelloWorld.setName(java.lang.String)
Fields info as below:
public java.lang.String sample.reflection.MyHelloWorld.name
實(shí)例化對(duì)象
我們可以使用class.netInstance的方式來(lái)創(chuàng)建一個(gè)對(duì)象,代碼如下:
private static void createInstanceTest() throws ClassNotFoundException, InstantiationException, IllegalAccessException
{
Class classType = Class.forName("sample.reflection.MyHelloWorld");
MyHelloWorld hello = (MyHelloWorld)classType.newInstance();
hello.sayHello("Zhang San");
}
輸出結(jié)果:
Hello Zhang San.
調(diào)用對(duì)象的方法
我們可以通過(guò)方法的名稱以及參數(shù)類型構(gòu)建一個(gè)Method實(shí)例,然后調(diào)用Method的invoke方法,來(lái)觸發(fā)方法。
示例代碼如下:
private static void invokeMethodTest() throws InstantiationException, IllegalAccessException, ClassNotFoundException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException
{
Class classType = Class.forName("sample.reflection.MyHelloWorld");
MyHelloWorld hello = (MyHelloWorld)classType.newInstance();
Method method = classType.getMethod("sayHello", new Class[]{String.class});
method.invoke(hello, new Object[]{"Zhang San"});
}
輸出結(jié)果同上。
修改字段的值
和C#不同,Java中一般使用setxxx和getxxx顯示為屬性賦值,因此Java中并沒(méi)有Property類型,而是有Field類型。
我們可以對(duì)Field的值進(jìn)行修改,代碼如下:
private static void setFieldTest() throws ClassNotFoundException, NoSuchFieldException, SecurityException, InstantiationException, IllegalAccessException
{
Class classType = Class.forName("sample.reflection.MyHelloWorld");
MyHelloWorld hello = (MyHelloWorld)classType.newInstance();
System.out.println("name is " + hello.name);
Field field = classType.getField("name");
field.set(hello, "Zhang San");
System.out.println("name is " + hello.name);
}
執(zhí)行結(jié)果如下:
name is null
name is Zhang San
可以看出,我們成功的修改了name的值。
Annotation探索
一開始我們提到,反射是很多技術(shù)的基礎(chǔ),Annotation就是這樣的,我們可以把Annotation看做是C#中的Attribute,它可以對(duì)類型、方法、屬性、字段、方法參數(shù)等信息進(jìn)行修飾。我們可以使用“@+Annotation名”的方式來(lái)使用Annotation。
Annotation基本操作
來(lái)看下面的代碼,我們定義了基于Type、Method、Parameter和Field上面的Annotation示例:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@interface ClassAnnotation
{
public String value();
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@interface MethodAnnotation
{
public String methodName();
public String returnType();
}
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@interface ParameterAnnotation
{
public String value();
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@interface FieldAnnotation
{
public String value();
}
接著,我們定義了一個(gè)MyClass類型,使用了上述的Annotation:
@ClassAnnotation("這是作用在類型上的Annotation")
class MyClass
{
@MethodAnnotation(methodName="printInfo", returnType="void")
public void printInfo(String info)
{
System.out.println(info);
}
@MethodAnnotation(methodName="printError", returnType="void")
public void printError(@ParameterAnnotation("這是作用在參數(shù)上的Annotation")String error)
{
System.err.println(error);
}
@FieldAnnotation("這是作用在字段上的Annotation")
public int count;
}
對(duì)于使用了Annotation,我們可以獲取其中的信息,下面兩種方式都可以獲取Annotation,第一種方式是通過(guò)反射遍歷類型及其方法、字段,一一讀取Annotation信息;第二種方式是讀取指定類型的Annotation:
讀取Annotation方式一
private static void annotationTest1()
{
MyClass temp = new MyClass();
Annotation[] annotations = temp.getClass().getAnnotations();
for(Annotation a : annotations)
{
System.out.println(a.toString());
}
Method[] methods = temp.getClass().getDeclaredMethods();
for(Method method : methods)
{
annotations = method.getAnnotations();
for(Annotation a : annotations)
{
System.out.println(a.toString());
}
Annotation[][] paraAnnotations = method.getParameterAnnotations();
for(int i = 0; i < paraAnnotations.length; i++)
{
for (Annotation a : paraAnnotations[i])
{
System.out.println(a.toString());
}
}
}
Field[] fields = temp.getClass().getFields();
for (Field field : fields)
{
annotations = field.getAnnotations();
for(Annotation a : annotations)
{
System.out.println(a.toString());
}
}
}
讀取Annotation方式二
private static void annotationTest2() throws ClassNotFoundException
{
Class classType = Class.forName("sample.reflection.annotation.MyClass");
boolean flag = classType.isAnnotationPresent(ClassAnnotation.class);
if (flag)
{
ClassAnnotation annotation = (ClassAnnotation) classType.getAnnotation(ClassAnnotation.class);
System.out.println(annotation.toString());
}
Method[] methods = classType.getMethods();
for(Method method : methods)
{
if (method.isAnnotationPresent(MethodAnnotation.class))
{
System.out.println(((MethodAnnotation)method.getAnnotation(MethodAnnotation.class)).toString());
}
Annotation[][] paraAnnotations = method.getParameterAnnotations();
for(int i = 0; i < paraAnnotations.length; i++)
{
for (Annotation a : paraAnnotations[i])
{
System.out.println(a.toString());
}
}
}
Field[] fields = classType.getFields();
for (Field field:fields)
{
if (field.isAnnotationPresent(FieldAnnotation.class))
{
System.out.println(((FieldAnnotation)field.getAnnotation(FieldAnnotation.class)).toString());
}
}
}
上述兩個(gè)方法的輸出都是一樣的,如下:
@sample.reflection.annotation.ClassAnnotation(value=這是作用在類型上的Annotation)
@sample.reflection.annotation.MethodAnnotation(methodName=printInfo, returnType=void)
@sample.reflection.annotation.MethodAnnotation(methodName=printError, returnType=void)
@sample.reflection.annotation.ParameterAnnotation(value=這是作用在參數(shù)上的Annotation)
@sample.reflection.annotation.FieldAnnotation(value=這是作用在字段上的Annotation)
在WebService中使用Annotation
上述代碼看上去可能有些枯燥,不能顯示出Annotation的威力,那么我們接下來(lái)看WebService,在WebService中,我們可以使用WebMethod、WebParam等Annotation來(lái)聲明方法或者參數(shù)。
接下來(lái),我們來(lái)實(shí)現(xiàn)一個(gè)非常簡(jiǎn)單的Web服務(wù):
@WebService(targetNamespace="http://test", serviceName="HelloService")
public class HelloServiceProvider
{
@WebResult(name="HelloString")
@WebMethod
public String sayHello(@WebParam(name="userName") String name)
{
return "Hello " + name;
}
@Oneway
@WebMethod(action="userLogin", operationName="userLogin")
public void login()
{
System.out.println("User has logged on.");
}
public static void main(String[] args)
{
Thread thread = new Thread(new HelloServicePublisher());
thread.start();
}
}
然后定義一個(gè)Publisher:
class HelloServicePublisher implements Runnable
{
public void run()
{
Endpoint.publish("http://localhost:8888/test/HelloService", new HelloServiceProvider());
}
}
在命令行中,我們定位到源代碼路徑,執(zhí)行下面的命令:
wsgen -cp . HelloServiceProvider
wsgen位于JDK的bin目錄中。
然后我們啟動(dòng)HelloServiceProvider,在瀏覽器中輸入如下地址:http://localhost:8888/test/HelloService,可以看到如下信息:
點(diǎn)擊WSDL鏈接,可以看到:
WSDL信息
<!-- Published by JAX-WS RI at http://jax-ws.dev.java.net. RI's version is JAX-WS RI 2.2.4-b01. --><!-- Generated by JAX-WS RI at http://jax-ws.dev.java.net. RI's version is JAX-WS RI 2.2.4-b01. --><definitions targetNamespace="http://test" name="HelloService"><types><xsd:schema><xsd:import namespace="http://test" schemaLocation="http://localhost:8888/test/HelloService?xsd=1"/></xsd:schema></types><message name="sayHello"><part name="parameters" element="tns:sayHello"/></message><message name="sayHelloResponse"><part name="parameters" element="tns:sayHelloResponse"/></message><message name="userLogin"><part name="parameters" element="tns:userLogin"/></message><portType name="HelloServiceProvider"><operation name="sayHello"><input wsam:Action="http://test/HelloServiceProvider/sayHelloRequest" message="tns:sayHello"/><output wsam:Action="http://test/HelloServiceProvider/sayHelloResponse" message="tns:sayHelloResponse"/></operation><operation name="userLogin"><input wsam:Action="userLogin" message="tns:userLogin"/></operation></portType><binding name="HelloServiceProviderPortBinding" type="tns:HelloServiceProvider"><soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/><operation name="sayHello"><soap:operation soapAction=""/><input><soap:body use="literal"/></input><output><soap:body use="literal"/></output></operation><operation name="userLogin"><soap:operation soapAction="userLogin"/><input><soap:body use="literal"/></input></operation></binding><service name="HelloService"><port name="HelloServiceProviderPort" binding="tns:HelloServiceProviderPortBinding"><soap:address location="http://localhost:8888/test/HelloService"/></port></service></definitions>
JDK中自帶了Web服務(wù)器,我們不需要把上述代碼部署到其他服務(wù)器中。
動(dòng)態(tài)代理機(jī)制
Spring中一大特色是AOP,面向方面編程也是框架設(shè)計(jì)一個(gè)趨勢(shì)。對(duì)于業(yè)務(wù)中的共通操作,諸如記錄日志、維護(hù)事務(wù)等,如果和業(yè)務(wù)邏輯糾纏在一起,會(huì)造成代碼職責(zé)不清,后續(xù)維護(hù)困難等問(wèn)題。利用AOP,我們可以很好的分離共通操作和業(yè)務(wù)操作。
下面我們來(lái)實(shí)現(xiàn)一個(gè)簡(jiǎn)單的AOP框架,要實(shí)現(xiàn)這樣一個(gè)框架,需要3部分:1)InvocationHandler,來(lái)觸發(fā)方法;2)Interceptor,來(lái)定義攔截器;3)DynamicProxy,來(lái)動(dòng)態(tài)創(chuàng)建代理對(duì)象。
首先我們看Interptor的定義:
interface AOPInterceptor
{
public void before(Method method, Object[] args);
public void after(Method method, Object[] args);
public void afterThrowing(Method method, Object[] args);
public void afterFinally(Method method, Object[] args);
}
接下來(lái)是InvocationHandler:
class DynamicProxyInvocationHandler implements InvocationHandler
{
private Object target;
private AOPInterceptor interceptor;
public DynamicProxyInvocationHandler(Object target, AOPInterceptor interceptor)
{
this.target = target;
this.interceptor = interceptor;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
{
try
{
interceptor.before(method, args);
Object returnValue = method.invoke(target, args);
interceptor.after(method, args);
return returnValue;
}
catch(Throwable t)
{
interceptor.afterThrowing(method, args);
throw t;
}
finally
{
interceptor.afterFinally(method, args);
}
}
}
最后是DynamicProxy:
class DynamicProxyFactoryImpl implements DynamicProxyFactory
{
public <T> T createProxy(Class<T> clazz, T target, AOPInterceptor interceptor)
{
InvocationHandler handler = new DynamicProxyInvocationHandler(target, interceptor);
return (T)Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class<?>[] {clazz}, handler);
}
}
至此,我們構(gòu)建了一個(gè)”簡(jiǎn)易“的AOP攔截器。下面我們來(lái)創(chuàng)建一些測(cè)試代碼。
首先是實(shí)現(xiàn)AOPInterceptor接口:
class MyInterceptor implements AOPInterceptor
{
public void after(Method method, Object[] args) {
System.out.println("方法執(zhí)行結(jié)束。");
}
public void afterFinally(Method method, Object[] args) {
System.out.println("方法體Finally執(zhí)行結(jié)束。");
}
public void afterThrowing(Method method, Object[] args) {
System.out.println("方法拋出異常。");
}
public void before(Method method, Object[] args) {
System.out.println("方法開始執(zhí)行");
}
}
然后利用本文一開始定義的HelloWorldService,來(lái)完成測(cè)試,需要在MyHello的sayHello方法最后,追加一行代碼:
throw new RuntimeException();
接著是測(cè)試代碼:
private static void test()
{
MyInterceptor interceptor = new MyInterceptor();
HelloWorldService hello = new MyHelloWorld();
DynamicProxyFactory factory = new DynamicProxyFactoryImpl();
HelloWorldService proxy = factory.createProxy(HelloWorldService.class, hello, interceptor);
proxy.sayHello("Zhang San");
}
最終,執(zhí)行結(jié)果如下:
方法開始執(zhí)行
Hello Zhang San.
方法拋出異常。
方法體Finally執(zhí)行結(jié)束。
Exception in thread "main" java.lang.reflect.UndeclaredThrowableException
at sample.reflection.dynamicproxy.$Proxy0.sayHello(Unknown Source)
at sample.reflection.dynamicproxy.Sample.test(Sample.java:18)
at sample.reflection.dynamicproxy.Sample.main(Sample.java:9)
Caused by: java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at sample.reflection.dynamicproxy.DynamicProxyInvocationHandler.invoke(Sample.java:60)
... 3 more
可以看出,我們已經(jīng)在業(yè)務(wù)執(zhí)行的前、后、異常拋出后以及finally執(zhí)行后進(jìn)行了攔截,達(dá)到了我們期望的效果。
相關(guān)文章
SpringBoot啟動(dòng)失敗的解決方法:A component required a&nb
這篇文章主要介紹了解決SpringBoot啟動(dòng)失?。篈 component required a bean of type ‘xxxxxxx‘ that could not be found.,目前解決方法有兩種,一種是不注入bean的方式,另一種是使用@Component的方式,本文給大家詳細(xì)講解,需要的朋友可以參考下2023-02-02利用Jackson解決Json序列化和反序列化問(wèn)題
Jackson是一個(gè)用于處理Json數(shù)據(jù)的Java庫(kù),它提供了一系列功能,包括Json序列化和反序列化,所以本文就來(lái)講講如何利用利用Jackson解決Json序列化和反序列化的問(wèn)題吧2023-05-05Tree組件實(shí)現(xiàn)支持50W數(shù)據(jù)方法剖析
這篇文章主要為大家介紹了Tree組件實(shí)現(xiàn)支持50W數(shù)據(jù)的方法剖析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-08-08OutOfMemoryError內(nèi)存不足和StackOverflowError堆棧溢出示例詳解
這篇文章主要為大家介紹了OutOfMemoryError內(nèi)存不足和StackOverflowError堆棧溢出示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-09-09SpringMVC @RequestBody自動(dòng)轉(zhuǎn)json Http415錯(cuò)誤的解決
這篇文章主要介紹了SpringMVC @RequestBody自動(dòng)轉(zhuǎn)json Http415錯(cuò)誤的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-04-04