java面試題之----spring MVC的原理和MVC


1.什么是mvc?

1.1原始比較初級的設計模式:

1.2 MVC設計模式

2MVC設計模式的優勢核心在於其能解耦和:

傳統的設計模式相當於是一個串聯的設計,只要其中一個環節出了問題便會使下一環節中止

網上普遍的對於mvc設計模式的解讀為:

2.1.定義:MVC 設計模型是一種使用 Model View Controller( 模型-視圖-控制器)設計創建 Web 應用程序的模式。

  由上主謂賓可以很容易看出,mvc模型是一種用來寫web應用程序的樣式,也就是說只能寫web不能寫其它?

2.2 既然使用了 Model View Controller( 模型-視圖-控制器),那么就很有必要來介紹一下該(模型-視圖-控制器)到底是怎樣的一個東西?

 

Model(模型):是應用程序中用於處理應用程序數據邏輯的部分。

    通常模型對象負責在數據庫中存取數據。

 

View(視圖):是應用程序中處理數據顯示的部分。
    通常視圖是依據模型數據創建的。

 

Controller(控制器):是應用程序中處理用戶交互的部分。
    通常控制器負責從視圖讀取數據,控制用戶輸入,並向模型發送數據。
MVC的流程:
用戶向服務器發送請求,控制層(Controller)接受請求,並委托模型層(Model)進行數據處理,模型層接到委托后處理請求並將結果返回給控制層(Controller),並將返回的結果交給視圖層(view),讓視圖層(view)進行視圖渲染,並將渲染后的jstl返回給控制層(Controller),最后控制層(Controller)響應結果請求返回給用戶。便可以顯示瀏覽器頁面了,其中C/S模式,模型層會主動推送數據到視圖層,而B/S則不能
最后終於是我們的SpringMVC了,
到底什么是Spring MVC呢?
Spring MVC是一個web層的mvc框架類似於struts2

3 Springmvc和spring?

 

 

Springmvc是spring的部分。

 

4.Springmvc執行流程

根據mvc設計模式:

用戶向服務器發送請求,前端控制器(web.xml)接受請求,前端控制器將請求分為如下幾步

1.向處理器映射器(HandlerMapping)請求獲取action類的url,處理器映射器(springmvc.xml)獲取到action類的url后便將帶有攔截器的chian返回給前端控制器

2.向處理器適配器(handlerAdapter)請求執行action,處理器適配器(springmvc.xml)執行action的方法,所以action需要調用業務層的方法,並將返回結果(mode and view)返回給處理器適配器,處理器適配器將得到的結果(mode and view)返回給前端控制器。

3.向視圖解析器(ViewResolver)請求視圖解析,視圖解析器(springmvc.xml)接受到請求后立即解析視圖,並將view返回給前端控制器。

4.前端控制器接收到view后向視圖層(view)發送視圖渲染請求,視圖層渲染完后將jstl返回給前端控制器,

5.最后前端控制器將得到的結果響應返回給用戶。

 
springmvc中mvc各自的職責
DispatcherServlet前端控制器設計模式的實現,提供Spring Web MVC的集中訪問點,而且負責職責的分派,而且與Spring IoC容器無縫集成,從而可以獲得Spring的所有好處。

DispatcherServlet主要用作職責調度工作,本身主要用於控制流程,主要職責如下:

1、文件上傳解析,如果請求類型是multipart將通過MultipartResolver進行文件上傳解析;

2、通過HandlerMapping,將請求映射到處理器(返回一個HandlerExecutionChain,它包括一個處理器、多個HandlerInterceptor攔截器);

3、  通過HandlerAdapter支持多種類型的處理器(HandlerExecutionChain中的處理器);

4、通過ViewResolver解析邏輯視圖名到具體視圖實現;

5、本地化解析;

6、渲染具體的視圖等;

7、如果執行過程中遇到異常將交給HandlerExceptionResolver來解析。
視圖層

