聽風講MVC丶 —— 一言不合就擼碼 (未完待續······)

来源:http://www.cnblogs.com/smbk/archive/2016/09/01/5828151.html
-Advertisement-
Play Games

希望你看了此小隨 可以實現自己的MVC框架 也祝所有的程式員身體健康一切安好 ——久伴深海丶默 1.什麼是前端控制器(font controller)。Java Web中的前端控制器是應用的門面,簡單的說所有的請求都會經過這個前端控制器,由前端控制器根據請求的內容來決定如何處理並將處理的結果返回給瀏 ...


                    希望你看了此小隨 可以實現自己的MVC框架   

                                                                 也祝所有的程式員身體健康一切安好          

                                                                                                                                     ——久伴深海丶默

        1.什麼是前端控制器(font controller)。Java Web中的前端控制器是應用的門面,簡單的說所有的請求都會經過這個前端控制器,由前端控制器根據請求的內容來決定如何處理並將處理的結果返回給瀏覽器。這就好比很多公司都有一個前臺,那裡通常站著幾位面貌姣好的美女,你要到這家公司處理任何的業務或者約見任何人都可以跟她們說,她們會根據你要做什麼知會相應的部門或個人來處理,這樣做的好處是顯而易見的,公司內部系統運作可能很複雜,但是這些對於外部的客戶來說應該是透明的,通過前臺,客戶可以獲得他們希望該公司為其提供的服務而不需要瞭解公司的內部實現。這裡說的前臺就是公司內部系統的一個門面,它簡化了客戶的操作。前端控制器的理念就是GoF設計模式門面模式(外觀模式)在Web項目中的實際應用。SUN公司為Java Web開發定義了兩種模型,Model 1和Model 2。Model 2是基於MVC(Model-View-Controller,模型-視圖-控制)架構模式的,通常將小服務(Servlet)或過濾器(Filter)作為控制器,其作用是接受用戶請求並獲得模型數據然後跳轉到視圖;將JSP頁面作為視圖,用來顯示用戶操作的結果;模型當然是POJO(Plain Old Java Object),它是區別於EJB(Enterprise JavaBean)的普通Java對象,不實現任何其他框架的介面也不扮演其他的角色,而是負責承載數據,可以作為VO(Value Object)或DTO(Data Transfer Object)來使用。當然,如果你對這些概念不熟悉,可以用百度或者維基百科查閱一下,想要深入的瞭解這些內容推薦閱讀大師Martin Fowler的《企業應用架構模式》(英文名:Patterns of Enterprise Application Architecture)。

 

package cn.sm.servlet;  
  
import java.io.IOException;  
  
import javax.servlet.ServletException;  
import javax.servlet.annotation.WebServlet;  
import javax.servlet.http.HttpServlet;  
import javax.servlet.http.HttpServletRequest;  
import javax.servlet.http.HttpServletResponse;  
  
@WebServlet("*.do")  
public class FrontController extends HttpServlet {  
    private static final long serialVersionUID = 1L;  
      
    private static final String DEFAULT_PACKAGE_NAME = "cn.sm.action.";// 這裡預設的Action類的包名首碼  
    private static final String DEFAULT_ACTION_NAME = "Action";// 這裡預設的Action類的類名尾碼  
  
    @Override  
    protected void service(HttpServletRequest req, HttpServletResponse resp)  
            throws ServletException, IOException {  
        // 這裡獲得請求的小服務路徑  
        String servletPath = req.getServletPath();  
        // 這裡從servletPath中去掉開頭的斜杠和末尾的.do就是要執行的動作(Action)的名字  
        int start = 1;  // 這裡去掉第一個字元斜杠從第二個字元開始  
        int end = servletPath.lastIndexOf(".do");   // 這裡找到請求路徑的尾碼.do的位置  
        String actionName = end > start ? servletPath.substring(start, end) + DEFAULT_ACTION_NAME : "";  
        String actionClassName = DEFAULT_PACKAGE_NAME + actionName.substring(0, 1).toUpperCase() + actionName.substring(1);  
        // 這裡接下來可以通過反射來創建Action對象並調用  
        System.out.println(actionClassName);  
    }  
}  

 

           FrontController類中用@WebServlet註解對該小服務做了映射,只要是尾碼為.do的請求,都會經過這個小服務,所以它是一個典型的前端控制器(當然,你也可以在web.xml中使用<servlet>和<servlet-mapping>標簽對小服務進行映射,使用註解通常是為了提升開發效率,但需要註意的是註解也是一種耦合,配置文件在解耦合上肯定是更好的選擇,如果要使用註解,最好是像Spring 3那樣可以基於程式配置應用,此外,使用註解配置Servlet需要你的伺服器支持Servlet 3規範)。假設使用Tomcat作為伺服器(使用預設設置),項目的部署名稱為sm,接下來可以瀏覽器地址欄輸入http://localhost:8080/sm/login.do,Tomcat的控制台會輸出cn.sm.action.LoginAction。

寫一個通用的前端控制器  用多態  先定義一個Action介面並定義一個抽象方法,不同的Action子類會對該方法進行重寫,用Action的引用引用不同的Action子類對象,調用子類重寫過的方法,執行不同的行為。

定義Action類的介面

 

package cn.sm.action;  
  
import java.io.IOException;  
  
import javax.servlet.ServletException;  
import javax.servlet.http.HttpServletRequest;  
import javax.servlet.http.HttpServletResponse;  
  
/** 
 *  處理用戶請求的控制器介面 
 * @author 微冷的風 
 * 
 */  
public interface Action {  
  
    public ActionResult execute(HttpServletRequest req, HttpServletResponse resp)   
            throws ServletException, IOException;  
}  

 

         介面中的execute方法是處理用戶請求的方法,它的兩個參數分別是HttpServletRequest和HttpServletResponse對象,在前端控制中通過反射創建Action,並調用execute方法,不同的Action子類通過重寫對execute方法給出了不同的實現版本,該方法是一個多態方法。execute方法的返回值是一個ActionResult對象,實現代碼如下。

package cn.sm.action;  
  
/** 
 * Action執行結果 
 * @author 微冷的風 
 * 
 */  
public class ActionResult {  
    private ResultContent resultContent;  
    private ResultType resultType;  
  
    public ActionResult(ResultContent resultContent) {  
        this(resultContent, ResultType.Forward);  
    }  
  
    public ActionResult(ResultContent resultContent, ResultType type) {  
        this.resultContent = resultContent;  
        this.resultType = type;  
    }  
  
    /** 
     * 獲得執行結果的內容 
     */  
    public ResultContent getResultContent() {  
        return resultContent;  
    }  
      
    /** 
     * 獲得執行結果的類型 
     */  
    public ResultType getResultType() {  
        return resultType;  
    }  
  
}  

         ActionResult類中的ResultContent代表了Action對用戶請求進行處理後得到的內容,可以存儲一個字元串表示要跳轉或重定向到的資源的URL,也可以存儲一個對象來保存對用戶請求進行處理後得到的數據(模型),為了支持Ajax操作,將此對象處理成JSON格式的字元串。

 

package cn.sm.action;  
  
import cn.google.gson.Gson;  
  
/** 
 * Action執行結束產生的內容 
 * @author 微冷的風 
 * 
 */  
public class ResultContent {  
    private String url;  
    private Object obj;  
      
    public ResultContent(String url) {  
        this.url = url;  
    }  
      
    public ResultContent(Object obj) {  
        this.obj = obj;  
    }  
      
    public String getUrl() {  
        return url;  
    }  
      
    public String getJson() {  
        return new Gson().toJson(obj);// 這裡使用了Google的JSON工具類gson  
    }  
}  

 

 ActionResult類中的ResultType代表了對用戶請求處理後如何向瀏覽器產生響應,是一個枚舉類型,代碼如下所示。

 

package cn.sm.action;  
  
/** 
 * Action執行結果類型 
 * @author 微冷的風 
 * 
 */  
public enum ResultType {  
    // 重定向 
    Redirect,   
    //轉發 
    Forward,   
    //非同步請求  
    Ajax,  
    // 數據流 
    Stream,  
    // 跳轉到向下一個控制器
    Chain,  
    //重定向到下一個控制器 
    RedirectChain  
}  

 

再寫一個工具類來封裝常用的工具方法

 

package cn.sm.util;  
  
import java.awt.Color;  
import java.text.DateFormat;  
import java.text.ParseException;  
import java.text.SimpleDateFormat;  
import java.util.ArrayList;  
import java.util.Date;  
import java.util.List;  
  
/** 
 * 通用工具類 
 * @author 微冷的風
 * 
 */  
public final class CommonUtil {  
    private static final List<String> patterns = new ArrayList<>();  
    private static final List<TypeConverter> converters = new ArrayList<>();  
      
    static {  
        patterns.add("yyyy-MM-dd");  
        patterns.add("yyyy-MM-dd HH:mm:ss");  
    }  
  
    private CommonUtil() {  
        throw new AssertionError();  
    }  
  
    /** 
     * 將字元串的首字母大寫 
     */  
    public static String capitalize(String str) {  
        StringBuilder sb = new StringBuilder();  
        if (str != null && str.length() > 0) {  
            sb.append(str.substring(0, 1).toUpperCase());  
            if (str.length() > 1) {  
                sb.append(str.substring(1));  
            }  
            return sb.toString();  
        }  
        return str;  
    }  
      
    /** 
     * 生成隨機顏色 
     */  
    public static Color getRandomColor() {  
        int r = (int) (Math.random() * 256);  
        int g = (int) (Math.random() * 256);  
        int b = (int) (Math.random() * 256);  
        return new Color(r, g, b);  
    }  
      
    /** 
     * 添加時間日期樣式 
     * @param pattern 時間日期樣式 
     */  
    public static void registerDateTimePattern(String pattern) {  
        patterns.add(pattern);  
    }  
  
    /** 
    * 取消時間日期樣式 
    * @param pattern 時間日期樣式 
    */  
    public static void unRegisterDateTimePattern(String pattern) {  
        patterns.remove(pattern);  
    }  
      
    /** 
     * 添加類型轉換器 
     * @param converter 類型轉換器對象 
     */  
    public static void registerTypeConverter(TypeConverter converter) {  
        converters.add(converter);  
    }  
  
    /** 
     * 取消類型轉換器 
     * @param converter 類型轉換器對象 
     */  
    public static void unRegisterTypeConverter(TypeConverter converter) {  
        converters.remove(converter);  
    }  
      
    /** 
     * 將字元串轉換成時間日期類型 
     * @param str 時間日期字元串  
     */  
    public static Date convertStringToDateTime(String str) {  
        if (str != null) {  
            for (String pattern : patterns) {  
                Date date = tryConvertStringToDate(str, pattern);  
      
                if (date != null) {  
                    return date;  
                }  
            }  
        }  
  
        return null;  
    }  
      
    /** 
     * 按照指定樣式將時間日期轉換成字元串 
     * @param date 時間日期對象 
     * @param pattern 樣式字元串 
     * @return 時間日期的字元串形式 
     */  
    public static String convertDateTimeToString(Date date, String pattern) {  
         return new SimpleDateFormat(pattern).format(date);  
    }  
      
    private static Date tryConvertStringToDate(String str, String pattern) {  
         DateFormat dateFormat = new SimpleDateFormat(pattern);   
         dateFormat.setLenient(false);  // 不允許將不符合樣式的字元串轉換成時間日期  
  
         try {  
             return dateFormat.parse(str);  
         }   
         catch (ParseException ex) {  
         }  
           
         return null;  
    }  
      
