《Spring實戰》-第五章:Web中的Spring(SpringMVC註解實現)

慢慢來比較快,虛心學技術

前言:前面我們學習了關於Spring核心的IOC和AOP知識,除此之外,以此爲基礎,Spring的MVC框架還經常被用於Web開發(SpringMVC)

一、什麼是SpringMVC框架?

在瞭解SpringMVC之前,我們先回顧一下Spring基礎架構:

Spring MVC 是Spring的一部分,基於模型 - 視圖 - 控制器( Model-View-Controller , MVC )模式實現,它能夠幫你構建像 Spring 框架那樣靈活和鬆耦合的 Web 應用程序。在實際開發中,接收瀏覽器的請求響應,對數據進行處理,然後返回頁面進行顯示。

二、SpringMVC組成以及運行原理

Ⅰ、SpringMVC的組成

  1. DispatcherServlet:前端控制器 (SpringMVC的核心)-----相當於MVC中的C,作爲中心調用其他組件,降低其他組件之間的耦合性
  2. HandlerMapping:處理器映射器 ----------------------------------根據用戶請求找到對應路徑的處理器(相當於處理器的名單)
  3. HandlAdapter:處理器適配器 --------------------------------------調用執行處理器方法(適配器模式的應用)
  4. Handler:處理器 --------------------------------------------------------處理用戶請求的類,相當於傳統意義上的Servlet
  5. ViewResolver:視圖解析器 ------------------------------------------處理返回結果,將處理器適配器返回的數據模型轉換成具體視圖,並進行渲染輸出(實際上就是將處理器返回的名稱補充成具體的路徑也就是一個視圖,同時從數據模型中提取數據進行填充)
  6. View:視圖 ---------------------------------------------------------------視圖是數據最終需要展現給客戶的地方,Spring支持多種類型的視圖:jstlVies,freemarkerView等,最常用的是JSP和使用模板實現的html等

Ⅱ、SpringMVC請求響應流程

用戶發起請求,攜帶請求信息到前端控制器進行調度

前端控制器(DispatcherServlet)調用處理器映射器,根據請求信息從處理器映射器中找到訪問路徑的目標處理器

前端控制器(DispatcherServlet)根據得到的目標處理器映射,調用處理器適配器方法(處理器適配器將處理器方法包裝成適配器模式)

處理器適配器(HandlerAdapter)調用處理器(Handler)相應功能方法,並將結果返回給前端控制器

前端控制器(DispatcherServlet)根據得到的數據結果和目標視圖名稱,調用視圖解析器(ViewResolver)返回目標視圖完整路徑

前端控制器(DispatcherServlet)根據得到的視圖路徑,對目標視圖(view)進行渲染(數據填充等),得到目標視圖

前端控制器(DispatcherServlet)將目標視圖展現給用戶

從上述流程可以看到,SpringMVC的功能流轉是圍繞前端控制器(DispatcherServlet)實現的,這樣的好處是使得各個組件之間的耦合性大大降低,各個組件只做自己應該做的事情。其實這是大部分框架想要實現的目標。

分析DispatcherServlet,從Spring官網查看到的結構圖如下:

從結構圖可以看到,DispatcherServlet包含了兩個Web應用上下文,用於獨立控制,其中:

  • Servlet WebApplicationContext:管理用於網絡請求的處理器適配器,視圖解析器,處理器映射器和處理器
  • Root WebApplicationContext:管理基本的數據庫操作類,業務邏輯類等Bean

通過兩個應用上下文管理基本Bean和網絡Bean,互不干擾,但是其中管理的Bean之間可以互相使用

三、SpringMVC的簡單使用(註解方式)

首先我們應該先引入Spring對WebMvc的支持,maven引入如下:

<!--引入網絡Servlet支持-->
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>3.1.0</version>
    <scope>provided</scope>
</dependency>
<!--引入SpringMVC-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>${org.springframework.version}</version>
</dependency>

①創建DispatcherServlet類,繼承並重載AbstractAnnotationConfigDispatcherServletInitializer類的三個方法:

//定義DispatcherServlet類名爲WebAppInitializer
public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    /**
     * 指定DispatcherServlet的基本Bean應用上下文配置類
     * @return
     */
    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class[]{RootConfig.class};
    }

    /**
     * 指定DispatcherServlet的網絡類應用上下文配置類
     * @return
     */
    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class[]{WebConfig.class};
    }

    /**
     * 將DispatcherServlet映射到“/”,即應用內所有訪問都會經過DispatcherServlet的處理
     * @return
     */
    @Override
    protected String[] getServletMappings() {
        logger.debug("DispatcherServlet獲取匹配的前端控制器。。。。。。");
        return new String[]{"/"};
    }
}

②創建上述代碼的兩個配置類:RootConfig.javaWebConfig.java,其中,RootConfig只掃描除了WebConfig掃描範圍外的基本類,而WebConfig只掃描基本的網絡類,同時配置視圖解析器和處理器映射器,並開啓mvc配置

//定義WebConfig配置類
@Configuration
@ComponentScan(basePackages = {"com.my.spring.controller"})//WebConfig掃描包的範圍
@EnableWebMvc /*<mvc:annotation-driven> 開啓mvc配置*/
public class WebConfig extends WebMvcConfigurationSupport {

    /**
     * 定義一個視圖解析器
     *
     * @return org.springframework.web.servlet.ViewResolver
     *
     * @author xxx 2019/3/5
     * @version 1.0
     **/
    @Bean
    public ViewResolver viewResolver(){
        //基本的視圖解析器
        InternalResourceViewResolver resourceViewResolver = new InternalResourceViewResolver();
        //視圖前綴,指向WEB-INF目錄下的view目錄,意思是所有的視圖名稱進入視圖解析器的時候都會被加上前綴
        resourceViewResolver.setPrefix("/WEB-INF/view/");
         //視圖後綴,此處指定後綴爲jsp,意思是所有的視圖名稱進入視圖解析器的時候都會被加上後綴,前綴+view名+後綴得到完整路徑
        resourceViewResolver.setSuffix(".jsp");
         //可以在JSP頁面中通過${}訪問beans
        resourceViewResolver.setExposeContextBeansAsAttributes(true);
        return resourceViewResolver;
    }

    /**
     * 配置一個默認的處理器,實現父類接口,自動處理靜態資源的映射
     * @param configurer
     */
    @Override
    protected void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }
}

//定義RootConfig配置類
@Configuration
//指定掃描範圍,排除過濾掉使用了@EnableWebMvc註解掃描範圍的bean,不進行掃描
@ComponentScan(basePackages ={"com.my.spring"},excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION,value = {EnableWebMvc.class})})
public class RootConfig {
}

③編寫基本的Bean類

@Data//lombok的註解,編譯添加setter和getter方法
public class BaseBean {

    private Integer id;

    private String name;

}

④編寫邏輯操作類(暫時沒有用到數據庫,所以只是模擬)

//定義基本Dao接口
public interface BaseRepository {
    /**
     * 根據id獲取BaseBean
     * @param id 目標id
     * @return
     */
    BaseBean findOne(Integer id);
}

//定義基本Dao實現類,@Repository註解使用了@Component,可以被當作組件裝配
@Repository
public class BaseRepositoryImpl implements BaseRepository {

    @Override
    public BaseBean findOne(Integer id) {
        if(id!=0){
            return null;
        }
        BaseBean baseBean = new BaseBean();
        baseBean.setId(0);
        baseBean.setName("測試bean");
        return baseBean;
    }
}

//定義基本Service接口
public interface BaseService {
     /**
     * 根據id獲取BaseBean
     * @param id 目標id
     * @return
     */
    BaseBean findBean(Integer id);
}

//定義基本操作實現類,使用@Service註解,標明該類是一個service,該註解使用了@Comonnet註解,所以可被作爲組件進行裝配
@Service
public class BaseServiceImpl implements BaseService {
    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    //注入基本操作dao
    @Autowired
    private BaseRepository baseRepository;

    @Override
    public BaseBean findBean(Integer id) {
        return this.baseRepository.findOne(id);
    }
}

⑤創建處理類HomeController,使用@Controller註解標明當前類爲一個處理類,同樣使用@Component註解,可被裝配

@Controller
public class HomeController {

    @Autowired
    private BaseService baseService;

    /**
     *使用@RequestMapping註解,將當前方法作爲可訪問路徑,value值指定了訪問路徑,而method值指定了訪問方式
     */
    @RequestMapping(method = RequestMethod.GET,value = "/home")
    public String home(){
        //返回試圖名爲home的視圖
        return "home";
    }
}

看到上述代碼,我們首先做最簡單的測試,通過訪問/home路徑,訪問具體的靜態資源:

根據WebConfig中的視圖解析器,我們在相應路徑下創建:/WEB-INF/view/home.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Home Page</title>
</head>
<body>
    <h1>Hello World</h1>
</body>
</html>

路徑如下:

啓動項目,頁面訪問如下:本項目名爲SpringAction05

從訪問結果可以看到,訪問被轉向了home.jsp,而我們在controller方法中只是return了一個home,也就是說,視圖解析器爲我們補充了完整的路徑並將視圖返回給瀏覽器

注:

AbstractAnnotationConfigDispatcherServletInitializer 會同時創建 DispatcherServletContextLoaderListener

GetServlet-ConfigClasses() 方法返回的帶有 @Configuration 註解的類將會用來定義 DispatcherServlet 應用上下文中的 bean 。

getRootConfigClasses() 方法返回的帶有 @Configuration 註解的類將會用來配置 ContextLoaderListener 創建的應用上下文中的 bean 。

四、信息交互

Ⅰ、傳遞模型數據到視圖中

有時候我們並不只是需要對訪問進行轉發,同時可能需要攜帶一些信息給瀏覽器端,SpringMVC提供了Model類對返回信息進行封裝返回:

下面我們在上述controller方法home()返回之前封裝信息返回給視圖,並從視圖中獲取到對應的數據:

@Controller
public class HomeController {

    @Autowired
    private BaseService baseService;

    @RequestMapping(method = RequestMethod.GET,value = "/home")
    public String home(Model model){

        //往model中放置信息(key,value)
        model.addAttribute("Message","I am HomePage!!!");

        //返回試圖名爲home的視圖
        return "home";
    }
}

修改home.jsp獲取目標數據,此處我們使用JSTL標籤庫獲取,所以需要先引入JSTL標籤庫的jar包:

<!-- jstl -->
<dependency>
    <groupId>jstl</groupId>
    <artifactId>jstl</artifactId>
    <version>1.2</version>
</dependency>

home.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %><!--引入JSTL標籤庫,並以c爲標籤前綴-->
<%@ page isELIgnored="false" %><!--禁用tomcat自帶的EL表達式,否則無法獲取對應的數據-->
<html>
<head>
    <title>Home Page</title>
</head>
<body>
    <h1>Hello World</h1>
    <!--通過EL表達式獲取key爲Message的屬性值-->
    <c:out value="${Message}"></c:out>
</body>
</html>

測試結果如下:

Ⅱ、接受請求的輸入

Spring MVC 允許以多種方式將客戶端中的數據傳送到控制器的處理器方法中,包括:

  • 查詢參數( Query Parameter )。
  • 路徑變量( Path Variable )
  • 表單參數( Form Parameter )。

①查詢參數形式

我們在controller中編寫一個getBean方法,要求接收一個參數,參數名爲id,通過拿到的id進行查詢,將查詢到的基本bean封裝到數據模型並返回給視圖

@RequestMapping(method = RequestMethod.GET,value = "/getBean")
public String getBeanByParam(@RequestParam("id")Integer beanId, Model model){

    BaseBean bean = this.baseService.findBean(beanId);
    //以bean爲key將目標對象封裝到數據模型
    model.addAttribute("bean",bean);

    //返回試圖名爲home的視圖
    return "showMessage";
}

然後創建一個showMessage.jsp作爲目標視圖

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page isELIgnored="false" %>
<html>
<head>
    <title>信息主頁</title>
</head>
<body>
    <!--判空-->
    <c:if test="${bean==null}">
        <li>bean不存在</li>
    </c:if>
    <c:out value="${bean.id}"></c:out><!--提取目標對象的信息-->
    <c:out value="${bean.name}"></c:out>
</body>
</html>

瀏覽器訪問路徑如下:http://locahost:8080/SpringAction05/getBean?id=0

注:其實getBeanByParam(@RequestParam("id")Integer id, Model model)方法中的@RequestParam("id")可以省略不寫,如果不寫的話,那麼訪問參數必須與參數位的名稱一致,即:http://locahost:8080/SpringAction05/getBean?beanId=0

②表單參數形式

將查詢參數通過form表單提交到前端控制器,此時,Spring允許通過對象接收查詢參數,要求form表單提交的字段與對象屬性字段對應

首先創建一個form表單,表單提交到/getBean

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<body>
<h2>Hello World!</h2>
<form action="./getBean" method="post">
    提交id:<input name="id" type="text"/><br>
    <button type="submit">提交</button>
</form>
</body>
</html>

Contorller接收參數(以對象形式):

@RequestMapping(method = RequestMethod.POST,value = "/getBean")
public String getBeanByForm(BaseBean baseBean, Model model){

    BaseBean bean = this.baseService.findBean(baseBean.getId());

    model.addAttribute("bean",bean);

    //返回試圖名爲home的視圖
    return "showMessage";
}

瀏覽器訪問結果如下:


③路徑變量方式

SpringMVC可以通過@PathVariable從路徑中提取參數變量:

@RequestMapping(method = RequestMethod.GET,value = "/getBean/{id}")//指定getBean/後的參數位id參數佔位
public String getBeanByPath(@PathVariable("id") Integer id, Model model){//通過@PathVariable註解提取變量
    BaseBean bean = this.baseService.findBean(id);

    model.addAttribute("bean",bean);

    //返回試圖名爲home的視圖
    return "showMessage";
}

訪問結果如下:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章