視圖是用戶看到並與之交互的界面。對老式的Web應用程序來說,視圖就是由HTML元素組成的界面,在新式的Web應用程序中,HTML依舊在視圖中扮演着重要的角色,但一些新的技術已層出不窮,它們包括Macromedia Flash和像XHTML,XML/XSL,WML等一些標識語言和Web services.   如何處理應用程序的界面變得越來越有挑戰性。MVC一個大的好處是它能為你的應用程序處理很多不同的視圖。在視圖中其實沒有真正的處理發生,不管這些數據是聯機存儲的還是一個雇員列表,作為視圖來講,它只是作為一種輸出數據並允許用戶操縱的方式。

模型層:

模型表示企業數據和業務規則。在MVC的三個部件中,模型擁有最多的處理任務。例如它可能用像EJBs和ColdFusion Components這樣的構件對象來處理數據庫。被模型返回的數據是中立的,就是說模型與數據格式無關,這樣一個模型能為多個視圖提供數據。由於應用於模型的代碼只需寫一次就可以被多個視圖重用,所以減少了代碼的重復性。

mvc設計模式的優點

 

1、開發人員可以只關注整個結構中的其中某一層;

 

2、可以很容易的用新的實現來替換原有層次的實現;

 

3、可以降低層與層之間的依賴;

 

4、有利於標准化;

 

5、利於各層邏輯的復用。

 

spring具體流程回顧及相應代碼

 

具體流程:

 

(1)首先用戶發送請求——>DispatcherServlet,前端控制器收到請求后自己不進行處理,而是委托給其他的解析器進行處理,作為統一訪問點,進行全局的流程控制;

 

(2)DispatcherServlet——>HandlerMapping,映射處理器將會把請求映射為HandlerExecutionChain對象(包含一個Handler處理器(頁面控制器)對象、多個HandlerInterceptor攔截器)對象;

 

(3)DispatcherServlet——>HandlerAdapter,處理器適配器將會把處理器包裝為適配器,從而支持多種類型的處理器,即適配器設計模式的應用,從而很容易支持很多類型的處理器;

 

(4)HandlerAdapter——>調用處理器相應功能處理方法,並返回一個ModelAndView對象(包含模型數據、邏輯視圖名);

 

(5)ModelAndView對象(Model部分是業務對象返回的模型數據,View部分為邏輯視圖名)——> ViewResolver, 視圖解析器將把邏輯視圖名解析為具體的View;

 

(6)View——>渲染,View會根據傳進來的Model模型數據進行渲染,此處的Model實際是一個Map數據結構;

 

(7)返回控制權給DispatcherServlet,由DispatcherServlet返回響應給用戶,到此一個流程結束。

 

 

SpringMVC入門程序

(1)web.xml

<web-app>
  <servlet>
  <!-- 加載前端控制器 -->
  <servlet-name>springmvc</servlet-name>
  <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  <!-- 
       加載配置文件
       默認加載規范:
       * 文件命名:servlet-name-servlet.xml====springmvc-servlet.xml
       * 路徑規范:必須在WEB-INF目錄下面
       修改加載路徑:
   -->
   <init-param>
   <param-name>contextConfigLocation</param-name>
   <param-value>classpath:springmvc.xml</param-value>   
   </init-param>
  </servlet>

  <servlet-mapping>
  <servlet-name>springmvc</servlet-name>
  <url-pattern>*.do</url-pattern>
  </servlet-mapping>
</web-app>

(2)springmvc.xml

<beans>
    <!-- 配置映射處理器:根據bean(自定義Controler)的name屬性的url去尋找hanler;springmvc默認的映射處理器是
    BeanNameUrlHandlerMapping
     -->
    <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"></bean>


    <!-- 配置處理器適配器來執行Controlelr ,springmvc默認的是
    SimpleControllerHandlerAdapter
    -->
    <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"></bean>

    <!-- 配置自定義Controler -->
    <bean id="myController" name="/hello.do" class="org.controller.MyController"></bean>

    <!-- 配置sprigmvc視圖解析器:解析邏輯試圖; 
        后台返回邏輯試圖:index
        視圖解析器解析出真正物理視圖:前綴+邏輯試圖+后綴====/WEB-INF/jsps/index.jsp
    -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/jsps/"></property>
        <property name="suffix" value=".jsp"></property>        
    </bean>
