Spring MVC 文件下载
准备下载的文件
示例:
download.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>文件下载</title>
</head>
<body>
<h3>SpringMVC文件下载</h3>
<a href="/hellospringmvc/download">下载</a>
</body>
</html>
@Controller
public class DownloadController {
@RequestMapping("/download")
public void download(HttpSession session, HttpServletRequest request, HttpServletResponse response) throws IOException {
//InputStream inputStream = session.getServletContext().getResourceAsStream("/download/template.gif");
InputStream inputStream = request.getSession().getServletContext().getResourceAsStream("/download/template.gif");
//2.输出文件
//设置响应头
response.setHeader("Content-Disposition","attachment;filename=template.gif");
OutputStream outputStream = response.getOutputStream();
byte[] buff = new byte[1024];
int lenth = 0;
while ((lenth= inputStream.read(buff))!= -1){
outputStream.write(buff,0,lenth);
}
//3.关闭资源
outputStream.close();
inputStream.close();
}
}
请求:http://localhost:8080/hellospringmvc/download.jsp
Spring MVC 拦截器
Spring MVC中的拦截器(Interceptor)类似于Servlet中的过滤器(Filter),它主要用于拦截用户请求并作相应的处理。例如通过拦截器可以进行权限验证、记录请求信息的日志、判断用户是否登录,session是否超时等。
要使用Spring MVC中的拦截器,就需要对拦截器类进行定义和配置。
通常拦截器类可以通过两种方式来定义:
- 通过实现HandlerInterceptor接口
- 继承HandlerInterceptor接口的实现类(如:HandlerInterceptorAdapter)来定义。
注意:拦截器配置的顺序决定了拦截器的执行顺序,先配置会先被执行!
注意:一个拦截器和多个拦截器的执行顺序看下图。
一个拦截器的执行顺序:
多个拦截器的执行顺序:
示例:
@Controller
@RequestMapping("/say")
public class HelloController {
@RequestMapping(value = "/hello", method = RequestMethod.GET)
public String sayHello() {
//System.out.println("----------HelloController-----sayHello---------------");
System.out.println("3.目标控制器-HelloController");
return "success";
}
}
public class FirstInterceptor implements HandlerInterceptor {
/**
*preHandle: 在控制器(目标)的方法之前被执行
* 返回值:控制afterCompletion方法是否被执行
* true: 执行afterCompletion
* false: 不执行afterCompletion
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("1.FirstInterceptor的preHandle");
return true;
}
/**
* postHandle: 在控制器(目标)的方法成功执行完成之后(注意:控制器方法失败不执行)
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("5.FirstInterceptor的postHandle");
}
/**
* afterCompletion: 在执行完前面所有(拦截器和目标)的方法之后执行(注意: 不管控制器方法执行成功与否都会被执行 )
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("7.FirstInterceptor的afterCompletion");
}
}
public class SecondInterceptor extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("2.SecondInterceptor的preHandle");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("4.SecondInterceptor的postHandle");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("6.SecondInterceptor的afterCompletion");
}
}
hellospringmvc-servlet.xml
<!--配置拦截器 :拦截器配置的顺序决定了拦截器的执行顺序,先配置会先被执行!-->
<mvc:interceptors>
<mvc:interceptor>
<!--要拦截请求路径-->
<mvc:mapping path="/**/*"/>
<bean class="com.self.interceptor.FirstInterceptor"/>
</mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/say/hello"/>
<!--如果只这么写就是只拦截路径为/hello的,上面的/say/hello是不会被拦截的-->
<mvc:mapping path="/hello"/>
<bean class="com.self.interceptor.SecondInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
请求:http://localhost:8080/hellospringmvc/say/hello
输出:
1.FirstInterceptor的preHandle
2.SecondInterceptor的preHandle
3.目标控制器-HelloController
4.SecondInterceptor的postHandle
5.FirstInterceptor的postHandle
6.SecondInterceptor的afterCompletion
7.FirstInterceptor的afterCompletion
登录超时拦截器:
public class RequestSessionTimeOutInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
try{
String uri = request.getRequestURI();
if(uri == null){
return true;
}
HttpSession session = request.getSession();
if (null == session) {
//401:HTTP401错误代表用户没有访问权限,需要进行身份认证
response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
return false;
}
return true;
} catch (Exception e){
//异常情况不拦截
logger.error("拦截器配置失败",e);
return true;
}
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
Spring MVC 异常处理机制
在控制器的方法发生异常后,默认情况会显示Tomcat的500页面,这种用户体验并不好。如果我们在每个控制器方法自行捕获异常,显然又太繁琐。有没有好的异常处理办法呢?有的,就是Spring MVC的全局异常处理机制。Spring MVC提供了两种全局异常处理机制:
- 定义异常类,实现HandlerExceptionResolver接口
- 定义异常类,使用@ControllerAdvice+@ExceptionHandler注解
编写全局异常处理类
全局异常类编写方式一
实现HandlerExceptionResolver接口,然后实现resolveException方法,编写处理异常逻辑。
示例:
@Controller
@RequestMapping("/say")
public class HelloController {
@RequestMapping(value = "/error")
public String error() {
int i = 100/0;
return "success";
}
}
public class SimpleHandlerExceptionResolver implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
ModelAndView mv = new ModelAndView("error");
mv.addObject("errorMsg",ex.getMessage());
return mv;
}
}
hellospringmvc-servlet.xml
<!--创建自定义异常处理对象-->
<bean class="com.self.exceptionhandler.SimpleHandlerExceptionResolver"/>
全局异常类编写方式二
直接在类上使用@ControllerAdvice,在异常处理方法上添加@ExceptionHandler注解。这种做法底层是AOP思想。
示例:
@ControllerAdvice
public class SimpleExceptionHandler {
@ExceptionHandler
public ModelAndView handlerException(Exception e){
ModelAndView mv = new ModelAndView();
mv.setViewName("error");
mv.addObject("errorMsg",e.getMessage());
return mv;
}
}
@ControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
@ResponseBody
Response handleException(Exception exception){
log.error("系统异常信息:{}",exception);
return Response.fail(null,"系统异常,请稍后重试或联系管理员!");
}
@ExceptionHandler({BusinessException.class})
@ResponseBody
Response handleBusinessException(BusinessException exception){
log.error("业务异常信息为:{}",exception);
return new Response<>(exception.getCode() != null ? Integer.parseInt(exception.getCode()) : ResultCode.FAIL.getCode(),exception.getMessage(),null);
}
@ExceptionHandler({MethodArgumentTypeMismatchException.class})
@ResponseBody
Response handleTypeMismatchException(MethodArgumentTypeMismatchException exception){
log.error("业务异常信息为:{}",exception);
return Response.fail(null,"参数有误,请检查参数信息!");
}
@ExceptionHandler(value = {MethodArgumentNotValidException.class})
@ResponseBody
public Object validationExceptionHandler(MethodArgumentNotValidException exception) {
StringBuffer errorInfo = new StringBuffer();
for(ObjectError error :exception.getBindingResult().getAllErrors()){
errorInfo.append(String.format(",%s",error.getDefaultMessage()));
}
log.error("参数验证异常信息为:{}",errorInfo.toString());
return Response.fail(null,errorInfo.toString().substring(1,errorInfo.toString().length()));
}
}
hellospringmvc-servlet.xml
<!--创建自定义异常处理对象-->
<bean class="com.self.exceptionhandler.SimpleExceptionHandler"/>
请求:http://localhost:8080/hellospringmvc/say/error
Spring MVC 异常处理机制没处理前:
HTTP Status 500 - Request processing failed; nested exception is java.lang.ArithmeticException: / by zero
处理后:
注意:如果两种都配置了,会被面向切面先执行返回了。类上使用@ControllerAdvice,在异常处理方法上添加@ExceptionHandler注解。这种做法底层是AOP思想。
Spring MVC 表单数据验证
Spring MVC提供了表单数据验证功能 。
前提:导入数据验证依赖包。
表单数据验证的重点是在Pojo类使用相关注解指定每个属性的验证规则。以下为可以使用的注解:
注解名称 | 说明 |
---|---|
@Null | 被注释的元素必须为 null |
@NotNull | 被注释的元素必须不为 null |
@AssertTrue | 被注释的元素必须为 true |
@AssertFalse | 被注释的元素必须为 false |
@Min(value) | 被注释的元素必须是一个数字,其值必须大于等于指定的最小值 |
@Max(value) | 被注释的元素必须是一个数字,其值必须小于等于指定的最大值 |
@DecimalMin(value) | 被注释的元素必须是一个数字,其值必须大于等于指定的最小值 |
@DecimalMax(value) | 被注释的元素必须是一个数字,其值必须小于等于指定的最大值 |
@Size(max, min) | 被注释的元素的大小必须在指定的范围内(长度大小) |
@Digits(integer, fraction) | 被注释的元素必须是一个数字,其值必须在可接受的范围内 |
@Past | 被注释的元素必须是一个过去的日期 |
@Future | 被注释的元素必须是一个将来的日期 |
@Pattern(value) | 被注释的元素必须符合指定的正则表达式 |
在Controller中,我们需要判断Pojo是否产生了验证错误信息,如果有的话,则把信息转给JSP页面显示。
示例:
pom.xml
<!-- 验证器所需的包 -->
<dependency>
<groupId>com.fasterxml</groupId>
<artifactId>classmate</artifactId>
<version>1.4.0</version>
</dependency>
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
<version>3.3.2.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.0.13.Final</version>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>2.0.1.Final</version>
</dependency>
public class User {
private Integer id;
@NotNull
@Pattern(regexp = "^([a-zA-Z]*[0-9_-]*$)", message = "只能包含字母、数字、下划线,且不能以数字或下划线开头")
@Size(min = 1, max = 110)
private String name;
@NotNull
@Range(min = 1,max = 100,message = "年龄不在合法范围内")
private Integer age;
@Pattern(regexp = "^([a-zA-Z]*$)", message = "只能包含字母")
private String ride;
//...
}
@Controller
public class ValidateController {
@RequestMapping("/check")
public String check(@Valid User user, BindingResult result, Model model) {
//如果表单数据验证有异常
if (result.hasErrors()) {
//取出所有失败信息
List<FieldError> fieldErrors = result.getFieldErrors();
for (FieldError fieldError : fieldErrors) {
//把错误信息存入request域,传递到JSP页面显示
model.addAttribute("ERROR_" + fieldError.getField(), fieldError.getDefaultMessage());
}
return "forward:validate.jsp";
}
System.out.println("User=" + JSON.toJSONString(user));
return "success";
}
}
validate.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>表单数据验证</title>
</head>
<body>
<form action="/hellospringmvc/check" method="post">
用户名:<input type="text" name="name">${ERROR_name}<br/>
年龄:<input type="text" name="age">${ERROR_age}<br/>
坐骑:<input type="text" name="ride">${ERROR_ride}<br/>
<input type="submit" value="提交">
</form>
</body>
</html>
请求:<http://localhost:8080/hellospringmvc/validate.jsp>
输出:
Maven单模块SSM整合
本文讲解使用Maven单模块方式进行Spring MVC+Spring+MyBatis整合。为了把整合步骤体现地更加清晰,我们可以把步骤分为以下六个部分:
- 准备数据库环境
- 单独搭建Spring环境
- 单独搭建Spring MVC环境
- Spring整合Spring MVC
- 单独搭建MyBatis环境
- MyBatis整合Spring
1、准备数据库环境
CREATE TABLE `t_user` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
`name` varchar(64) NOT NULL COMMENT '姓名',
`dept` varchar(254) NOT NULL COMMENT '部门',
`phone` varchar(16) NOT NULL COMMENT '电话',
`height` decimal(10,2) DEFAULT NULL COMMENT '身高',
`create_emp` bigint(20) NOT NULL COMMENT '创建人',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`modify_emp` bigint(20) DEFAULT NULL COMMENT '修改人',
`modify_time` datetime DEFAULT NULL COMMENT '修改时间',
PRIMARY KEY (`id`),
KEY `idx_name` (`name`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COMMENT='用户表';
2、单独搭建Spring环境
创建Web项目——使用原来的hellospringmvc
SSM相关依赖
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.self</groupId>
<artifactId>hellospringmvc</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
</plugins>
</build>
<!--
SSM整合的基础依赖
-->
<!-- 1.spring相关的依赖 -->
<dependencies>
<!-- 1.1 ioc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!--1.2 aop -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.7</version>
</dependency>
<!-- 1.3 声明式事务-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!-- 1.4 test -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!-- 2. mybatis相关依赖 -->
<!-- 2.1 mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.46</version>
</dependency>
<!-- 2.2 数据源 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.6</version>
</dependency>
<!-- 2.3 mybatis核心包 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.5</version>
</dependency>
<!-- 3. springmvc相关依赖-->
<!-- 3.1 springmvc核心包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!--3.2 servlet依赖 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
</dependency>
<!--3.3 jstl标签库-->
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!-- 4. log4j日志 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.2</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.2.3</version>
</dependency>
<!-- 5. spring与mybatis整合包 *** -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.1</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.29</version>
</dependency>
<!-- jackson支持包 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.9.5</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.9.5</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.5</version>
</dependency>
<!-- 验证器所需的包 -->
<dependency>
<groupId>com.fasterxml</groupId>
<artifactId>classmate</artifactId>
<version>1.4.0</version>
</dependency>
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
<version>3.3.2.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.0.13.Final</version>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>2.0.1.Final</version>
</dependency>
</dependencies>
</project>
设计Pojo
public class User {
/**
* id
*/
private int id;
/**
* 名字
*/
private String name;
/**
* 部门,帝国
*/
private String dept;
/**
* 联系号码
*/
private String phone;
/**
* 身高
*/
private BigDecimal height;
/**
* 创建人
*/
private Long createEmp;
/**
* 创建时间
*/
private Date createTime;
/**
* 修改人
*/
private Long modifyEmp;
/**
* 修改时间
*/
private Date modifyTime;
//...
}
编写业务接口和实现
public interface UserService {
List<User> getALlUsers();
}
//给业务实现类加入@Service注解,目的是把该对象放入Spring IOC容器。
@Service("userService")
//@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
private static final Logger logger = LoggerFactory.getLogger(UserServiceImpl.class);
@Override
public List<User> getALlUsers() {
logger.error("查询所有用户成员...");
return userMapper.getALlUsers();
}
}
编写Spring配置
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--spring 容器扫描配置-->
<context:component-scan base-package="com.self">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
</beans>
Spring环境单元测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class SpringTest {
@Autowired
private UserService userService;
@Test
public void test() {
userService.getALlUsers();
}
}
输出表示spring环境搭建成功:
查询所有用户成员...
3、 单独搭建Spring MVC环境
Spring MVC核心控制器web.xml配置
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">
<!-- 配置核心控制器 :DispatcherServlet -->
<servlet>
<servlet-name>hellospringmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- springmvc配置文件加载路径
1)默认情况下,读取WEB-INF下面的default-servlet.xml文件
2)可以改为加载类路径下(resources目录),加上classpath:
-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>WEB-INF/hellospringmvc-servlet.xml</param-value>
<!--<param-value>WEB-INF/simple-servlet.xml</param-value>-->
</init-param>
<!--init-param必须放在load-on-startup前,否则会报错:invalid content was found starting with element 'init-param'. One of '{"http://java.sun.com/xml/ns/javaee":run-as, "http://java.sun.com/xml/ns/javaee":security-role-ref}' is expected-->
<!--
DispatcherServlet对象创建时间问题
1)默认情况下,第一次访问该Servlet时创建对象,默认是访问时创建,意味着在这个时间才去加载hellospringmvc-servlet.xml
2)可以改变为在项目启动时候就创建该Servlet,提高用户访问体验。
<load-on-startup>1</load-on-startup>
数值越大,对象创建优先级越低! (数值越低,越先创建)
-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>hellospringmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!--重新配置Tomcat的DefaultServlet的映射路径-->
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.html</url-pattern>
<url-pattern>*.jpg</url-pattern>
<url-pattern>*.css</url-pattern>
<url-pattern>*.js</url-pattern>
<url-pattern>*.png</url-pattern>
</servlet-mapping>
</web-app>
springmvc配置——hellospringmvc-servlet.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 1.扫描Controller的包-->
<context:component-scan base-package="com.self" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!-- 2.配置视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 2.1 页面前缀 -->
<property name="prefix" value="/WEB-INF/pages/"/>
<!-- 2.2 页面后缀 -->
<property name="suffix" value=".jsp"/>
</bean>
<!-- 3.开启mvc注解驱动-->
<mvc:annotation-driven/>
</beans>
编写UserController
@RequestMapping("/user")
@Controller
public class UserController {
/**
* 查询所有用户
*/
@RequestMapping("list")
public String showAll(Model model) {
List<User> users = userService.getALlUsers();
//存入数据到request域
model.addAttribute("list","用户数据");
//返回list.jsp页面
return "userList";
}
}
userList.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>显示用户数据</title>
</head>
<body>
${list}
</body>
</html>
项目部署到Tomcat
请求:<http://localhost:8080/user/list>
4 、Spring整合Spring MVC
配置Spring监听器
Spring和Spring MVC融合使用,只要在web.xml配置监听器,在项目启动的时候,加载applicationContext.xml文件,把Spring环境启动即可。
web.xml
<!-- 配置spring监听器,用于加载applicationContext.xml(初始化SpringIOC容器) -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
在控制层调用业务层,如果Controller成功注入Service,代表Spring与Spring MVC整合成功!
@RequestMapping("/user")
@Controller
public class UserController {
@Autowired
private UserService userService;
/**
* 查询所有用户
*/
@RequestMapping("list")
public String showAll(Model model) {
List<User> users = userService.getALlUsers();
//存入数据到request域
model.addAttribute("list","用户数据");
//返回list.jsp页面
return "userList";
}
}
输出:
5、 单独搭建MyBatis环境
编写UserDao接口
@Repository
public interface UserMapper {
List<User> getALlUsers();
}
UserMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.self.dao.UserMapper">
<select id="getALlUsers" resultType="User">
select u.create_time createTime,u.id id ,u.name name ,u.dept dept,u.phone phone from `t_user` u where 1 = 1
</select>
</mapper>
mybatis-config.xml——该文件是MyBatis核心配置,里面配置数据源及Dao接口映射等信息。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 读取jdbc.properties -->
<properties resource="jdbc.properties"/>
<!--1.别名扫描 -->
<typeAliases>
<package name="com.self.pojo"/>
</typeAliases>
<!--2.数据库连接 -->
<environments default="mysql">
<environment id="mysql">
<transactionManager type="jdbc"></transactionManager>
<dataSource type="pooled">
<property name="url" value="${jdbc.url}"/>
<property name="driver" value="${jdbc.driver}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<!--3.映射关联 -->
<mappers>
<package name="com.self.dao"/>
</mappers>
</configuration>
jdbc.properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/hello_mybatis?characterEncoding=utf8
jdbc.username=root
jdbc.password=123456
jdbc.initialSize=3
jdbc.maxActive=10
MyBatis测试类
public class MabatisTest {
@Test
public void testFindAll() throws IOException {
//1.加载SqlMapConfig.xml
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
//2.创建SqlSessionFactory
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(in);
//3.创建SqlSession
SqlSession sqlSession = factory.openSession();
//4.创建Dao代理对象
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
//5.执行方法
List<User> list = userMapper.getALlUsers();
System.out.println(JSON.toJSONString(list));
//6.释放资源
sqlSession.close();
in.close();
}
}
输出:
[{"createTime":1585635455000,"dept":"amy empire","id":1,"name":"大青山","phone":"123456"},{"createTime":1585635455000,"dept":"amy empire","id":2,"name":"艾米哈珀","phone":"123456"},{"createTime":1585635455000,"dept":"amy empire","id":3,"name":"池寒枫","phone":"123456"},{"createTime":1585647970000,"dept":"森林矮人王国","id":4,"name":"霍恩斯","phone":"852-253521"}]
参考代码位置:
6 、MyBatis整合Spring
整合的思路是:Spring依赖IOC容器创建MyBatis所需要的SqlSessionFactory,从而利用SqlSessionFactory完成Dao层的操作。
注意:
因为Spring已经把之前MyBatis的数据源及Dao映射等信息都集成了,所以MyBatis的mybatis-config.xml已经不需要啦,可以删除。
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--spring 容器扫描配置-->
<context:component-scan base-package="com.self">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!--载入properties-->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 1. 创建数据源 -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!-- 2. 为了创建Dao代理对象,先创建SqlSessionFactory对象 -->
<!-- SqlSessionFactoryBean: 创建SqlSessionFactory对象的工具 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 注入数据源 -->
<property name="dataSource" ref="dataSource"/>
<!--typeAliasesPackage:批量别名处理 通过这些property就可以把mybatis-config.xml替代掉了-->
<property name="typeAliasesPackage" value="com.self.pojo"/>
<!-- 所有配置的mapper文件 该配置相当于是mybatis-config.xml里的mappers配置,在这边直接扫描获取了-->
<!--<property name="mapperLocations" value="classpath*:com/self/dao/*.xml"/>-->
</bean>
<!-- 3. 扫描Dao接口所在包,扫描后用于创建Dao代理对象,把代理对象放入IOC容器 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!-- Dao扫描目录 -->
<property name="basePackage" value="com.self.dao"/>
<!--<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>-->
</bean>
<!-- 添加事务支持 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 注册事务管理驱动 表示支持声明式事务 @Transactional 注解标注的会被代理实现事务,但要用在有接口的public方法中-->
<!--基于注解的方式使用事务配置声明-->
<tx:annotation-driven transaction-manager="transactionManager" />
</beans>
业务层注入持久层对象
//给业务实现类加入@Service注解,目的是把该对象放入Spring IOC容器。
@Service("userService")
//@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
private static final Logger logger = LoggerFactory.getLogger(UserServiceImpl.class);
@Override
public List<User> getALlUsers() {
logger.error("查询所有用户成员...");
return userMapper.getALlUsers();
}
}
编写测试类
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(value = "classpath:applicationContext.xml")
public class SpringMyBatisTest {
//从IOC容器中获取业务实现
@Autowired
private UserService userService;
@Test
public void testFindAll(){
System.out.println(JSON.toJSONString(userService.getALlUsers()));
}
}
SSM框架已经整合完成。剩下就是把数据显示到JSP页面上。
修改UserController类
@RequestMapping("/user")
@Controller
public class UserController {
@Autowired
private UserService userService;
/**
* 查询所有用户
*/
@RequestMapping("list")
public String showAll(Model model) {
List<User> users = userService.getALlUsers();
//存入数据到request域
model.addAttribute("list", users);
//model.addAttribute("list","用户数据");
//返回list.jsp页面
return "userList";
}
}
修改JSP页面显示内容
userList.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<title>显示用户数据</title>
</head>
<body>
<h3>用户列表</h3>
<table border="1">
<tr>
<td>编号</td>
<td>用户名</td>
<td>帝国</td>
<td>电话号码</td>
<td>创建时间</td>
</tr>
<!--
items: 需要需要遍历的集合
var: 每个对象的变量名称
-->
<c:forEach items="${list}" var="user">
<tr>
<td>${user.id}</td>
<td>${user.name}</td>
<td>${user.dept}</td>
<td>${user.phone}</td>
<td>${user.createTime}</td>
</tr>
</c:forEach>
</table>
</body>
</html>
输出:
报错:
1、在通过controller请求到底层service时报错:springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.self.service.UserService'。
A:这种情况下一般是UserService没有注册到spring容器中,一般分两种情况,要么扫描bean时没有扫到,所以没添加,要嘛是UserService实现类(注意不是UserService接口)没有配置@Service,所以spring没有把他当成一个组件注册到容器中。还有就是在配置UserService实现类时配置@Service错误,要指定名称,否则默认是类名首字母小写后的全称,因此会找不到bean。
还有就是web.xml缺少ContextLoaderListener配置,导致spring容器里的bean没有被加载。
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
2、启动项目时无法启动成功,userService注入不了dao依赖。
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'userService': Unsatisfied dependency expressed through field 'userMapper'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'userMapper' defined in file [F:\practice\hellospringmvc\target\hellospringmvc-1.0-SNAPSHOT\WEB-INF\classes\com\self\dao\UserMapper.class]: Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: org.apache.ibatis.builder.BuilderException: Error parsing Mapper XML. Cause: org.apache.ibatis.builder.BuilderException: Error resolving class. Cause: org.apache.ibatis.type.TypeException: Could not resolve type alias 'User'. Cause: java.lang.ClassNotFoundException: Cannot find class: User
<!--A:这是由于UserMapper.xml的resultType="User",没有指定全路径,这时候要嘛指定全路径,要嘛在applicationContext.xml配置sqlSessionFactory时批量别名处理。
如下: -->
<!-- 2. 为了创建Dao代理对象,先创建SqlSessionFactory对象 -->
<!-- SqlSessionFactoryBean: 创建SqlSessionFactory对象的工具 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 注入数据源 -->
<property name="dataSource" ref="dataSource"/>
<!--typeAliasesPackage:批量别名处理 通过这些property就可以把mybatis-config.xml替代掉了-->
<property name="typeAliasesPackage" value="com.self.pojo"/>
</bean>
Maven多模块SSM整合
一些中大型项目,我希望采用Maven多模块构建方式来搭建SSM整合项目。
Maven多模块构建SSH项目架构图:
示例:
新建一个noodle-parent的project工程。子项目通过右击项目名创建新的maven依赖模块。
整体结构:
1、建立parent工程
配置父工程noodle-parent——pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.self</groupId>
<artifactId>noodle-parent</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
<module>noodle-pojo</module>
<module>noodle-dao</module>
<module>noodle-service</module>
<module>noodle-web</module>
</modules>
<!--
SSM整合的基础依赖
-->
<!-- 1.spring相关的依赖 -->
<dependencies>
<!-- 1.1 ioc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!--1.2 aop -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.7</version>
</dependency>
<!-- 1.3 声明式事务-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!-- 1.4 test -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!-- 2. mybatis相关依赖 -->
<!-- 2.1 mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.46</version>
</dependency>
<!-- 2.2 数据源 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.6</version>
</dependency>
<!-- 2.3 mybatis核心包 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.5</version>
</dependency>
<!-- 3. springmvc相关依赖-->
<!-- 3.1 springmvc核心包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!--3.2 servlet依赖 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
</dependency>
<!--3.3 jstl标签库-->
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!-- 4. log4j日志 -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<!-- 5. spring与mybatis整合包 *** -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.1</version>
</dependency>
</dependencies>
</project>
2、建立pojo工程
编写Pojo类
public class User {
/**
* id
*/
private int id;
/**
* 名字
*/
private String name;
/**
* 部门,帝国
*/
private String dept;
/**
* 联系号码
*/
private String phone;
/**
* 身高
*/
private BigDecimal height;
/**
* 创建人
*/
private Long createEmp;
/**
* 创建时间
*/
private Date createTime;
/**
* 修改人
*/
private Long modifyEmp;
/**
* 修改时间
*/
private Date modifyTime;
//...
}
3、建立dao工程
依赖domain工程
<dependencies>
<dependency>
<groupId>com.self</groupId>
<artifactId>noodle-pojo</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
public interface UserMapper {
List<User> getALlUsers();
}
UserMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.self.dao.UserMapper">
<select id="getALlUsers" resultType="User">
select u.create_time createTime,u.id id ,u.name name ,u.dept dept,u.phone phone from `t_user` u where 1 = 1
</select>
</mapper>
编写Spring的Dao配置
jdbc.properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/hello_mybatis?characterEncoding=utf8
jdbc.username=root
jdbc.password=123456
applicationContext-dao.xml文件只存放与Dao有关的配置 。
<?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:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--载入properties-->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 1. 创建数据源 -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!-- 2. 为了创建Dao代理对象,先创建SqlSessionFactory对象 -->
<!-- SqlSessionFactoryBean: 创建SqlSessionFactory对象的工具 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 注入数据源 -->
<property name="dataSource" ref="dataSource"/>
<!--typeAliasesPackage:批量别名处理 通过这些property就可以把mybatis-config.xml替代掉了-->
<property name="typeAliasesPackage" value="com.self.pojo"/>
<!-- 所有配置的mapper文件 该配置相当于是mybatis-config.xml里的mappers配置,在这边直接扫描获取了-->
<!--<property name="mapperLocations" value="classpath*:com/self/dao/*.xml"/>-->
</bean>
<!-- 3. 扫描Dao接口所在包,扫描后用于创建Dao代理对象,把代理对象放入IOC容器 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!-- Dao扫描目录 -->
<property name="basePackage" value="com.self.dao"/>
<!--<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>-->
</bean>
</beans>
4、建立Service工程
依赖Dao工程 pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>noodle-parent</artifactId>
<groupId>com.self</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>noodle-service</artifactId>
<dependencies>
<dependency>
<groupId>com.self</groupId>
<artifactId>noodle-dao</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
public interface UserService {
List<User> getALlUsers();
}
//给业务实现类加入@Service注解,目的是把该对象放入Spring IOC容器。
@Service("userService")
//@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Override
public List<User> getALlUsers() {
System.out.println("查询所有用户成员...");
return userMapper.getALlUsers();
}
}
编写Spring的Service配置
applicationContext-service.xml
该配置主要需要扫描Service实现类和配置Spring声明式事务。
<?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:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--spring 容器扫描配置-->
<context:component-scan base-package="com.self">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!--Spring声明式事务-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--事务通知-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="get*" isolation="DEFAULT" propagation="SUPPORTS" read-only="true"/>
<tx:method name="load*" isolation="DEFAULT" propagation="SUPPORTS" read-only="true"/>
<tx:method name="select*" isolation="DEFAULT" propagation="SUPPORTS" read-only="true"/>
<tx:method name="find*" isolation="DEFAULT" propagation="SUPPORTS" read-only="true"/>
<tx:method name="*" isolation="DEFAULT" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!--事务切面-->
<aop:config>
<!--切入点-->
<aop:pointcut id="pt" expression="execution(* com.self.service.impl.*ServiceImpl.*(..))"/>
<!--切面-->
<aop:advisor advice-ref="txAdvice" pointcut-ref="pt"/>
</aop:config>
</beans>
5、建立Web工程——项目为Web项目
依赖Service工程 pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>noodle-parent</artifactId>
<groupId>com.self</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<packaging>war</packaging>
<artifactId>noodle-web</artifactId>
<dependencies>
<dependency>
<groupId>com.self</groupId>
<artifactId>noodle-service</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
配置监听器和核心控制器
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">
<!-- 配置核心控制器 :DispatcherServlet -->
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- springmvc配置文件加载路径
1)默认情况下,读取WEB-INF下面的default-servlet.xml文件
2)可以改为加载类路径下(resources目录),加上classpath:
-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:noodlespringmvc-servlet.xml</param-value>
<!--<param-value>WEB-INF/simple-servlet.xml</param-value>-->
</init-param>
<!--init-param必须放在load-on-startup前,否则会报错:invalid content was found starting with element 'init-param'. One of '{"http://java.sun.com/xml/ns/javaee":run-as, "http://java.sun.com/xml/ns/javaee":security-role-ref}' is expected-->
<!--
DispatcherServlet对象创建时间问题
1)默认情况下,第一次访问该Servlet时创建对象,默认是访问时创建,意味着在这个时间才去加载hellospringmvc-servlet.xml
2)可以改变为在项目启动时候就创建该Servlet,提高用户访问体验。
<load-on-startup>1</load-on-startup>
数值越大,对象创建优先级越低! (数值越低,越先创建)
-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!--重新配置Tomcat的DefaultServlet的映射路径-->
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.html</url-pattern>
<url-pattern>*.jpg</url-pattern>
<url-pattern>*.css</url-pattern>
<url-pattern>*.js</url-pattern>
<url-pattern>*.png</url-pattern>
</servlet-mapping>
<!-- 配置spring监听器,用于加载applicationContext.xml(初始化SpringIOC容器) -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:applicationContext-*.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- 字符编码过滤器 -->
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
注意:
Spring监听器读取的路径为classpath*:,
这个语法指加载当前项目及依赖工程的所有符合条件的文件。因为applicationContext.xml分布在不同的Maven工程,所以必须使用该语法加载!
noodlespringmvc-servlet.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 1.扫描Controller的包-->
<context:component-scan base-package="com.self" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!-- 2.配置视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 2.1 页面前缀 -->
<property name="prefix" value="/WEB-INF/pages/"/>
<!-- 2.2 页面后缀 -->
<property name="suffix" value=".jsp"/>
</bean>
<!-- 3.开启mvc注解驱动-->
<!--不添加也能使用,高版本spring已经默认实现了。
在Spring中一般采用@RequestMapping注解来完成映射关系,要想使@RequestMapping注解生效必须向上下文中注册DefaultAnnotationHandlerMapping和一个AnnotationMethodHandlerAdapter实例,这两个实例分别在类级别和方法级别处理。而<mvc:annotation-driven/>配置帮助我们自动完成上述两个实例的注入。
-->
<mvc:annotation-driven/>
<!--<mvc:default-servlet-handler/>-->
</beans>
@RequestMapping("/user")
@Controller
public class UserController {
@Autowired
private UserService userService;
/**
* 查询所有用户
*/
@RequestMapping("/list")
public String showAll(Model model) {
List<User> users = userService.getALlUsers();
//存入数据到request域
model.addAttribute("list", users);
//model.addAttribute("list","用户数据");
//返回list.jsp页面
return "userList";
}
}
userList.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<title>显示用户数据</title>
</head>
<body>
<h3>用户列表</h3>
<table border="1">
<tr>
<td>编号</td>
<td>用户名</td>
<td>帝国</td>
<td>电话号码</td>
<td>创建时间</td>
</tr>
<!--
items: 需要需要遍历的集合
var: 每个对象的变量名称
-->
<c:forEach items="${list}" var="user">
<tr>
<td>${user.id}</td>
<td>${user.name}</td>
<td>${user.dept}</td>
<td>${user.phone}</td>
<td>${user.createTime}</td>
</tr>
</c:forEach>
</table>
</body>
</html>
输出:
注意:当我们配置项目到tomcat上时,在创建artifact时可以指定项目名称(不一定要是项目名)作为请求的路径,像这样接在localhost后面,http://localhost:8080/noodle/user/list
。如果只是设置/ (斜杠)则直接在端口后面接请求地址。
疑问
Q:在springmvc容器的配置文件hellospringmvc-servlet.xml中配置开启mvc注解驱动的作用是什么?都作用在哪些地方?什么时候用到?
教程里的解释是在Spring中一般采用@RequestMapping注解来完成映射关系,要想使@RequestMapping注解生效必须向上下文中注册DefaultAnnotationHandlerMapping和一个AnnotationMethodHandlerAdapter实例,这两个实例分别在类级别和方法级别处理。而<mvc:annotation-driven/>配置帮助我们自动完成上述两个实例的注入。 但是在实践中我们就算没有显式注册这两个bean实例或者在spring mvc配置的hellospringmvc-servlet.xml中加上 <mvc:annotation-driven/>这句配置也不妨碍我们使用@RequestMapping注解来完成映射关系,为什么?
<!-- 3.开启mvc注解驱动-->
<mvc:annotation-driven/>
Q:什么叫Ant风格的路径匹配功能?
A: ANT通配符有以下三种:
通配符 | 说明 |
---|---|
? | 匹配任何单字符 |
* | 匹配0或者任意数量的字符 |
** | 匹配0或者更多的目录 |
例子:
URL路径 | 说明 |
---|---|
/app/*.x | 匹配(Matches)所有在app路径下的.x文件 |
/app/p?ttern | 匹配(Matches) /app/pattern 和 /app/pXttern,但是不包括/app/pttern |
/**/example | 匹配(Matches) /app/example, /app/foo/example, 和 /example |
/app/*/dir/file. | 匹配(Matches) /app/dir/file.jsp, /app/foo/dir/file.html,/app/foo/bar/dir/file.pdf, 和 /app/dir/file.java |
/*/.jsp | 匹配(Matches)任何的.jsp 文件 |
属性: 最长匹配原则(has more characters) 说明,URL请求/app/dir/file.jsp,现在存在两个路径匹配模式//.jsp和/app/dir/.jsp,那么会根据模式/app/dir/*.jsp来匹配
Q:RequestMappingHandlerMapping会被默认创建么?在什么情况下创建,标记有@RequestMapping("/hello") 注解时还是扫描Controller时?
A:是在web.xml配置<mvc:annotation-driven/>
时。RequestMappingHandlerMapping和RequestMappingHandlerAdapter会默认注册。
当然,一般情况下我们是不需要配置<mvc:annotation-driven/>
的,默认会注册,但当我们web.xml中配置了其他如BeanNameUrlHandlerMapping处理映射器时,就要加这句,否则不会默认帮我们注册,这个需要研究下代码是怎么个处理方式。
在不配置HandlerMapping 的情况下,容器默认会注册初始化BeanNameUrlHandlerMapping 和 RequestMappingHandlerMapping来处理映射。而HandlerAdapter会有三种,分别是HttpRequestHandlerAdapter,SimpleControllerHandlerAdapter,RequestMappingHandlerAdapter。
org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping
org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter
注意:如果有在web.xml中配置指定的HandlerMapping 和 HandlerAdapter 的话,则只注册配置的处理器。
mvc:annotation-driven的作用
Spring 3.0.x中使用了mvc:annotation-driven后,默认会帮我们注册默认处理请求,参数和返回值的类,其中最主要的两个类:DefaultAnnotationHandlerMapping 和 AnnotationMethodHandlerAdapter ,分别为HandlerMapping的实现类和HandlerAdapter的实现类,从3.1.x版本开始对应实现类改为了RequestMappingHandlerMapping和RequestMappingHandlerAdapter。
Q:如果我们没有配置HandlerAdapter ,默认是创建什么类型的HandlerAdapter 来处理我们的Controller呢?
A:默认不配置SimpleControllerHandlerAdapter, 也能处理Controller类型为org.springframework.web.servlet.mvc.Controller的接口,SimpleControllerHandlerAdapter会默认注册.
Q:保存HandlerMethod类型的容器mappingLookup为什么要初始化为new LinkedHashMap<>();而不是HashMap<>()类型呢?出于什么考虑?LinkedHashMap和HashMap各种的使用场景和优势缺点是什么?
A:考虑动态添加的效率?
Q:转发和重定向的区别?
Q:在目前主流的三种 Web 服务交互方案中,REST 相比于SOAP(Simple Object Access protocol, 简单对象访问协议) 以及 XML-RPC 更加简单明了。了解下目前主流的Web 服务交互方案。
Q:什么叫 Payload 的编码?
Q:一般实践中的RESTful风格的请求开发是通过请求方式POST、GET等的不同来区分的么?
Q:ApplicationContext和WebApplicationContext有什么区别?用他们去getBean()是一样的?
A:Spirng容器只有一个,spring和springmvc的配置可以互相放在各自的配置xml中,最后都作用在spring容器中。待详细了解。
Q:为什么叫Spring MVC、MyBatis整合Spring?而不是反过来叫Spring MVC整合Spring呢?是随便叫还是有什么区别?
Q:ContextLoaderListener的作用?
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
Q:@Repository注解在mybatis中有什么用么?一般不都是通过扫包来获得dao对象么?
//不需要加注解
//@Repository
//@Mapper
public interface UserMapper {
List<User> getALlUsers();
}
其他:
1、md删除线使用:
如果段落上的文字要添加删除线,只需要在文字的两端加上两个波浪线 ~~ 即可
~~shanchu.com~~
2、DispatcherServlet.properties ——DispatcherServlet初始化默认配置
org.springframework.web.servlet.DispatcherServlet#defaultStrategies jdk1.8 284行
# Default implementation classes for DispatcherServlet's strategy interfaces.
# Used as fallback when no matching beans are found in the DispatcherServlet context.
# Not meant to be customized by application developers.
org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver
org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver
org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping
org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter
org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,\
org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver
org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator
org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver
org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager
3、IDEA的tomcat日志输出乱码问题的解决。
这是因为下载的tomcat8中\apache-tomcat-8.5.55\conf\logging.properties 配置默认编码是UTF-8.而Windows系统的默认编码格式是GBK,因此在对输出的字符打印时因为解码不对而导致的乱码。只要对logging.properties中的编码格式UTF-8配置注释掉即可。
4、JDK1.8对接口的默认实现。
public interface HandlerInterceptor {
default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return true;
}
}