反射机制的应用

来源:互联网 发布:淘宝上刘老中医可靠吗 编辑:程序博客网 时间:2024/05/21 07:47

前两天朋友(小许)问我,为什么要用反射?反射都用哪些地方应用到,还有就是Struts框架用反射是不是多此一举,所以今天我腾出时间,就这几个问题做个系统的回答。
首先先说说反射,程序集包含模块,而模块包含类型,类型又包含成员。反射则提供了封装程序集、模块和类型的对象。您可以使用反射动态地创建类型的实例,将类型绑定到现有对象,或从现有对象中获取类型。然后,可以调用类型的方法或访问其字段和属性”–《来自百度的解释》
上面已经解释了反射,下面我就说说他在具体框架或开发中的应用。

大部分的框架都用到了反射机制,比如Struts1、2,Spring,Nspring,Hibernate等等,那就先说说我朋友比较关心的struts为什么用反射机制;

struts中反射的应用

这里写图片描述
根据下图可以看到,期主要原因就是应用反射将提交过来的请求经过(第三步)进行反射后找到对应的业务(方法)步骤如下:

1)StrutsPrepareAndExecuteFilter会去创建Action实例      ——假如我们请求abc.action,StrutsPrepareAndExecuteFilter会搜索struts.xml文件中的name为abc的Action配置,根据class属性使用反射来创建Action实例(2)调用 “Map <String, String> paramMap = request.getParameterMap();"//返回所有请求参数,这就是为什么开发Struts2的Action**无需与Servlet耦合**      使用循环for(String paramName: paramMap.keySet())     {        //得到请求参数名对应的setter方法        Method setter = actionClazz.getMethod("set"                    + paramName.substring(0,1).toUpperCase()                    + paramName.substring(1), string.class);        //得到请求参数对应的值        String paramValue = paramMap.get(paramName);        //以Action实例为调用者,调用setter方法,把请求参数值作为参数值传入,,这就是为什么开发Struts2的Action无需与Servlet耦合        setter.invoke(actionInst, paramValue);    }(3)再次通过反射来调用method属性所指定的方法,返回一个字符串

优点: 这样就可以降低耦合度、开发模块化、提高复用性,全局结果与声明式异常 等优点。
说白了就是你前端该干你们的活干你们的活,我后台该写什么写什么,两不耽误,回头只需要在配载文件上一配,通过反射前后一结合,程序就成了。谁也不依赖谁(低耦合)后面给你好好说说。

反射在IOC中的应用
SPRING(JAVA)、NSPRING(.NET)、AUTOFAC(.NET)的核心就是IOC,下面就重点说下IOC(控制反转)
IOC:
为了降低程序的耦合度我们会相应的引用IOC框架,IOC的核心实现机制就是反射。
估计初学者(小许)够呛能理解,下面具体的解释一下:
1、控制反转:
对于你来说,平时写代码,“一段代码会完全控制了程序的流程”,因为我们习惯于用new关键字来创建对象,再通过对象来调用它的方法。对于我们来说,这是再正常不过的了,可是如果此时我们的需求发生了一点改变,我们就不得不修改源代码,这就是我们常讲的“硬编码(就是编码一点也不活,面对好的产品设计以及好的产品经理来说可能修改少点,但是有2b的就苦逼了)”,而修改源代码是程序设计的大忌,这违反了程序设计的“开闭原则”,即对扩展是开放的,对修改是关闭的。
举个例子,当我们写完代码时,我们的程序只能执行强哥吃东西,可是如果此时需求变了我们要李屁股吃东西不是强哥了,是不是我们的代码要变了呢,回头2b产品经理又抽风了,要林哥吃……。此时是不是很抓狂,所以我们会引入了配置文件机制,我们事先在配置文件中已经配置好了该是谁吃东西,等到程序运行时,程序会读取配置文件中的值通过反射机制创建一个我们需要的对象,再调用它的eat方法就ok了。这样看来,我们程序的执行流程控制权是不是转交给了配置文件了(只要改配置文件就行,爱谁吃谁吃),而程序代码自己却成了被控制的对象,这就是实现了“控制反转”了。(不知道明白没。。。不明白再电联)
2、依赖注入:(降低程序耦合度的核心方法)
在讲依赖注入之前,我想先讲一讲依赖注入的三种实现方式。

1)构造注入, 是通过构造器方法注入实例变量public class 强哥 {public void 吃(){        //••••    }}public class People {    private 强哥 小强;    public People(强哥 小强) {//构造注入        this.小强=小强;    }    public void eat(){        小强.吃();    }} (2)设值注入,是通过setter方法注入实例变量public class People {    private 强哥 小强;    public void set强哥(强哥 小强) {//设置注入        this.小强= 小强;    }    public void 吃(){        小强.吃();    }} (3)接口注入,是通过接口方法注入实例变量(最常用的)public interface I强哥 {    public void 吃();}public class 强哥:I强哥 {public void 吃(){        。。。    }}public class People {public void 吃(I强哥  I小强){//接口注入        I小强.吃();     }}

那么问题来了:依赖注入有毛用呢?
这样既没降低耦合,也没降低程序的复杂度(到底有毛用),可是当我们考虑到配置文件时,我们就豁然开朗了,不管是哪种注入方法,目的都是给我们的程序传入一个实例变量。而这个实例变量我们可以事先在配置文件中配置,当程序运行时就可以读取配置文件,通过反射机制来创建所需的对象了,如果此时我们的需求发生了变化,我们也只需改一下配置文件就o了。这里有些人可能会认为这也修改了文件呀,为什么修改配置文件就可以了呢。呵呵,其实配置文件是不需要编译的,我们可以随时修改配置文件,而不需要通过更改一行源代码的方法就实现了程序流程的控制。这就是ICO一个很伟大的功能。
3、IOC容器
使用IOC后,我们不需要自己去创建某个类的实例,而由IOC容器去创建,当我们需要使用某个对象时,直接到容器中去获取就可以了。现在已经有许多非常优秀的IOC容器,比如spring等。
反射在其他地方的应用
就拿最近项目的几个例子给你分享下吧。

例子1:

问题:当你的表单项很多时,而且每个控件的内容都需要保存你会怎么办?哈哈,如果一个一个写会累死的而且程序的可维护性会降低,所以我选择用反射,将控件的名称在命名的时候与相对应的类名相同,回头一个循环再加几个判断就搞定了,给你找个代码:

以前我们是这样的…有种累傻小子的赶脚没?

            News news = new News();            news.Id = int.Parse(Request.Form["Id"]);            news.Category = int.Parse(Request.Form["Category"]);            news.Title = Request.Form["Title"];            ...N多行            news.CreateTime = DateTime.Parse(Request.Form["CreateTime"]);

用反射之后是这样的

Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->public static class HenqPost<T> where T: new()//new()为泛型约束,约束类型T必须具有无参的构造函数    {       /// <summary>        /// 为Model赋值        /// </summary>        /// <typeparam name="T">Model</typeparam>        /// <param name="t">model</param>        /// <param name="form">Request</param>        /// <returns></returns>        public static int GetPost<T>(ref T t, NameValueCollection form)        {            int va=0;            Type type = t.GetType();//获取类型            PropertyInfo[] pi=type.GetProperties();//获取属性集合            foreach( PropertyInfo p in pi )            {                if (form[p.Name] != null)                {                    try                    {                        p.SetValue(t, Convert.ChangeType(form[p.Name], p.PropertyType), null);//为属性赋值,并转换键值的类型为该属性的类型                        va++;//记录赋值成功的属性数                    }                    catch                    {                                            }                }            }            return va;        }    }

简单的几行,解决了所有表单向对象的赋值问题。同理对象向表单赋值也可以模仿着写下。

例子2:

与例子1类似,这次我们是从数据库里读数据之后封装成Model
以前是这样的

  bll_tydModel model = new bll_tydModel();            DataSet ds = InitHelperDAL.dbHelper.GetDataSet(strSql.ToString(), parameters);            if (ds.Tables[0].Rows.Count > 0)            {                if (ds.Tables[0].Rows[0]["no"].ToString() != "")                {                    model.no = int.Parse(ds.Tables[0].Rows[0]["no"].ToString());                }                model.ydcfgs = ds.Tables[0].Rows[0]["ydcfgs"].ToString();                model.ydbc = ds.Tables[0].Rows[0]["ydbc"].ToString();                model.kczt = ds.Tables[0].Rows[0]["kczt"].ToString();               ...//以下N行代码

虽然有动软、codesmith等代码生成工具但是维护起来还是巨费劲的。
下面看看用反射是什么样的

 public List<T> FillModel(DataTable dt)        {            if (dt == null || dt.Rows.Count == 0)            {                return null;            }            List<T> modelList = new List<T>();            foreach (DataRow dr in dt.Rows)            {                T model = new T();                foreach (PropertyInfo propertyInfo in propertys)                {                    SetValue(propertyInfo, dr, model);                }                modelList.Add(model);            }            return modelList;        }        private void SetValue(PropertyInfo p, DataRow dr, T model)        {            if (!dr.Table.Columns.Contains(p.Name))            {                return;            }            if (dr[p.Name] == DBNull.Value)            {                return;            }            string value = string.Empty;            DateTime dt;            int i;            double d;            float f;            long l;            switch (p.PropertyType.Name)            {                case "String":                    value = dr[p.Name].ToString();                    p.SetValue(model, value, null);                    break;                case "DateTime":                    value = dr[p.Name].ToString();                    if (value.Trim() == "" || !DateTime.TryParse(value, out dt))                    {                        break;                    }                    p.SetValue(model, dt, null);                    break;                case "Int32":                    value = dr[p.Name].ToString();                    if (value.Trim() == "" || !int.TryParse(value, out i))                    {                        break;                    }                    p.SetValue(model, i, null);                    break;                case "Int64":                    value = dr[p.Name].ToString();                    if (value.Trim() == "" || !long.TryParse(value, out l))                    {                        break;                    }                    p.SetValue(model, l, null);                    break;                case "Double":                    value = dr[p.Name].ToString();                    if (value.Trim() == "" || !double.TryParse(value, out d))                    {                        break;                    }                    p.SetValue(model, d, null);                    break;                case "Single":                    value = dr[p.Name].ToString();                    if (value.Trim() == "" || !float.TryParse(value, out f))                    {                        break;                    }                    p.SetValue(model, f, null);                    break;                case "Byte[]":                    p.SetValue(model, dr[p.Name], null);                    break;                default:                    break;            }        }

代码是这样的 。。。是泛型方法适用于各种类型,()hibernate核心的东西就是这个。(看不懂电联)

例子3

动态创建实例
问题:估计你们用到的类都是已知的,那么问题来了,当你用到了类在设计的时候是不确定的(如动态表的构造-不懂电联哈)属性不定、属性值类型不定。。。怎么办?
遇到这种情况就是反射机制发挥作用的时候了。
java动态创建类,写得很详细,有空可以看看~!
http://www.tuicool.com/articles/7BVnyau
.net 动态创建类写得不如上面的。。。
http://www.cnblogs.com/lichdr/archive/2004/11/02/59620.html

在工作中目前就遇到这些,希望对你有点帮助~!不懂的再联系

原创粉丝点击