基于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"}); } 输出结果同上。 修改字段的值 我们可以对Field的值进行修改,代码如下: 复制代码 代码如下: private static void setFieldTest() throws ClassNotFoundException,NoSuchFieldException,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基本操作 复制代码 代码如下: @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信息 <!-- 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服务器,我们不需要把上述代码部署到其他服务器中。 动态代理机制 下面我们来实现一个简单的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执行后进行了拦截,达到了我们期望的效果。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |