1. 程式人生 > >SpringMVC框架的簡介

SpringMVC框架的簡介

JavaEE體系結構包括四層,從上到下分別是應用層、Web層、業務層、持久層。Struts和SpringMVC是Web層的框架,Spring是業務層的框架,Hibernate和MyBatis是持久層的框架。

為什麼要使用SpringMVC?

很多應用程式的問題在於處理業務資料的物件和顯示業務資料的檢視之間存在緊密耦合,通常,更新業務物件的命令都是從檢視本身發起的,使檢視對任何業務物件更改都有高度敏感性。而且,當多個檢視依賴於同一個業務物件時是沒有靈活性的。

SpringMVC是一種基於Java,實現了Web MVC設計模式,請求驅動型別的輕量級Web框架,即使用了MVC架構模式的思想,將Web層進行職責解耦。基於請求驅動指的就是使用請求-響應模型,框架的目的就是幫助我們簡化開發,SpringMVC也是要簡化我們日常Web開發。

MVC設計模式

MVC設計模式的任務是將包含業務資料的模組與顯示模組的檢視解耦。這是怎樣發生的?在模型和檢視之間引入重定向層可以解決問題。此重定向層是控制器,控制器將接收請求,執行更新模型的操作,然後通知檢視關於模型更改的訊息。

SpringMVC架構

SpringMVC是Spring的一部分,如圖:

SpringMVC的核心架構:

具體流程:

(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
  • 1

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>
  • 1
  • 2

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>