轉(zhuǎn)載請(qǐng)注明出處:
牽手生活--頭條新聞:筆記是整理思路方式,分享是一個(gè)美德,牽手是我的生活方式
牽手生活--簡(jiǎn)書:筆記是整理思路方式妇蛀,分享是一個(gè)美德耕突,牽手是我的生活方式
Postman 使用方法詳解
JavaWeb項(xiàng)目放棄jsp?為什么要前后端解耦评架?為什么要前后端分離眷茁?
注:此文承接上一文:Spring MVC -Hello World(環(huán)境搭建)下面開始我們的工作
感謝慕課網(wǎng)提供的免費(fèi)視頻,感謝Arthur講師精彩講解
web.xml修改對(duì)el表達(dá)式的支持
<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
如果涉及涉及到el表達(dá)式被關(guān)閉纵诞,注意web-app的支持版本
關(guān)于web.xml中<web-app>報(bào)錯(cuò)解決方案
web.xml添加Spring容器的聲明
Spring MVC -Hello World搭建并不需要Spring的容器上祈。而我們現(xiàn)在要開始真正搭建Spring MVC,所以我們需要在web.xml中添加Spring的聲明
- ContextLoaderListener
- 配置信息
-
applicationContext.xml
<!-- Spring應(yīng)用上下文, 理解層次化的ApplicationContext -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/configs/spring/applicationContext*.xml</param-value>
</context-param>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
- spring 的上下文配置文件:applicationContext.xml
- springmvc 的DispatcherServlet的上下文配置文件:mvc-dispatcher-servlet.xml
Spring MVC的上下文層級(jí)說明:
WebApplicaiton Context(s):我們的跟挣磨,是有contextLoader加載的形成的上下文雇逞,他為我們所以應(yīng)用提供了公共所使用的一些組件、一些服務(wù)茁裙;有service層塘砸、DAO層等等。這些服務(wù)應(yīng)當(dāng)是給所以服務(wù)所共享的晤锥,他一應(yīng)該被極限在某個(gè)DispatcherServlet上下文之中掉蔬。
WebApplicaiton Context:
DispatcherServlet:可能有多個(gè)DispatcherServlet,用不同類型的DispatcherServlet分發(fā)不同的請(qǐng)求矾瘾。在web.xml配置文件中對(duì)應(yīng)的<servlet-maping>中的<url-pattern>用于攔截不同來源的url劃分依據(jù)女轿,用一個(gè)DispatcherServlet來攔截web app根下的請(qǐng)求。
創(chuàng)建Spring上下文相關(guān)文件:applicaitonContext.xml配置文件
- 啟動(dòng)annotation的DI管理
- 告訴Spring不需要再管理annotation
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--啟動(dòng)annotation的DI管理 -->
<context:annotation-config />
<!-- 告訴Spring不需要再管理annotation -->
<context:component-scan base-package="com.younghare.mvcdemo">
<context:exclude-filter type="annotation"
expression="org.springframework.stereotype.Controller" />
</context:component-scan>
</beans>
mvc-dispatcher-servlet.xml配置說明
注 :本配置文件是工名為mvc-dispatcher的DispatcherServlet使用壕翩, 提供其相關(guān)的Spring MVC配置
<context:annotation-config />部分:?jiǎn)⒂肧pring基于annotation的DI, 使用戶可以在Spring MVC中使用Spring的強(qiáng)大功能蛉迹。 激活 @Required ,@Autowired,JSR 250's @PostConstruct, @PreDestroy and @Resource 等標(biāo)注
<context:component-scan>部分:告訴DispatcherServlet上下文放妈, 只管理(掃描)@Controller類型的bean即可北救, 忽略其他型的bean,不需要去管理service芜抒,service交給我們的spring上下文去管理珍策。
HandlerMapping:HandlerMapping, 無需配置, Spring MVC有很強(qiáng)大的默認(rèn)啟動(dòng)宅倒,如果我們不進(jìn)行任何配置攘宙,他將啟用我們的默認(rèn)配置。默認(rèn)配置會(huì)啟動(dòng)DefaultAnnotationHandlerMapping拐迁,這個(gè)類就可以為我們?nèi)ソ馕鑫覀冏⒔獾?annotation-driven HandlerMapping蹭劈。
<mvc:annotation-driven />:這配置擴(kuò)展了我么的驅(qū)動(dòng)注解,他可以進(jìn)一步的將我么請(qǐng)求參數(shù)綁定到控制器參數(shù) 线召;也就是url中的查詢的某個(gè)變量链方,可以直接映射到Controller中某個(gè)方法的輸入?yún)?shù)。
ViewResolver bean的配置:dispatcher-servlet中可以依次的配置多個(gè)ViewResolver灶搜,他們之間可以使用order屬性進(jìn)行排序祟蚀,唯一注意的是InternalResourceViewResolver必須放在最后工窍,因?yàn)樗囟〞?huì)返回一個(gè)對(duì)象
-
靜態(tài)資源處理,如果沒有這些配置我們將無法獲得靜態(tài)資源
mvc:resources :這部分就是我們要添加的前酿,如果沒有這些配置患雏,我就講無法獲得我們的的靜態(tài)資源,比如: css罢维, js淹仑, imgs -。比如resources及時(shí)把resources映射到我們本地目錄的resources目錄下肺孵。
修改mvc-dispatcher-servlet.mxl配置
添加到mvc-dispatcher-servlet.xml代碼
注意:目錄項(xiàng)目資源名稱的resources也匀借,本地映射的resources目錄名稱要與配置中保持一致
<!-- 靜態(tài)資源處理, css平窘, js吓肋, imgs -->
<!--這個(gè)在hello world項(xiàng)目中沒有 后面的location="/resources/" 通常用于存放css、js瑰艘、image-->
<mvc:resources mapping="/resources/**" location="/resources/" />
修復(fù)日志部分的錯(cuò)誤-創(chuàng)建log4j.properties
log4j.properties配置信息
log4j.appender.Cons=org.apache.log4j.ConsoleAppender
log4j.appender.Cons.layout=org.apache.log4j.PatternLayout
log4j.appender.Cons.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n
# Root logger set to DEBUG using the A2 appender defined above.
log4j.rootLogger=info, Cons
log4j.additivity=false
#Application Logger+
log4j.logger.com.imooc.mvcdemo=debug, Cons
log4j.logger.org.springframework=debug, Cons
log4j.additivity.com=false
java日志框架log4j詳細(xì)配置及與slf4j聯(lián)合使用教程
完成上述的工作是鬼,說明的的Spring MVC環(huán)境才真正搭建起來,下面開始Spring MVC 方面的真正開放運(yùn)用紫新。
SpringMVC Controller開發(fā)基礎(chǔ)設(shè)施Model均蜜、service、展示的jsp芒率、其他資源(css囤耳、image、js)偶芍、Controller
先看一下目錄結(jié)構(gòu)
關(guān)注service的spring注解
Spring MVC controller用到的Model
- Chapter.java (章節(jié))
- Course.java( 課程)
Spring MVC controller用到的service(接口&接口實(shí)現(xiàn))
- Course mode相關(guān)的服務(wù)類CourseService(沒用用到數(shù)據(jù)庫紫皇,使用硬代碼的方式完成課程的組裝,在下篇中將介紹如何接入mysql數(shù)據(jù)庫)
CourseService.java(接口)
CourseServiceImpl.java(接口實(shí)現(xiàn))
Spring MVC controller用到的jsp
- course_overview.jsp
Spring MVC controller用到的其他資源(css腋寨、image、js)
Spring MVC controller本身CourseController.java
package com.younghare.mvcdemo.controller;
import java.io.File;
import java.io.IOException;
import java.util.Iterator;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import com.younghare.mvcdemo.model.Course;
import com.younghare.mvcdemo.service.CourseService;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.builder.ReflectionToStringBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
//告訴spring mvc的dispatcher-servlet這時(shí)一個(gè)Controller,會(huì)被dispatcher-servlet所管理化焕,并且依賴注入
@Controller
//類級(jí)別的RequestMapping萄窜,它處理我們Controller的根url
@RequestMapping("/courses")
// /courses/**
public class CourseController {
private static Logger log = LoggerFactory.getLogger(CourseController.class);
private CourseService courseService;
@Autowired
public void setCourseService(CourseService courseService) {
this.courseService = courseService;
}
//本方法將處理 /courses/view?courseId=123 形式的URL
@RequestMapping(value="/view", method=RequestMethod.GET)
//方法內(nèi)的@RequestParam注釋用來表示url請(qǐng)求的參數(shù)
public String viewCourse(@RequestParam("courseId") Integer courseId,
Model model) {
log.debug("In viewCourse, courseId = {}", courseId);
//獲取課程,并把課程信息通過session返回給jsp
Course course = courseService.getCoursebyId(courseId);
model.addAttribute(course);
return "course_overview";
}
//本方法將處理 /courses/view2/123 形式的URL
@RequestMapping("/view2/{courseId}")
public String viewCourse2(@PathVariable("courseId") Integer courseId,
Map<String, Object> model) {
log.debug("In viewCourse2, courseId = {}", courseId);
Course course = courseService.getCoursebyId(courseId);
model.put("course",course);
return "course_overview";
}
//傳統(tǒng)的HttpServletRequest參數(shù)方式處理
//本方法將處理 /courses/view3?courseId=123 形式的URL
@RequestMapping("/view3")
public String viewCourse3(HttpServletRequest request) {
Integer courseId = Integer.valueOf(request.getParameter("courseId"));
Course course = courseService.getCoursebyId(courseId);
request.setAttribute("course",course);
return "course_overview";
}
//添加模擬操作
@RequestMapping(value="/admin", method = RequestMethod.GET, params = "add")
public String createCourse(){
return "course_admin/edit";
}
//保存模擬操作
@RequestMapping(value="/save", method = RequestMethod.POST)
//@ModelAttribute參數(shù)級(jí)別的標(biāo)簽實(shí)現(xiàn)頁面對(duì)象與
public String doSave(@ModelAttribute Course course){
log.debug("Info of Course:");
log.debug(ReflectionToStringBuilder.toString(course));
//在此進(jìn)行業(yè)務(wù)操作撒桨,比如數(shù)據(jù)庫持久化
course.setCourseId(123);
//重定向自己其他方法的url
return "redirect:view2/"+course.getCourseId();
}
//SpringMVC內(nèi)置了文件上傳功能查刻,我們只需通過一些配置,就可以使用Spring給我們暴露的接口實(shí)現(xiàn)文件上傳
//上傳操作
@RequestMapping(value="/upload", method=RequestMethod.GET)
public String showUploadPage(@RequestParam(value= "multi", required = false) Boolean multi){
if(multi != null && multi){
return "course_admin/multifile";
}
return "course_admin/file";
}
//文件上傳
@RequestMapping(value="/doUpload", method=RequestMethod.POST)
//MultipartFile:是SpringMVC提供的一個(gè)文件上傳類凤类;@RequestParam指定文件上傳input 的name=file
public String doUploadFile(@RequestParam("file") MultipartFile file) throws IOException{
if(!file.isEmpty()){
log.debug("Process file: {}", file.getOriginalFilename());
//copy 輸入流到服務(wù)器的c:\temp\younghare
FileUtils.copyInputStreamToFile(file.getInputStream(), new File("c:\\temp\\younghare\\", System.currentTimeMillis()+ file.getOriginalFilename()));
}
return "success";
}
@RequestMapping(value="/doUpload2", method=RequestMethod.POST)
public String doUploadFile2(MultipartHttpServletRequest multiRequest) throws IOException{
Iterator<String> filesNames = multiRequest.getFileNames();
while(filesNames.hasNext()){
String fileName =filesNames.next();
MultipartFile file = multiRequest.getFile(fileName);
if(!file.isEmpty()){
log.debug("Process file: {}", file.getOriginalFilename());
FileUtils.copyInputStreamToFile(file.getInputStream(), new File("c:\\temp\\younghare\\", System.currentTimeMillis()+ file.getOriginalFilename()));
}
}
return "success";
}
//直接返回json數(shù)據(jù)
//http://localhost:8080/courses/123
//jquery的異步動(dòng)態(tài)加載:http://localhost:8080/course_json.jsp?courseId=123
@RequestMapping(value="/{courseId}",method=RequestMethod.GET)
public @ResponseBody Course getCourseInJson(@PathVariable Integer courseId){
return courseService.getCoursebyId(courseId);
}
//SpringMVC對(duì)json的支持
//http://localhost:8080/courses/jsontype/123
@RequestMapping(value="/jsontype/{courseId}",method=RequestMethod.GET)
//ResponseEntity類可以可以把數(shù)據(jù)模型轉(zhuǎn)換成json格式
public ResponseEntity<Course> getCourseInJson2(@PathVariable Integer courseId){
Course course = courseService.getCoursebyId(courseId);
return new ResponseEntity<Course>(course, HttpStatus.OK);
}
}
出現(xiàn)問題解決:
IntelliJ IDEA :Error:(1, 1) java: 非法字符: '\ufeff'
運(yùn)行工程穗泵,并在瀏覽器上直接訪問CourseController控制器對(duì)應(yīng)的地址
地址如:
http://localhost:8080/courses/view?courseId=5
或
http://localhost:8080/courses/view2/123
運(yùn)行效果如下
添加的編輯url
http://localhost:8080/courses/admin?add
點(diǎn)擊提交后可以多看到日志輸出
碰到問題:沒有打印日志情況
log4j 設(shè)置日志輸出文件的路徑
文件上傳Spring提供了一個(gè)bean:CommonsMultipartResolver
SpringMVC內(nèi)置了文件上傳功能,我們只需通過一些配置谜疤,就可以使用Spring給我們暴露的接口實(shí)現(xiàn)文件上傳
需要apache的ommons-fileupload包支持
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
<!--200*1024*1024即200M resolveLazily屬性啟用是為了推遲文件解析佃延,以便捕獲文件大小異常
maxUploadSize:上傳文件大小限制
defaultEncoding:編碼
resolveLazily:是否延遲加載
-->
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="209715200" />
<property name="defaultEncoding" value="UTF-8" />
<property name="resolveLazily" value="true" />
</bean>
文件上傳的url
http://localhost:8080/courses/upload
SpringMVC對(duì)json的支持mvc-dispatcher-servlet.xml添加配置bean的ContentNegotiatingViewResolver類(后來發(fā)現(xiàn)這個(gè)配置已經(jīng)過時(shí)现诀,后面有問題描述及解決辦法)
<!-- 配置ViewResolver。 可以用多個(gè)ViewResolver履肃。 使用order屬性排序仔沿。 InternalResourceViewResolver放在最后。 -->
<bean
class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
<property name="order" value="1" />
<property name="mediaTypes">
<map>
<entry key="json" value="application/json" />
<entry key="xml" value="application/xml" />
<entry key="htm" value="text/html" />
</map>
</property>
<property name="defaultViews">
<list>
<!-- JSON View -->
<bean
class="org.springframework.web.servlet.view.json.MappingJackson2JsonView">
</bean>
</list>
</property>
<property name="ignoreAcceptHeader" value="true" />
</bean>
碰到問題
參考:
ContentNegotiationConfigurer (Spring Framework 4.3.2.RELEASE API)
https://docs.spring.io/autorepo/.../ContentNegotiationConfigurer.html
ignoreAcceptHeader(boolean) · Header strategy, On ... Whether to ignore requests with path extension that cannot be resolved to any media type. ... When favorPathExtension(boolean) is set, this propertydetermines whether to allow use of JAF (Java Activation Framework) to resolve a path extension to a specific ...
mediaTypes錯(cuò)誤(Bean property 'mediaTypes' is not writable or has an invalid setter method.)
修改對(duì)mvc-dispatcher-servlet.xml對(duì)json的支持(同時(shí)記得刪除錯(cuò)誤的配置)
<bean id="contentNegotiationManagerFactoryBean" class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean"
p:favorPathExtension="false" p:favorParameter="true" p:parameterName="format" p:ignoreAcceptHeader="true"
p:defaultContentType="application/json">
<property name="mediaTypes">
<props>
<prop key="json">application/json</prop>
<prop key="xml">application/xml</prop>
</props>
</property>
</bean>
<bean
class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean"
p:favorPathExtension="false" p:favorParameter="true"
p:parameterName="format" p:ignoreAcceptHeader="true"
p:defaultContentType="application/json">
<property name="mediaTypes">
<props>
<prop key="json">application/json</prop>
<prop key="xml">application/xml</prop>
</props>
</property>
</bean>
返回json的url
http://localhost:8080/courses/123
或
http://localhost:8080/courses/jsontype/123
jsp對(duì)后臺(tái)請(qǐng)求的異步處理course_json.jsp (采用到的是jquery庫中的ajax異步訪問技術(shù)尺棋,記得如果有更換jquery版本要在這個(gè)json中做引用的修改)
<script type="text/javascript"
<%--src="<%=request.getContextPath()%>/resources/js/jquery-1.11.3.min.js"></script>--%>
src="<%=request.getContextPath()%>/resources/js/jquery-3.3.1.min.js"></script>
</head>
jsp異步獲取json數(shù)據(jù)處理的url
http://localhost:8080/course_json.jsp?couseId=123
course_json.jsp 源碼
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Spring MVC json 返回異步處理</title>
<link rel="stylesheet"
href="<%=request.getContextPath()%>/resources/css/main.css"
type="text/css" />
<script type="text/javascript"
<%--src="<%=request.getContextPath()%>/resources/js/jquery-1.11.3.min.js"></script>--%>
src="<%=request.getContextPath()%>/resources/js/jquery-3.3.1.min.js"></script>
</head>
<script>
jQuery(function($){
var urlStr = "<%=request.getContextPath()%>/courses/<%=request.getParameter("courseId")%>";
//alert("Before Call:"+urlStr);
$.ajax({ //調(diào)用jQuery中的ajax
method: "GET", //才用get方式請(qǐng)求
url: urlStr, //ajax 訪問web后端的地址
success:function(data,status,jqXHR){//ajax拿到數(shù)據(jù)之后動(dòng)態(tài)加載dom的方式加載數(shù)據(jù)
//alert("Success:"+data);
var course = data;
var path = "<%=request.getContextPath()%>/";
$(".course-title").html(course.title);//動(dòng)態(tài)加載dom的方式加載數(shù)據(jù)
$(".course_video").attr("src", path+course.imgPath);
$("#learningNum").text(course.learningNum);
$("#duration").text(course.duration);
$("#levelDesc").text(course.levelDesc);
$(".course_shortdecription").html(course.descr);
var chapterList = course.chapterList;
var chapter;
for(var i = 0;i<chapterList.length;i++){
chapter = chapterList[i];
var liObj = $("li",$("#chapterTemplate")).clone();
$(".outline_name", liObj).text(chapter.title);
$(".outline_descr", liObj).text(chapter.descr);
liObj.appendTo("#couList");
}// ~ end for
}
}); // end ajax
});
</script>
<body>
<div id="main">
<div class="newcontainer" id="course_intro">
<div class="course-title"></div>
<div class="course_info">
<div class="course-embed l">
<div id="js-course-img" class="img-wrap">
<img width="600" height="340" alt=""
class="course_video" />
</div>
<div id="js-video-wrap" class="video" style="display: none">
<div class="video_box" id="js-video"></div>
</div>
</div>
<div class="course_state">
<ul>
<li><span>學(xué)習(xí)人數(shù)</span> <em id="learningNum"></em></li>
<li class="course_hour"><span>課程時(shí)長(zhǎng)</span> <em
class="ft-adjust"><span id="duration"></span>秒</em></li>
<li><span>課程難度</span> <em id="levelDesc"></em></li>
</ul>
</div>
</div>
<div class="course_list">
<div class="outline">
<h3 class="chapter_introduces">課程介紹</h3>
<div class="course_shortdecription"></div>
<h3 class="chapter_catalog">課程提綱</h3>
<ul id="couList">
</ul>
</div>
</div>
</div>
</div>
<div id="chapterTemplate" style="display:none">
<li class="clearfix open"><a href="#">
<div class="openicon"></div>
<div class="outline_list l">
<h5 class="outline_name"></h5>
<p class="outline_descr"></p>
</div>
</a></li>
</div>
</body>
</html>
所以工作全部完成后目錄結(jié)構(gòu)
下面把用到的代碼貼出來
代碼下載地址
https://yunpan.#/surl_yQXTvTU5vFb (提取碼:1a1c)
不錯(cuò)的源碼推薦:
Spring+SpringMvc+MyBatis+Redis基于SSM-EasyUI的權(quán)限管理系統(tǒng)