</beans>

(3)自定義Controler

public class MyController implements Controller{

    public ModelAndView handleRequest(HttpServletRequest arg0,
            HttpServletResponse arg1) throws Exception {
        ModelAndView mv = new ModelAndView();
        //設置頁面回顯數據
        mv.addObject("hello", "歡迎學習springmvc!");

        //返回物理視圖
        //mv.setViewName("/WEB-INF/jsps/index.jsp");

        //返回邏輯視圖
        mv.setViewName("index");
        return mv;
    }
}

(4)index頁面

<html>
<body>
<h1>${hello}</h1>
</body>
</html>

(5)測試地址

http://localhost:8080/springmvc/hello.do

HandlerMapping

HandlerMapping 將會把請求映射為 HandlerExecutionChain 對象(包含一個 Handler 處理器(頁面控制器)對象、多個 HandlerInterceptor 攔截器)對象,通過這種策略模式,很容易添加新的映射策略。

映射處理器有三種,三種可以共存,相互不影響,分別是BeanNameUrlHandlerMapping、SimpleUrlHandlerMapping和ControllerClassNameHandlerMapping;

BeanNameUrlHandlerMapping

//默認映射器,即使不配置,默認就使用這個來映射請求。
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"></bean>
//映射器把請求映射到controller
<bean id="testController" name="/hello.do" class="org.controller.TestController"></bean>

SimpleUrlHandlerMapping

<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
    <property name="mappings">
        <props>
            <prop key="/ss.do">testController</prop>
            <prop key="/abc.do">testController</prop>
        </props>
    </property>
</bean>
//那么上面的這個映射配置:表示多個*.do文件可以訪問多個Controller或者一個Controller。 
//前提是:都必須依賴自定義的控制器bean
<bean id="testController" name="/hello.do" class="org.controller.TestController"></bean>

ControllerClassNameHandlerMapping

//這個Mapping一配置:我們就可以使用Contrller的 [類名.do]來訪問這個Controller.
<bean class="org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping"></bean>

HandlerMapping架構圖

HandlerAdapter

處理器適配器有兩種,可以共存,分別是SimpleControllerHandlerAdapter和HttpRequestHandlerAdapter。

SimpleControllerHandlerAdapter
SimpleControllerHandlerAdapter是默認的適配器,表示所有實現了org.springframework.web.servlet.mvc.Controller 接口的Bean 可以作為SpringMVC 中的處理器。

HttpRequestHandlerAdapter
HTTP請求處理器適配器將http請求封裝成HttpServletResquest 和HttpServletResponse對象,和servlet接口類似。

(1)配置HttpRequestHandlerAdapter適配器

<!-- 配置HttpRequestHandlerAdapter適配器 -->
<bean class="org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter"></bean>

(2)編寫Controller

public class HttpController implements HttpRequestHandler{

    public void handleRequest(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        //給Request設置值,在頁面進行回顯
        request.setAttribute("hello", "這是HttpRequestHandler!");
        //跳轉頁面
        request.getRequestDispatcher("/WEB-INF/jsps/index.jsp").forward(request, response);
    }
}

(3)准備jsp頁面

<html>
<body>
<h1>${hello}</h1>
</body>
</html>

Adapter源碼分析
模擬場景:前端控制器(DispatcherServlet)接收到Handler對象后,傳遞給對應的處理器適配器(HandlerAdapter),處理器適配器調用相應的Handler方法。

(1)模擬Controller

//以下是Controller接口和它的是三種實現 
public interface Controller {
}

public class SimpleController implements Controller{
    public void doSimpleHandler() {
        System.out.println("Simple...");
    }
}

public class HttpController implements Controller{
    public void doHttpHandler() {
        System.out.println("Http...");
    }
}

public class AnnotationController implements Controller{
    public void doAnnotationHandler() {
        System.out.println("Annotation..");
    }
} 

(2)模擬HandlerAdapter

//以下是HandlerAdapter接口和它的三種實現
public interface HandlerAdapter {
    public boolean supports(Object handler);
    public void handle(Object handler);
}

public class SimpleHandlerAdapter implements HandlerAdapter{
    public boolean supports(Object handler) {
        return (handler instanceof SimpleController);
    }

    public void handle(Object handler) {
        ((SimpleController)handler).doSimpleHandler();
    }
}

public class HttpHandlerAdapter implements HandlerAdapter{
    public boolean supports(Object handler) {
        return (handler instanceof HttpController);
    }

    public void handle(Object handler) {
        ((HttpController)handler).doHttpHandler();
    }
}

public class AnnotationHandlerAdapter implements HandlerAdapter{
    public boolean supports(Object handler) {
        return (handler instanceof AnnotationController);
    }

    public void handle(Object handler) {
        ((AnnotationController)handler).doAnnotationHandler();
    }
}

(3)模擬DispatcherServlet

public class Dispatcher {
    public static List<HandlerAdapter> handlerAdapter = new ArrayList<HandlerAdapter>();

    public Dispatcher(){
        handlerAdapter.add(new SimpleHandlerAdapter());
        handlerAdapter.add(new HttpHandlerAdapter());
        handlerAdapter.add(new AnnotationHandlerAdapter());
    }

    //核心功能
    public void doDispatch() {
        //前端控制器(DispatcherServlet)接收到Handler對象后
        //SimpleController handler = new SimpleController();
        //HttpController handler = new HttpController();
        AnnotationController handler = new AnnotationController();

        //傳遞給對應的處理器適配器(HandlerAdapter)
        HandlerAdapter handlerAdapter = getHandlerAdapter(handler);

        //處理器適配器調用相應的Handler方法
        handlerAdapter.handle(handler);
    }

    //通過Handler找到對應的處理器適配器(HandlerAdapter)
    public HandlerAdapter getHandlerAdapter(Controller handler) {
        for(HandlerAdapter adapter : handlerAdapter){
            if(adapter.supports(handler)){
                return adapter;
            }
        }
        return null;
    }
}

(4)測試

public class Test {
    public static void main(String[] args) {
        Dispatcher dispather = new Dispatcher();
        dispather.doDispatch();
    }
}

控制器

控制器架構圖

Controller 簡介

  • 收集、驗證請求參數並綁定到命令對象;
  • 將命令對象交給業務對象,由業務對象處理並返回模型數據;
  • 返回ModelAndView(Model部分是業務對象返回的模型數據,視圖部分為邏輯視圖名)。

ServletForwardingController(轉發控制器)

將接收到的請求轉發到一個命名的servlet,具體示例如下:當我們請求/forwardToServlet.do時,會被轉發到名字為“forwarding”的servlet處理,該sevlet的servlet-mapping標簽配置是可選的.

(1)控制器

public class ForwardingServlet extends HttpServlet{
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        resp.getWriter().write("Controller forward to Servlet");
    }
}

(2)web.xml

<servlet>  
<servlet-name>forwarding</servlet-name>  
<servlet-class>org.controller.ForwardingServlet</servlet-class>  
</servlet> 

(3)spring.xml

<bean name="/forwardToServlet.do"   
class="org.springframework.web.servlet.mvc.ServletForwardingController">  
<property name="servletName" value="forwarding"></property>  

AbstractCommandController(命令控制器)

使用post請求進行表單提交

模擬提交用戶表信息。

(1)spring.xml配置文件:

