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

反射可以帮助我们查看指定类型中的信息、创建类型的实例,调用类型的方法。我们平时使用框架,例如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,可以看到如下信息:

  点击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执行后进行了拦截,达到了我们期望的效果。

(0)

相关推荐

  • JAVA反射机制实例教程

    本文以实例形式详细讲述了Java的反射机制,是Java程序设计中重要的技巧.分享给大家供大家参考.具体分析如下: 首先,Reflection是Java 程序开发语言的特征之一,它允许运行中的 Java 程序对自身进行检查,或者说"自审",并能直接操作程序的内部属性.例如,使用它能获得 Java 类中各成员的名称并显示出来. Java 的这一能力在实际应用中也许用得不是很多,但是在其它的程序设计语言中根本就不存在这一特性.例如,Pascal.C 或者 C++ 中就没有办法在程序中获得函数

  • java使用dom4j解析xml配置文件实现抽象工厂反射示例

    逻辑描述: 现在我们想在B层和D层加上接口层,并使用工厂.而我们可以将创建B和创建D看作是两个系列,然后就可以使用抽象工厂进行创建了. 配置文件:beans-config.xml.service-class与dao-class分别对应两个系列的产品.子菜单中id对应接口的命名空间,class对应实现类的命名空间. 复制代码 代码如下: [html] view plaincopyprint? <?xml version="1.0" encoding="UTF-8"

  • java根据方法名称取得反射方法的参数类型示例

    复制代码 代码如下: /** * 根据方法名称取得反射方法的参数类型(没有考虑同名重载方法使用时注意) * @param obj         类实例   * @param methodName  方法名 * @return * @throws ClassNotFoundException */public static Class[]  getMethodParamTypes(Object classInstance,  String methodName) throws ClassNotF

  • Java中反射的一个简单使用

    简介 首先介绍一些不太实用的解释:JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意方法和属性:这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制. 简单使用 反射,在java中是非常常见和好用的一种方式,(但是大家需要知道,他的效率是比较低的,所以要慎用)当然在基于java语言而产生的Android中也是可以使用的,我们可以使用反射来获取一些系统并不开放,但是存在的类,从而调用他的一些方法,下面就简单的写一下

  • 实例讲解Java编程中数组反射的使用方法

    什么是反射 "反射(Reflection)能够让运行于JVM中的程序检测和修改运行时的行为."这个概念常常会和内省(Introspection)混淆,以下是这两个术语在Wikipedia中的解释: 内省用于在运行时检测某个对象的类型和其包含的属性: 反射用于在运行时检测和修改某个对象的结构及其行为. 从它们的定义可以看出,内省是反射的一个子集.有些语言支持内省,但并不支持反射,如C++. 内省示例:instanceof 运算符用于检测某个对象是否属于特定的类. if (obj inst

  • java反射使用示例分享

    复制代码 代码如下: public class ReflexTest { public static void main(String[] args)      throws ClassNotFoundException, NoSuchMethodException, SecurityException,     IllegalAccessException, IllegalArgumentException, InvocationTargetException,      Instantiat

  • java类加载器和类反射使用示例

    一.一个命令对应一个进程. 当我们启动一个Java程序,即启动一个main方法时,都将启动一个Java虚拟机进程,不管这个进程有多么复杂.而不同的JVM进程之间是不会相互影响的.这也就是为什么说,Java程序只有一个入口--main方法,让虚拟机调用.而两个mian方法,对应的是2个JVM进程,启动的是两个不同的类加载器,操作的实际上是不同的类.故而不会互相影响. 二.类加载. 当我们使用一个类,如果这个类还未加载到内存中,系统会通过加载.连接.初始化对类进行初始化. 1.类加载:指的是将类的c

  • Java反射机制及Method.invoke详解

    JAVA反射机制 JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法:这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制. Java反射机制主要提供了以下功能: 在运行时判断任意一个对象所属的类:在运行时构造任意一个类的对象:在运行时判断任意一个类所具有的成员变量和方法:在运行时调用任意一个对象的方法:生成动态代理. 1. 得到某个对象的属性 复制代码 代码如下: public Object get

  • Java 反射获取类详细信息的常用方法总结

    类ReflectionDemo 复制代码 代码如下: package Reflection; @Deprecated public class ReflectionDemo {     private String pri_field;     public String pub_field;     public ReflectionDemo(){}     public ReflectionDemo(String name){}     private ReflectionDemo(Stri

  • java反射技术与类使用示例

    复制代码 代码如下: package com.java.db;import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;import java.util.ArrayList;import java.util.Arrays;import java.util.

随机推荐