欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

基于Java回顾之反射的使用分析

程序员文章站 2023-12-17 08:33:40
反射可以帮助我们查看指定类型中的信息、创建类型的实例,调用类型的方法。我们平时使用框架,例如spring、ejb、hibernate等都大量的使用了反射技术。反射简单示例 ...

反射可以帮助我们查看指定类型中的信息、创建类型的实例,调用类型的方法。我们平时使用框架,例如spring、ejb、hibernate等都大量的使用了反射技术。
反射简单示例
  下面来演示反射相关的基本操作

  首先是基础代码,我们定义一个接口及其实现,作为我们反射操作的目标:

复制代码 代码如下:

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;
     }
 }

  获取方法及字段信息  
  下面的代码会输出给定类型中的方法和字段的声明信息:
复制代码 代码如下:

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());
     }
 }

  在使用反射时,我们一般会使用java.lang.reflect包中的内容。

  然后我们调用下面的代码:

复制代码 代码如下:

printclasstypeinfo("sample.reflection.myhelloworld");

  输出结果如下:
复制代码 代码如下:

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

  实例化对象
  我们可以使用class.netinstance的方式来创建一个对象,代码如下:
复制代码 代码如下:

private static void createinstancetest() throws classnotfoundexception, instantiationexception, illegalaccessexception
 {
     class classtype = class.forname("sample.reflection.myhelloworld");
     myhelloworld hello = (myhelloworld)classtype.newinstance();
     hello.sayhello("zhang san");
 }

  输出结果:
复制代码 代码如下:

hello zhang san.

  调用对象的方法
  我们可以通过方法的名称以及参数类型构建一个method实例,然后调用method的invoke方法,来触发方法。

  示例代码如下:

复制代码 代码如下:

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"});
 }

  输出结果同上。

  修改字段的值
  和c#不同,java中一般使用setxxx和getxxx显示为属性赋值,因此java中并没有property类型,而是有field类型。

  我们可以对field的值进行修改,代码如下:

复制代码 代码如下:

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);
 }

  执行结果如下:
复制代码 代码如下:

name is null
name is zhang san

  可以看出,我们成功的修改了name的值。

  annotation探索
  一开始我们提到,反射是很多技术的基础,annotation就是这样的,我们可以把annotation看做是c#中的attribute,它可以对类型、方法、属性、字段、方法参数等信息进行修饰。我们可以使用“@+annotation名”的方式来使用annotation。

  annotation基本操作
  来看下面的代码,我们定义了基于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();
 }

  接着,我们定义了一个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("这是作用在参数上的annotation")string error)
     {
         system.err.println(error);
     }

     @fieldannotation("这是作用在字段上的annotation")
     public int count;
 }

  对于使用了annotation,我们可以获取其中的信息,下面两种方式都可以获取annotation,第一种方式是通过反射遍历类型及其方法、字段,一一读取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());
         }
     }
 }

  上述两个方法的输出都是一样的,如下:
复制代码 代码如下:

@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=这是作用在参数上的annotation)
@sample.reflection.annotation.fieldannotation(value=这是作用在字段上的annotation)

  在webservice中使用annotation
  上述代码看上去可能有些枯燥,不能显示出annotation的威力,那么我们接下来看webservice,在webservice中,我们可以使用webmethod、webparam等annotation来声明方法或者参数。

  接下来,我们来实现一个非常简单的web服务:

复制代码 代码如下:

@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();
     }
 }

  然后定义一个publisher:
复制代码 代码如下:

class helloservicepublisher implements runnable
 {
     public void run()
     {
         endpoint.publish("http://localhost:8888/test/helloservice", new helloserviceprovider());
     }
 }

  在命令行中,我们定位到源代码路径,执行下面的命令:
复制代码 代码如下:

wsgen -cp . helloserviceprovider

  wsgen位于jdk的bin目录中。

  然后我们启动helloserviceprovider,在浏览器中输入如下地址:http://localhost:8888/test/helloservice,可以看到如下信息:
基于Java回顾之反射的使用分析
  点击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服务器,我们不需要把上述代码部署到其他服务器中。

  动态代理机制
  spring中一大特色是aop,面向方面编程也是框架设计一个趋势。对于业务中的共通操作,诸如记录日志、维护事务等,如果和业务逻辑纠缠在一起,会造成代码职责不清,后续维护困难等问题。利用aop,我们可以很好的分离共通操作和业务操作。

  下面我们来实现一个简单的aop框架,要实现这样一个框架,需要3部分:1)invocationhandler,来触发方法;2)interceptor,来定义拦截器;3)dynamicproxy,来动态创建代理对象。

  首先我们看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);
 }

  接下来是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);
     }
 }

  至此,我们构建了一个”简易“的aop拦截器。下面我们来创建一些测试代码。

  首先是实现aopinterceptor接口:

复制代码 代码如下:

class myinterceptor implements aopinterceptor
 {

     public void after(method method, object[] args) {
         system.out.println("方法执行结束。");
     }

     public void afterfinally(method method, object[] args) {
         system.out.println("方法体finally执行结束。");
     }

     public void afterthrowing(method method, object[] args) {
         system.out.println("方法抛出异常。");
     }

     public void before(method method, object[] args) {
         system.out.println("方法开始执行");
     }
 }

  然后利用本文一开始定义的helloworldservice,来完成测试,需要在myhello的sayhello方法最后,追加一行代码:
复制代码 代码如下:

throw new runtimeexception();

  接着是测试代码:
复制代码 代码如下:

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");
 }

  最终,执行结果如下:
复制代码 代码如下:

方法开始执行
hello zhang san.
方法抛出异常。
方法体finally执行结束。
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

  可以看出,我们已经在业务执行的前、后、异常抛出后以及finally执行后进行了拦截,达到了我们期望的效果。

上一篇:

下一篇: