文章目录
- 核心:
- 这里建议创建的maven项目每次运行前clean 、package后再运行项目,否则可能会报以下错误:
- 图片上传
- 图片下载:
- 多文件上传
- 问题
- 绪论
因为是把之前分散写的博客组合,错漏指出敬请指出!
1Spring MVC核心组件:
(1) DispatcherServlet : 前端控制器,调度其他组件的执行,降低不同组件间的耦合性,为核心模块.
(2) Handler : 处理器,完成具体的业务逻辑,相当于Servlet或者Action.
(3) HandlerMapping : DispatcherServlet 通过HandlerMapping 将请求映射到对应的Handler.
(4) HandlerInterceptor : 处理器拦截器,如果需要进行拦截处理,可实现该接口.
(5) HandlerExecutionChain : 处理器执行链, 包括Handler和HandlerInterceptor (有一个默认的)
(6) HandlerAdapter : 处理器适配器,Handler执行业务方法前,需要进行一系列的操作,如表单数据验证、数据类型转换、将表单数据封装到pojo等.
(7) ModelAndView : 装载模型数据和视图信息,作为Handler的处理结果,返回给DispatcherServlet.
(8) ViewResolver : 视图解析器,DispatcherServlet 通过该解析器将逻辑视图解析为物理视图,渲染给客户端.
2.SpringMVC 工作流程:
(1) 客户端请求被DispatcherServlet 接收.
(2) 通过 Handler Mapping 映射到 Handler.
(3) 生成 Handler 和 HandlerInterceptor .
(4) Hander 和HanderInterceptor 以 HandlerExecutionCahie 形式返回给DispatcherServlet.
(5) DispatcherServlet 通过 HandlerAdapter 调用 Handler 的方法进行业务逻辑处理.
(6) 返回一个ModelAndView 对象给DispatcherServlet
(7) DispatcherServlet 将获取到的 ModelAndView 对象传给ViewResolver 视图解析器,将逻辑视图转换成物理视图.
(8) ViewResolver 返回一个View给 DispatcherServlet.
(9) DispatcherServlet 根据View 进行视图渲染,也就是将模型数据填入到视图中.
(10) DispatcherServlet 将渲染后的视图响应给客户端.
3.maven快速创建简单SpringMVC:
pom.xml 中引入依赖:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.2.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>5.2.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.2.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.2.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.2.3.RELEASE</version>
</dependency>
<!--springmvc核心依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.2.3.RELEASE</version>
</dependency>
<!--servlet依赖-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.2</version>
<scope>provided</scope>
</dependency>
4.在handler包下创建
package com.redocloud.handler;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* ClassName : HelloHandler
* package : com.redocloud.handler
* 功能描述:
*
* @Date : 2020/3/16 0016 12:08
* @Author : one world
*/
@Controller
public class HelloHandler {
@RequestMapping("/index")
public String index(){
System.out.println("执行index代码");
return "index";
}
}
5.web.xml中配置
<web-app>
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<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>/</url-pattern>
</servlet-mapping>
</web-app>
6.在springmvc.xml文件下配置:
<context:component-scan base-package="com.redocloud.handler"></context:component-scan>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--前缀-->
<property name="prefix" value="/"></property>
<!--后缀-->
<property name="suffix" value=".jsp"></property>
</bean>
7.配置tomcat运行测试
核心:
通过DispatcherServlet 调用其他组件,实现业务。主要有controller 调用业务方法Method,业务逻辑 和ViewResolver 视图解析器,将业务方法返回值解析为物理视图和模型数据,返回给客户端。
文章目录
- 核心:
- 这里建议创建的maven项目每次运行前clean 、package后再运行项目,否则可能会报以下错误:
- 图片上传
- 图片下载:
- 多文件上传
- 问题
- 绪论
1 创建 MyController 注解作用于类:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyController{
String value() default "";
}
2 创建 MyRequestMapping 注解,作用于类和方法
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyRequestMapping{
String value() default "";
}
3 创建 MyViewResolver视图解析器
package com.redocloud.test;
/**
* ClassName : MyViewResolver
* package : PACKAGE_NAME
* 功能描述:
*
* @Date : 2020/3/16 0016 19:49
* @Author : one world
*/
public class MyViewResolver {
private String prefix;
private String suffix;
public String getPrefix() {
return prefix;
}
public void setPrefix(String prefix) {
this.prefix = prefix;
}
public String getSuffix() {
return suffix;
}
public void setSuffix(String suffix) {
this.suffix = suffix;
}
public String jspMapping(String value){
return this.prefix+value+this.suffix;
}
}
4创建MyDispatcherServlet ,核心控制器,init 完成初始化工作,doPost来处理HTTP请求。
public class MyDispatcherServlet extends HttpServlet {
//模拟IOC 保存Controller的实例对象
private Map<String,Object> iocContainer = new HashMap<String,Object>();
//保存handler映射
private Map<String, Method> handlerMapping = new HashMap<String,Method>();
//自定义视图解析器
private MyViewResolver myViewResolver;
@Override
public void init(ServletConfig config) throws ServletException {
//扫描Controller ,创建实例对象,存入iocContainer
System.out.println("servlet初始化");
scanController(config);
//初始化handler映射
initHandlerMapping();
//加载视图解析器
loadViewResolver(config);
}
//扫描Controller
public void scanController(ServletConfig config){
SAXReader reader = new SAXReader();
try{
//解析springmvc.xml
String path = config.getServletContext().getRealPath("")+"\\WEB-INF\\classes\\"+config.getInitParameter("contextConfigLocation");
Document document = reader.read(path);
Element root = document.getRootElement();
Iterator iter = root.elementIterator();
while (iter.hasNext()){
Element ele = (Element)iter.next();
if(ele.getName().equals("component-scan")){
String packageName = ele.attributeValue("base-package");
//获取base-package包下的所有类名
List<String> list = getClassNames(packageName);
for(String str:list){
Class clazz = Class.forName(str);
//判断是否有MyController注解
if(clazz.isAnnotationPresent(MyController.class)){
//获取Controller中的MyRequsetMapping注解的Value
MyRequestMapping annotion = (MyRequestMapping)clazz.getAnnotation(MyRequestMapping.class);
String value = annotion.value().substring(1);
//controller实例对象存入到iocContainer
iocContainer.put(value,clazz.newInstance());
}
}
}
}
}catch (Exception e){
e.printStackTrace();
}
}
/**
* 获取包下的所有类名
* @param packageName
* @return
*/
public List<String> getClassNames(String packageName){
List<String> classNameList = new ArrayList<String>();
String packagePath = packageName.replace(".", "/");
ClassLoader loader = Thread.currentThread().getContextClassLoader();
URL url = loader.getResource(packagePath);
if(url != null){
File file = new File(url.getPath());
File[] childFiles = file.listFiles();
for(File childFile : childFiles){
String className = packageName+"."+childFile.getName().replace(".class", "");
classNameList.add(className);
}
}
return classNameList;
}
/**
* 初始化 handler 映射
*/
public void initHandlerMapping(){
for(String str:iocContainer.keySet()){
Class clazz = iocContainer.get(str).getClass();
Method[] methods = clazz.getMethods();
for (Method method : methods) {
//判断方式是否添加 MyRequestMapping 注解
if(method.isAnnotationPresent(MyRequestMapping.class)){
//获取 Method 中 MyRequestMapping 注解的 value
MyRequestMapping annotation = method.getAnnotation(MyRequestMapping.class);
String value = annotation.value().substring(1);
//method 存入 methodMapping
handlerMapping.put(value, method);
}
}
}
}
/**
* 加载自定义视图解析器
* @param config
*/
public void loadViewResolver(ServletConfig config){
SAXReader reader = new SAXReader();
try {
//解析 springmvc.xml
String path = config.getServletContext().getRealPath("")+"\\WEB-INF\\classes\\"+config.getInitParameter("contextConfigLocation");
Document document = reader.read(path);
Element root = document.getRootElement();
Iterator iter = root.elementIterator();
while(iter.hasNext()){
Element ele = (Element) iter.next();
if(ele.getName().equals("bean")){
String className = ele.attributeValue("class");
Class clazz = Class.forName(className);
Object obj = clazz.newInstance();
//获取 setter 方法
Method prefixMethod = clazz.getMethod("setPrefix", String.class);
Method suffixMethod = clazz.getMethod("setSuffix", String.class);
Iterator beanIter = ele.elementIterator();
//获取 property 值
Map<String,String> propertyMap = new HashMap<String,String>();
while(beanIter.hasNext()){
Element beanEle = (Element) beanIter.next();
String name = beanEle.attributeValue("name");
String value = beanEle.attributeValue("value");
propertyMap.put(name, value);
}
for(String str:propertyMap.keySet()){
//反射机制调用 setter 方法,完成赋值
if(str.equals("prefix")){
prefixMethod.invoke(obj, propertyMap.get(str));
}
if(str.equals("suffix")){
suffixMethod.invoke(obj, propertyMap.get(str));
}
}
myViewResolver = (MyViewResolver) obj;
}
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
// TODO Auto-generated method stub
this.doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
// TODO Auto-generated method stub
//获取请求
String handlerUri = req.getRequestURI().split("/")[2];
//获取 Controller 实例
Object obj = iocContainer.get(handlerUri);
String methodUri = req.getRequestURI().split("/")[3];
//获取业务方法
Method method = handlerMapping.get(methodUri);
try {
//反射机制调用业务方法
String value = (String) method.invoke(obj);
//视图解析器将逻辑视图转换为物理视图
String result = myViewResolver.jspMapping(value);
//页面跳转
req.getRequestDispatcher(result).forward(req, resp);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
### 5编写测试类
@MyController
@MyRequestMapping
public class TestController {
@MyRequestMapping(value = "/test")
public String test(){
System.out.println("执行test相关业务");
return "test";
}
}
6 修改web.xml 文件中的servlet 为自定义的MyDispactcherServlet和 spring.xml文件中的解析器为MyViewResolver,
spring.xml:
<?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/tool"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tool http://www.springframework.org/schema/tool/spring-tool.xsd">
<!--配置自动扫描-->
<!-- <context:component-scan base-package="com.redocloud.handler"></context:component-scan>-->
<context:component-scan base-package="com.redocloud.test"></context:component-scan>
<!--配置视图解析器-->
<!-- <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">-->
<!--配置自定义的视图解析器-->
<bean class="com.redocloud.test.MyViewResolver">
<!--前缀-->
<property name="prefix" value="/"></property>
<!--后缀-->
<property name="suffix" value=".jsp"></property>
</bean>
</beans>
```yaml
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<!-- <servlet>-->
<!-- <servlet-name>springmvc</servlet-name>-->
<!-- <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>-->
<!-- <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>/index</url-pattern>-->
<!-- </servlet-mapping>-->
<!-- <servlet-mapping>-->
<!-- <servlet-name>springmvc</servlet-name>-->
<!-- <url-pattern>/addUser</url-pattern>-->
<!-- </servlet-mapping>-->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>com.redocloud.test.MyDispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>springmvc.xml</param-value>
</init-param>
</servlet>
<!-- <servlet-mapping>-->
<!-- <servlet-name>springmvc</servlet-name>-->
<!-- <url-pattern>/index</url-pattern>-->
<!-- </servlet-mapping>-->
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!--字符过滤器,解决中文乱码问题-->
<filter>
<filter-name>encodingFilter</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>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
7 注意事项:
自定义注解时,spring.xml的路径会有些问题,我的解决方式是直接放在了webapp下面
启动tomcat 在浏览器输入:http://localhost:8080/mvc/controllerTest/test
在测试时,由于粗心除了以下错误:
由于忘记了在TestController类中的路径值,导致输入路径错误,导致以上异常。
1 @requestMapping:将URL请求和业务方法映射,用于控制器处
value:
@controller
@RequestMapping("/helloHandler")//相当于多了一个helloHandler访问路径,括号中内容可替换为(value = "hello")
public class HelloHandler{
@RequestMapping(value="hello")
public String hello(){
System.out.println("hello");
return "hello";
}
}
其访问路径为:…/项目名/helloHandler/hello
method:指定方法类型
@RequestMapping(value="/testMethod",method=RequestMethod.POST) //指明方法为POST方法访问
params: 指定参数和参数类型
@RequestMapping(value="test",params={"name","money=100"})//此时必须包含name和money两个参数时才能使用标注下的方法
参数绑定:
@RequestMapping(value = “paramsBind”)
//在业务方法定义时声明参数列表
public String paramsBind(@RequestParam("name") String name, @RequestParam("id") int id){//将String 类型的id自动转为int 型 具体的数据类型转换是通过HandlerAdapter完成的
System.out.print(name+id);
return "test";
}
RESTful的URL参数获取:
@RequestMapping(value="rest/{name}")
public String restTest(@PathVariable("name") String name){
System.out.print(name);
return "index";
}
路径为:../项目名/控制器名/rest/chen
chen为name参数
**#映射cookie**
Spring mvc获取Cookie的值
@RequestMapping("/cookieTest")
public String getCookie(@CookieValue(value="JSESSIONID"),String sessionId){
System.out.print(sessionId);
return "index";
}
2.使用POJO绑定参数
Spring MVC根据请求参数名和POJO属性名进行匹配,自动为该对象填充属性值,且支持属性级联
(1)创建实体Address 、 User 并进行级联设置
public class Address{
private int id;
private String name;
}
public class User{
private int id;
private String name;
private Address address;
}
(2)创建addUser.jsp
<form action="addUser" method="post">
编号:<input type="text" name="id"/><br/>
姓名:<input type="text" name="name"/><br/>
地址:<input type="text" name="address.name"/><br/>
<input type="submit" value="提交"/>
</form>
(3)业务方法:
@RequestMapping("/addUser")
public String getPOJO(User user){
System.out.print(user);
retturn "index";
}
(4)运行tomcat,
这里建议创建的maven项目每次运行前clean 、package后再运行项目,否则可能会报以下错误:
org.springframework.web.servlet.DispatcherServlet.noHandlerFound No mapping for…
中文乱码问题的解决:filter
约束文件需要修改为
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<web-app>
字符过滤器:
<filter>
<filter-name>encodingFilter</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>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
JSP页面的转发和重定向:
Spring MVC默认以转发的形式响应JSP也可以手动进行修改
@ResuestMapping(“名称”)
public String 方法名(){
return “forward:/index.jsp”;转发 浏览器地址不改变
或者
return “redirect:/index.jsp”;重定向 浏览器地址改变
}
#1.Spring MVC业务数据绑定,将数据绑定给pageContext、request、session和application:(常用的域对象是request 和session,pageContext 和 application通过获取原生Servlet的方式进行绑定)
首先添加业务数据,再由ViewResolver 来完成数据绑定,Spring MVC添加业务数据的方式如下:
Map:
//使用map进行数据绑定
@RequestMapping("/mapTest")
public String mapTest(Map<String, Object> map){
User user = new User();
user.setId(2);
user.setName("张三");
map.put("user",user);
return "index";
}
Model:
//使用Model进行数据绑定
@RequestMapping("/modelTest")
public String modelTest(Model model){
User user = new User();
user.setId(2);
user.setName("李四");
model.addAttribute("user",user);
return "index";
}
ModelAndView:
//ModelAndView使用的8种方式
@RequestMapping("modelAndViewTest")
public ModelAndView modelAndViewTest(){
ModelAndView modelAndView = new ModelAndView();
User user = new User();
user.setId(2);
user.setName("李四");
modelAndView.addObject("user",user);
//方式1
modelAndView.setViewName("index");
//方式2
View view = new InternalResourceView("/index.jsp");
modelAndView.setView(view);
return modelAndView;
}
//方式3
@RequestMapping("/modelAndViewTest1")
public ModelAndView modelAndViewTest1(){
ModelAndView modelAndView = new ModelAndView("index");
User user = new User();
user.setId(2);
user.setName("李四");
modelAndView.addObject("user",user);
return modelAndView;
}
//方式4
@RequestMapping("/modelAndViewTest2")
public ModelAndView modelAndViewTest4(){
View view = new InternalResourceView("/index.jsp");
ModelAndView modelAndView = new ModelAndView(view);
User user = new User();
user.setId(1);
user.setName("张三");
modelAndView.addObject("user", user);
return modelAndView;
}
//方式5
@RequestMapping("/modelAndViewTest3")
public ModelAndView modelAndViewTest5(){
Map<String,Object> map = new HashMap<String,Object>();
User user = new User();
user.setId(1);
user.setName("张三");
map.put("user", user);
ModelAndView modelAndView = new ModelAndView("index", map);
return modelAndView;
}
//方式6
@RequestMapping("/modelAndViewTest4")
public ModelAndView modelAndViewTest6(){
Map<String,Object> map = new HashMap<String,Object>();
User user = new User();
user.setId(1);
user.setName("张三");
map.put("user", user);
View view = new InternalResourceView("/index.jsp");
ModelAndView modelAndView = new ModelAndView(view, map);
return modelAndView;
}
//方式7
@RequestMapping("/modelAndViewTest5")
public ModelAndView modelAndViewTest7(){
User user = new User();
user.setId(1);
user.setName("张三");
ModelAndView modelAndView = new ModelAndView("index", "user", user);
return modelAndView;
}
//方式8
@RequestMapping("/modelAndViewTest6")
public ModelAndView modelAndViewTest8(){
User user = new User();
user.setId(1);
user.setName("张三");
View view = new InternalResourceView("/index.jsp");
ModelAndView modelAndView = new ModelAndView(view, "user", user);
return modelAndView;
}
@SessionAttributes:
//将业务数据绑定到request对象
@Controller
@SessionAttributes(value = {"username","password"})
//或者以(type={User.class,Address.class})这种方式
public class SessionHandler {
//存入
@RequestMapping("/testPut")
public void testPut(Model model){
model.addAttribute("username","张三");
model.addAttribute("password","322728");
System.out.println("保存到session");
}
//获取session内对象
@RequestMapping("/testGet")
public void testGet(ModelMap modelMap){
System.out.println("获取用户名和密码");
Object username = modelMap.get("username");
System.out.println(username);
Object password = modelMap.get("password");
System.out.println(password);
}
//清空session
@RequestMapping("/testClear")
public void testClear(SessionStatus sessionStatus){
sessionStatus.setComplete();//清空session
System.out.println("清空session");
}
}
在tomcat运行后,在浏览器中依次输入:
http://localhost:8080/mvc/testPut
http://localhost:8080/mvc/testGet
http://localhost:8080/mvc/testClear
http://localhost:8080/mvc/testGet
运行结果:
保存到session
获取用户名和密码
张三
322728
清空session
获取用户名和密码
null
null
和@ModelAttribute
//ModelAttribute 添加业务数据,执行本方法前,先执行@ModelAttribute注解的方法,该注解注解的方法,会在Spring MVC调用任意方法之前自动调用
@RequestMapping("/modelAttributeTest")
public String modelAttributeTest(){
return "index";
}
@ModelAttribute
public User getUser(){
User user = new User();
user.setId(1);
user.setName("张三");
return user;
}
//域对象中的数据都是以键值对 (key-value) 的形式保存的,那么此时的 key 默认取业务数据对应类的首字母小写之后的类名,如 User 类首字母小写之后为 "user",因此 JSP 页面中,可以直接通过 "user" 取值。
//若 getUser 没有返回值,则必须手动在该方法中填充业务数据,使用 Map 或者 Model 均可。
@ModelAttribute
public void getUser2(Map<String,Object> map){
User user = new User();
user.setId(1);
user.setName("张三");
map.put("user", user);
}
#1 RESTful :互联网软件架构模型,完成不同终端的数据访问交互,四种常规请求类型:
1)GET :获取资源
2)POST:创建资源
3)PUT:修改资源
4)DELETE:删除资源
注意:传统Web开发,form表单只支持GET和POST请求,可以通过添加HiddenHttpMethodFilter过滤器,将请求类型转化成PUT和DELETE。
HiddenHttpMethodFilter实现:识别参数中是否含有_method,如有根据其值判断是哪种操作后完成请求类型转换。
1)在隐藏域中添加参数_method
<form action="httpPut" method="post">
<input type="hidden" name="_method" value="PUT/DELETE"/>
<input type="submit" value="修改"/>
</form>
2)在web.xml中配置HiddenHttpMethodFilter(注意这里如果配置字符过滤器的话,需要将字符过滤器放在所有过滤器之前,如下)
<!--字符过滤器,解决中文乱码问题-->
<filter>
<filter-name>encodingFilter</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>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter>
<filter-name>HiddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>HiddenHttpMethodFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
3)以简单商品管理模块演示
public class Goods{
private int id;
private String name;
private Double price;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Double getPrice() {
return price;
}
public void setPrice(Double price) {
this.price = price;
}
@Override
public String toString() {
return "Goods{" +
"id=" + id +
", name='" + name + '\'' +
", price=" + price +
'}';
}
}
Dao存储数据和处理
@Repository
public class GoodsDao {
private Map<Integer,Goods> goodses = new HashMap<Integer,Goods>();
public void add(Goods goods){
goodses.put(goods.getId(),goods);
}
public Collection<Goods> getAll(){
return goodses.values();
}
public Goods getById(int id){
return goodses.get(id);
}
public void update(Goods goods){
goodses.put(goods.getId(),goods);
}
public void deleteById(int id){
goodses.remove(id);
}
}
创建GoodsController
@PostMapping、@GetMapping、@PutMapping、@DeleteMapping 分别用来映射 Post、Get、Put、Delete 请求。
@Controller
public class GoodsController {
@Autowired
private GoodsDao goodsDao;
@PostMapping(value = "/add")
public String add(Goods goods){
goodsDao.add(goods);
return "redirect:/getAll";
}
@GetMapping(value = "/getAll")
public ModelAndView getAll(){
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("index");//跳转到index.jsp
modelAndView.addObject("goodses",goodsDao.getAll());
return modelAndView;
}
@GetMapping(value = "/getById/{id}")
public ModelAndView getById(@PathVariable(value = "id") int id){
ModelAndView modelAndView = new ModelAndView();
//这里只能跳转jsp,InternalResourceViewResolver这个解析器只能解析jsp文件,而不能解析html,设置视图名就是视图名.jsp文件页面
modelAndView.setViewName("edit");//跳转到edit.jsp页面
modelAndView.addObject("goods",goodsDao.getById(id));
return modelAndView;
}
@PutMapping(value = "/update")
public String update(Goods goods){
goodsDao.update(goods);
return "redirect:/getAll";
}
@DeleteMapping(value = "/delete/{id}")
public String delete(@PathVariable(value = "id") int id){
goodsDao.deleteById(id);
return "redirect:/getAll";
}
}
jsp页面
addGoods.jsp
<form action="add" method="post">
请输入id:<input type="text" name="id"/><br/>
请输入name:<input type="text" name="name"/><br/>
请输入price:<input type="text" name="price"/><br/>
<input type="submit" value="提交"/>
</form>
index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
${goodses}
</body>
</html>
此处省略edit页面…
图片上传
1 引入依赖:
使用Apache fileupload 组件,在pom.xml中引入fileupload组件依赖:
<!--上传组件-->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
引入JSTL依赖,用于展示上传的图片:
<!--jstl组件-->
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
</dependency>
2 jsp页面:
input 的 type 设置为 file
form 表单的 method 设置为 post(get 请求只会将文件名传给后台)
form 表单的 enctype 设置为 multipart/form-data,以二进制的形式传输数据
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core_rt" %>
<%
String path = request.getContextPath();
System.out.println("页面上的path"+path);
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
System.out.println("basePath:"+basePath);
%>
<html>
<head>
<title>Title</title>
</head>
<body>
<form action="upload" method="post" enctype="multipart/form-data">
<input type="file" name="img">
<input type="submit" name="提交">
</form><br />
<c:if test="${filePath!=null }">
<h1>上传的图片</h1><br />
<h1>${filePath}</h1>
<img width="300px" src="${basePath}${filePath}"/>
</c:if>
</body>
</html>
3 springmvc.xml文件
配置springmvc.xml中的CommonsMultipartResolver
<!--扫描这个包下-->
<context:component-scan base-package="com.redocloud.handler"></context:component-scan>
<!--配置视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--前缀-->
<property name="prefix" value="/"></property>
<!--后缀-->
<property name="suffix" value=".jsp"></property>
</bean>
<!-- 配置 CommonsMultipartResolver bean,id 必须是 multipartResolver -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 处理文件名中文乱码 -->
<property name="defaultEncoding" value="utf-8"/>
<!-- 设置多文件上传,总大小上限,不设置默认没有限制,单位为字节,1M=1*1024*1024 -->
**<!--如果上传的文件大小大于以下设定的值,会出现不能上传的情况-->**
<property name="maxUploadSize" value="10485760"/>
<!-- 设置每个上传文件的大小上限 -->
<property name="maxUploadSizePerFile" value="10485760"/>
</bean>
<!-- 设置异常解析器,当上传失败时,跳转到error.jsp -->
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="defaultErrorView" value="error.jsp"/>
</bean>
<!--默认的mvc注解映射的支持-->
<mvc:annotation-driven/>
<!--这里的这个配置很关键,不加的话,上传的文件在显示时会读取不到-->
<!--使用DispatcherServlet拦截了所有的请求,导致无法获取静态资源-->
<!--SpringMvc默认资源处理:-->
<!--1.通过在springmvc.xml文件中加入-->
<mvc:default-servlet-handler/>
<!--表示将对静态资源的处理由Spring mvc框架交回Web应用处理-->
<!-- 2.使用mvc:resources,配置映射路径如下-->
<mvc:resources mapping="/file/**" location="/file/"/>
4 编写controller
业务方法,使用 MultipartFile 对象作为参数,接收前端发送过来的文件,并完成上传操作。
@RequestMapping(value="/upload", method = RequestMethod.POST)
public String upload(@RequestParam(value="img")MultipartFile img, HttpServletRequest request)
throws Exception {
//getSize() 方法获取文件的大小来判断是否有上传文件
if (img.getSize() > 0) {
System.out.println("要开始传了");
//获取保存上传文件的 file 文件夹绝对路径,就是webapp下我创建的file文件夹位置
String path = request.getSession().getServletContext().getRealPath("file");
//获取上传文件名
System.out.println("path:"+path);
String fileName = img.getOriginalFilename();
//没有该文件时,创建
if(!new File(path).exists()){
System.out.println("没有该文件,创建中...");
new File(path).mkdir();
}
//获取文件类型
String contentType = img.getContentType();
//获取源文件的扩展名
String fileType ="."+ contentType.substring(contentType.indexOf("/")+1);
//创建修改文件名,防止文件重复时覆盖
String newFilename = UUID.randomUUID().toString().replace("-","")+fileType;
System.out.println("fileName:"+newFilename);
//-----创建文件路径
File file = new File(path, newFilename);
System.out.println("----file:"+file);
img.transferTo(file);
//保存上传之后的文件路径,传给jsp
request.setAttribute("filePath", "file/"+newFilename);
request.setAttribute("filename",newFilename);
return "upload";
}
return "error";
}
5 文件结构
这里的
这里的file将其作为静态资源加载的文件夹。
完成后使用mvn打包后运行
结果:
还是有关于项目下路径的很多疑惑!
图片下载:
1 jsp页面 这里简单的用a标签来请求
<%--
Created by IntelliJ IDEA.
User: Administrator
Date: 2020/4/7 0007
Time: 13:22
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core_rt" %>
<%
String path = request.getContextPath();
System.out.println("页面上的path"+path);
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
System.out.println("basePath:"+basePath);
%>
<html>
<head>
<title>Title</title>
</head>
<body>
<form action="upload" method="post" enctype="multipart/form-data">
<input type="file" name="img">
<input type="submit" name="提交">
</form><br />
<c:if test="${filePath!=null }">
<h1>上传的图片</h1><br />
<h1>${filePath}</h1>
<img width="300px" src="${basePath}${filePath}"/>
<h1>${filename}</h1>
<a href="download?filename=${filename}">下载</a>
</c:if>
</body>
</html>
为了简便,我就直接在刚刚上传图片的页面上,添加了
<a href="download?filename=${filename}">下载</a>
2 controller:
/**
使用这种方式传参时,会出现跳转到download.jsp 原因暂不清楚
@RequestMapping("/download")
public void test(@RequestParam(value="filename") String fileName){
System.out.println(fileName);
}
但是如果使用request来获取a标签所传的值,则可以正常跳转到download,并获取文件地址
**/
@RequestMapping(value = "/download")
public void download(HttpServletRequest request,HttpServletResponse response){
String fileName = request.getParameter("filename");
System.out.println(fileName);
if(fileName!=null){
//获取 file 绝对路径
String realPath = request.getSession().getServletContext().getRealPath("file/");
System.out.println("realPath:"+realPath);
File file = new File(realPath,fileName);
OutputStream out = null;
if(file.exists()){
//设置下载完后不打开文件
response.setContentType("application/force-download");
//设置文件名
response.setHeader("Content-Disposition", "attachment;filename="+fileName);
try {
out = response.getOutputStream();
out.write(FileUtils.readFileToByteArray(file));
out.flush();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
if(out != null){
try {
out.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
}
多文件上传
和单文件上传一样
1JSP
<%--
Created by IntelliJ IDEA.
User: Administrator
Date: 2020/4/9 0009
Time: 0:53
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core_rt" %>
<%
String path = request.getContextPath();
System.out.println("页面上的path"+path);
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
System.out.println("basePath:"+basePath);
%>
<html>
<head>
<title>Title</title>
</head>
<body>
<form action="uploads" method="post" enctype="multipart/form-data">
file1:<input type="file" name="imgs"><br />
file2:<input type="file" name="imgs"><br />
file3:<input type="file" name="imgs"><br />
<input type="submit" name="提交">
</form>
<c:if test="${filePaths!=null }">
<h1>上传的图片</h1><br />
<c:forEach items="${filePaths }" var="filePath">
<img width="300px" src="<%=basePath %>${filePath}"/>
</c:forEach>
</c:if>
</body>
</html>
2 编写controller
@RequestMapping(value="/uploads", method = RequestMethod.POST)
public String uploads(@RequestParam MultipartFile[] imgs, HttpServletRequest request)
throws Exception {
//创建集合,保存上传后的文件路径
List<String> filePaths = new ArrayList<String>();
for (MultipartFile img : imgs) {
if (img.getSize() > 0) {
String path = request.getSession().getServletContext().getRealPath("file");
String fileName = img.getOriginalFilename();
if(!new File(path).exists()){
System.out.println("没有该文件,创建中...");
new File(path).mkdir();
}
//获取文件类型
String contentType = img.getContentType();
//获取文件扩展名
String fileType = contentType.substring(contentType.indexOf("/")+1);
//新文件名
String newFilename = UUID.randomUUID().toString().replace("-","");
File file = new File(path, newFilename);
filePaths.add("file/"+newFilename);
img.transferTo(file);
}
}
request.setAttribute("filePaths", filePaths);
return "uploads";
}
问题
1 传参问题
`<a href="download?filename=${filename}">下载</a>`
@RequestMapping("/download")
public void test(@RequestParam(value="filename") String fileName){
System.out.println(fileName);
}
这样发送请求和传参时,有问题
2 在关闭tomcat后,所上传的文件全部消失
第二个问题原因:每次存的文件都是在项目下,重新部署tomcat时,会清楚非系统性或无关文件。
主要原因:每次重新打开tomcat相当于将原来代码重新部署,就会覆盖上传了照片的包。
解决方案:在看了多个博主的方案后:
在server.xml文件中添加:
<Context docBase="D:\file" path="/file" reloadable="true"/>
这个我没有试过…
1.背景:
HTTP请求传输的参数都是String类型,因此需要用到参数的类型转换,通过Spring MVC的HandlerAdapter组件在执行Handler的业务方法前,完成参数的绑定。
@RequestMapping(value="/getType")
@ResponseBody
public String getType(int id){
return "id"+id;
}
当请求中不带参数时:500错误,id为int型不能为null;
当传入的参数为非数字符串时:400错误,类型String不能转换为int
当参数类型使用包装类时,可以为null,String 仍然不能转换成包装类型
2. 对参数列表添加@RequestParam 注解,对参数进行设置
@RequestMapping(value="/getType")
@ResponseBody
public String getType(@RequestParam(value="id",required=false,defaultValue="1") Integer id){
return "id"+id;
}
引入
http表单中的请求参数都是String类型,业务方法中的参数为String 或者int 类型时,HandlerAdapter可以自动完成数据转换,但是如果是其他类型,如Date,无法从String自动转换成Date类型,所以需要通过实现Converter接口来辅助Spring MVC定制数据类型转换。
1 自创建DateConverter类实现springframework下的Converter接口:
public class DateConverter implements Converter<String,Date>{
private String pattern;
public DateConverter(String pattern){
this.pattern = pattern;
}
public Date convert(String source) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(pattern);
try {
return simpleDateFormat.parse(source);
} catch (ParseException e) {
e.printStackTrace();
}
return null;
}
}
2配置spring.xml文件
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<list>
<bean class="com.southwind.utils.DateConverter">
<!-- 调用有参构造函数创建 bean -->
<constructor-arg type="java.lang.String" value="yyyy-MM-dd"/>
</bean>
</list>
</property>
</bean>
<mvc:annotation-driven conversion-service="conversionService"/>
3创建add.jsp
<form action="dateConverterTest" method="post">
请输入日期:<input type="text" name="date"/><font style="font-size:13px">(yyyy-MM-dd)</font><br/>
<input type="submit" value="提交"/>
</form>
4创建Controller
@RequestMapping(value="/dateConverterTest")
@ResponseBody
public String dateConverterTest(Date date){
return date.toString();
}
绪论
数据校验保证数据安全,Spring MVC中主要有两种方式:
(1) Validator接口
具体的数据校验规则要开发者手动设置,相对复杂
以简单的学生登录为例:
Student类
public class Student {
private String name;
private String password;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", password='" + password + '\'' +
'}';
}
引入依赖:
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.1.0.Final</version>
</dependency>
实现Validaor接口:
/**
* ClassName : StudentValidation
* package : com.redocloud.validation
* 功能描述:
*
* @Date : 2020/4/9 0009 16:01
* @Author : one world
*/
public class StudentValidator implements Validator {
@Override
public boolean supports(Class<?> clazz) {
// TODO Auto-generated method stub
return Student.class.equals(clazz);
}
@Override
public void validate(Object target, Errors errors) {
// TODO Auto-generated method stub
ValidationUtils.rejectIfEmpty(errors, "name", null, "姓名不能为空");
ValidationUtils.rejectIfEmpty(errors, "password", null, "密码不能为空");
}
}
controller:
/**
* ClassName : ValidationController
* package : com.redocloud.handler
* 功能描述:
*
* @Date : 2020/4/9 0009 13:52
* @Author : one world
*/
@Controller
public class ValidationController {
@InitBinder
//必须初始化,因为我忘记了这个找了很久的原因
public void initBinder(DataBinder binder){
binder.setValidator(new StudentValidator());
}
@GetMapping(value = "/login")
public String login(Model model){
model.addAttribute(new Student());
System.out.println("Model");
return "login";
}
@PostMapping(value = "/login")
public String login(@Validated Student student,
BindingResult bindingResult){
if(bindingResult.hasErrors()){
return "login";
}
return "success";
}
}
jsp文件
<%--
Created by IntelliJ IDEA.
User: Administrator
Date: 2020/4/9 0009
Time: 13:59
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page isELIgnored="false" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<jsp:useBean id="student" class="com.redocloud.entity.Student" scope="request"/>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>学生登录</h1>
<form:form modelAttribute="student" action="login" method="post">
学生姓名:<form:input path="name" /><form:errors path="name"/><br/>
学生密码:<form:password path="password" /><form:errors path="password"/><br/>
<input type="submit" value="提交"/>
</form:form>
</body>
</html>