您的位置:首页 > 编程语言 > Java开发

Java反射的应用

2014-07-13 09:54 309 查看

补充    

    针对上篇文章《JAVA反射原理》的图,再稍作补充:

    


    这个图是上篇博客中图的一部分,实际上,这部分才是反射的核心,即:对象、class的相互关系:

对象实例化后,在头部保留其class引用,即对象→class
根据加载的class,实例化对象,即class→对象

    如上篇文章所说,我们关注反射,是因为反射可以达到以下作用:

根据一个字符串形式的类名加载类
由加载的类直接实例化对象
已知一个对象,获取其类信息(接口、函数……)

    这些作用听起来没什么大不了,但是合理使用,威力很大:Struts、Hibernate、Spring、动态代理……没有反射,这些都白搭。

Struts

    此处以Struts1为例,来看一下struts-config.xml:

<struts-config>
<form-beans>
<form-bean name="loginForm" type="com.tgb.struts.LoginActionForm"/>
</form-beans>

<action-mappings>
<action path="/login"
type="com.tgb.struts.LoginAction"
name="loginForm"
scope="request"
>
<forward name="success" path="/login_success.jsp" />
<forward name="error" path="/login_error.jsp"/>
</action>
</action-mappings>
</struts-config>
    根据Struts1的运行机制,我们知道在ActionServlet截获请求后,会将页面数据存放到一个ActionForm中,然后把此ActionForm交由Action处理,使用如下:

LoginActionForm laf = (LoginActionForm)form;
    你可以发现ActionForm传给Action时,已经是一个对象了,这个对象从哪里来的?我们知道对象来源有两种:
new(工厂、clone)
Class.newInstance()
    很明显我们使用Struts并没有手动new过任何一个ActionForm或者Action;Struts倒是想替你new,但是我们知道Class1 obj=new Class1()需要手动编程(工厂、clone和这属于一类),所以new这条路就被封死了。

    Struts也仅仅知道你配置了一个loginForm,类是com.tgb.struts.LoginActionForm……,没错,Struts就是使用反射加载这些类,从而实例化ActionForm和Action的,大致过程如下:

ActionServlet截取请求
根据请求对应的Struts配置,使用Class.forName("com.tgb.struts.LoginActionForm")获取loginForm对象;
调用ActionForm的各种setter赋值表单值
根据Struts配置,使用Class.forName("com.tgb.struts.LoginAction")获取loginAction对象
将loginForm传给loginAction
强制转化后,在loginAction中使用loginForm

Hibernate

    Hibernate使用了ORM(Object/Relation Mapping)机制,R(Relation)来自数据库,O(Object)呢?来看一下hibernate映射文件:

<hibernate-mapping >
<class name="com.tgb.hibernate.Person"  table="t_person">
<id name="id">
<generator class="foreign" >
<param name="property">idCard</param>
</generator>
</id>
<property name="name" />
<one-to-one name="idCard"  constrained="true" />
</class>
</hibernate-mapping>
    看到<class name="com.tgb.hibernate.Person" table="t_person">,和上面说的Struts一样,也是使用到反射,以Hibernate的根据主键获取对象为例:session.get(Person.class, "0001"):

生成sql语句,操作数据库获得数据

Class.forName("com.tgb.hibernate.Person")获取Person实例

调用Person的setter对属性赋值

返回此Person对象

Spring

    Spring是一个大工厂,用于组织对象之间的关系,配置如下:

<bean id="userManager" class="com.tgb.usermgr.manager.UserManagerImpl">
<property name="logManager" ref="logManager"/>
</bean>
<bean id="logManager" class="com.tgb.usermgr.manager.LogManager" />
    简单说IoC的过程如下:

Class.forName("com.tgb.usermgr.manager.UserManagerImpl")实例化userManagerImpl

Class.forName("com.tgb.usermgr.manager.LogManager")实例化logManager

调用userManagerImpl的setLogManager,注入logManager

当使用userManagerImpl,已经是处理完依赖关系的userManagerImpl

    可以说,只要在配置文件有对类名的标记,基本上就是为了利用到反射的特性。

动态代理

    上面说的都是关于class→对象的demo,而反射的作用不止这个,再来看看动态代理中的反射。由于动态反射中的内容较多,会单独再说,我们只看其中一部分,与invoke相关的内容。
    Person
package reflection;

public class Person {

public void eat()
{
System.out.println("im eating");
}
}

    TestMethod
package reflection;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class TestMethod {

public static void main(String [] args) throws InstantiationException, IllegalAccessException, ClassNotFoundException, SecurityException, NoSuchMethodException, IllegalArgumentException, InvocationTargetException
{
//获取Person类
Class clazz=Class.forName("reflection.Person");
//实例化对象
Person p=(Person)clazz.newInstance();
//得到person的eat方法
Method m=clazz.getDeclaredMethod("eat",new Class[]{});
//执行person的eat方法
m.invoke(p, new Object[]{});
}
}
    如上,知道一个类的某个方法名,再有这个类的实例,我们就可以执行这个函数。有对象,有函数名,我直接obj.method()不就行了?这就跟用不用MVC道理一样,不用照样可以完成功能,但是却少了灵活性。   

    基于这种方法名的调用,我们可以延迟调用方法,这也给了我们很多的操作空间,比如我先执行一段代码,再根据函数名调用函数,再执行后续的代码……这不就是动态代理的应用么。

总结

    关于反射细节,如在JVM中的执行流程,较为复杂,待以后整理清楚再说。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: