反射机制的应用

前两天朋友(小许)问我,为什么要用反射?反射都用哪些地方应用到,还有就是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

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值