第二章 spring mvc
1. 回顾spring mvc几个重要组件
- 前端控制器DispatcherServlet:spring mvc的一个控制单元
- 处理器映射器HandlerMapping:使用url地址查找处理器
- 处理器适配器HandlerAdapter:用于执行处理器
- 处理器(控制器):处理业务逻辑
- 视图解析器ViewResolver:解析逻辑视图,创建视图对象
- 拦截器HandlerInterceptor
- 异常解析器
- 文件上传的解析器
2. spring mvc 执行流程
- 客户端发送请求到DispatcherServlet,DispatcherServlet请求处理器映射器查找处理器;
- 处理器映射器查找到处理器之后,返回一个处理器执行链给DispatcherServlet;
- DispatcherServlet请求处理器适配器执行处理器;
- 处理器适配器执行处理器,处理器执行完成之后,返回ModelAndView给处理器适配器;
- 处理器适配器把ModelAndView返回给DispatcherServlet;
- DispatcherServlet请求视图解析器解析ModelAndView;
- 视图解析器解析完成之后,返回一个View视图对象给DispatcherServlet
- DispatcherServlet使用模型数据和View视图对象渲染页面,响应客户端请求;
3. 创建hello工程
3.1 maven 依赖
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
3.2 在web.xml中配置前端控制器
不指定spring-mvc容器的话,容器的默认名字为 [servlet-name]-servlet.xml ,此处就应该为 et1912-servlet.xml ,默认存放在webapp/WEB-INF目录下
<!-- 配置前端控制器DispatcherServlet -->
<servlet>
<servlet-name>et1912</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>et1912</servlet-name>
<!-- 拦截所有以.action结尾的请求 -->
<url-pattern>*.action</url-pattern>
</servlet-mapping>
3.3 配置spring mvc容器
<!-- 1. 配置处理器映射器: 查找处理器 -->
<!-- BeanNameUrlHandlerMapping: 使用bean的name属性作为url查找处理器 -->
<bean
class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping" />
<!-- 2. 配置处理器适配器 -->
<!-- SimpleControllerHandlerAdapter: 要求处理器必须实现Controller接口 -->
<bean
class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter" />
<!-- 3. 配置视图解析器 -->
<!-- InternalResourceViewResolver: 处理jsp -->
<bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver"></bean>
<!-- 4. 开发处理器,并配置处理器 -->
<bean name="/hello.action"
class="com.etoak.controller.HelloController" />
3.4 控制器
package com.etoak.controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
public class HelloController implements Controller {
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
String name = request.getParameter("name");
System.out.println("param name -" + name);
request.setAttribute("result", "Hello \t" + name);
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("/hello.jsp");
return modelAndView;
}
}
4.第二组映射器和适配器
SimpleUrlHandlerMapping
HttpRequestHandlerAdapter:要求处理器实现HttpRequestHandler接口
4.1 配置web.xml
此处指定了spring-mvc的容器位置,通过
<init-param>
标签指定springmvc容器的位置
<param-name>
:contextConfgiLocation,一定不能写错classpath:表示从包的统计结构去寻找,按照maven的协议,就是放在resources下的文件
<load-on-startup>
:设置的数字越小启动的时间约早,数值为1-5,如果不设置此参数,则第一次请求到到大的时候加载,当出现异常范围如负数等,默认设置为0
url-pattern
:
- *.action *.do
表示拦截以特定格式结尾的请求- /*
拦截所有的请求,包括静态资源 会转发到Dispatcher去寻找相应的方法,就会找不到404- /
同样是拦截所有的请求,但是会过滤掉静态资源
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<servlet>
<servlet-name>et</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>et</servlet-name>
<!-- -->
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
4.2 配置springmvc容器
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<!-- 1.配置处理器映射器 -->
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<!-- key是url地址 value 处理器bean对应的id属性 -->
<prop key="/hello">helloController</prop>
</props>
</property>
</bean>
<!-- 2.配置处理器适配器 -->
<bean class="org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter" />
<!-- 3.配置视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"></bean>
<!-- 4.开发处理器,配置处理器 -->
<bean id="helloController" class="com.etoak.controller.HelloController"/>
</beans>
4.3 控制器
package com.etoak.controller;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.HttpRequestHandler;
public class HelloController implements HttpRequestHandler {
public void handleRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//接受前端请求
String name = request.getParameter("name");
System.out.println("param name " + name);
//向request域赋值
request.setAttribute("result", "Hello " + name);
//使用servlet的请求转发回到页面
request.getRequestDispatcher("/hello.jsp").forward(request, response);
}
}
5. 第三组映射器和适配器
RequestMappingHandlerMapping
RequestMappingHandlerAdapter
特点
- 必须配对使用
- 处理器不需要事先任何接口
- 使用@RequestMapping注解映射 url 到处理器方法上
第三种的web.xml与第二种一样
5.1配置springmvc容器
此处配置的视图解析器中的 prefix和suffix 会给返回值添加上前缀和后缀, 所以页面需要在 webapp/pages/ 目录下,如果需要hello.jsp接手值,则直接返回hello即可,容器会自动拼接上/pages/和.jsp。
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<!-- 1.配置处理器映射器 -->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping" />
<!-- 2.配置处理器适配器 -->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter" />
<!-- 3.配置视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/pages/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
<!-- 4.开发处理器,配置处理器 -->
<bean class="com.etoak.controller.HelloController"></bean>
</beans>
5.2 控制器实现类
package com.etoak.controller;
import javax.servlet.http.HttpServletRequest;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
@RequestMapping("/hello")
public class HelloController {
@RequestMapping("/index")
public ModelAndView index(HttpServletRequest request) {
//获取前端请求参数
String name = request.getParameter("name");
ModelAndView modelAndView = new ModelAndView();
//向request传值
modelAndView.addObject("result","Hello " + name);
//设置页面名称
modelAndView.setViewName("index");
return modelAndView;
}
}
6. 容器配置
6.1 配置容器
- 可以代替 RequestMapppingHandlerMapping 和 RequestMappingHandlerAdapter
- 默认注册很多参数转换器(包括json转换器)
- 默认没有java.util.Date的转换器,需要自己手动书写日期类型的转换器,详见第十二章
<?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-4.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.2.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.2.xsd">
<!-- 开启包扫描 -->
<context:component-scan base-package="com.etoak">
<!-- 指定需要扫描的注解 -->
<context:include-filter type="annotation"
expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!--
1. 可以代替 RequestMapppingHandlerMapping 和 RequestMappingHandlerAdapter
2. 默认注册很多参数转换器
3. 包括json转换器
-->
<mvc:annotation-driven></mvc:annotation-driven>
<!-- 放行静态文件 把静态文件(js css 图片等)交给servlet容器处理-->
<mvc:default-servlet-handler/>
<!-- 视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/pages/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
</beans>
6.2 控制器
注意:
@RestController
和@Controller
注解的区别:
@ResetController
相当于@Controller
和@ResponseBody
一起使用的结果
package com.etoak.controller;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import com.etoak.bean.Student;
@Controller
@RequestMapping("/student")
public class StudentController {
@RequestMapping("/getById")
public String getById(
@RequestParam(required=false,defaultValue="1") int id,Map<String,Object> requestMap) {
Student stu = new Student();
stu.setId(id);
stu.setName("张三");
stu.setAge(20);
stu.setGender("男");
//把student放在map中
requestMap.put("student",stu);
//返回页面
return "student/student";
}
@RequestMapping("/getList")
public String getList(Map<String,Object> resultMap) {
List<Student> studentList = new ArrayList<>();
studentList.add(new Student(1,"zs",21));
studentList.add(new Student(2,"lisi",21));
studentList.add(new Student(3,"wangwu",21));
//像request域传值
resultMap.put("studentList",studentList);
return "forward:/student/getById";
}
}
6.3 spring 容器与 spring mvc容器
为了防止容器之间互相影响,通常将于web相关的内容放置在mvc 容器中,与web无关的或者是与其他框架整合的东西放置在spring容器中
- 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">
<!--配置spring容器,放在包路径下,也就是maven工程的resources目录下-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-root.xml</param-value>
</context-param>
<!--解析前端编码为utf-8-->
<filter>
<filter-name>encoding</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>encoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 前端控制器
springmvc 容器
由DispatcherServlet分发前端的请求
-->
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
<!--设置的数值越小启动的越早,通常为1-5 设置为1则tomcat启动时就启动mvc容器-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<!--拦截所有的请求-->
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
- spring-root.xml 【spring容器】
通常用来配置与 web 无关的内容 譬如数据源,分页插件等
<context:include-filter>
里边书写的是本容器需要扫描的注解,与之相对的<context:exclude-filter>
则是容易不需要扫描的注解,include 必须要在 exclude 上边,否则会报错
<context:property-placeholder >
通过这个标签加载外部的配置文件,如数据源的相关数据等
<import>
:导入其他的xml文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.2.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.2.xsd">
<context:component-scan base-package="com.etoak">
<!-- 只扫描 @Service @Repositoryinclude-filter必须在exclude-filter上边 -->
<context:include-filter type="annotation"
expression="org.springframework.stereotype.Service"/>
<context:include-filter type="annotation"
expression="org.springframework.stereotype.Repository"/>
<!-- 排除 @Controller @RestController -->
<context:exclude-filter type="annotation"
expression="org.springframework.stereotype.Controller"/>
<context:exclude-filter type="annotation"
expression="org.springframework.web.bind.annotation.RestController"/>
<context:exclude-filter type="annotation"
expression="org.springframework.web.bind.annotation.ControllerAdvice"/>
</context:component-scan>
<!-- 加载外部配置文件 -->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 数据源 -->
<bean id="dataSource"
class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${m.driver}"></property>
<property name="url" value="${m.url}"></property>
<property name="username" value="${m.username}"></property>
<property name="password" value="${m.pwd}"></property>
</bean>
<!-- SqlSessionFactoryBean -->
<bean class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 数据源 -->
<property name="dataSource" ref="dataSource" />
<!-- mapper配置文件位置 -->
<property name="mapperLocations"
value="classpath:mappers/*.xml" />
<!-- bean的别名 -->
<property name="typeAliasesPackage" value="com.etoak.bean" />
<!-- 分页插件 -->
<property name="plugins">
<list>
<bean class="com.github.pagehelper.PageInterceptor">
<property name="properties">
<props>
<prop key="reasonable">true</prop>
</props>
</property>
</bean>
</list>
</property>
</bean>
<!-- MapperScan -->
<!-- 扫描指定包中的接口,为接口创建代理对象 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.etoak.mapper"></property>
</bean>
<!--导入外部的xml文件-->
<import resource="classpath:standalone.xml"/>
<!--访问localhost:8080/contextPath 直接跳转到 /templates/index.html -->
<mvc:view-controller path="/" view-name="index"/>
<!-- 跳转登录页面 -->
<mvc:view-controller path="/user/toLogin" view-name="login"/>
<!-- 配置登录拦截器 -->
<mvc:interceptors>
<mvc:interceptor>
<!-- 拦截所有的请求 -->
<mvc:mapping path="/**"/>
<!-- 排除以下请求 -->
<mvc:exclude-mapping path="/user/toLogin/**"/>
<mvc:exclude-mapping path="/user/login/**"/>
<mvc:exclude-mapping path="/code/**"/>
<mvc:exclude-mapping path="/static/**"/>
<!-- 登录拦截器 -->
<bean class="com.etoak.interceptor.LoginInterceptor"></bean>
</mvc:interceptor>
</mvc:interceptors>
</beans>
- spring-mvc.xml【mvc容器】
重要的注解
- 开启包扫描 context:component-scan
- 开启mvc配置 mvc:annotation-driven
- 静态文件交给servlet容器处理 mvc:default-servlet-handler
<?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-4.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.2.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.2.xsd">
<!-- 1.开启包扫描 -->
<!-- spring mvc 只加载和 web相关的bean (controller)
不需要加载业务层和持久层以及其他中间件的bean
-->
<context:component-scan base-package="com.etoak">
<!-- 只扫描 @Controller @RestController include-filter必须在exclude-filter上边 -->
<context:include-filter type="annotation"
expression="org.springframework.stereotype.Controller"/>
<context:include-filter type="annotation"
expression="org.springframework.web.bind.annotation.RestController"/>
<context:include-filter type="annotation"
expression="org.springframework.web.bind.annotation.ControllerAdvice"/>
<!-- 排除@Service @Repository -->
<context:exclude-filter type="annotation"
expression="org.springframework.stereotype.Service"/>
<context:exclude-filter type="annotation"
expression="org.springframework.stereotype.Repository"/>
</context:component-scan>
<!-- 2.开启springmvc配置
1. 可以代替 RequestMapppingHandlerMapping 和 RequestMappingHandlerAdapter
2. 默认注册很多参数转换器
3. 包括json转换器
-->
<mvc:annotation-driven></mvc:annotation-driven>
<!-- 3. 把静态文件(js css 图片等)交给servlet容器处理-->
<mvc:default-servlet-handler/>
<!-- 4. 定制静态资源访问 -->
<!-- 上传的车辆照片 上传到D:/upload 访问url : /pic/文件名 -->
<mvc:resources location="file:d:/upload/" mapping="/pic/**"/>
<!-- 文件上传解析器 id必须是multipartResolver-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="1048576"></property>
</bean>
<!-- 5. 视图解析器 配置 Thymeleaf 相关的 bean-->
<bean id="templateResolver"
class="org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver">
<!-- 页面放置位置 -->
<property name="prefix" value="/templates/" />
<!-- 文件后缀 -->
<property name="suffix" value=".html" />
<!-- 页面编码 -->
<property name="characterEncoding" value="UTF-8" />
<property name="templateMode" value="HTML"/>
<!-- 开发环境不进行缓存,方便调试 -->
<property name="cacheable" value="false" />
</bean>
<!-- 用来解析语法的 -->
<bean id="templateEngine"
class="org.thymeleaf.spring5.SpringTemplateEngine">
<property name="templateResolver" ref="templateResolver"></property>
</bean>
<!-- 渲染页面的 -->
<bean class="org.thymeleaf.spring5.view.ThymeleafViewResolver">
<property name="templateEngine" ref="templateEngine"></property>
<property name="characterEncoding" value="UTF-8" />
</bean>
<!--访问localhost:8080/contextPath 直接跳转到 /templates/index.html -->
<mvc:view-controller path="/" view-name="index"/>
<!-- 跳转登录页面 -->
<mvc:view-controller path="/user/toLogin" view-name="login"/>
<!-- 配置登录拦截器 -->
<mvc:interceptors>
<mvc:interceptor>
<!-- 拦截所有的请求 -->
<mvc:mapping path="/**"/>
<!-- 排除以下请求 -->
<mvc:exclude-mapping path="/user/toLogin/**"/>
<mvc:exclude-mapping path="/user/login/**"/>
<mvc:exclude-mapping path="/code/**"/>
<mvc:exclude-mapping path="/static/**"/>
<!-- 登录拦截器 -->
<bean class="com.etoak.interceptor.LoginInterceptor"></bean>
</mvc:interceptor>
</mvc:interceptors>
</beans>
6.4 纯java代码版本
- 配置相关文件代替web.xml 文件
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
import com.etoak.config.AppConfig;
import com.etoak.config.RootConfig;
public class Initializer extends AbstractAnnotationConfigDispatcherServletInitializer{
/**
* spring 容器
*/
@Override
protected Class<?>[] getRootConfigClasses() {
// TODO Auto-generated method stub
return new Class[] {RootConfig.class};
}
/**
* spring mvc 容器
*/
@Override
protected Class<?>[] getServletConfigClasses() {
// TODO Auto-generated method stub
return new Class[] {AppConfig.class};
}
/**
* web.xml
*/
@Override
protected String[] getServletMappings() {
// TODO Auto-generated method stub
return new String[] {"/"};
}
}
- 配置spring容器
作为代替配置文件的代码 必须要添加
@Configuration
注解注解解释xml版本@Configuration添加次注解将变成容器的配置文件@ComponentScan开启包扫描
<context:component-scan>
@PropertySource引入外部配置文件<context:property-placeholder>
@MapperScan扫描Mapper文件位置@Import导入外部xml文件<import>
@EnableTransactionManagement开启事务的注解
import java.io.IOException;
import javax.sql.DataSource;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import com.alibaba.druid.pool.DruidDataSource;
@Configuration
@ComponentScan(basePackages = { "com.etoak" }, //
excludeFilters = { @ComponentScan.Filter(type = FilterType.ANNOTATION, //
classes = { Controller.class, RestController.class, //
ControllerAdvice.class, EnableWebMvc.class }) }, //
includeFilters = { @ComponentScan.Filter(type = FilterType.ANNOTATION, //
classes = { Service.class, Repository.class }) })
@PropertySource(value="classpath:jdbc.properties")
@MapperScan(basePackages = {"com.etoak.mapper"}) //<MapperScannerConfigu..>
@EnableTransactionManagement //相当于 <tx:annotation-driven>
@Import(ActiveMQConfig.class)
public class RootConfig {
@Value("${m.driver}")
private String driver;
@Value("${m.url}")
private String url;
@Value("${m.username}")
private String username;
@Value("${m.pwd}")
private String password;
@Bean
public DataSource dataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName(this.driver);
dataSource.setUrl(this.url);
dataSource.setUsername(this.username);
dataSource.setPassword(this.password);
return dataSource;
}
@Bean
public SqlSessionFactoryBean sessionFactoryBean() {
SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
factory.setDataSource(this.dataSource());
factory.setTypeAliasesPackage("com.etoak");
PathMatchingResourcePatternResolver resovler = new PathMatchingResourcePatternResolver();
try {
factory.setMapperLocations(resovler.getResources("classpath:/mappers/*.xml"));
}catch(IOException e) {
e.printStackTrace();
}
return factory;
}
@Bean
public DataSourceTransactionManager tx() {
return new DataSourceTransactionManager(this.dataSource());
}
}
- springmvc 容器 []
package com.etoak.config;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.thymeleaf.spring5.SpringTemplateEngine;
import org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver;
import org.thymeleaf.spring5.view.ThymeleafViewResolver;
/**
* 1. <context:component-scan> <br>
* 2. <mvc:annotation-driven > <br>
* 3. <mvc:default-servlet-handler /> <BR>
* 4. 可选 <mvc:resources location="" mapping="" /> <BR>
* 5. Thymeleaf配置
*/
@Configuration
@ComponentScan(basePackages = { "com.etoak" }, //
includeFilters = { @ComponentScan.Filter(type = FilterType.ANNOTATION, //
classes = { Controller.class, RestController.class, //
ControllerAdvice.class, EnableWebMvc.class }) }, //
excludeFilters = { @ComponentScan.Filter(type = FilterType.ANNOTATION, //
classes = { Service.class, Repository.class }) })
@EnableWebMvc // 相当于<mvc:annotation-driven >
public class AppConfig implements WebMvcConfigurer {
/**
* WebMvcConfigurer非常非常非常重要 <BR>
* <mvc:default-servlet-handler> 通过WebMvcConfigurer配置 <br>
* <mvc:resources> 通过WebMvcConfigurer配置 <BR>
* <mvc:interceptors> 通过WebMvcConfigurer配置 <BR>
* <mvc:view-controller > 通过WebMvcConfigurer配置 <BR>
* 也就是所有以<mvc:>开头的配置都是用通过WebMvcConfigurer配置 <BR>
*/
/**
* 相当于<mvc:default-servlet-handler>
*/
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
//下边的三个方法为了给thymeleaf配置
@Bean // <bean>
public SpringResourceTemplateResolver templateResolver() {
SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();
// prefix、suffix、templateMode、characterEncoding、cacheable
resolver.setPrefix("classpath:/templates/");
resolver.setSuffix(".html");
resolver.setTemplateMode("HTML");
resolver.setCharacterEncoding("UTF-8");
resolver.setCacheable(false);
return resolver;
}
@Bean
public SpringTemplateEngine templateEngine() {
SpringTemplateEngine engine = new SpringTemplateEngine();
engine.setTemplateResolver(this.templateResolver());
return engine;
}
@Bean
public ThymeleafViewResolver viewResolver(//
@Qualifier("templateEngine") SpringTemplateEngine templateEngine) {
ThymeleafViewResolver resolver = new ThymeleafViewResolver();
resolver.setCharacterEncoding("UTF-8");
resolver.setTemplateEngine(templateEngine);
return resolver;
}
}
7. spring mvc参数传递
1、使用HttpServletRequest
2、使用简单类型基本数据类型
+ String
3、使用数组
<form action="${pageContext.request.contextPath}/simple/array" method="post">
爱好:
<input type="checkbox" name="hobby" value="唱歌"/> 唱歌
<input type="checkbox" name="hobby" value="打球"/> 打球
<input type="checkbox" name="hobby" value="玩游戏"/> 玩游戏
<br />
<input type="submit" value="提交"/>
</form>
@PostMapping("/array")
public void array(String[] hobby) {
for(String hobbyStr : hobby) {
System.out.println(hobbyStr);
}
}
4、使用java bean
5、使用List
- 如果contentType是x-www-form-urlencoded,那么List参数需要封装在java bean中
- 如果contentType是application/json,那么List参数可以写在Controller的方法上;
public class Student{
private int id;
private String name;
private List<String> hobbyList;
//getter and stter
}
<h3>使用list接受前端表单参数</h3>
<form action="${pageContext.request.contextPath}/complex/list" method="post">
爱好:
<input type="checkbox" name="hobbyList" value="唱歌"/> 唱歌
<input type="checkbox" name="hobbyList" value="打球"/> 打球
<input type="checkbox" name="hobbyList" value="玩游戏"/> 玩游戏
<br />
<input type="submit" value="提交"/>
</form>
@RequestMapping("/list")
public String list(Student student,Map<String,Object> resultMap) {
//打印爱好列表
student.getHobbyList().forEach(System.out::println);
/*
student.setId(123);
student.setName("Rush");
resultMap.put("stu",student);
return "";
*/
}
6、使用Map
- 如果contentType是x-www-form-urlencoded,那么Map参数需要封装在java bean中
- 如果contentType是application/json,那么Map参数可以写在Controller的方法上;
<button type="button" id="mapBtn" onclick="test()">测试Map传参</button>
<script>
test(){
$.ajax({
url:path + '/complex/map',
//url:`\${path}/complex/map`,
type:'post',
data:"stuMap['id']=120&stuMap['name']=et1912",
dataType:'text',
success:function(res){
alert(res)
}
})
}
</script>
//shi用Map封装在javabean中
@RequestMapping("/map")
@ResponseBody
public String map(Student student) {
System.out.println(student.getStuMap());
return "success";
}
7、使用JSON
- spring mvc默认使用jackson框架实现json的转换
- 使用spring mvc接收或返回json至少需要三个jar
jackson-core.jar
jackson-annotations.jar
jackson-databind.jar
由于使用的maven,只需要引入jackson-databind
的依赖即可,因为jackson-databind传递依赖了jackson-core和jackson-annotations; - 涉及的三个注解
@RestController:(@Controller + @ResponseBody)
@RequestBody:可以将json参数转成java bean、List和Map
@ResponseBody:用于返回json数据(但是它并不是只能返回json,还可以返回xml等) - 在前端可以使用
JSON.stringify(obj)
将 js 对象转换成 JSON对象
<script>
//获取contextPath
const path = '${pageContext.request.contextPath}'
//thymeleaf版本
//const path = [[${#request.getContextPath()}]]
//const path = /*[[${#request.getContextPath()}]]*/
jsonToMap:function(){
let obj = {id:1,name:'zhangsan'}
obj.hobby = ['唱个歌','打游戏']
$.ajax({
url:`\${path}/json/jsonToMap`,
type:'post',
//将js对象转换成json对象
data:JSON.stringify(obj),
dataType:'json',
contentType:'application/json;charset=utf-8',
success(res){
alert(res.code + ':' + res.msg)
}
})
}
</script>
@RequestMapping("/jsonToMap")
@ResponseBody
public Map<String,Object> jsonToMap(@RequestBody Map<String,Object> jsonMap){
System.out.println(jsonMap);
Map<String,Object> result = new HashMap<>();
result.put("code",200);
result.put("msg","成功了");
return result;
}
/**
* 测试前端json数组转换成后端的List参数
* @param studentList
* @return
*/
@RequestMapping("/jsonToList")
@ResponseBody
public ResultVo<String> jsonToList(@RequestBody List<Student> studentList){
studentList.forEach(System.out::println);
//spring mvc 把javabean转成json返回给前端
return new ResultVo<String>(200,"success","");
}
@PostMapping("/jsonToBean")
@ResponseBody
public ResultVo<Student> jsonToBean(
@RequestBody Student student){
System.out.println(student);
return new ResultVo<Student>(200, "success", student);
}
8 向request域传值
- 使用
HttpServletRequest
@RequestMapping("/servlet")
public String servlet(HttpServletRequest request) {
String name = request.getParameter("name");
System.out.println("param " + name);
//向request传值
request.setAttribute("result", "使用servletAPI向request域传值");
return "param";
}
- 使用ModelAndView
在参数上使用
@RequestParam
表示此参数必须传递过来,默认的required =true 可以改为 false 不强制传值
@RequestMapping("/basic")
public ModelAndView basic(
@RequestParam(required=false,defaultValue="1") int id,
@RequestParam String name) {
System.out.println("param id" + id);
System.out.println("param name" + name);
ModelAndView mv = new ModelAndView();
mv.addObject("result","使用modelAndView向request域传值");
mv.setViewName("param");
return mv;
}
- 使用Model
@PostMapping("/array")
public String array(String[] hobby,Model model) {
for(String hobbyStr : hobby) {
System.out.println(hobbyStr);
}
model.addAttribute("result","使用model向request域传值");
return "param";
}
- 使用ModelMap
@RequestMapping("/bean")
public String bean(Student student,ModelMap modelMap) {
System.out.println(student);
//向request域传值
modelMap.addAttribute("stu", student);
//转发到 /simple/servlet
return "forward:/simple/servlet?name=aaa";
}
- 使用map
@RequestMapping("/list")
public String list(Student student,Map<String,Object> resultMap) {
//打印爱好列表
student.getHobbyList().forEach(System.out::println);
student.setId(123);
student.setName("Rush");
resultMap.put("stu",student);
return "forward:/simple/basic?id=123&name=lisi";
}
测试前端json数据转换
package com.etoak.vo;
public class ResultVo<T> {
private Integer code;
private String message;
private T data;
public ResultVo (Integer code,String message, T data){
this.code = code;
this.message = message;
this.data = data;
}
//getter and setter
}
<h3>测试JSON转成map</h3>
<button type="button" id="jsonToMap">测试JSON转成map</button>
<hr />
<h3>测试Json转换成List</h3>
<button type="button" id="jsonToList">测试Json转换成List</button>
<hr />
<h3>测试Json转换成Bean</h3>
<button type="button" id="jsonToBean">测试Json转换成Bean</button>
<script src="${pageContext.request.contextPath }/jquery.min.js" type="text/javascript"></script>
<script>
const path = '${pageContext.request.contextPath}'
$(function(){
$('#jsonToMap').click(function(){
et1912.jsonToMap()
})
$('#jsonToList').click(() =>{
et1912.jsonToList()
})
$('#jsonToBean').click(() =>{
et1912.jsonToBean()
})
})
let et1912 = {
jsonToMap:function(){
console.log(11)
let obj = {id:1,name:'zhangsan'}
obj.hobby = ['唱个歌','打游戏']
$.ajax({
//url:path + '/json/jsonToMap',
url:`\${path}/json/jsonToMap`,
type:'post',
//将js对象转换成json对象
data:JSON.stringify(obj),
dataType:'json',
contentType:'application/json;charset=utf-8',
success(res){
alert(res.code + ':' + res.msg)
}
})
},
jsonToList:function(){
let array = [
{id:1,name:'zs',age:22,gender:'男',hobbyList:['唱歌','游戏']},
{id:2,name:'lisi',age:22,gender:'男',stuMap:{a:1,b:'abc'}},
]
$.ajax({
url:`\${path}/json/jsonToList`,
data:JSON.stringify(array),
type:'post',
dataType:'json',
contentType:'application/json;charset=utf-8',
success:res=>{
//res包括code msg data 三个属性
alert(res.code + ':' + res.message)
}
})
},
jsonToBean:function(){
let obj = {id:1,name:'et1912',age:22,hobbyList:['唱歌','游戏']}
$.ajax({
url:path + '/json/jsonToBean',
data:JSON.stringify(obj),
type:'post',
dataType:'json',
contentType:'application/json;charset=utf-8',
success(res){
alert(res.code + ':' + res.message + res.data.name + ':' + res.data.age)
}
})
}
}
</script>
package com.etoak.controller;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.etoak.bean.Student;
import com.etoak.vo.ResultVo;
@Controller
@RequestMapping("/json")
public class JsonController {
/**
*
*
*/
@RequestMapping("/jsonToMap")
@ResponseBody
public Map<String,Object> jsonToMap(@RequestBody Map<String,Object> jsonMap){
System.out.println(jsonMap);
Map<String,Object> result = new HashMap<>();
result.put("code",200);
result.put("msg","成功了");
return result;
}
/**
* 测试前端json数组转换成后端的List参数
* @param studentList
* @return
*/
@RequestMapping("/jsonToList")
@ResponseBody
public ResultVo<String> jsonToList(@RequestBody List<Student> studentList){
studentList.forEach(System.out::println);
//spring mvc 把javabean转成json返回给前端
return new ResultVo<String>(200,"success","");
}
@PostMapping("/jsonToBean")
@ResponseBody
public ResultVo<Student> jsonToBean(
@RequestBody Student student){
System.out.println(student);
return new ResultVo<Student>(200, "success", student);
}
}
9.定制静态资源访问路径
使用
<mvc:default-servlet-handler />
让DispatcherServlet不处理静态文件,交给容器处理
- 默认的静态资源访问路径是在
/webapp/
<!-- js 后边的 / 一定要带上 -->
<mvc:resources location="/js/"></mvc:resources>
- 定制在
classpath
下
<mvc:resources location="classpath:/static/">
- 访问本地文件
<mvc:resources location="file:d:/">
- 定制访问路径
<mvc:resources location="classpath:/static/"
mapping="/s/**">
<!--
资源路径是classpath:/static/
访问url为 localhost:8080/contextpath/s/XXX
*代表该目录下的所有文件,不包含子目录
** 包含子目录及下边的文件
-->
注意
范围精确的要放在上边
<mvc:resources location="classpath:/static/" mapping="/s/**"></mvc:resources>
<!--这个是 webapp/js-->
<mvc:resources location="/js/" mapping="/**"></mvc:resources>
<!--此时上边的范围包含了下边的范围,导致访问下边的文件失败-->
10 spring mvc 与 thymeleaf整合
- 用来开发Web和独立环境项目的现代服务器端Java模板引擎。
-目标:
为咱们的开发带来优雅的自然模板 - HTML。
可以在直接浏览器中直接显示,并且可以作为静态原型,从而在开发团队中实现更强大的协作。借助Spring Framework的模块,可以根据自己的喜好进行自由选择,可插拔功能组件,Thymeleaf是现代HTML5 Web开发的理想选择 - 尽管它可以做的更多。
Spring官方支持的服务的渲模板中,并不包含JSP。而是Thymeleaf和Freemarker等,而Thymeleaf与SpringMVC的视图技术以及SpringBoot的自动化配置集成的非常完美,几乎没有任何成本,咱们只需要关注一下Thymeleaf的语法即可。
Thymeleaf是一种模板语言(模板引擎)。那模板语言或模板引擎是什么?
常见的模板语言都包含以下几个概念:
数据(Data)。
模板(Template)。
模板引擎(Template Engine)。
结果文档(Result Documents)。-数据
数据是信息的表现形式和载体,可以是符号、文字、数字、语音、图像、视频等。
数据本身没有意义,数据只有对实体行为产生影响时才成为信息。-模板
是一个与类型无关的类。
编译器在使用模板时,会根据模板实参对模板进行实例化,得到一个与类型相关的类。-模板引擎
这里特指用于Web开发的模板引擎;
是为了使用户界面与业务数据(内容)分离而产生的,它可以生成特定格式的文档;
用于网站的时候模板引擎就会生成一个标准的HTML文档。-结果文档
一种特定格式的文档,比如用于网站的模板引擎就会生成一个标准的HTML文档。
thymeleaf的特点
动静结合:
Thymeleaf 在有网络和无网络的环境下皆可运行,即它可以让美工在浏览器查看页面的静态效果,也可以让程序员在服务器查看带数据的动态页面效果。
这是由于它支持html原型,然后在html标签里增加额外的属性来达到“模板+数据”的展示方式。浏览器解释html时会忽略未定义的标签属性,所以Thymeleaf的模板可以静态地运行;当有数据返回到页面时,Thymeleaf标签会动态地替换掉静态内容,使页面动态显示。开箱即用:
它提供标准和Spring标准两种方言,可以直接套用模板实现JSTL、 OGNL表达式效果,避免每天套模板、改jstl、改标签的困扰。同时开发人员也可以扩展和创建自定义的方言。多方言支持:
它提供Spring标准方言和一个与SpringMVC完美集成的可选模块,可以快速的实现表单绑定、属性编辑器、国际化等功能。与SpringBoot完美整合:
SpringBoot提供了Thymeleaf的默认配置,并且为Thymeleaf设置了视图解析器,我们可以像以前操作jsp一样来操作Thymeleaf。代码几乎没有任何区别,就是在模板语法上有区别。
1. 加载相关的配置文件
- 引入maven依赖
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf</artifactId>
<version>3.0.11.RELEASE</version>
</dependency>
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring5</artifactId>
<version>3.0.11.RELEASE</version>
</dependency>
- 在spring-mvc容器中原来放视图解析器的地地方改为以下版本
<!-- 5. 视图解析器 配置 Thymeleaf 相关的 bean-->
<bean id="templateResolver"
class="org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver">
<!-- 页面放置位置 -->
<property name="prefix" value="/templates/" />
<!-- 文件后缀 -->
<property name="suffix" value=".html" />
<!-- 页面编码 -->
<property name="characterEncoding" value="UTF-8" />
<property name="templateMode" value="HTML"/>
<!-- 开发环境不进行缓存,方便调试 -->
<property name="cacheable" value="false" />
</bean>
<!-- 用来解析语法的 -->
<bean id="templateEngine"
class="org.thymeleaf.spring5.SpringTemplateEngine">
<property name="templateResolver" ref="templateResolver"></property>
</bean>
<!-- 渲染页面的 -->
<bean class="org.thymeleaf.spring5.view.ThymeleafViewResolver">
<property name="templateEngine" ref="templateEngine"></property>
<property name="characterEncoding" value="UTF-8" />
</bean>
- 在webapp下创建 template 文件夹,存放模板文件
2. Thymeleaf常用语法
要想使用Thymeleaf 还需要在 html标签里添加内容
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org">
- 变量表达式
${}
Thymeleaf通过${}来获取model中的变量,注意这不是EL表达式,而是OGNL表达式,但是语法非常像。
这个语法糖,并不能单独使用,必须借助其他的标签,在标签里输出值的之后,可以使用
th:text
这个语法糖,是标签是通过value来显示值的时候则需要使用th:value
.当我们直接用浏览器打开静态文件时,页面会显示默认值,而不用模板里的内容,当呗tomcat等web容器加载的时候才会显示模板里的内容
@Controller
public class UserController {
@RequestMapping("queryUser")
public String queryUser(Model model) {
User user = new User(1, "张三", 20);
model.addAttribute("user", user);
return "index";
}
}
<span th:text="${stu.name}">默认值</span>
<input th:value="${stu.age}">
- 链式表达式
@{}
相当于 jsp 中的 ${pageContext.request.contextPath} 获取工程的contextPath
<link th:src="@{/js/jquery.js}">
<link th:href="@{/css/layui.css}">
- 选择表达式
*{}
通过
th:object
获取对象,然后使用th:xxx="*{}"
获取对象属性值
<form th:object="${user}">
<input name="id" th:value="*{id}"/>
<input name="name" th:value="*{name}"/>
<input name="age" th:value="*{age}"/>
</form>
<hr>
<table>
<tr>
<td>id</td>
<td>名字</td>
<td>年龄</td>
</tr>
<tr th:object="${user}">
<td><span th:text="*{id}"></td>
<td><span th:text="*{name}"></td>
<td><span th:text="*{age}"></td>
</tr>
</table>
- 循环
th:each
前端页面获取到的stat对象包含以下属性
属性含义index下标count元素的个数,从1开始size总元素个数current当前遍历到的元素even/odd返回是否为奇偶 boolean类型first/last返回是否为第一或最后 boolean类型
@Controller
public class UserController {
@RequestMapping("queryUser")
public String queryUser(Model model) {
User user2 = new User(2, "lisi", 22);
User user3 = new User(3, "Jack", 23);
User user4 = new User(4, "Jhon", 24);
List<User> userList = new ArrayList<User>();
userList.add(user2);
userList.add(user3);
userList.add(user4);
model.addAttribute("userList", userList);
return "index";
}
}
<table>
<td>id</td>
<td>名字</td>
<td>年龄</td>
<td>操作</td>
</tr>
<tr th:each="user,stat:${userList}">
<td><span th:text="${user.id}"></td>
<td><span th:text="${user.name}"></td>
<td><span th:text="${user.age}"></td>
</tr>
</table>
- JS中使用模板数据
<script th:inline="javascript">
//const path = [[#request.getContextPath()]]
// const path = /*[[${#request.getContextPath()}]]*/
//const path = [[#httpServletRequest.getContextPath]]
//获取contextPath
</script>
- get请求传参
<a th:href="@{/hello(name=test,k1=v1)}">get传参</a>
11. spring mvc 与 mybatis整合
12. REST(Restful)
12.1 Rest 简介
REST 是
Roy Thomas Fielding
在他2000年的博士论文中提出的,全程是 Representational State Transfer
- 资源
所谓资源,就是网络上的实体,或者说是网络上的一个具体信息,他可以是一段文本、一张图片、一首歌曲、一种服务、总之就是一个具体的实现。可以用一个URI(同意资源定位符)指向它,每种资源对应一个特定的 URI ,要获取这个资源,访问他的 URI 就可以,因此 URI|就成Wie了每一个资源的地址或者第一无二的识别符。
- 表现层
资源是一种信息实体,可以有多种外在表现形式。我们把“资源”具体呈现出来的形式,叫做它的“表现层”(Representation)。
比如,文本可以用txt格式表现,也可以用HTML格式、XML格式、JSON格式表现,设置可以采用二进制格式,图片可以用 JPG 或者 PNG 形式表现。
URI只代表资源的实体,不代表它的形式。严格地说,有些网址最后的".html"后缀名是不必要的,因为这个后缀名表示格式,属于"表现层"范畴,而URI应该只代表"资源"的位置。它的具体表现形式,应该在HTTP请求的头信息中用Accept和Content-Type字段指定,这两个字段才是对"表现层"的描述。
- 状态转化
访问一个网站,就代表了客户端和服务器的一个互动过程。在这个过程中,势必涉及到数据和状态的变化。
互联网通信协议HTTP协议,是一个无状态协议。这意味着,所有的状态都保存在服务器端。因此,如果客户端想要操作服务器,必须通过某种手段,让服务器端发生"状态转化"(State Transfer)。而这种转化是建立在表现层之上的,所以就是"表现层状态转化"。
客户端用到的手段,只能是HTTP协议。具体来说,就是HTTP协议里面,四个表示操作方式的动词:GET、POST、PUT、DELETE。它们分别对应四种基本操作:GET用来获取资源,POST用来新建资源(也可以用于更新资源),PUT用来更新资源,DELETE用来删除资源。
- 总结
使用URI表示网络的资源(资源)
使用HTTP请求方法表示动作(状态转换)
传输的小时可以使用 JSON 、xml 、k=v (表现层)
请求格式 | 传统的 | REST格式 |
GET(查询) | /user/getById?id=1 | /user/1 |
POST(添加) | /user/add?name=x&age=10 | /user json |
PUT(更新) | /user/update?id=1&name=x | /user json |
DELETE(删除) | /user/delById?id=1 | /user/1 |
传统格式
@Controller
@RequestMapping("/user")
public class UserController{
@RequestMapping("/getById")
public String getById(Integer id){
}
}
REST格式
使用
@PathVariable
标签可是将参数映射到 URL 中,当@RequestMapping
里边的参数与方法里边的参数一致时,@PathVariable
后边的方法就可以不用携带参数,不一致时就需要填写参数。@RequestMapping("/{id}") public String queryList(@PathVariable("id") String groupId){}
@Controller
@RequestMapping("/user")
public class UserController{
@RequestMapping("/{id}")
public String queryList(@PathVariable String groupId){
}
}
12.2 spring mvc 支持REST请求
- spring 如何支持REST请求
注解 | 解释 |
@PathVariable | 可以将 url 地址上的参数赋值给控制器的方法参数 |
@RequestBody | 接收 JSON 参数 |
@ResponseBody | 返回 JSON 参数 |
- spring配置 HiddenHttpMethodFilter 支持 REST 请求
表单是不支持 PUT、DELETE 等请求的,仅仅支持 GET、POST请求 , HiddenHttpMethodFilter 可以将表单的post请求转换成 PUT、DELETE等
操作步骤
- 在web.xml 配置 HiddenHttpMethodFilter
- 表单的 method 属性 必须是 POST 请求
- 表单**必须有一个隐藏域,隐藏域的 name 属性必须是 “_method” value的啥属性值就是具体的请求方法 (如 PUT \ DELETE )**
13. lombok
13.1 Eclipse 安装 lombok 插件
- 将 lombok.jar 复制到 Eclipse 安装目录
- 在 eclipse.ini 文件的 -Xmx:1024m 的下一行添加如下内容
-javaagent:lombok.jar
- 在项目中添加 lombok 依赖
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.20</version>
<scope>provided</scope>
</dependency>
13.2 IDEA 安装 lombok 插件
- 点击 file -> setting -> plugins -> 输入 lombok 查找评分最高的即可
13.3 Lombok 注解
在 javaBean 中添加相关注解即可 自动生成 getter setter 等方法
@Setter
和@Gtter
生成getter 和 setter
方法
@ToString
可以生成toString()
方法
@Setter
@Getter
@ToString
public class User{
private Integer id;
private String name;
private Integer age;
}
@Data
可以代替 前边那三个注解 生成 getter 、 setter 、toString 方法
@Data
public class User{
private Integer id;
private String name;
private Integer age;
}
@NoArgsConstructor
定义无参构造方法@AllArgsConstructor
按照所有参数定义顺序生成构造方法
14.spring mvc 后端校验
使用hibernate-validator实现后端接口校验
- 配置maven依赖
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.1.2.Final</version>
</dependency>
- 校验规则
注解 | 规则 |
@AssertFalse | 该字段的值只能为false |
@CreditCardNumber | 对信用卡号进行一个大致的验证 |
@DecimalMax | 只能小于或等于该值 |
@DecimalMin | 只能大于或等于该值 |
@Digits(integer=,fraction=) | 检查是否是一种数字的整数、分数,小数位数的数字 |
@Email | 检查是否是一个有效的email地址 |
@Future | 检查该字段的日期是否是属于将来的日期 |
@Length(min=,max=) | 检查所属的字段的长度是否在min和max之间,只能用于字符串 |
@Max | 该字段的值只能小于或等于该值 |
@Min | 该字段的值只能大于或等于该值 |
@NotNull | 不能为null |
@NotBlank | 不能为空,检查时会将空格忽略 |
@NotEmpty | 不能为空,可用于String、Collection、Map |
@Null | 检查该字段为空 |
@Past | 检查该字段的日期是在过去 |
@Pattern(regex=,flag=) | 被注释的元素必须符合指定的正则表达式 |
@Range(min=,max=,message=) | 被注释的元素必须在合适的范围内 |
@Size(min=, max=) | 检查该字段的size是否在min和max之间,可以是字符串、数组、集合、Map等 |
@URL(protocol=,host,port) | 检查是否是一个有效的URL,如果提供了protocol,host等,则该URL还需满足提供的条件 |
@Valid | 该注解主要用于字段为一个包含其他对象的集合或map或数组的字段,或该字段直接为一个其他对象的引用,这样在检查当前对象的同时也会检查该字段所引用的对象 |
- 校验示例
package com.etoak.bean;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import org.hibernate.validator.constraints.Length;
import lombok.Data;
@Data
public class Car {
private Integer id;
@NotNull(message = "brand不能为空")
@NotEmpty(message = "brand不能为空字符串")
@Length(min = 2, max = 10, message = "brand必须介于2-10之间")
private String brand;
@NotNull(message = "series不能为空")
@NotEmpty(message = "series不能为空字符串")
@Length(min = 2, max = 10, message = "series必须介于2-10之间")
private String series;
@NotNull(message = "price不能为空")
@Min(value = 1L, message = "pirce最小是1")
@Max(value = 100L, message = "pirce最大是100")
private Double price;
@NotNull(message = "licensingDate不能为空")
@NotEmpty(message = "licensingDate不能为空字符串")
private String licensingDate;
@NotNull(message = "level不能为空")
@NotEmpty(message = "level不能为空字符串")
private String level;
@NotNull(message = "gearbox不能为空")
@NotEmpty(message = "gearbox不能为空字符串")
private String gearbox;
@NotNull(message = "outputVolume不能为空")
@NotEmpty(message = "outputVolume不能为空字符串")
private String outputVolume;
@NotNull(message = "mileage不能为空")
@Min(value = 1L, message = "mileage最小是1")
@Max(value = 100L, message = "mileage最大是100")
private Double mileage;
@NotNull(message = "location不能为空")
@NotEmpty(message = "location不能为空字符串")
private String location;
private String pic;
@NotNull(message = "summary不能为空")
@NotEmpty(message = "summary不能为空字符串")
private String summary;
private String createTime;
}
- 前端校验 [基于jquery的]
引用 js 文件 jquery.validate.min.js
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<link rel="stylesheet"
th:href="@{/static/js/bootstrap/css/bootstrap.min.css}">
<title>车辆添加页面</title>
<style type="text/css">
body {
margin-top: 20px;
}
</style>
</head>
<body>
<h4>
<span th:text="${error}"></span>
</h4>
<form class="form-horizontal" role="form" id="addForm"
th:action="@{/car/add}" method="post" enctype="multipart/form-data">
<!-- -->
<!-- 品牌 -->
<div class="form-group ">
<label for="brand" class="col-sm-2 control-label">品牌</label>
<div class="col-sm-4 input-group">
<input type="text" class="form-control" name="brand" id="brand"
placeholder="品牌">
</div>
</div>
<!-- 车系 -->
<div class="form-group">
<label for="series" class="col-sm-2 control-label">车系</label>
<div class="col-sm-4 input-group">
<input type="text" class="form-control" name="series" id="series"
placeholder="车系">
</div>
</div>
<!-- 价格 -->
<div class="form-group">
<label for="price" class="col-sm-2 control-label">价格</label>
<div class="col-sm-4 input-group">
<input type="number" class="form-control" name="price" id="price"
placeholder="价格">
</div>
</div>
<!-- 上牌日期 - 仅精确到月 -->
<div class="form-group">
<label for="licensingDate" class="col-sm-2 control-label">上牌日期</label>
<div class="col-sm-4 input-group">
<input type="text" class="form-control" name="licensingDate"
id="licensingDate" placeholder="上牌日期">
</div>
</div>
<!-- 级别 -->
<div class="form-group">
<label for="level" class="col-sm-2 control-label">级别</label>
<div class="col-sm-4 input-group">
<select id="level" name="level" class="form-control">
</select>
</div>
</div>
<!-- 变速箱 -->
<div class="form-group">
<label class="col-sm-2 control-label">变速箱</label>
<div class="col-sm-4 input-group" id="gearbox"></div>
</div>
<!-- 排量 -->
<div class="form-group">
<label for="output_volume" class="col-sm-2 control-label">排量</label>
<div class="col-sm-4 input-group">
<select id="output_volume" name="outputVolume" class="form-control">
</select>
</div>
</div>
<!-- 里程 -->
<div class="form-group">
<label for="mileage" class="col-sm-2 control-label">里程</label>
<div class="col-sm-4 input-group">
<input type="number" class="form-control" name="mileage"
id="mileage" placeholder="里程">
</div>
</div>
<!-- 归属地 -->
<div class="form-group">
<label for="location" class="col-sm-2 control-label">归属地</label>
<div class="col-sm-4 input-group">
<input type="text" class="form-control" name="location"
id="location" placeholder="归属地">
</div>
</div>
<!-- 车辆照片 -->
<div class="form-group">
<label for="file" class="col-sm-2 control-label">照片</label>
<div class="col-sm-4 input-group">
<input type="file" class="form-control" name="file" id="file">
</div>
</div>
<!-- 归属地 -->
<div class="form-group">
<label for="summary" class="col-sm-2 control-label">概述</label>
<div class="col-sm-4 input-group">
<textarea name="summary" id="summary" rows="5" style="width: 100%"></textarea>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-primary">添加</button>
<button type="reset" class="btn btn-default">重置</button>
</div>
</div>
</form>
<script th:src="@{/static/js/jquery/jquery.min.js}"
type="text/javascript"></script>
<script th:src="@{/static/js/bootstrap/js/bootstrap.min.js}"
type="text/javascript"></script>
<script th:src="@{/static/js/axios/axios.js}" type="text/javascript"></script>
<script th:src="@{/static/js/laydate/laydate.js}"
type="text/javascript"></script>
<script th:src="@{/static/js/jquery/jquery.validate.min.js}"></script>
<script th:src="@{/static/js/jquery/messages_zh.min.js}"></script>
<script th:inline="javascript" type="text/javascript">
const path = /*[[${#request.getContextPath()}]]*/
$(function(){
laydate.render({
elem: '#licensingDate'
,type: 'month'
});
loadSelectDict('level');
loadSelectDict('output_volume');
loadRadioDict()
addFormValidate()
})
function addFormValidate(){
$('#addForm').validate({
//把校验信息的显示元素有label修改为div
errorElement:'div',
// 修改错误显示位置;error:表示那个label标签; element:input输入框
errorPlacement: function(error, element) {
error.addClass('input-group-addon');
error.appendTo(element.parent());
},
// 校验成功之后, 执行这个方法
success: function (label) {
// 给label添加样式
label.addClass('success input-group-addon').text('OK');
},
rules:{
brand:{
required:true,
rangelength:[2,10]
},
series:{
required:true,
rangelength:[2,10]
},
price:{
required:true,
digits:true,
min:1,
max:100
},
mileage:{
required:true,
positiveInt:true,
max:100,
}
},
messages:{
brand:{
required:'品牌必填',
rangelength:'品牌长度应在2-10个字符之间'
},
series:{
required:'车系必填',
rangelength:'车系长度应在2-10个字符之间'
},
price:{
required:'价格必填',
digits:'必选填写正整数',
min:'最小值为1',
max:'最大值为100'
},
mileage:{
required:'里程必填',
positiveInt:'只能是正整数',
max:'最大是100'
}
}
})
//添加自定义校验规则
/**
* 1: 校验规则名字
* 2:具体的校验策略
value:文本框的值
element:被校验的元素
* 3:校验失败的返回信息
*/
$.validator.addMethod('positiveInt',function(value,element){
let expression = /^\+?[1-9]\d*$/
return this.optional(element) || (expression.test(value))
},'必须是大于0的正整数')
}
//加载级别、排量
//groupid:level、output_volume
function loadSelectDict(groupId) {
groupId = groupId || 'level'
$.ajax({
url:path+'/dict/'+groupId,
type:'GET',
data:'',
dataType:'json',
success:function(res){
let options = "<option value=''>——请选择——</option>"
for(let dict of res){
options += "<option value='"+ dict.value +"'>" + dict.name + "</option>"
}
$('#'+groupId).html(options);
}
})
}
//加载变速箱
function loadRadioDict() {
axios.get(path+'/dict/gearbox')
.then(res=>{
let radios = ""
for(let dict of res.data){
radios += "<label class='radio-inline'><input type='radio' name='gearbox' value='"+dict.value+"'>"+dict.name+"</label>"
}
$('#gearbox').html(radios);
}).catch(error =>{
console.log(error)
})
/* $.ajax({
url:path+'/dict/'+groupId,
type:'GET',
data:'',
dataType:'json',
success:function(res){
let radios = ""
for(let dict of res){
radios += "<label class='radio-inline'><input type='radio' name='inlineRadioOptions' id='inlineRadio2' value='"+dict.value+"'>"+dict.name+"</label>"
}
$('#'+groupId).html(radios);
}
}) */
}
</script>
</body>
</html>
15.springmvc 全局异常处理
spring MVC3.2提供了@ControllerAdvice
注解作为 @Controller
的增强,这个注解主要有以下三个作用,其中以第一个最为常用
- 全局异常处理
- 全局数据绑定
- 全局数据预处理
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface ControllerAdvice {
}
// 从定义看,@ControllerAdvice包括了@Component,也就是说使用他的类型会被加载到spring容器中
// 需要在spring mvc容器中配置扫描@ControllerAdvice注解
// 在使用@ControllerAdvice注解的类型里,使用@ExceptionHandler注解方法,可以设置这个方法拦截什么类型的异常
1X . springmvc 事务
X. 错误集锦
- Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException:
No qualifying bean of type ‘com.etoak.service.UserService’ available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
引发原因:Service实现类上边没有加 @Service 注解
解决办法:在com.etoak.service.UserService 的实现类上增加 @Service 注解
- no handler found
<bean name="/hello.action" class="com.etoak.controller.HelloController"/>
引发原因:name属性前边忘记添加/
解决办法:name="/hello.action"
- Content type ‘application/x-www-form-urlencoded;charset=UTF-8’ not supported]
HTTP 415 错误
org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver logException
引发原因:发送异步请求的时候contentType写错了
4.java.lang.IllegalArgumentException: Result Maps collection does not contain value
引发原因:mybatis 的 XXXMapper.xml中 的 parameterType 写成了 parameterMap
解决办法:查找错误信息,有个XXXMapper.XXX 中的 parameterType 写错了
- java.lang.IllegalArgumentException: Unknown return value type: java.lang.Integer