mvc: m model v view c controller
入门案例的实现
1、导入依赖,如果配置dispatcherServlet报错,再导入servlet-api依赖即可
<!--1、springmvc的依赖--><dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId><version>5.2.8.RELEASE</version></dependency>
2、配置前端控制器dispatcherServlet(web.xml文件)
<!--2、配置DispatcherServlet--><servlet><servlet-name>springmvc</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><!-- 修改springmvc项目的配置文件名称及路径 --><init-param><param-name>contextConfigLocation</param-name><param-value>classpath:springmvc.xml</param-value></init-param></servlet><servlet-mapping><servlet-name>springmvc</servlet-name><!-- 如果使用/拦截所有请求,那么今后一定要进行静态资源映射 --><url-pattern>/</url-pattern></servlet-mapping>
3、编写控制器
/*** controller就相当于web项目中的servlet,用于接收请求,作出响应*/@Controllerpublic class UserController {// http://localhost:8080/hello@RequestMapping("/hello")public String hello(){System.out.println("hello springmvc");return "main";//逻辑视图名}}
4、编写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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttps://www.springframework.org/schema/context/spring-context.xsd"><!--扫描@Controller注解--><context:component-scan base-package="com.woniuxy.controller"/><!--配置springmvc的三大组件--><!--配置映射器处理器--><bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/><!--配置映射器适配器--><bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"/><!--配置视图解析器--><bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"><!-- main -> /main.jsp --><property name="prefix" value="/"/><property name="suffix" value=".jsp"/></bean></beans>
springmvc静态资源映射
当dispatcherservlet的url-pattern设置的是”/“,拦截所有请求,此时需要对项目中的静态资源进行放行,否则访问不到。
使用mvc:resources标签进行静态资源的放行设置。
<!--静态资源映射http://localhost:8080/css/main.cssmapping:用于匹配请求的url中的内容location:用于指定一个项目中的路径--><mvc:resources mapping="/css/**" location="/css/"/><mvc:resources mapping="/js/**" location="/js/"/><mvc:resources mapping="/images/**" location="/images/"/><mvc:resources mapping="/html/**" location="/html/"/>
自定义类型转换器
当spring提供的类型转换器不足以解决请求参数中传递的参数值的时候,需要使用自定义类型转换器。
1、自定义转换器需要实现一个converter接口 S:要被转换的类型 T:转换之后得到的类型
public class String2Date implements Converter<String, Date> {@Overridepublic Date convert(String source) {Date date=null;if (StringUtils.hasLength(source)) {//使用simpleDateFormat对当前字符串进行转换即可。//yyyy-MM-dd//yyyy/MM/ddString pattern1="[0-9]{4}-[0-9]{2}-[0-9]{2}";String pattern2="[0-9]{4}/[0-9]{2}/[0-9]{2}";try {if (source.matches(pattern1)) {date=new SimpleDateFormat("yyyy-MM-dd").parse(source);}if (source.matches(pattern2)) {date=new SimpleDateFormat("yyyy/MM/dd").parse(source);}} catch (ParseException e) {e.printStackTrace();}}else{//被转换的字符串为空时,进行自定义的处理。throw new RuntimeException("参数为空");}return date;}}
2、将自定义的类型转换器添加到spring提供的类型转换器集合中
<!--配置自定义的类型转换器:conversionServiceFactoryBean--><bean id="conversionService2" class="org.springframework.context.support.ConversionServiceFactoryBean"><property name="converters"><set><bean class="com.woniuxy.converter.MyConverter"/></set></property></bean>
3、将修改过后的conversionService2重新注册到spring中。
<!--spring3.1.*以上版本中,使用了 <mvc:annotation-driven/>替换了RequestMappingHandlerMapping和RequestMappingHandlerAdapter<mvc:annotation-driven>会自动注册RequestMappingHandlerMapping与RequestMappingHandlerAdapter两个Bean,conversion-service:将变更后的conversionService重新注册给spring--><mvc:annotation-driven conversion-service="conversionService2"/>
请求参数映射
1、简单类型的请求参数映射(String 和 基本数据类型)
需要注意的是:请求参数的名称必须和控制器方法的形参名称保持一致,否则注入不了。
<form action="/user/login" method="post">用户名:<input type="text" name="username" value="tom"><br>密码:<input type="text" name="password" value="111"><br><input type="submit" value="登录" id="btn"></form>
//请求参数映射:基本数据类型和String,直接就进行转换并注入到控制器的方法形参中//要求:方法的形参名必须和请求参数的名称保持一致,否则无法注入@RequestMapping("/user/login")public String login(User user){System.out.println(user);return "index";}
2、同名参数的映射
<input type="checkbox" name="hobbies" value="eat">吃饭<input type="checkbox" name="hobbies" value="sleep">睡觉<input type="checkbox" name="hobbies" value="watch tv">看电视
@RequestMapping("/login")public String login(String[] hobbies){System.out.println(hobbies.length);return "index";}或@RequestMapping("/login")public String login(@RequestParam ArrayList<String> hobbies){System.out.println(hobbies);return "index";}
3、javaBean接收请求参数
要求:javaBean的成员变量名必须和请求参数名称保持一致
<form action="/user/login" method="post">用户名:<input type="text" name="username" value="tom"><br>密码:<input type="text" name="password" value="111"><br><input type="submit" value="登录" id="btn"></form>
@Datapublic class User {String username;String password;}
4、ajax传递的参数
4.1 传递JSON对象时,与普通参数传递没有区别
var form={username:$("#username").val(),password:$("#password").val()}$.ajax({url:"/user/realLogin",data:form,type:"post",dataType:"json",success:function (resp) {alert(resp)}})
@RequestMapping("/realLogin")public String realLogin(User user){System.out.println(1111);System.out.println(user);return "";}
4.2 传递json字符串,要求是一个标准的json字符串
如果传递的是json字符串,
请求方式必须是post
请求的内容类型必须是 contentType:”application/json;charset=utf-8”
后台接收时必须使用@RequestBody
springmvc使用jackson来解析json字符串,所以必须导入相应的三个依赖。
jackson-core jackson-annotations jackson-databind(如果报错,则导入其中一个)
<dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-core</artifactId><version>2.10.5</version></dependency><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-annotations</artifactId><version>2.10.5</version></dependency><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.10.5</version></dependency>
var form={username:$("#username").val(),password:$("#password").val()}//以json字符串的方式向后台传递参数//1、请求方式必须是post//2、请求的内容类型必须是 contentType:"application/json;charset=utf-8"//3、后台接收时必须使用@RequestBody 只能使用一个//4、springmvc使用jackson来解析json字符串,所以必须导入相应的三个依赖。//jackson-core jackson-annotations jackson-databindform=JSON.stringify(form)$.ajax({url:"/user/realLogin",data:form,contentType:"application/json;charset=utf-8",type:"post",dataType:"json",success:function (resp) {alert(resp)}})
@RequestMapping("/realLogin")public String realLogin(@RequestBody User user){System.out.println(1111);System.out.println(user);return "";}
路径传参
restful风格的路径传参:http://localhost:8080/login/tom/111 参数存在于路径中。
不是路径传参:http://localhost:8080/login?username=tom&password=111
1、在@RequestMapping注解完整映射路径 /login/{username}/{password}
@PathVariable 映射 URL 绑定的占位符
- 带占位符的 URL 是 Spring3.0 新增的功能,该功能在SpringMVC 向 REST 目标挺进发展过程中具有里程碑的意义
- 通过 @PathVariable 可以将 URL 中占位符参数绑定到控制器处理方法的入参中:URL 中的 {xxx} 占位符可以通过@PathVariable(“xxx“) 绑定到操作方法的入参中
@RequestMapping("testRestFul/{username}/{password}")public String testRestFul(@PathVariable String username,@PathVariable String password){System.out.println(username+":"+password);return "redirect:/html/main.html";//redirect 重定向}
注意:使用路径传参,不能再使用javaBean的形式进行参数注入。
servletAPI的获取
在Spring MVC 中使用 @RequestMapping 来映射请求,也就是通过它来指定控制器可以处理哪些URL请求,相当于Servlet中在web.xml中配置
@RequestMapping("testServletApi")public String testServletApi(HttpServletRequest request, HttpSession session){System.out.println(request);System.out.println(session);System.out.println(session.getServletContext());return "redirect:/html/main.html";}
注意:servletcontext是无法注入的
响应返回值
string:
//响应回去的类型:String :逻辑视图名 物理视图(forward:请求转发 redirect:重定向)//String//逻辑视图名:一定配置视图解析器//物理视图:forward redirect关键字来实现直接的跳转。//也可以使用forward 或redirect 关键字实现控制器方法的跳转//ajax请求: JS域中发出来的 @responseBody
逻辑视图名:
@RequestMapping("/login")public String login(ArrayList<String> hobbies){System.out.println(hobbies);return "index";}
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"><property name="prefix" value="/"/><property name="suffix" value=".html"/></bean>
物理视图
默认请求转发,forward关键字可以省略不写
//http://localhost:8080/user/testForward@RequestMapping("testForward")public String testForward(){System.out.println("请求转发");//return "/html/main.html";return "forward:/html/main.html";}
重定向
//http://localhost:8080/user/testRedirect@RequestMapping("testRedirect")public String testRedirect(){System.out.println("重定向");return "redirect:/html/main.html";}
控制器方法内部跳转
//http://localhost:8080/user/testRedirect@RequestMapping("testRedirect")public String testRedirect(){System.out.println("重定向");return "redirect:toLogin";}@RequestMapping("toLogin")public String toLogin(){System.out.println("重定向");return "redirect:/html/main.html";}
ajax请求: JS域中发出来的 @responseBody
@ResponseBody的作用其实是将java对象转为json格式的数据。
@responseBody注解的作用是将controller的方法返回的对象通过适当的转换器转换为指定的格式之后,写入到response对象的body区,通常用来返回JSON数据或者是XML数据。
serialize() 方法通过序列化表单值,创建 URL 编码文本字符串。
$(function () {$("#btn").click(function () {$.ajax({url:"/user/login",data:$("#myForm").serialize(),dataType:"json",type:"post",success:function (resp) {console.log(resp)}})})})
@RequestMapping("login") //http://localhost:8080/user/login?username=tom&password=111@ResponseBodypublic String login(String username,String password){System.out.println(username+":"+password);// return "redirect:/html/main.html";// return "{\"msg\":\"success\"}";return new JSONObject().toJSONString("success");}
void(了解)
如果控制器方法无返回值时,会自动将请求路径中的requestMapping部分作为逻辑视图名进行相应的跳转,如果没有视图解析器,会报错,在项目中必须存在与逻辑名对应的物理视图才可以正常访问,否则404。
object 给ajax用 @ResponseBody
//Object//http://localhost:8080/user/getUser?username=tom&password=111@RequestMapping("getUser")@ResponseBody//一定需要统一返回给前端的格式 dto处理 data transfer objectpublic User getUser(User user){System.out.println(user);return user;}
统一返回给前端的数据格式 dto
dto: data transfer object
自定义一个dto
@Data@AllArgsConstructor@NoArgsConstructorpublic class Result<T> {private Boolean flag;//当前端发起调用时,告诉前端此次调用实现的功能是否成功private String code;//自定义的响应状态码private String message;//封装返回给前端的消息private T data;//用于封装返回给前端的真实数据}
@RequestMapping("findAll")@ResponseBodypublic Result findAll(){//执行了某个业务,返回一个用户数据的集合List<User> users= Arrays.asList(new User("tom","111"),new User("jack","222"));return new Result(true, StatusCode.OK,"查询用户数据成功",users);}
ModelAndView
spring提供的一个对象,可用该对象向对应的域中存储数据(model),也可使用该对象设置对应的视图(view)
//ModelAndView model模型 view视图@RequestMapping("testModelAndView")public ModelAndView testModelAndView(){ModelAndView mv = new ModelAndView();mv.addObject("user",new User("tom","111"));//向model中保存数据mv.setViewName("/html/main.html");//设置视图名称return mv;}
注意的是:数据存放在request域中,如果不使用模板,是无法取得保存的数据的。
如果是前后端分离的项目,不会再使用此种方式。
restful风格的接口设计
实际项目开发中,目前基本上使用的都是前后端分离的技术,前端ajax发送rest时,后台应对应的使用restful风格去进行接口设计。
restful风格的接口设计* 后台使得rest请求方式进行接口设计* rest: GET POST PUT DELETE* GET 查询* POST 新增* PUT 修改* DELETE 删除** restful风格的接口设计:在requestmapping中不应该出现动词** 幂等性:面试的时候可能被问到* 对同一个接口的相同操作,结果应该相同* 只有POST请求不是幂等性的,其余的GET\PUT\DELETE都是幂等性的。
@Controllerpublic class RestFulController {@RequestMapping(value="user",method= RequestMethod.GET)public String find(){System.out.println("执行了find操作");return "redirect:/html/main.html";}@RequestMapping(value = "user",method = RequestMethod.POST)public String save(){System.out.println("执行了save操作");return "redirect:/html/main.html";}@RequestMapping(value = "user",method = RequestMethod.PUT)public String update(){System.out.println("执行了update操作");return "redirect:/html/main.html";}@RequestMapping(value = "user",method = RequestMethod.DELETE)public String delete(){System.out.println("执行了delete操作");return "redirect:/html/main.html";}}
测试时,可以使用postman或者发送$.ajax({type:GET|POST|PUT|DELETE}),也可以使用idea提供的测试工具
tools->http client->test restful web service
自定义异常处理器
实现一个handlerExceptionResolver接口,注意,自定义异常处理器只要注册到容器中,会自动生效。
@Component (把普通pojo实例化到spring容器中,相当于配置文件中的 )
泛指各种组件,就是说当我们的类不属于各种归类的时候(不属于@Controller、@Services等的时候),我们就可以使用@Component来标注这个类。
/*** 自定义异常处理器*/@Componentpublic class CustomerExceptionHandler implements HandlerExceptionResolver {@Overridepublic ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {//看看用户发送的请求是否是ajax请求,如果是,处理的方式一定和普通形式的请求是不一样的。ModelAndView mv = new ModelAndView();if ("XMLHttpRequest".equals(request.getHeader("X-Requested-With"))) {//ajax请求//创建json视图,使用的是fastJsonFastJsonJsonView jsonView = new FastJsonJsonView();HashMap<String, Object> map = new HashMap<>();map.put("message","服务器繁忙");jsonView.setAttributesMap(map);mv.setView(jsonView);}else{mv.setViewName("/html/error.html");}return mv;}}
全局异常总类
在spring 3.2中,新增了@ControllerAdvice 注解,可以用于定义@ExceptionHandler、@InitBinder、@ModelAttribute,并应用到所有@RequestMapping中。参考:@ControllerAdvice 文档
@RestControllerAdvicepublic class GlobalExceptionHandler {//全局异常总类中的编写规则:// 一定会有一个处理Exception类的方法兜底// 在异常总类中,对某一个具体的异常进行处理。@ExceptionHandler(NullPointerException.class)public Result handlerNullPointException(){return new Result(false,"20002","空指针异常",null);}@ExceptionHandler(ArithmeticException.class)public Result handlerArithmeticException(){return new Result(false,"20003","算术异常",null);}@ExceptionHandler(Exception.class)public Result handlerException(){return new Result(false,"20001","服务器异常",null);}}
使用@RestController代替@Controller和@ResponseBody
//使用了@RestController注解,一定没有办法进行页面跳转。
在使用restful风格进行接口设计时
使用@(Get|Post|Put|Delete)Mapping代替@RequestMapping(value=”user”,method= RequestMethod.GET|POST|PUT|DELETE)
文件上传
springmvc提供的文件解析器CommonsMultipartResolver,由类名可知,springmvc的文件上传是依赖于commons组件的,因此,要实现文件上传,必须导入commons-fileupload组件。
springmvc提供了一个类用于封装文件域中的内容:MultipartFile,需要注意的是此类型的对象名必须与文件名的name属性值保持一致。
文件上传的前置条件:请求提交方式只能是POST,表单的enctype属性只能是multipart/form-data
文件上传的步骤:
1、导入依赖
2、编写配置,文件解析器的id必须配置,且值只能为multipartResolver
<!-- 配置文件解析器:必须加id且值只能是multipartResolver --><bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"><property name="maxUploadSize" value="#{1024*1024*200}"/></bean>
3、编写接口处理上传请求
@PostMapping("user/register")public String register(User user, MultipartFile upload) throws IOException {System.out.println(user);System.out.println("中国");//确定文件上传后的存储路径String path="D:/files".replace("/", File.separator);File file = new File(path);if (!file.exists()) {//如果文件不存在,则创建一个file.mkdirs();}//处理上传之后的存储的文件名称String originalFilename = upload.getOriginalFilename();//获取文件的原始名称String fileType=originalFilename.substring(originalFilename.lastIndexOf("."));String savefileName = UUID.randomUUID().toString().replace("-", "")+System.currentTimeMillis()+fileType;//创建接收上传文件内容的文件对象File saveFile = new File(path, savefileName);//执行上传操作upload.transferTo(saveFile);return "redirect:/html/login.html";}
文件下载
文件如果需要被下载,响应头中Content-Disposition的值只要不是内连,就可以以附件形式提供出来,否则文件的内容会直接出现在浏览器中。
springmvc提供了一个responseEntity(响应实体),用于封装响应体、响应头、响应状态码
/*** 文件下载* @param filename* @return* @throws IOException*/@GetMapping("user/download")public ResponseEntity download(String filename) throws IOException {//1、将对应的文件读取到程序中来String path="D:/files".replace("/", File.separator);File file = new File(path, filename);//重新对文件名称进行编码,解决的是中文文件名乱码导致的下载后的文件名丢失的问题filename= URLEncoder.encode(filename,"UTF-8");//2、修改响应头信息HttpHeaders httpHeaders = new HttpHeaders();//attchement表示文件以附件的形式存在httpHeaders.add("Content-Disposition","a;filename="+filename);//3、设置响应状态码HttpStatus status = HttpStatus.OK;//4、封装响应体byte[] bytes = FileUtils.readFileToByteArray(file);//将响应头、响应码、响应体封装成responseEntity对象ResponseEntity responseEntity = new ResponseEntity(bytes,httpHeaders,status);return responseEntity;}//文件下载的步骤://1、获取下载的文件名,并对其进行处理(中文文件名的重新编码)//2、设置响应头 Content-Disposition:attchement//3、设置响应状态码//4、读取文件系统中要被下载的文件 FileUtils.readFileToByteArray()//5、使用responseEntity的构造方法封装响应体、响应头、响应状态码,将之返回出去即可实现
另一种方式,直接使用IO也可以完成文件的下载。
@RequestMapping("/download/{filename}/{filetype}")public void download(@PathVariable("filename")String filename,@PathVariable("filetype")String filetype,HttpServletRequest request, HttpServletResponse response) throws IOException {String downloadFileName = filename + "." + filetype;String path="D:/uploads".replace("/",File.separator);File downloadFile = new File(path, downloadFileName);//设置响应头信息response.setHeader("Content-Disposition", "attachment;filename="+URLEncoder.encode(downloadFile.getName(),"UTF-8"));//获取字节输出注ServletOutputStream os = response.getOutputStream();//使用commons组件FileUtils读取下载文件获得字节数组byte[] bytes = FileUtils.readFileToByteArray(downloadFile);//使用输出将字节数组写到浏览器os.write(bytes);//清空输出流os.flush();//关闭输出流os.close();}}
参数校验
对于提交到后台的参数,会做两步验证:第一步验证发生在前端表单提交时,第二步验证发生在后台参数注入的时候。
JSR303方式进行的参数校验,官方推荐的实践:hibernate-validator
导入依赖
<dependency><groupId>org.hibernate.validator</groupId><artifactId>hibernate-validator</artifactId><version>6.1.5.Final</version></dependency>
控制器方法中使用javaBean来接收请求参数,在javaBean的成员变量上使用对应的注解,来对注入的参数值进行校验。
@Datapublic class User {@NotNull(message = "用户名不能为空")@Length(min = 10,max = 16,message = "用户名长度必须在10-16之间")@Pattern(regexp = "[a-zA-Z]\\w+",message = "用户名不能以数字开头")private String username;@NotNull(message = "密码不能为空")@Length(min = 6,max = 8,message = "长度只能在6-8之间")private String password;@NotNull(message = "邮箱不能为空")@Pattern(regexp= "\\w{1,15}@\\w{1,15}(.\\w{2,3}){1,2}",message = "邮箱格式不正确")private String email;}
控制器中使用@valid注解对参数注入的值进行校验,校验结果保存在bindingResult中,请注意:bindingResult只能紧跟在要校验的对象之后。
@PostMapping@ResponseBodypublic Result register(@Valid User user, BindingResult bindingResult) throws ParameterValidetorException {//判断验证结果中,是否有错误,如果有错误,意味着参数验证没通过if (bindingResult.hasErrors()) {throw new ParameterValidetorException();}System.out.println(user);return new Result(true, StatusCode.OK,"注册成功",null);}
拦截器
拦截器是springmvc中一个强大的控件,它的功能有点类似于过滤器。拦截器的作用时期是在前端控制器(dispatcherservlet)和被@Controller注解的控制器之间。
在springmvc中,如果要创建拦截器,需要实现一个接口:handlerInterceptor。实现业务时,主要关注:preHandle(),此方法决定是请求是否能向下一步进入到controller中。
步骤1:创建拦截器
/*** 只要实现了HandlerInterceptor接口,当前类就会是一个拦截器*/public class FirstInterceptor implements HandlerInterceptor {/*** 前置处理:在业务控制器方法执行之前执行的* 根据preHandle方法的返回值,决定是否放行 true 放行 false 拦截* @param request* @param response* @param handler* @return* @throws Exception*/@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {System.out.println("FirstInterceptor的preHandle执行了");//放行return true;}/*** 后置处理:在控制器方法执行之后再执行的* @param request* @param response* @param handler* @param modelAndView* @throws Exception*/@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {System.out.println("FirstInterceptor的postHandle执行了");}/*** 渲染视图完成之后执行的* @param request* @param response* @param handler* @param ex* @throws Exception*/@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {System.out.println("FirstInterceptor的afterCompletion执行了");}}
步骤2:配置拦截器
<!--开启拦截器配置--><mvc:interceptors><!--配置某个拦截器,该配置可以有多个--><mvc:interceptor><!--mvc:mapping:用于配置被拦截器拦截的请求资源 黑名单 --><mvc:mapping path="/**"/><!--mvc:exclude-mapping:用于配置不被拦截器拦截的请求资源 白名单 --><mvc:exclude-mapping path="/"/><mvc:exclude-mapping path="/user/login"/><mvc:exclude-mapping path="/user/register"/><mvc:exclude-mapping path="/html/login.html"/><mvc:exclude-mapping path="/html/register.html"/><mvc:exclude-mapping path="/css/*"/><mvc:exclude-mapping path="/js/*"/><mvc:exclude-mapping path="/images/*"/><!--配置使用哪个拦截器来处理被拦截的资源--><bean class="com.woniuxy.interceptor.FirstInterceptor"/></mvc:interceptor></mvc:interceptors>
拦截器链的执行顺序由配置文件中的拦截器配置顺序来决定。
拦截器应用 登录验证
public class LoginInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {//登录验证//判断session中是保存了登录状态Object loginUser = request.getSession().getAttribute("loginUser");if (ObjectUtils.isEmpty(loginUser)) {response.setCharacterEncoding("utf-8");response.setContentType("text/html;charset=utf-8");response.getWriter().write("当前用户尚未登录");//拦截return false;}return true;}}
<!--开启拦截器配置--><mvc:interceptors><mvc:interceptor><mvc:mapping path="/**"/><mvc:exclude-mapping path="/"/><mvc:exclude-mapping path="/user/login"/><mvc:exclude-mapping path="/user/register"/><mvc:exclude-mapping path="/html/login.html"/><mvc:exclude-mapping path="/html/register.html"/><mvc:exclude-mapping path="/css/*"/><mvc:exclude-mapping path="/js/*"/><mvc:exclude-mapping path="/images/*"/><bean class="com.woniuxy.interceptor.LoginInterceptor"/></mvc:interceptor></mvc:interceptors>
ssm整合 (spring springmvc mybatis)
整合时如果不考虑父子容器的问题,就一定导致一个后果:声明式事务失效。
父子容器
让spring扫描service
让springmvc扫描controller
整合实现
spring整合mybatis
与之前的配置一样。
spring整合springmvc
1、web.xml配置,让web容器启动时先创建父容器,后创建子容器。
<!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>
<display-name>Archetype Created Web Application</display-name>
<!--加载类路径下的xml文件创建父容器-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<!--监听servletContext对象的创建-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- 创建dispatcherservlet时,创建子容器 -->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
2、配置spring和springmvc各自扫描的包
让spring扫描service
<context:component-scan base-package="com.woniuxy.service"/>
让springmvc扫描controller
<context:component-scan base-package="com.woniuxy.controller"/>
此时整合即已完成。
SSM整合及声明式事务:
applicationContext.xml 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:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<context:component-scan base-package="com.woniuxy.service"/>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql:///bank?characterEncoding=utf-8&useSSL=false"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
<!--
配置sqlSessionFactoryBean
作用:产生一个sqlsessionfactory 只要ioc容器中有任何一个bean需要使用sqlsession,
那么就会由sqlsessionfactory自动创建一个给其使用。
-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<!-- <property name="configLocation" value="classpath:sqlConfig.xml"/>-->
</bean>
<!--
配置MapperScannerConfigurer:
作用:用于扫描指定的包,创建对应接口的代理对象
-->
<bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.woniuxy.mapper"/>
</bean>
<!--配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 对声明式事务的注解提供支持 -->
<tx:annotation-driven/>
</beans>
springmvc.xml 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"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<context:component-scan base-package="com.woniuxy.controller"/>
<mvc:annotation-driven/>
<!-- <mvc:resources mapping="/js/**" location="/js/"/>-->
<mvc:resources mapping="/html/**" location="/html/"/>
</beans>
数据访问层
public interface AccountMapper {
@Select("select * from account where username=#{username}")
Account findByUserName(String username);
@Update("update account set balance=#{balance} where username=#{username}")
int updateAccount(Account account);
}
业务层
@Service("accountService")
@Transactional
public class AccountServiceImpl implements AccountService {
@Resource
private AccountMapper accountMapper;
@Override
public void transfer(TransferVo transferVo){
//查询要进行转帐操作的两个人的帐户信息
Account sourceAccount = accountMapper.findByUserName(transferVo.getSource());
Account targetAccount = accountMapper.findByUserName(transferVo.getTarget());
//判断被转出人的帐户余额是否足够
if (sourceAccount.getBalance()>transferVo.getMoney()) {
//真正执行转账操作
sourceAccount.setBalance(sourceAccount.getBalance()-transferVo.getMoney());
targetAccount.setBalance(targetAccount.getBalance()+transferVo.getMoney());
accountMapper.updateAccount(sourceAccount);
System.out.println(1/0);
accountMapper.updateAccount(targetAccount);
}
}
}
控制层
@Controller
public class AccountController {
@Resource
private AccountService accountService;
@RequestMapping("account/transfer")
@ResponseBody
public Result transfer(TransferVo transferVo){
accountService.transfer(transferVo);
return new Result(true, StatusCode.OK,"转账成功",null);
}
}
Springmvc执行流程