<beans>
    <!-- 配置映射處理器-->
    <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"></bean>

    <!-- 配置處理器適配器-->
    <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"></bean>

    <!-- 配置自定義Controler -->
    <bean name="/command.do" class="org.controller.CommandController"></bean>

    <bean name="/toAdd.do" class="org.controller.ToAddController"></bean>

    <!-- 配置sprigmvc視圖解析器:解析邏輯試圖 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/jsps/"></property>
        <property name="suffix" value=".jsp"></property>        
    </bean>
</beans>

(2)表單跳轉控制器:跳轉到表單頁面

public class ToAddController implements Controller{
    public ModelAndView handleRequest(HttpServletRequest request,
            HttpServletResponse response) throws Exception {
        ModelAndView mv = new ModelAndView();
        //調轉到add添加頁面視圖
        mv.setViewName("add");
        return mv;
    }
}

(3)編輯頁面控制器:轉發表單信息

public class CommandController extends AbstractCommandController{

    //指定參數綁定到哪個javaBean
    public CommandController(){
        this.setCommandClass(User.class);
    }

    @Override
    protected ModelAndView handle(HttpServletRequest request,
            HttpServletResponse response, Object command, BindException errors)
            throws Exception {
        //把命令對象強轉成User對象
        User user = (User) command;
        ModelAndView mv = new ModelAndView();

        mv.addObject("user", user);
        mv.setViewName("MyJsp");

        return mv;
    }

    /*
     * 進行時間類型各種格式的覆蓋
     */
    @Override
    protected void initBinder(HttpServletRequest request,
            ServletRequestDataBinder binder) throws Exception {
        String str = request.getParameter("birthday");

        if(str.contains("/")){
            binder.registerCustomEditor(Date.class, 
                    new CustomDateEditor(new SimpleDateFormat("yyyy/MM/dd"), true));
        }else{
            binder.registerCustomEditor(Date.class, 
                    new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd"), true));    
        }
    }
}

(4)表單頁面:

<html>
<body>
<form action="${pageContext.request.contextPath }/command.do" method="post">
姓名:<input type="text" name="username" id="username"><p>
生日:<input type="text" name="birthday" id="birthday"><p>
性別:<input type="text" name="sex" id="sex"><p>
地址:<input type="text" name="address" id="address"><p>
<input type="submit" value="提交">
</form>
</body>
</html>

(5)表單信息呈現頁面:

<html>
<body>
${user.username } <br>
${user.birthday } <br>
${user.sex } <br>
${user.address } <br>
</body>
</html>

(6)進入表單頁面

http://localhost:8080/springmvc/toAdd.do 

使用get請求進行表單提交

在上面的代碼基礎上,直接輸入地址:

http://localhost:8080/springmvc/command.do?username=ltx&birthday=1996/11/01&sex=男&address=廣東

ParameterizableViewController(參數控制器)

使用參數控制器,不用自己定義Controller,可以直接使用toIndex進行訪問。

<bean name="/toIndex.do" class="org.springframework.web.servlet.mvc.ParameterizableViewController">
<!-- 配置你所要跳轉到視圖的名稱。跳轉到index頁面-->
<property name="viewName" value="index"></property>
</bean>

中文亂碼解決

Get請求亂碼

對於get請求中文參數出現亂碼解決方法有兩個:
修改tomcat配置文件添加編碼與工程編碼一致,如下:
<Connector URIEncoding="UTF-8" connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443"/>

另外一種方法對參數進行重新編碼:
String userName =new
String(request.getParamter("userName").getBytes("ISO8859-1"),"UTF-8")
ISO8859-1是Tomcat默認編碼,需要將Tomcat編碼后的內容按UTF-8編碼

Post請求亂碼
在web.xml中加入:

<filter>
    <filter-name>characterEncoding</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
        <param-name>encoding</param-name>
        <param-value>UTF-8</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>characterEncoding</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

 

 總結:

在面試中問到這個問題后,如果將mvc和springmvc的執行流程和整體結構講述清楚便可以了

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM