转载请注明出处:
牵手生活--头条新闻:笔记是整理思路方式,分享是一个美德,牵手是我的生活方式
牵手生活--简书:笔记是整理思路方式,分享是一个美德,牵手是我的生活方式
Postman 使用方法详解
JavaWeb项目放弃jsp?为什么要前后端解耦?为什么要前后端分离?
注:此文承接上一文:Spring MVC -Hello World(环境搭建)下面开始我们的工作
感谢慕课网提供的免费视频,感谢Arthur讲师精彩讲解
web.xml修改对el表达式的支持
<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表达式被关闭,注意web-app的支持版本
关于web.xml中<web-app>报错解决方案
web.xml添加Spring容器的声明
Spring MVC -Hello World搭建并不需要Spring的容器。而我们现在要开始真正搭建Spring MVC,所以我们需要在web.xml中添加Spring的声明
- ContextLoaderListener
- 配置信息
-
applicationContext.xml
<!-- Spring应用上下文, 理解层次化的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的上下文层级说明:
WebApplicaiton Context(s):我们的跟,是有contextLoader加载的形成的上下文,他为我们所以应用提供了公共所使用的一些组件、一些服务;有service层、DAO层等等。这些服务应当是给所以服务所共享的,他一应该被极限在某个DispatcherServlet上下文之中。
WebApplicaiton Context:
DispatcherServlet:可能有多个DispatcherServlet,用不同类型的DispatcherServlet分发不同的请求。在web.xml配置文件中对应的<servlet-maping>中的<url-pattern>用于拦截不同来源的url划分依据,用一个DispatcherServlet来拦截web app根下的请求。
创建Spring上下文相关文件:applicaitonContext.xml配置文件
- 启动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">
<!--启动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使用, 提供其相关的Spring MVC配置
<context:annotation-config />部分:启用Spring基于annotation的DI, 使用户可以在Spring MVC中使用Spring的强大功能。 激活 @Required ,@Autowired,JSR 250's @PostConstruct, @PreDestroy and @Resource 等标注
<context:component-scan>部分:告诉DispatcherServlet上下文, 只管理(扫描)@Controller类型的bean即可, 忽略其他型的bean,不需要去管理service,service交给我们的spring上下文去管理。
HandlerMapping:HandlerMapping, 无需配置, Spring MVC有很强大的默认启动,如果我们不进行任何配置,他将启用我们的默认配置。默认配置会启动DefaultAnnotationHandlerMapping,这个类就可以为我们去解析我们注解的 annotation-driven HandlerMapping。
<mvc:annotation-driven />:这配置扩展了我么的驱动注解,他可以进一步的将我么请求参数绑定到控制器参数 ;也就是url中的查询的某个变量,可以直接映射到Controller中某个方法的输入参数。
ViewResolver bean的配置:dispatcher-servlet中可以依次的配置多个ViewResolver,他们之间可以使用order属性进行排序,唯一注意的是InternalResourceViewResolver必须放在最后,因为他必定会返回一个对象
-
静态资源处理,如果没有这些配置我们将无法获得静态资源
mvc:resources :这部分就是我们要添加的,如果没有这些配置,我就讲无法获得我们的的静态资源,比如: css, js, imgs -。比如resources及时把resources映射到我们本地目录的resources目录下。
修改mvc-dispatcher-servlet.mxl配置
添加到mvc-dispatcher-servlet.xml代码
注意:目录项目资源名称的resources也,本地映射的resources目录名称要与配置中保持一致
<!-- 静态资源处理, css, js, imgs -->
<!--这个在hello world项目中没有 后面的location="/resources/" 通常用于存放css、js、image-->
<mvc:resources mapping="/resources/**" location="/resources/" />
修复日志部分的错误-创建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详细配置及与slf4j联合使用教程
完成上述的工作,说明的的Spring MVC环境才真正搭建起来,下面开始Spring MVC 方面的真正开放运用。
SpringMVC Controller开发基础设施Model、service、展示的jsp、其他资源(css、image、js)、Controller
先看一下目录结构
关注service的spring注解
Spring MVC controller用到的Model
- Chapter.java (章节)
- Course.java( 课程)
Spring MVC controller用到的service(接口&接口实现)
- Course mode相关的服务类CourseService(没用用到数据库,使用硬代码的方式完成课程的组装,在下篇中将介绍如何接入mysql数据库)
CourseService.java(接口)
CourseServiceImpl.java(接口实现)
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这时一个Controller,会被dispatcher-servlet所管理,并且依赖注入
@Controller
//类级别的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)
//方法内的@RequestParam注释用来表示url请求的参数
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";
}
//传统的HttpServletRequest参数方式处理
//本方法将处理 /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参数级别的标签实现页面对象与
public String doSave(@ModelAttribute Course course){
log.debug("Info of Course:");
log.debug(ReflectionToStringBuilder.toString(course));
//在此进行业务操作,比如数据库持久化
course.setCourseId(123);
//重定向自己其他方法的url
return "redirect:view2/"+course.getCourseId();
}
//SpringMVC内置了文件上传功能,我们只需通过一些配置,就可以使用Spring给我们暴露的接口实现文件上传
//上传操作
@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提供的一个文件上传类;@RequestParam指定文件上传input 的name=file
public String doUploadFile(@RequestParam("file") MultipartFile file) throws IOException{
if(!file.isEmpty()){
log.debug("Process file: {}", file.getOriginalFilename());
//copy 输入流到服务器的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数据
//http://localhost:8080/courses/123
//jquery的异步动态加载: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对json的支持
//http://localhost:8080/courses/jsontype/123
@RequestMapping(value="/jsontype/{courseId}",method=RequestMethod.GET)
//ResponseEntity类可以可以把数据模型转换成json格式
public ResponseEntity<Course> getCourseInJson2(@PathVariable Integer courseId){
Course course = courseService.getCoursebyId(courseId);
return new ResponseEntity<Course>(course, HttpStatus.OK);
}
}
出现问题解决:
IntelliJ IDEA :Error:(1, 1) java: 非法字符: '\ufeff'
运行工程,并在浏览器上直接访问CourseController控制器对应的地址
地址如:
http://localhost:8080/courses/view?courseId=5
或
http://localhost:8080/courses/view2/123
运行效果如下
添加的编辑url
http://localhost:8080/courses/admin?add
点击提交后可以多看到日志输出
碰到问题:没有打印日志情况
log4j 设置日志输出文件的路径
文件上传Spring提供了一个bean:CommonsMultipartResolver
SpringMVC内置了文件上传功能,我们只需通过一些配置,就可以使用Spring给我们暴露的接口实现文件上传
需要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对json的支持mvc-dispatcher-servlet.xml添加配置bean的ContentNegotiatingViewResolver类(后来发现这个配置已经过时,后面有问题描述及解决办法)
<!-- 配置ViewResolver。 可以用多个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错误(Bean property 'mediaTypes' is not writable or has an invalid setter method.)
修改对mvc-dispatcher-servlet.xml对json的支持(同时记得删除错误的配置)
<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对后台请求的异步处理course_json.jsp (采用到的是jquery库中的ajax异步访问技术,记得如果有更换jquery版本要在这个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数据处理的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({ //调用jQuery中的ajax
method: "GET", //才用get方式请求
url: urlStr, //ajax 访问web后端的地址
success:function(data,status,jqXHR){//ajax拿到数据之后动态加载dom的方式加载数据
//alert("Success:"+data);
var course = data;
var path = "<%=request.getContextPath()%>/";
$(".course-title").html(course.title);//动态加载dom的方式加载数据
$(".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>学习人数</span> <em id="learningNum"></em></li>
<li class="course_hour"><span>课程时长</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>
所以工作全部完成后目录结构
下面把用到的代码贴出来
代码下载地址
https://yunpan.360.cn/surl_yQXTvTU5vFb (提取码:1a1c)