    /** 
     * 將字元串值按指定的類型轉換成轉換成對象 
     * @param elemType 類型 
     * @param value 字元串值 
     */  
    public static Object changeStringToObject(Class<?> elemType, String value) {  
        Object tempObj = null;  
          
        if(elemType == byte.class || elemType == Byte.class) {  
            tempObj = Byte.parseByte(value);  
        }  
        else if(elemType == short.class || elemType == Short.class) {  
            tempObj = Short.parseShort(value);  
        }  
        else if(elemType == int.class || elemType == Integer.class) {  
            tempObj = Integer.parseInt(value);  
        }  
        else if(elemType == long.class || elemType == Long.class) {  
            tempObj = Long.parseLong(value);  
        }  
        else if(elemType == double.class || elemType == Double.class) {  
            tempObj = Double.parseDouble(value);  
        }  
        else if(elemType == float.class || elemType == Float.class) {  
            tempObj = Float.parseFloat(value);  
        }  
        else if(elemType == boolean.class || elemType == Boolean.class) {  
            tempObj = Boolean.parseBoolean(value);  
        }  
        else if(elemType == java.util.Date.class) {  
            tempObj = convertStringToDateTime(value);  
        }  
        else if(elemType == java.lang.String.class) {  
            tempObj = value;  
        }  
        else {  
            for(TypeConverter converter : converters) {  
                try {  
                    tempObj = converter.convert(elemType, value);  
                    if(tempObj != null) {  
                        return tempObj;  
                    }  
                }   
                catch (Exception e) {  
                }  
            }  
        }  
          
        return tempObj;  
    }  
  
    /** 
     * 獲取文件尾碼名 
     * @param filename 文件名 
     * @return 文件的尾碼名以.開頭 
     */  
    public static String getFileSuffix(String filename) {  
        int index = filename.lastIndexOf(".");  
        return index > 0 ? filename.substring(index) : "";  
    }
}

 

寫了Action介面和相關類後,再改寫寫前端控制器的代碼,如下

package cn.sm.servlet;  
  
import java.io.IOException;  
import java.io.PrintWriter;  
  
import javax.servlet.ServletException;  
import javax.servlet.annotation.WebServlet;  
import javax.servlet.http.HttpServlet;  
import javax.servlet.http.HttpServletRequest;  
import javax.servlet.http.HttpServletResponse;  
  
import cn.sm.action.Action;  
import cn.sm.action.ActionResult;  
import cn.sm.action.ResultContent;  
import cn.sm.action.ResultType;  
  
@WebServlet("*.do")  
public class FrontController extends HttpServlet {  
    private static final long serialVersionUID = 1L;  
      
    private static final String DEFAULT_PACKAGE_NAME = "cn.sm.action.";      // 這裡預設的Action類的包名首碼  
    private static final String DEFAULT_ACTION_NAME = "Action";       // 預設的Action類的類名尾碼  
    private static final String DEFAULT_JSP_PATH = "/WEB-INF/jsp";          // 預設的JSP文件的路徑  
  
    @Override  
    protected void service(HttpServletRequest req, HttpServletResponse resp)  
            throws ServletException, IOException {  
        String contextPath = req.getContextPath() + "/";  
        // 獲得請求的小服務路徑  
        String servletPath = req.getServletPath();  
        // 從servletPath中去掉開頭的斜杠和末尾的.do就是要執行的動作(Action)的名字  
        int start = 1;  // 去掉第一個字元斜杠從第二個字元開始  
        int end = servletPath.lastIndexOf(".do");   // 找到請求路徑的尾碼.do的位置  
        String actionName = end > start ? servletPath.substring(start, end) + DEFAULT_ACTION_NAME : "";  
        String actionClassName = DEFAULT_PACKAGE_NAME + actionName.substring(0, 1).toUpperCase() + actionName.substring(1);  
        try {  
            // 通過反射來創建Action對象並調用  
            Action action = (Action) Class.forName(actionClassName).newInstance();  
            // 執行多態方法execute得到ActionResult  
            ActionResult result = action.execute(req, resp);  
            ResultType resultType = result.getResultType();// 結果類型  
            ResultContent resultContent = result.getResultContent();// 結果內容  
            // 根據ResultType決定如何處理  
            switch (resultType) {  
            case Forward: // 跳轉  
                req.getRequestDispatcher(  
                        DEFAULT_JSP_PATH + resultContent.getUrl()).forward(req,  
                        resp);  
                break;  
            case Redirect: // 重定向  
                resp.sendRedirect(resultContent.getUrl());  
                break;  
            case Ajax: // Ajax  
                PrintWriter pw = resp.getWriter();  
                pw.println(resultContent.getJson());  
                pw.close();  
                break;  
            case Chain:  
                req.getRequestDispatcher(contextPath + resultContent.getUrl())  
                        .forward(req, resp);  
                break;  
            case RedirectChain:  
                resp.sendRedirect(contextPath + resultContent.getUrl());  
                break;  
            default:  
            }  
        } catch (Exception e) {  
            e.printStackTrace();  
            throw new ServletException(e);  
        }  
    }  
}  

         在前端控制器中設置的幾個常量(預設的Action類的包名首碼、預設的Action類的類名尾碼以及預設的JSP文件的路徑)算硬代碼,可以將其看作一種約定,約定好Action類的名字和路徑,JSP頁面的名字和路徑可以省去很多的配置,甚至可以做到零配置,叫做約定優於配置(CoC,Convenient over Configuration)。符合約定的部分可以省去配置,不合符約定的部分用配置文件或者註解加以說明。繼續修改前端控制器,如下。

 

package cn.sm.servlet;  
  
import java.io.IOException;  
import java.io.PrintWriter;  
  
import javax.servlet.ServletConfig;  
import javax.servlet.ServletException;  
import javax.servlet.annotation.MultipartConfig;  
import javax.servlet.annotation.WebInitParam;  
import javax.servlet.annotation.WebServlet;  
import javax.servlet.http.HttpServlet;  
import javax.servlet.http.HttpServletRequest;  
import javax.servlet.http.HttpServletResponse;  
  
import cn.sm.action.Action;  
import cn.sm.action.ActionResult;  
import cn.sm.action.ResultContent;  
import cn.sm.util.CommonUtil;  
  
/** 
 * 前端控制器(門面模式[提供用戶請求的門面]) 
 * @author 微冷的風 
 * 
 */  
@WebServlet(urlPatterns = { "*.do" }, loadOnStartup = 0,   
        initParams = {   
            @WebInitParam(name = "packagePrefix", value = "cn.sm.action."),  
            @WebInitParam(name = "jspPrefix", value = "/WEB-INF/jsp/"),  
            @WebInitParam(name = "actionSuffix", value = "Action")  
        }  
)  
@MultipartConfig  
public class FrontController extends HttpServlet {  
    private static final long serialVersionUID = 1L;  
      
    private static final String DEFAULT_PACKAGE_NAME = "cn.sm.action.";  
    private static final String DEFAULT_JSP_PATH = "/WEB-INF/content/";  
    private static final String DEFAULT_ACTION_NAME = "Action";  
      
    private String packagePrefix = null;        // 包名的首碼  
    private String jspPrefix = null;            // JSP頁面路徑的首碼  
    private String actionSuffix = null;         // Action類名的尾碼  
      
    @Override  
    public void init(ServletConfig config) throws ServletException {  
        String initParam = config.getInitParameter("packagePrefix");  
        packagePrefix = initParam != null ? initParam :  DEFAULT_PACKAGE_NAME;  
        initParam = config.getInitParameter("jspPrefix");  
        jspPrefix = initParam != null ? initParam : DEFAULT_JSP_PATH;  
        initParam = config.getInitParameter("actionSuffix");  
        actionSuffix = initParam != null ? initParam : DEFAULT_ACTION_NAME;  
    }  
  
    @Override  
    protected void service(HttpServletRequest req, HttpServletResponse resp)  
            throws ServletException, IOException {  
        String contextPath = req.getContextPath() + "/";  
        String servletPath = req.getServletPath();  
          
        try {  
            Action action = (Action) Class.forName(getFullActionName(servletPath)).newInstance();  
            ActionResult actionResult = action.execute(req, resp);  
            ResultContent resultContent = actionResult.getResultContent();  
            switch(actionResult.getResultType()) {  
            case Redirect:  
                resp.sendRedirect(contextPath + resultContent.getUrl());  
                break;  
            case Forward:  
                req.getRequestDispatcher(getFullJspPath(servletPath) + resultContent.getUrl())  
                        .forward(req, resp);  
                break;  
            case Ajax:  
                PrintWriter pw = resp.getWriter();  
                pw.println(resultContent.getJson());  
                pw.close();  
                break;  
            case Chain:  
                req.getRequestDispatcher(contextPath + resultContent.getUrl())  
                        .forward(req, resp);  
                break;  
            case RedirectChain:  
                resp.sendRedirect(contextPath + resultContent.getUrl());  
                break;  
            default:  
            }  
        }   
        catch (Exception e) {  
            e.printStackTrace();  
            resp.sendRedirect("error.html");  
        }  
    }  
      
    // 根據請求的小服務路徑獲得對應的Action類的名字  
    private String getFullActionName(String servletPath) {  
        int start = servletPath.lastIndexOf("/") + 1;  
        int end = servletPath.lastIndexOf(".do");  
        return packagePrefix + getSubPackage(servletPath) + CommonUtil.capitalize(servletPath.substring(start, end)) + actionSuffix;  
    }  
      
    // 根據請求的小服務路徑獲得對應的完整的JSP頁面路徑  
    private String getFullJspPath(String servletPath) {  
        return jspPrefix + getSubJspPath(servletPath);  
    }  
      
    // 根據請求的小服務路徑獲得子級包名  
    private String getSubPackage(String servletPath) {  
        return getSubJspPath(servletPath).replaceAll("\\/", ".");  
    }  
      
    // 根據請求的小服務路徑獲得JSP頁面的子級路徑  
    private String getSubJspPath(String servletPath) {  
        int start = 1;  
        int end = servletPath.lastIndexOf("/");  
        return end > start ? servletPath.substring(start, end > 0 ? end + 1 : 0) : "";  
    }  
      
}  

 

          讓前端控制器在解析用戶請求的小服務路徑時,將請求路徑和Action類的包以及JSP頁面的路徑對應起來,如用戶請求的小服務路徑是/user/order/save.do,對應的Action類的完全限定名就是cn.sm.action.user.order.SaveAction,如需跳轉到ok.jsp頁面,那JSP頁面的預設路徑是/WEB-INF/jsp/user/order/ok.jsp。這樣做才能滿足對項目模塊進行劃分的要求,而不是把所有的Action類都放在一個包中,把所有的JSP頁面都放在一個路徑下。

          前端控制器寫到這裡還沒完成,如每個Action都要寫若幹的req.getParameter(String)從請求中獲得請求參數再組裝對象而後調用業務邏輯層的代碼,這樣Action實現類中就會有很多重覆的樣板代碼,解決這一問題的方案仍是反射,通過反射可以將Action需要的參數註入到Action類中。需註意的是,反射雖可幫我們寫通用性很強的代碼,但反射開銷也不可視而不見,自定義MVC框架有很多可優化的地方,先解決請求參數的註入問題。

封裝一個反射的工具類,代碼如下

package cn.sm.util;  
  
public interface TypeConverter {  
  
    public Object convert(Class<?> elemType, String value) throws Exception;  
} 
package cn.sm.util;  
  
import java.lang.reflect.Constructor;  
import java.lang.reflect.Field;  
import java.lang.reflect.Modifier;  
import java.util.ArrayList;  
import java.util.List;  
  
/** 
 * 反射工具類 
 * @author 微冷的風 
 * 
 */  
public class ReflectionUtil {  
  
    private ReflectionUtil() {  
        throw new AssertionError();  
    }  
      
    /** 
     * 根據欄位名查找欄位的類型 
     * @param target 目標對象 
     * @param fieldName 欄位名 
     * @return 欄位的類型 
     */  
    public static Class<?> getFieldType(Object target, String fieldName) {  
        Class<?> clazz = target.getClass();  
        String[] fs = fieldName.split("\\.");  
          
        try {  
            for(int i = 0; i < fs.length - 1; i++) {  
                Field f = clazz.getDeclaredField(fs[i]);  
                target = f.getType().newInstance();  
                clazz = target.getClass();  
            }  
            return clazz.getDeclaredField(fs[fs.length - 1]).getType();  
        }  
        catch(Exception e) {  
                // throw new RuntimeException(e);  
        }  
        return null;  
    }  
      
    /** 
     * 獲取對象所有欄位的名字 
     * @param obj 目標對象 
     * @return 欄位名字的數組 
     */  
    public static String[] getFieldNames(Object obj) {  
        Class<?> clazz = obj.getClass();  
        Field[] fields = clazz.getDeclaredFields();  
        List<String> fieldNames = new ArrayList<>();  
        for(int i = 0; i < fields.length; i++) {  
            if((fields[i].getModifiers() & Modifier.STATIC) == 0) {  
                fieldNames.add(fields[i].getName());  
            }  
        }  
        return fieldNames.toArray(new String[fieldNames.size()]);  
    }  
  
    /** 
     * 通過反射取對象指定欄位(屬性)的值 
     * @param target 目標對象 
     * @param fieldName 欄位的名字 
     * @throws 如果取不到對象指定欄位的值則拋出異常 
     * @return 欄位的值 
     */  
    public static Object getValue(Object target, String fieldName) {  
        Class<?> clazz = target.getClass();  
        String[] fs = fieldName.split("\\.");  
          
        try {  
            for(int i = 0; i < fs.length - 1; i++) {  
                Field f = clazz.getDeclaredField(fs[i]);  
                f.setAccessible(true);  
                target = f.get(target);  
                clazz = target.getClass();  
            }  
          
            Field f = clazz.getDeclaredField(fs[fs.length - 1]);  
            f.setAccessible(true);  
            return f.get(target);  
        }  
        catch (Exception e) {  
            throw new RuntimeException(e);  
        }  
    }  
      
    /** 
     * 通過反射給對象的指定欄位賦值 
     * @param target 目標對象 
     * @param fieldName 欄位的名稱 
     * @param value 值 
     */  
    public static void setValue(Object target, String fieldName, Object value) {  
        Class<?> clazz = target.getClass();  
        String[] fs = fieldName.split("\\.");  
        try {  
            for(int i = 0; i < fs.length - 1; i++) {  
                Field f = clazz.getDeclaredField(fs[i]);  
                f.setAccessible(true);  
                Object val = f.get(target);  
                if(val == null) {  
                    Constructor<?> c = f.getType().getDeclaredConstructor();  
                    c.setAccessible(true);  
                    val = c.newInstance();  
                    f.set(target, val);  
                }  
                target = val;  
                clazz = target.getClass();  
            }  
          
            Field f = clazz.getDeclaredField(fs[fs.length - 1]);  
            f.setAccessible(true);  
            f.set(target, value);  
        }  
        catch (Exception e) {  
            throw new RuntimeException(e);  
        }  
    }  
      
}  

             這工具類中封裝了四個方法,通過這個工具類可給對象指定欄位賦值,也可獲取對象指定欄位值和類型,

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 從控制台輸入輸出,來進行資料庫的插入和查詢操作的小程式(利用JDBC) ...
  • 一、Redis基礎介紹 redis是一個key-value存儲系統。和Memcached類似,它支持存儲的value類型相對更多,包括string(字元串)、list(鏈表)、set(集合)、zset(sorted set --有序集合)和hash(哈希類型)。這些數據類型都支持push/pop、a ...
  • 在寫C++程式中,總會遇到要從一個字元串中查找一小段子字元串的情況,對於在C中,我們經常用到strstr()或者strchr()這兩種方法。而對於C++的string,我們往往會用到find()。 C++:#inlcude<string>C: #include<string.h>find():在一個 ...
  • 一、spring xml配置(不包括AOP,主要瞭解在初始化及實例化過程中spring配置文件中每項內容的具體實現過程,從根本上掌握spring) 二、BeanFactory容器後置處理介面(BeanFactoryPostProcessor) 三、基於註解的依賴註入 四、aop部分 五、spring ...
  • 今天我們來聊聊如何跟Unity學代碼優化,準確地說,是通過學習Unity的IL2CPP技術的優化策略,應用到我們的日常邏輯開發中。 ...
  • ...
  • turbine介紹:附7 turbine turbine部署:第二章 部署war包到tomcat 一、總體圖 前提: 假設有兩個服務A/B分別布在兩台機器上(這裡為了簡單,使用一個服務myboot來代替),A、B都訪問myboot2的兩個介面getHotelInfo和getHotelInfo2(這兩 ...
  • ApplicationContext作為資源載入器;ApplicationContext作為事件發佈者; Java原生提供了事件發佈機制 EventObject對象作為發佈的事件,EventListener作為處理髮布事件的監聽器。但是其並沒有提供發佈者的角色來橋接EventObject和Event ...
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...