实现简易的Struts.xml解析[内含反射的理解]

来源:互联网 发布:高校网络舆情工作方案 编辑:程序博客网 时间:2024/06/04 22:12

大道为简,我们直接上需求和代码。

首先我们有两个文件一个是View:

package com.basic.litestruts;
import java.util.Map;
public class View {
     private String jsp;
     private Map parameters;
     public String getJsp() {
      return jsp;
     }
     public View setJsp(String jsp) {
      this.jsp = jsp;
      return this;
     }
     public Map getParameters() {
      return parameters;
     }
     public View setParameters(Map parameters) {
      this.parameters = parameters;
      return this;

     }
}

里面有两个属性,最重要的是那个parameters是一个Map,我们后期需要传值进入。

第二个就是需要我们解析的Struts.xml:

<?xml version="1.0" encoding="UTF-8"?>
<struts>
    <action name="login" class="com.basic.litestruts.LoginAction">
        <result name="success">/jsp/homepage.jsp</result>
        <result name="fail">/jsp/showLogin.jsp</result>
    </action>
    <action name="logout" class="com.coderising.action.LogoutAction">
     <result name= "success">/jsp/welcome.jsp</result>
     <result name= "error">/jsp/error.jsp</result>
    </action>

</struts>


好了,现在我们上需求:

    0. 读取配置文件struts.xml
    1. 根据actionName找到相对应的class例如LoginAction,通过反射实例化(创建对象)
   据parameters中的数据,调用对象的setter方法, 例如parameters中的数据是
   ("name"="test","password"="1234"),      
   那就应该调用 setName和setPassword方法
   2. 通过反射调用对象的exectue 方法, 并获得返回值,例如"success"
   3. 通过反射找到对象的所有getter方法(例如 getMessage), 
   通过反射来调用, 把值和属性形成一个HashMap , 例如 {"message":  "登录成功"} , 
   放到View对象的parameters
   4. 根据struts.xml中的 <result> 配置,以及execute的返回值,  确定哪一个jsp, 
   放到View对象的jsp字段中。


于是我们创建了StrutsTest类来写代码:

public class Struts {
  
    public static View runAction(String actionName, Map<String,String> parameters){

           View v = new View();//定义一个view对象,用于对象的返回
          //1.创建一个DocumentBuilderFactory对象
          DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
          //2.创建一个DocumentBuilder对象
           DocumentBuilder db = null;
        
        
       try {
           db = dbf.newDocumentBuilder();
        } catch (ParserConfigurationException e) {
            e.printStackTrace();
        }
         //3.通过documentBuilder对象加载xml文件
         Document document = null;
   try {
    document = db.parse("src/com/basic/litestruts/struts.xml");//parse去解析struts文件
   } catch (SAXException e) {
    e.printStackTrace();
   } catch (IOException e) {
    e.printStackTrace();
   }
        
         NodeList nodeList = document.getElementsByTagName("action");//获取为action的节点集合
         
         
         for(int i = 0;i<nodeList.getLength();i++){
         
          Node item = nodeList.item(i);//获取每一个action节点
          NamedNodeMap attrs = item.getAttributes();//获取action节点下的所有属性
         
          Node currentAction = item;//存放当前action节点
          //根据需求在name为login时获得到他的class名
          if(attrs.getNamedItem("name").getNodeValue().equals(actionName)){
              String className = attrs.getNamedItem("class").getNodeValue();//获得login对应的类路径
              try {

                //开始反射
               Class<?> fn = Class.forName(className);
               Object obj = fn.newInstance();//反射出来new的对象
               Method methodSetName = fn.getMethod("setName", String.class);//获得当前对象的方法,参数传的是方法名和参数类型
               Method methodSetPwd = fn.getMethod("setPassword", String.class);
         for(Map.Entry<String, String> m :parameters.entrySet()){
          if(m.getKey().equals("name")){
         methodSetName.invoke(obj, m.getValue());//触发方法,参数是当前反射出来的对象,需要set方法的值
          }
          if(m.getKey().equals("password")){
           methodSetPwd.invoke(obj, m.getValue());
          }
         }
         /**
          * 调用excute方法返回success
          */
         Object returnValue = fn.getMethod("execute", null).invoke(obj, null);
         System.out.println(returnValue.toString());//打印返回值
         /**
          * 通过反射找到所有getter方法
          */
         //先获得所有属性
         Field[] fd = fn.getDeclaredFields();
        
         Map newMap = new HashMap();
         for(int m = 0;m<fd.length;m++){
          String fieldName = fd[m].getName();
          String methodName = "get"+init(fieldName);//将所有属性名装饰城get+属性名形式[init方法在下面]
          Method method = fn.getMethod(methodName, null);
          System.out.println(method);//遍历出的每一个getter方法
          String invoke = (String)method.invoke(obj, null);
          newMap.put(fieldName, invoke);
         }
         v.setParameters(newMap);
         System.out.println(v.getParameters());
         //fn.getMethod("get"+, parameterTypes)
         /**
          * 根据struts.xml中的 <result> 配置,
          * 以及execute的返回值,  确定哪一个jsp, 
          放到View对象的jsp字段中
          */
         NodeList childNodes = currentAction.getChildNodes();//将action节点下的所有子节点获取出来
         for(int q = 0;q<childNodes.getLength();q++){
          NamedNodeMap attributes = childNodes.item(q).getAttributes();//每个子节点所有属性
          if(attributes!=null){//两个result节点的属性
          for(int e = 0;e<attributes.getLength();e++){
           if(attributes.item(e).getNodeValue().equals(returnValue)){
            v.setJsp(childNodes.item(q).getTextContent());//在view中塞入success对应的文章内容
           }
          }
          }
         }
      } catch (ClassNotFoundException e) {
       e.printStackTrace();
      } catch (InstantiationException e) {
       e.printStackTrace();
      } catch (IllegalAccessException e) {
       e.printStackTrace();
      } catch (NoSuchMethodException e) {
       e.printStackTrace();
      } catch (SecurityException e) {
       e.printStackTrace();
      } catch (IllegalArgumentException e) {
       e.printStackTrace();
      } catch (InvocationTargetException e) {
       e.printStackTrace();
      }
             }
          }
        
     
      return v;
     }   
    /**
     * 将首字母大写,用于组成getter方法的方法名
     * @param name
     * @return
     */

    private static String init(String name){
     name = name.substring(0,1).toUpperCase()+name.substring(1);
     return name;
    }
}

好了,这里用的解析struts.xml主要是用的dom解析树。用到了documentBuilderFactory,创建一个DocumentBuilder,调用它的parse方法解析一个xml文件,返回一个document树形对象。后面就可以document.getElementsByTagName(填入你需要寻找的树形节点名称)。

基于用到了反射,我这里还是要给大家复习一下反射的原理:

我们正常开发状态中用反射的情况是少之又少,主要是那些框架中都给我们封装好了,我们直接照着在xml文件中写对应的格式就行了。为什么用到反射。【注:截取知乎上的一张图】


当我们用到一个对象时,比如person p = new Person();这时我们程序先跑起来,jvm飞起来,这段代码会被编译成.class文件

当我们这个Person类加载到方法区里面的时候,于是乎会创出person类的class对象到堆中。所以在jvm创建对象会先先检查类是否加载,再寻找类对应的class对象,如果加载完毕。开始分配内存进行初始化。代码走完之后jvm就关闭了。

但是我们经常在需求中会遇到,用不到的类不会加载到jvm中,比如说当我们person用完时突然又要用到monkey类,但是jvm已经在跑了,你不会和他说,兄弟,你先停一下让我new 一个猴子吧!所有反射的好处就是可以再运行期间根据包.类名来动态加载这个类然后为所欲为,想干嘛干嘛!


复习一下反射的基本调用方法【天地万物相反相成,反射既是通过对象找到类】:

首先获取类的三种方法:


//第一种方式:  

Classc1 = Class.forName("Person");  

//第二种方式:  


Classc2 = Person.class;  

   

//第三种方式:  


Person= new Person();  

Classc3 = e.getClass(); 


创建对象:

Classc1.newInstance();调用无参构造器


获取属性:

Field[] fs = Classc1.getDeclaredFields();

之后遍历它就可以获得各个属性名,然后调用他的getName和getType方法来获得类型和名称


这里有一点要注意:

使用.setAccessible(true); //使用反射机制可以打破封装性,意思是原来的是私有属性,我们可以获得到他的默认值并且可以修改,即使是final属性也可以!  

0 0