spring-mvc

1 篇文章 0 订阅
1 篇文章 0 订阅

第二章 spring mvc

1. 回顾spring mvc几个重要组件

  1. 前端控制器DispatcherServlet:spring mvc的一个控制单元
  2. 处理器映射器HandlerMapping:使用url地址查找处理器
  3. 处理器适配器HandlerAdapter:用于执行处理器
  4. 处理器(控制器):处理业务逻辑
  5. 视图解析器ViewResolver:解析逻辑视图,创建视图对象
  6. 拦截器HandlerInterceptor
  7. 异常解析器
  8. 文件上传的解析器

2. spring mvc 执行流程

  1. 客户端发送请求到DispatcherServlet,DispatcherServlet请求处理器映射器查找处理器;
  2. 处理器映射器查找到处理器之后,返回一个处理器执行链给DispatcherServlet;
  3. DispatcherServlet请求处理器适配器执行处理器;
  4. 处理器适配器执行处理器,处理器执行完成之后,返回ModelAndView给处理器适配器;
  5. 处理器适配器把ModelAndView返回给DispatcherServlet;
  6. DispatcherServlet请求视图解析器解析ModelAndView;
  7. 视图解析器解析完成之后,返回一个View视图对象给DispatcherServlet
  8. DispatcherServlet使用模型数据和View视图对象渲染页面,响应客户端请求;
1.springmvc执行流程图

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 :

  1. *.action *.do

    表示拦截以特定格式结尾的请求

  2. /*

    拦截所有的请求,包括静态资源 会转发到Dispatcher去寻找相应的方法,就会找不到404

  3. /

    同样是拦截所有的请求,但是会过滤掉静态资源

<!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

特点

  1. 必须配对使用

  2. 处理器不需要事先任何接口

  3. 使用@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容器中

  1. 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>
  1. 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>
  1. 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代码版本

  1. 配置相关文件代替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[] {"/"};
		
	}
	
}
  1. 配置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());
	}
	
	
}
  1. 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

  1. spring mvc默认使用jackson框架实现json的转换

  2. 使用spring mvc接收或返回json至少需要三个jar

    jackson-core.jar

    jackson-annotations.jar

    jackson-databind.jar

    由于使用的maven,只需要引入jackson-databind的依赖即可,因为jackson-databind传递依赖了jackson-core和jackson-annotations;

  3. 涉及的三个注解

    @RestController:(@Controller + @ResponseBody)

    @RequestBody:可以将json参数转成java bean、List和Map

    @ResponseBody:用于返回json数据(但是它并不是只能返回json,还可以返回xml等)

  4. 在前端可以使用 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域传值

  1. 使用 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";
}
  1. 使用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;
}
  1. 使用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";
}
  1. 使用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";
}
  1. 使用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不处理静态文件,交给容器处理

  1. 默认的静态资源访问路径是在/webapp/
<!-- js 后边的 / 一定要带上  -->
<mvc:resources location="/js/"></mvc:resources>
  1. 定制在classpath
<mvc:resources location="classpath:/static/">
  1. 访问本地文件
<mvc:resources location="file:d:/">
  1. 定制访问路径
<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. 加载相关的配置文件

  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>
  1. 在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>
  1. 在webapp下创建 template 文件夹,存放模板文件

2. Thymeleaf常用语法

要想使用Thymeleaf 还需要在 html标签里添加内容

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
  1. 变量表达式 ${}

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}">	
  1. 链式表达式 @{}

相当于 jsp 中的 ${pageContext.request.contextPath} 获取工程的contextPath

<link th:src="@{/js/jquery.js}">
<link th:href="@{/css/layui.css}">
  1. 选择表达式 *{}

通过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>
  1. 循环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>
  1. JS中使用模板数据
<script th:inline="javascript">
    //const path = [[#request.getContextPath()]]
    // const path = /*[[${#request.getContextPath()}]]*/
   	//const path = [[#httpServletRequest.getContextPath]] 
    //获取contextPath      
</script>
  1. 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

  1. 资源

所谓资源,就是网络上的实体,或者说是网络上的一个具体信息,他可以是一段文本、一张图片、一首歌曲、一种服务、总之就是一个具体的实现。可以用一个URI(同意资源定位符)指向它,每种资源对应一个特定的 URI ,要获取这个资源,访问他的 URI 就可以,因此 URI|就成Wie了每一个资源的地址或者第一无二的识别符。

  1. 表现层

资源是一种信息实体,可以有多种外在表现形式。我们把“资源”具体呈现出来的形式,叫做它的“表现层”(Representation)

比如,文本可以用txt格式表现,也可以用HTML格式、XML格式、JSON格式表现,设置可以采用二进制格式,图片可以用 JPG 或者 PNG 形式表现。

URI只代表资源的实体,不代表它的形式。严格地说,有些网址最后的".html"后缀名是不必要的,因为这个后缀名表示格式,属于"表现层"范畴,而URI应该只代表"资源"的位置。它的具体表现形式,应该在HTTP请求的头信息中用Accept和Content-Type字段指定,这两个字段才是对"表现层"的描述。

  1. 状态转化

访问一个网站,就代表了客户端和服务器的一个互动过程。在这个过程中,势必涉及到数据和状态的变化。

互联网通信协议HTTP协议,是一个无状态协议。这意味着,所有的状态都保存在服务器端。因此,如果客户端想要操作服务器,必须通过某种手段,让服务器端发生"状态转化"(State Transfer)。而这种转化是建立在表现层之上的,所以就是"表现层状态转化"。

客户端用到的手段,只能是HTTP协议。具体来说,就是HTTP协议里面,四个表示操作方式的动词:GET、POST、PUT、DELETE。它们分别对应四种基本操作:GET用来获取资源,POST用来新建资源(也可以用于更新资源),PUT用来更新资源,DELETE用来删除资源。

  1. 总结

使用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请求

  1. spring 如何支持REST请求
注解解释
@PathVariable可以将 url 地址上的参数赋值给控制器的方法参数
@RequestBody接收 JSON 参数
@ResponseBody返回 JSON 参数
  1. spring配置 HiddenHttpMethodFilter 支持 REST 请求

表单是不支持 PUT、DELETE 等请求的,仅仅支持 GET、POST请求HiddenHttpMethodFilter 可以将表单的post请求转换成 PUT、DELETE等

操作步骤

  1. 在web.xml 配置 HiddenHttpMethodFilter
  2. 表单的 method 属性 必须是 POST 请求
  3. 表单**必须有一个隐藏域,隐藏域的 name 属性必须是 “_method” value的啥属性值就是具体的请求方法 (如 PUT \ DELETE )**

13. lombok

13.1 Eclipse 安装 lombok 插件

  1. 将 lombok.jar 复制到 Eclipse 安装目录

  2. 在 eclipse.ini 文件的 -Xmx:1024m 的下一行添加如下内容

    -javaagent:lombok.jar
    
  3. 在项目中添加 lombok 依赖

    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.16.20</version>
        <scope>provided</scope>
    </dependency>
    

13.2 IDEA 安装 lombok 插件

  1. 点击 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实现后端接口校验

  1. 配置maven依赖
<dependency>
  <groupId>org.hibernate</groupId>
  <artifactId>hibernate-validator</artifactId>
  <version>6.1.2.Final</version>
</dependency>
  1. 校验规则
注解规则
@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或数组的字段,或该字段直接为一个其他对象的引用,这样在检查当前对象的同时也会检查该字段所引用的对象
  1. 校验示例
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;
}
  1. 前端校验 [基于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. 错误集锦

  1. 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 注解

  1. no handler found
<bean name="/hello.action" class="com.etoak.controller.HelloController"/>  

引发原因:name属性前边忘记添加/

解决办法:name="/hello.action"

  1. 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 写错了

  1. java.lang.IllegalArgumentException: Unknown return value type: java.lang.Integer

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值