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

Java教程 2025-08-21

反射可以帮助我们查看指定类型中的信息、创建类型的实例,调用类型的方法。我们平时使用框架,例如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://**tes*t", 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://localho**s*t:8888/test/HelloService", new HelloServiceProvider());

}

}

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

复制代码代码如下:

wsgen -cp . HelloServiceProvider

 wsgen位于JDK的bin目录中。

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

 点击WSDL链接,可以看到:

复制代码代码如下:

WSDL信息

< !-- Published by JAX-WS RI at http://jax-ws.dev.jav*a.n**et. RI's version is JAX-WS RI 2.2.4-b01. -- >< !-- Generated by JAX-WS RI at http://jax-ws.dev.jav*a.n**et. RI's version is JAX-WS RI 2.2.4-b01. -- >< definitions targetNamespace="http://**tes*t" name="HelloService" >< types >< xsd:schema >< xsd:import namespace="http://**tes*t" schemaLocation="http://localho**s*t: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://**tes*t/HelloServiceProvider/sayHelloRequest" message="tns:sayHello"/ >< output wsam:Action="http://**tes*t/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.x*m*ls*oap.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://localho**s*t: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执行后进行了拦截,达到了我们期望的效果。