} } }

    Java回顾之反射

    添加时间:2013-5-9 点击量:

      第一篇:Java回顾之I/O


      第二篇:Java回顾之收集通信


      第三篇:Java回顾之多线程


      第四篇:Java回顾之多线程同步


      第五篇:Java回顾之凑集


      第六篇:Java回顾之序列化



      在这一篇文章里,我们存眷反射及其相干话题。


      反射可以帮助我们查看指定类型中的信息、创建类型的实例,调用类型的办法。我们日常平凡应用框架,例如Spring、EJB、Hibernate等都多量的应用了反射技巧。


      反射简单示例


      下面来演示反射相干的根蒂根基操纵


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



     1 interface HelloWorldService
    
    2 {
    3 void sayHello(String name);
    4 }
    5
    6 class MyHelloWorld implements HelloWorldService
    7 {
    8 public String name;
    9
    10
    11 public void sayHello(String name)
    12 {
    13 System.out.println(Hello + name + .);
    14 }
    15
    16 public void setName(String name) {
    17 this.name = name;
    18 }
    19
    20 public String getName() {
    21 return name;
    22 }
    23 }


      获取办法及字段信息  


      下面的代输出给定类型中的办法和字段的声明信息:



     1 private static void printClassTypeInfo(String type) throws ClassNotFoundException
    
    2 {
    3 Class classType = Class.forName(type);
    4 Method[] methods = classType.getDeclaredMethods();
    5 System.out.println(Methods info as below:);
    6 for(Method method : methods)
    7 {
    8 System.out.println(method.toGenericString());
    9 }
    10 Field[] fields = classType.getFields();
    11 System.out.println(Fields info as below:);
    12 for (Field field : fields)
    13 {
    14 System.out.println(field.toGenericString());
    15 }
    16 }


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


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



    1 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的体式格式来创建一个对象,代码如下:



    1 private static void createInstanceTest() throws ClassNotFoundException, InstantiationException, IllegalAccessException
    
    2 {
    3 Class classType = Class.forName(sample.reflection.MyHelloWorld);
    4 MyHelloWorld hello = (MyHelloWorld)classType.newInstance();
    5 hello.sayHello(Zhang San);
    6 }


      输出成果:



    Hello Zhang San.


      调用对象的办法


      我们可以经由过程办法的名称以及参数类型构建一个Method实例,然后调用Method的invoke办法,来触发办法。


      示例代码如下:



    1 private static void invokeMethodTest() throws InstantiationException, IllegalAccessException, ClassNotFoundException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException
    
    2 {
    3 Class classType = Class.forName(sample.reflection.MyHelloWorld);
    4 MyHelloWorld hello = (MyHelloWorld)classType.newInstance();
    5 Method method = classType.getMethod(sayHello, new Class[]{String.class});
    6 method.invoke(hello, new Object[]{Zhang San});
    7 }


      输出成果同上。


      批改字段的值


      和C#不合,Java中一般应用setxxx和getxxx显示为属性赋值,是以Java中并没有Property类型,而是有Field类型。


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



    1 private static void setFieldTest() throws ClassNotFoundException, NoSuchFieldException, SecurityException, InstantiationException, IllegalAccessException
    
    2 {
    3 Class classType = Class.forName(sample.reflection.MyHelloWorld);
    4 MyHelloWorld hello = (MyHelloWorld)classType.newInstance();
    5 System.out.println(name is + hello.name);
    6 Field field = classType.getField(name);
    7 field.set(hello, Zhang San);
    8 System.out.println(name is + hello.name);
    9 }


      履行成果如下:



    name is null
    
    name is Zhang San


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


      Annotation摸索


      一开端我们提到,反射是很多技巧的根蒂根基,Annotation就是如许的,我们可以把Annotation看做是C#中的Attribute,它可以对类型、办法、属性、字段、办法参数等信息进行润饰。我们可以应用“@+Annotation名”的体式格式来应用Annotation。


      Annotation根蒂根基操纵


      来看下面的代码,我们定义了基于Type、Method、Parameter和Field上方的Annotation示例:



     1 @Target(ElementType.TYPE)
    
    2 @Retention(RetentionPolicy.RUNTIME)
    3 @Documented
    4interface ClassAnnotation
    5 {
    6 public String value();
    7 }
    8
    9 @Target(ElementType.METHOD)
    10 @Retention(RetentionPolicy.RUNTIME)
    11 @Documented
    12interface MethodAnnotation
    13 {
    14 public String methodName();
    15 public String returnType();
    16 }
    17
    18 @Target(ElementType.PARAMETER)
    19 @Retention(RetentionPolicy.RUNTIME)
    20 @Documented
    21interface ParameterAnnotation
    22 {
    23 public String value();
    24 }
    25
    26 @Target(ElementType.FIELD)
    27 @Retention(RetentionPolicy.RUNTIME)
    28 @Documented
    29interface FieldAnnotation
    30 {
    31 public String value();
    32 }


      接着,我们定义了一个MyClass类型,应用了上述的Annotation:



     1 @ClassAnnotation(这是感化在类型上的Annotation
    2 class MyClass
    3 {
    4 @MethodAnnotation(methodName=printInfo, returnType=void
    5 public void printInfo(String info)
    6 {
    7 System.out.println(info);
    8 }
    9
    10 @MethodAnnotation(methodName=printError, returnType=void
    11 public void printError(@ParameterAnnotation(这是感化在参数上的Annotation)String error)
    12 {
    13 System.err.println(error);
    14 }
    15
    16 @FieldAnnotation(这是感化在字段上的Annotation
    17 public int count;
    18 }


      对于应用了Annotation,我们可以获取此中的信息,下面两种体式格式都可以获取Annotation,第一种体式格式是经由过程反射遍历类型及其办法、字段,一一读取Annotation信息;第二种体式格式是读取指定类型的Annotation:


    读取Annotation体式格式一

     1 private static void annotationTest1()
    
    2 {
    3 MyClass temp = new MyClass();
    4
    5 Annotation[] annotations = temp.getClass().getAnnotations();
    6 for(Annotation a : annotations)
    7 {
    8 System.out.println(a.toString());
    9 }
    10
    11 Method[] methods = temp.getClass().getDeclaredMethods();
    12 for(Method method : methods)
    13 {
    14 annotations = method.getAnnotations();
    15 for(Annotation a : annotations)
    16 {
    17 System.out.println(a.toString());
    18 }
    19 Annotation[][] paraAnnotations = method.getParameterAnnotations();
    20 forint i = 0; i < paraAnnotations.length; i++
    21 {
    22 for (Annotation a : paraAnnotations[i])
    23 {
    24 System.out.println(a.toString());
    25 }
    26 }
    27 }
    28
    29 Field[] fields = temp.getClass().getFields();
    30 for (Field field : fields)
    31 {
    32 annotations = field.getAnnotations();
    33 for(Annotation a : annotations)
    34 {
    35 System.out.println(a.toString());
    36 }
    37 }
    38 }



    读取Annotation体式格式二

     1 private static void annotationTest2() throws ClassNotFoundException
    
    2 {
    3 Class classType = Class.forName(sample.reflection.annotation.MyClass);
    4 boolean flag = classType.isAnnotationPresent(ClassAnnotation.class);
    5 if (flag)
    6 {
    7 ClassAnnotation annotation = (ClassAnnotation) classType.getAnnotation(ClassAnnotation.class);
    8 System.out.println(annotation.toString());
    9 }
    10 Method[] methods = classType.getMethods();
    11 for(Method method : methods)
    12 {
    13 if (method.isAnnotationPresent(MethodAnnotation.class))
    14 {
    15 System.out.println(((MethodAnnotation)method.getAnnotation(MethodAnnotation.class)).toString());
    16 }
    17 Annotation[][] paraAnnotations = method.getParameterAnnotations();
    18 forint i = 0; i < paraAnnotations.length; i++
    19 {
    20 for (Annotation a : paraAnnotations[i])
    21 {
    22 System.out.println(a.toString());
    23 }
    24 }
    25 }
    26 Field[] fields = classType.getFields();
    27 for (Field field:fields)
    28 {
    29 if (field.isAnnotationPresent(FieldAnnotation.class))
    30 {
    31 System.out.println(((FieldAnnotation)field.getAnnotation(FieldAnnotation.class)).toString());
    32 }
    33 }
    34 }



      上述两个办法的输出都是一样的,如下:



    @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办事:



     1 @WebService(targetNamespace=http://test, serviceName=HelloService
    2 public class HelloServiceProvider
    3 {
    4 @WebResult(name=HelloString
    5 @WebMethod
    6 public String sayHello(@WebParam(name=userName) String name)
    7 {
    8 return Hello + name;
    9 }
    10
    11 @Oneway
    12 @WebMethod(action=userLogin, operationName=userLogin
    13 public void login()
    14 {
    15 System.out.println(User has logged on.);
    16 }
    17
    18 public static void main(String[] args)
    19 {
    20 Thread thread = new Thread(new HelloServicePublisher());
    21 thread.start();
    22 }
    23 }


      然后定义一个Publisher:



    1 class HelloServicePublisher implements Runnable
    
    2 {
    3 public void run()
    4 {
    5 Endpoint.publish(http://localhost:8888/test/HelloService, new HelloServiceProvider());
    6 }
    7 }


      在号令行中,我们定位到源代码路径,履行下面的号令:



    wsgen -cp . HelloServiceProvider


      wsgen位于JDK的bin目次中。


      然后我们启动HelloServiceProvider,在浏览器中输入如下地址:http://localhost:8888/test/HelloService,可以看到如下信息:



      点击WSDL链接,可以看到:


    WSDL信息

    <!-- Published by JAX-WS RI at http://jax-ws.dev.java.net. RIs version is JAX-WS RI 2.2.4-b01. --><!-- Generated by JAX-WS RI at http://jax-ws.dev.java.net. RIs 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的定义:



    1 interface AOPInterceptor
    
    2 {
    3 public void before(Method method, Object[] args);
    4 public void after(Method method, Object[] args);
    5 public void afterThrowing(Method method, Object[] args);
    6 public void afterFinally(Method method, Object[] args);
    7 }


      接下来是InvocationHandler:



     1 class DynamicProxyInvocationHandler implements InvocationHandler
    
    2 {
    3 private Object target;
    4 private AOPInterceptor interceptor;
    5
    6 public DynamicProxyInvocationHandler(Object target, AOPInterceptor interceptor)
    7 {
    8 this.target = target;
    9 this.interceptor = interceptor;
    10 }
    11
    12 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
    13 {
    14 try
    15 {
    16 interceptor.before(method, args);
    17 Object returnValue = method.invoke(target, args);
    18 interceptor.after(method, args);
    19 return returnValue;
    20 }
    21 catch(Throwable t)
    22 {
    23 interceptor.afterThrowing(method, args);
    24 throw t;
    25 }
    26 finally
    27 {
    28 interceptor.afterFinally(method, args);
    29 }
    30 }
    31 }


      最后是DynamicProxy:



    1 class DynamicProxyFactoryImpl implements DynamicProxyFactory
    
    2 {
    3 public <T> T createProxy(Class<T> clazz, T target, AOPInterceptor interceptor)
    4 {
    5 InvocationHandler handler = new DynamicProxyInvocationHandler(target, interceptor);
    6 return (T)Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class<?>[] {clazz}, handler);
    7 }
    8 }


      至此,我们构建了一个”简略单纯“的AOP阻碍器。下面我们来创建一些测试代码。


      起首是实现AOPInterceptor接口:



     1 class MyInterceptor implements AOPInterceptor
    
    2 {
    3
    4 public void after(Method method, Object[] args) {
    5 System.out.println(办法履行停止。);
    6 }
    7
    8 public void afterFinally(Method method, Object[] args) {
    9 System.out.println(办法体Finally履行停止。);
    10 }
    11
    12 public void afterThrowing(Method method, Object[] args) {
    13 System.out.println(办法抛出异常。);
    14 }
    15
    16 public void before(Method method, Object[] args) {
    17 System.out.println(办法开端履行);
    18 }
    19 }


      然后哄骗本文一开端定义的HelloWorldService,来完成测试,须要在MyHello的sayHello办法最后,追加一行代码:



    1 throw new RuntimeException();


      接着是测试代码:



    1 private static void test()
    
    2 {
    3 MyInterceptor interceptor = new MyInterceptor();
    4 HelloWorldService hello = new MyHelloWorld();
    5 DynamicProxyFactory factory = new DynamicProxyFactoryImpl();
    6 HelloWorldService proxy = factory.createProxy(HelloWorldService.class, hello, interceptor);
    7 proxy.sayHello(Zhang San);
    8 }


      终极,履行成果如下:



    办法开端履行
    
    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履行掉队行了阻碍,达到了我们期望的结果。

    我所有的自负皆来自我的自卑,所有的英雄气概都来自于我的软弱。嘴里振振有词是因为心里满是怀疑,深情是因为痛恨自己无情。这世界没有一件事情是虚空而生的,站在光里,背后就会有阴影,这深夜里一片寂静,是因为你还没有听见声音。—— 马良《坦白书》
    分享到: