一、SpringMVC入门
-
导包:除了5个基本jar外加入 spring-web-.jar spring-webmvc-.jar
-
Springmvc核心组件: 核心控制器:DispatcherServlet(web.xml当中配置) 控制器: Controller 处理映射器:HandlerMapping 视图解析器:ViewResolver 拦截器: Interceptors
DispatcherServlet是前端控制器的实现,提供Spring Web MVC的集中访问点,负责职责的分派,且与Spring IoC容器无缝集成,从而可以获得Spring的所有好处。
DispatcherServlet主要用作职责调度,主要职责如下:
-
通过HandlerMapping,将请求映射到处理器;
-
通过HandlerAdapter支持多种类型的处理器(HandlerExecutionChain中的处理器);
-
通过ViewResolver解析逻辑视图名到具体视图实现;
-
本地化、国际化解析;
-
文件上传解析,如果请求类型是multipart将通过MultipartResolver进行文件上传解析;
-
如果执行过程中遇到异常将交给HandlerExceptionResolver来解析。
-
web.xml配置(安装了springtools插件alt+/可以直接提示)
<servlet> <servlet-name>hello</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> </servlet>
默认: servletName-servlet.xml:即hello-servlet.xml,修改方式:
<init-param> <param-name>contextConfigLocation</param-name> <!-- 多个文件使用通配符: *-servlet.xml --> <param-value>/WEB-INF/hello-servlet.xml(classpath:xxx.xml)</param-value> </init-param> <servlet-mapping> <servlet-name>hello</servlet-name> <!-- 直接使用/(不必/*,这样会拦截*.jsp请求,url可以任意.action|.do|.html) --> <url-pattern>/</url-pattern> </servlet-mapping>
-
MVC配置文件:
<!-- 处理器映射:Handler Mapping -->(默认,可以省略不配) <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/> <!-- Controller(通过beanName完成url映射,必须加'/') --> <bean name="/start" class="com.cssl.mvc.StartController"/> <!-- 视图解析器:ViewResolver -->(不配可以使用指示符“forward:|redirect:”) <bean class="org.springframework.web.servlet.view.UrlBasedViewResolver"> <property name="prefix" value="/WEB-INF/jsp/"></property> <property name="suffix" value=".jsp"></property> <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/> </bean> <!-- 相对UrlBasedViewResolver对jsp和jstl提供了更好的支持,不需要配置viewClass --> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/jsp/"></property> <property name="suffix" value=".jsp"></property> </bean>
Spring2.5之前,我们都是通过实现Controller接口或其实现类来定义我们的控制器类。
-
使用注解:配置少 低侵入
<mvc:annotation-driven/>:
自动注册DefaultAnnotationHandlerMapping与AnnotationMethodHandlerAdapter两个分发请求bean 视图解析:mav.setViewName("forward:success.jsp"|"success");
-
常用注解: 类上:
@Controller | @RestController(4.0+) | @ControllerAdvice //将所有属性为UsersVo类型和键名是usersVoList的集合存入request的同时存入session作用域 @SessionAttributes(types=UsersVo.class,value|names={"usersVoList"})两者是or关系 //写在类上相当于namespace @RequestMapping("/parent") //或者结合方法上注解@RequestMapping(params="method=方法名")绑定该类下某个方法来执行 @RequestMapping("/user.do") 表单提交action=user.do?method=reg 如:@RequestMapping(params="method=reg")public String reg(String uname){ ... }
方法上:
@RequestMapping(value={"/a","/hello"},method=RequestMethod.GET,params="...") 对于相同value的@RequestMapping可以通过method或params区别调用不同方法 对于返回Model或无返回的方法默认视图就是这里的value,如@RequestMapping("/hello")就是hello.jsp @GetMapping("/...")|@PostMapping("/...")(从Spring4.3开始)
Spring 3.X系列增加了新注解@ResponseBody,@RequestBody @ResponseBody将内容或对象作为HTTP响应正文返回,并调用适合HttpMessageConverter转换对象,写入输出流 @ResponseBody绑定返回对象或集合,帮你将其转为Json字符串(默认jackson包,Spring4.0以上版本需要Jackson版本2.x以上的3个包) @ModelAttribute相当前置通知,将返回值注入到model中,能继续传递 @ExceptionHandler异常处理
参数上:
@RequestParam(name|value="sid",required=false) int id @RequestBody将HTTP请求正文转换为Json对象。requestheader Content-Type格式必须application/json或application/xml @PathVariable 用于restful风格 @CookieValue 取cookie值 @CookieValue(name="JSESSIONID") Postman添加cookie(先添加域,如:es1.com,然后Add Cookie输入key=value保存就可以了) @RequestHeader 取请求头信息 @SessionAttribute通常和@SessionAttributes("user")一起使用直接注给方法形参,根据byName注入 @ModelAttribute("user")也可以给模型添加属性(model.addAttribute(user)) 如: 在Controller上指定了@SessionAttributes,所以在@ModelAttribute("xxx")注解的参数会直接在 @SessionAttributes中查找名为"xxx"的对象,如果没有找到则调用@ModelAttribute("xxx")注解的方法返回 对象并存入@SessionAttributes
-
方法返回值:String(视图名),Model,ModelMap,ModelAndView,Set,List,Object,Map... 除了返回String和ModelAndView,逻辑视图名都会和请求URL绑定 ModelAndView: addObject("uu",user) addObject(new User())==addObject("user",new User()) 使用类名首字母小写作为键 addObject(new ArrayList<User>)==add Object("userList",new ArrayList<User>) addObject("username")==addObject("string","username") 类型名首字母小写作为键 如果返回Map、Model、ModelMap到前台,页面直接取键名 如果将对象或集合当作方法返回值前台页面取值规则也同上!
重定向传参:RedirectAttributes attr attr.addFlashAttribute("token",token); //(Model model)model.getAttribute("token"); attr.addAttribute("username",username); //相当于地址拼接?username"+username
-
访问静态资源:(如jpg,js,css) 如果DispatcherServlet拦截 .action这样的URL,就不存在访问不到静态资源的问题。如果拦截“/”,那么就拦截了所有的请求,即.js,.jpg,.css的访问也就被拦截了当做控制器访问
解决方法引入资源: a、<mvc:resources location="/static/" mapping="/static/**"/> (注意'/') (静态资源直接映射到对应的文件夹,不被DispatcherServlet处理,3.04新增功能,需要重新设置spring-mvc-3.x.xsd) b、mvc:default-servlet-handler/ (放过所有静态资源) c、原理同上,使用容器默认servlet处理资源,下面是tomcat配置
<servlet-mapping> <servlet-name>default</servlet-name> <url-pattern>*.css,*.js,*.jpg,*.gif</url-pattern> </servlet-mapping>
-
解决中文乱码过滤器:(POST)
<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>
二、参数注入及Ajax
vo|dto注入、@ResponseBody|@RequestBody|@RestController
-
实体类注入:注入多实体类,需要避免实体类同名属性;支持对象导航注入
-
SpringMvc可以接收数组、List或Map作为参数,但List和Map必须在参数前面加@RequestParam,不能加下标[index],List的泛型类型必须是基本数据类型或String,不能使用自定义类(如:List<Users>),Map注值name为键名
-
SpringMvc也可以把数组、List或Map作为一个对象的属性接收:
注意: 如果泛型集合类型是自定义类型:List<Users>,前台必须带[index] 如果泛型集合类型是基本类型或String前台加不加[index]都可以 如果是数组作为实体类属性时前台加不加[index]也都可以
-
AJAX请求@ResponseBody|@RequestBody(必须有Jackson包)|@RestController 时间转换的处理:(使用Fastjson注解删除Jackson包) a、通过后台的get方法使用SimpleDateFormat解决 b、@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone = "GMT+8") @JSONField(format="yyyy-MM-dd",name="")[fastJSON,老版本不支持中文"yyyy年..."] c、@JsonIgnore(true)不序列化该属性 @JSONField(serialize = false)[fastJSON]
-
Json转换可以由Jackson修改为Fastjson处理:(前端要设置返回类型为json)
<mvc:annotation-driven> <mvc:message-converters register-defaults="true"> <bean class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter"> <property name="supportedMediaTypes"> <list> <value>text/plain;charset=UTF-8</value> <value>text/html;charset=UTF-8</value> </list> </property> </bean> </mvc:message-converters> </mvc:annotation-driven>
注意:
-
application/x-www-form-urlencoded这种默认请求@RequestBody不支持
-
@ResponseBody不能转换ModelAndView,仍然使用其中的viewName视图
-
application/json, application/xml等格式的数据,必须使用@RequestBody来处理
$.ajax({ type: "POST", url: "json.action", contentType: "application/json", //说明data必须是json字符串 data: '[{"username":"aaaa"},{"username":"bbbb"}]', success: function(msg){ alert( "Data Saved: " + msg ); } }); //@RequestBody List<UsersVo> list
三、文件上传、Restful及异常处理
文件上传下载|@PathVariable|异常处理@ExceptionHandler|@ControllerAdvice|@ModelAttribute
-
文件上传: 表单:enctype="multipart/form-data" method="post" 依赖:commons-fileupload-x.jar、commons-io-x.jar包
@RequestMapping(value="/upload.action") public String upload(@RequestParam MultipartFile file) throws IOException { //获得真实路径 (可采用接口ServletContextAware注入) String path = application.getRealPath("/upload")+File.separator; //保证用户没上传不报该错:exists but is a directory if(!file.isEmpty()){ String type = file.getContentType(); //文件类型 String name = file.getOriginalFilename(); //真实文件名 String filename = path + name; FileUtils.copyInputStreamToFile(file.getInputStream(), new File(filename)); //或者使用 //file.transferTo(new File(path, name)); } return "success"; }
-
上传必须配置如下解析器,否则MultipartFile为空:
<!-- 上传解析,如最大上传值及最小上传值,必须配置multipartResolver,名字也不能改 --> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <property name="maxUploadSize" value="10000000" /> <!-- 10M --> <property name="maxInMemorySize" value="10240"/> <!-- 最大临时内存大小 --> <property name="defaultEncoding" value="UTF-8"/> </bean>
-
多文件上传: a、使用MultipartFile[],参数名和前台file的name相同 b、参数前加@RequestParam在新版本中可以省略(教材上版本低,所以要求必须加)
-
下载:
response.setContentType("application/x-msdownload;charset=UTF-8"); response.setHeader("Content-disposition", "attachment;filename="+fileName); OutputStream os = response.getOutputStream(); //用输入流读取文件再使用os下载到客户端
-
@RequestMapping不但支持标准的URL,还支持Ant、restful风格。以下URL都是合法的: Ant风格: ?: 匹配一个字符 *: 匹配任意字符 **:匹配多层路径
-
/user/*/createUser
匹配/user/aaa/createUser、/user/bbb/createUser等URL。 -
/user/**/createUser
匹配/user/createUser、/user/aaa/bbb/createUser等URL。 -
/user/createUser??
匹配/user/createUseraa、/user/createUserbb等URL。
-
-
Restful风格: rest:Representational State Transfer(以资源为导向)
-
/user/{userId}
匹配user/123、user/abc等URL。 -
/user/**/{userId}
匹配user/aaa/bbb/123、user/aaa/456等URL。 -
/company/{companyId}/??/{userId}/detail
匹配company/123/us/456/detail等的URL -
错误格式
/user/**/{userId}/**/{username}
优点:因为隐藏了参数与路径的关系,可以提升网站的安全性,静态化页面,降低恶意攻击风险
//springmvc/path/zhangsan/33 @RequestMapping(value="/path/{name}/{age}") public String path(@PathVariable("name") String sname,@PathVariable("age")...) { System.out.println(sname); return "hello"; }
-
-
Restful风格也可以注入对象,不用写@PathVariable
@GetMapping("/company/{username}/{password}") public String rest(UsersVo uvo) { System.out.println("rest:"+uvo.getUsername()+":"+uvo.getPassword()); return "success"; }
-
SpringMVC提供的异常处理主要有
-
实现HandlerExceptionResolver接口,自定义异常处理 SpringMVC本身也已经对其有一个默认的实现——DefaultExceptionResolver,该解析器只是对其中的一些比较典型的异常进行了拦截处理。
<bean id="exceptionResolver" class="com.cssl.handler.MyExceptionHandler"/>
-
使用@ExceptionHandler进行处理(局部异常) 使用@ExceptionHandler进行异常处理的方法必须与出错的方法在同一个Controller里面 一定要注明处理哪种异常类型 @ExceptionHandler(Exception.class) public String error(Exception e){}
-
针对所有Controller异常处理(全局异常但不包括类型转换异常) springmvc.xml配置
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver"> <property name="exceptionMappings"> <props> <prop key="java.lang.Exception">error</prop> </props> </property> </bean>
-
@ControllerAdvice+@ExceptionHandler 全局处理Controller层异常 优点:将Controller层的异常和数据校验的异常进行统一处理,减少编码量,提升扩展性和可维护性 缺点:只能处理Controller层未捕获(往外抛)的异常,对于Interceptor(拦截器)层的异常,Spring框架层的异常,就无能为力了
@ControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(Exception.class) @ResponseBody public String handleException(){ ...; } /** * 前置通知 * @return */ @ModelAttribute public Users getModel() { Users u = new Users(); u.setUsername("匿名用户"); return u; } //姓名 密码 角色 【正是用户 游客】 @ModelAttribute //先帮你效验数据 ,提前去补缺 public void userLogin2(Users user/* ,Model md */) { System.out.println("进来了:"+user); //md.addAttribute("users", user); } @RequestMapping("/userLogin2.action") public String login2(@RequestParam(required = false) Users user,Model md) { Users u = (Users) md.getAttribute("users"); System.out.println("u:"+u); System.out.println("UsersController-login2-登录2方法--:"+u); return "success"; } }
https://www.jianshu.com/p/0ec4e7afb7ed
@ControllerAdvice配合@ExceptionHandler最有用,其它两个不常用
@ControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(IllegalArgumentException.class) public ModelAndView handleException(IllegalArgumentException e){ ModelAndView modelAndView = new ModelAndView("error"); modelAndView.addObject("errorMessage", "参数不符合规范!"); return modelAndView; } }
@ControllerAdvice
配合@ModelAttribute
预设全局数据我们先来看看
ModelAttribute
注解类的源码@ControllerAdvice public class MyGlobalHandler { @ModelAttribute public void presetParam(Model model){ model.addAttribute("globalAttr","this is a global attribute"); } }
@ControllerAdvice public class MyGlobalHandler { @ModelAttribute() public Map<String, String> presetParam(){ Map<String, String> map = new HashMap<String, String>(); map.put("key1", "value1"); map.put("key2", "value2"); map.put("key3", "value3"); return map; } }
@RestController public class AdviceController { @GetMapping("methodOne") public String methodOne(Model model){ Map<String, Object> modelMap = model.asMap(); return (String)modelMap.get("globalAttr"); } @GetMapping("methodTwo") public String methodTwo(@ModelAttribute("globalAttr") String globalAttr){ return globalAttr; } @GetMapping("methodThree") public String methodThree(ModelMap modelMap) { return (String) modelMap.get("globalAttr"); } }
@ControllerAdvice 配合 @InitBinder 实现对请求参数的预处理 @ControllerAdvice public class MyGlobalHandler { @InitBinder public void processParam(WebDataBinder dataBinder){ /* * 创建一个字符串微调编辑器 * 参数{boolean emptyAsNull}: 是否把空字符串("")视为 null */ StringTrimmerEditor trimmerEditor = new StringTrimmerEditor(true); /* * 注册自定义编辑器 * 接受两个参数{Class<?> requiredType, PropertyEditor propertyEditor} * requiredType:所需处理的类型 * propertyEditor:属性编辑器,StringTrimmerEditor就是 propertyEditor的一个子类 */ dataBinder.registerCustomEditor(String.class, trimmerEditor); //同上,这里就不再一步一步讲解了 binder.registerCustomEditor(Date.class, new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd"), false)); } }
@RestController public class BinderTestController { @GetMapping("processParam") public Map<String, Object> test(String str, Date date) throws Exception { Map<String, Object> map = new HashMap<String, Object>(); map.put("str", str); map.put("data", date); return map; } }
class Person { private String name; private Integer age; // omitted getters and setters. } class Book { private String name; private Double price; // omitted getters and setters. } @RestController public class BinderTestController { @PostMapping("bindParam") public void test(Person person, Book book) throws Exception { System.out.println(person); System.out.println(book); } }
我们会发现
Person
类和Book
类都有name
属性,那么这个时候就会出先问题,它可没有那么只能区分哪个name
是哪个类的。因此@InitBinder
就派上用场了:@ControllerAdvice public class MyGlobalHandler { /* * @InitBinder("person") 对应找到@RequstMapping标识的方法参数中 * 找参数名为person的参数。 * 在进行参数绑定的时候,以‘p.’开头的都绑定到名为person的参数中。 */ @InitBinder("person") public void BindPerson(WebDataBinder dataBinder){ dataBinder.setFieldDefaultPrefix("p."); } @InitBinder("book") public void BindBook(WebDataBinder dataBinder){ dataBinder.setFieldDefaultPrefix("b."); } }
因此,传入的同名信息就能对应绑定到相应的实体类中: p.name -> Person.name b.name -> Book.name 还有一点注意的是如果 @InitBinder("value") 中的 value 值和 Controller 中 @RequestMapping() 标识的方法的参数名不匹配,则就会产生绑定失败的后果,如: @InitBinder(“p”)、@InitBinder(“b”) public void test(Person person, Book book) 上述情况就会出现绑定失败,有两种解决办法 第一中:统一名称,要么全叫p,要么全叫person,只要相同就行。 第二种:方法参数加 @ModelAttribute,有点类似@RequestParam @InitBinder(“p”)、@InitBinder(“b”) public void test(@ModelAttribute(“p”) Person person, @ModelAttribute(“b”) Book book)
@ControllerAdvice也可配合@InitBinder和@ModelAttribute方法,适用所有使用@RequestMapping方法
@ControllerAdvice在Spring4之前协助所有控制器。Spring4+已经改变:支持配置控制器的子集 如:@ControllerAdvice(basePackages="com.cssl.controller")对其他包控制器不起作用
-
四、拦截器、类型转换、国际化、验证
https://www.cnblogs.com/sfnz/p/16478087.html
-
拦截器:实现HandlerInterceptor接口或继承HandlerInterceptorAdapter(责任链模式)
<!-- intercepter --> <mvc:interceptors> <!-- 配置多个形成拦截器链 --> <mvc:interceptor> <!-- /**将拦截所有包括静态资源但不包括jsp,如果jsp包含静态js也会经过拦截器 --> <!-- /*只拦截一级目录下所有Controller,如:/he.action,不拦截二级目录 --> <mvc:mapping path="/**"/>|<mvc:mapping path="/*"/> <!-- 如果拦截了所有,必须设置放过静态资源 --> <mvc:exclude-mapping path="/static/**"/> <bean class="com.cssl.interceptor.MyInterceptor"/> </mvc:interceptor> </mvc:interceptors>
拦截器参数handler中包含请求类及方法,可以精确控制调用方法权限 handler.toString().indexOf("方法名")!=-1
-
类型转换: a、基本类型的数据类型转换自动完成 日期类型转换(springmvc只提供yyyy/MM/dd这样的格式转换)
b、@InitBinder //注解方式转换,只能转换一种格式,项目中日期类型较多首选
public void initBind(DataBinder binder){ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); //true表示该日期字段可以为空 binder.registerCustomEditor(Date.class, new CustomDateEditor(sdf,false)); }
c、直接在vo的属性上(get|set也可以)注解@DateTimeFormat(pattern="yyyy-MM-dd")
BindingResult:该类封装转换失败的信息,必须紧跟在command类型参数后面,日期必须是command属性 result.hasErrors()判断是否转换出错
-
自定义类型转换:
Spring3
引入了一个Converter接口,它支持从一个Object转为另一个Object。除了Converter接口之外,实现 ConverterFactory接口和GenericConverter接口也可以实现我们自己的类型转换逻辑。 a、定义转换类实现Converter<S,T>接口重写convert实现类型转换功能 b、添加注解@Component注册到bean到Spring容器 c、配置<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean"> <property name="converters"> <set> <ref bean="dateConverter"/> </set> </property> </bean> 或者手动注入日期格式类型: <set> <bean class="com.cssl.converter.DateConverter"> <constructor-arg type="java.lang.String" value="yyyy-MM-dd"/> </bean> </set>
d、修改<mvc:annotation-driven conversion-service="conversionService"/>
-
国际化:IE11设置语言问题(不显示中文可以先删除英文语言选项) a、先配置属性文件message_en.properties | message_zh_CN.properties b、在JSP文件中使用fmt标记就可以实现语言国际化 如:
<fmt:setBundle basename="message"/> <fmt:message key="info.login.title" />
属性文件显示中文插件: Eclipse Marketplace->find properties->选择Properties Editor
-
Spring3开始支持JSR-303验证框架: 验证使用的包hibernate-validator-5.0.1.Final-dist 最少需要4个jar: hibernate-validator-5.0.1.Final.jar required:classmate-0.8.0.jar、jboss-logging-3.1.1.GA.jar、validation-api-1.1.0.Final.jar
注解: 1、在vo参数前添加@Valid|@Validated,后面紧跟BindingResult参数不能间隔其他参数 2、在实体类要验证的属性getXxx上添加各种验证注解 @NotNull(空对象)、 @NotEmpty(字符串)、@NotBlank(去首尾空格)、 @Size(min=4,max=6)、@Email 3、使用<sf:errors path="vo.fieldName"/>显示错误(vo对应实体类首字母小写UsersVo-usersVo) 4、国际化显示可以不写message,有默认出错信息提示,如果想自定义则修改键名(key)为: Size.usersVo.username ,其含义为(Size代表@Size,usersVo代表vo,username代表fileName), 使用sf提示消息,也必须配置bean:ResourceBundleMessageSource 5、<%@ taglib uri="http://www.springframework.org/tags/form" prefix="sf" %> <sf:errors path="usersVo.username"/> 注意:使用springmvc标签必须配置监听器,除非是通过控制器访问页面
<listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>
五、SSM整合
-
整合 a、导入之前整合的Spring+MyBatis b、web.xml配置监听器启动Spring的IoC容器(或者直接在springmvc.xml引入spring.xml)
<listener> <listener-class> org.springframework.web.context.ContextLoaderListener </listener-class> </listener> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:beans.xml</param-value> //spring配置beans.xml </context-param>
注意:MyBatis没有Spring事务管理的问题:(直接在springmvc.xml引入spring.xml不会有事务问题)
第一种情况: 两个容器产生两套控制器和业务类,请求调用的是springmvc容器的而事务是控制在spring容器上!
解决:MVC的springmvc.xml配置文件中只扫描包含controller的注解,Spring的beans.xml文件中扫描包时,排除controller的注解,如下所示:
SpringMVC的xml配置:
<context:component-scan base-package="com.cssl.controller"/>
Spring的xml配置:(不扫描带有@Controller注解的类,因为这些类在springmvc.xml中扫描)
<context:component-scan base-package="com.cssl"> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan>
只用一个配置文件:(相当直接在springmvc.xml引入spring.xml)
第二种情况:MySQL数据没用InnoDB引擎,修改数据库引擎即可
-
Web环境中可以使用:@Scope("request|session")
-
Web(过滤器|监听器)获取当前的SpringIoC容器(必须使用监听器启动IoC容器:因为监听器先于Servlet启动) 方法一:(通过ServletContext加载spring容器,SpringBoot适用) ApplicationContext ctx=WebApplicationContextUtils.getWebApplicationContext(application);
方法二:(获取当前的spring容器,任何java类中适用,SpringBoot不适用,SpringBoot可以直接注入ioc容器中的对象) WebApplicationContext act=ContextLoader.getCurrentWebApplicationContext();
-
防止Spring内存溢出监听器
<listener> <listener-class> org.springframework.web.util.IntrospectorCleanupListener </listener-class> </listener>
注意:Web组件加载顺序:监听器 -》 过滤器 -》 Servlet
扩展知识点:
自定义视图:
-
写一个类实现View接口,实现其两方法
public String getContentType() { return "text/html;charset=utf-8"; }
//渲染视图,输出到客户端
public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception { System.out.println("render:"+model.get("usersVo")); response.getWriter().print("<h1>MyView:"+new Date()+"</h1>"); }
-
使用@Component将该类注册到spring ioc容器
-
配置ViewResolver(根据id查找对应的视图解析类)
<bean class="org.springframework.web.servlet.view.BeanNameViewResolver"> <!-- 数字越小代表优先级越高 --> <property name="order" value="100"/> </bean>
如:@Component("myView")类映射方法不能通过超链接的方式直接访问
//这里的myview映射前台链接,return的"myView"对应@Component("myView") @RequestMapping("/myview") public String myview(){ return "myView"; }
-
如果经常用到下载可以封装下载view:
response.setContentType("application/x-excle;charset=UTF-8"); response.setHeader("Content-disposition", "attachment;filename="+fileName); OutputStream os = response.getOutputStream(); //读 InputStream in = new FileInputStream(path); byte[] b = new byte[4096]; int len = in.read(b); //写 while(len!=-1) { os.write(b,0,len); len = in.read(b); } os.flush(); os.close(); in.close();
5.自定义视图 package com.cssl.view; import java.util.Date; import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.stereotype.Component; import org.springframework.web.servlet.View; import com.cssl.pojo.Student; @Component public class MyView implements View { @Override public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception { response.setCharacterEncoding("utf-8"); response.setContentType("text/html;charset=utf-8"); Student object = (Student) model.get("stu"); System.out.println("render:"+object); response.getWriter().print("<h1>"+object.getStudentName()+"MyView:"+new Date()+"</h1>"); } }
下载视图:
package com.cssl.view; import java.io.OutputStream; import java.util.Iterator; import java.util.List; import java.util.Map; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Workbook; import org.springframework.stereotype.Component; import org.springframework.web.servlet.view.document.AbstractXlsView; import com.cssl.pojo.Student; @Component public class ExcelView extends AbstractXlsView { @Override protected void buildExcelDocument( Map<String, Object> model, Workbook workbook, HttpServletRequest request, HttpServletResponse response) throws Exception { //从model取出数据 List<Student> stus = (List<Student>) model.get("stus"); //定义下载后excel表格名称 String fileName="student.xlsx"; //设置下载头 response.setContentType("application/ms-excel;charset=UTF-8"); response.setHeader("Content-Disposition", "inline; filename=" + fileName); //创建一个Sheet Sheet sheet = workbook.createSheet("学生信息"); //创建行 Row firstRow = sheet.createRow(0); firstRow.createCell(0).setCellValue("序号"); firstRow.createCell(1).setCellValue("主键"); firstRow.createCell(2).setCellValue("姓名"); firstRow.createCell(3).setCellValue("生日"); firstRow.createCell(4).setCellValue("备份"); if(stus==null) { return; }else { for (int i = 0; i < stus.size(); i++) { Student stu=stus.get(i); Row tempRow = sheet.createRow(i+1); tempRow.createCell(0).setCellValue(i+1); tempRow.createCell(1).setCellValue(stu.getSid()); tempRow.createCell(2).setCellValue(stu.getStudentName()); tempRow.createCell(3).setCellValue(stu.getBorndate()); tempRow.createCell(4).setCellValue("菜鸡"); } } ServletOutputStream out=response.getOutputStream(); workbook.write(out);//输出 System.out.println("下载ok"); out.flush(); out.close(); } }
@RequestMapping("/test") public String myViewTest(Model model) { Student student =new Student(); student.setSid(1111); student.setStudentName("老刘"); student.setBorndate(new Date()); model.addAttribute("stu", student); return "myView"; } @RequestMapping("/export") public ModelAndView export(ModelAndView mv,Model model) { List<Student> stus = studentService.findAllStudent(); model.addAttribute("stus", stus); mv.setView(new ExcelView()); return mv; }