springmvc是什么?
springmvc是spring为展现层提供基于mvc设计理念优秀的框架。3.0之后的版本全面超过struts2
通过一套mvc注解,让pojo成为处理请求的控制器,而无需实现任何接口
支持rest风格的url请求 采用了松散耦合可插拔组件结构,可扩展性和灵活度高
上面提到了pojo那什么是pojo呢?
具体来说POJO是一个简单的、正规Java对象,它包含业务逻辑处理或持久化逻辑等,但不是JavaBean、EntityBean等,不具有任何特殊角色和不继承或不实现任何其它Java框架的 类或接口。
springmvc有什么用?
对发送过来的请求进行处理,对业务逻辑的处理,还有持久化逻辑,处理模型数据
springmvc怎么使用
一些重要的mvc注解
用的最多的@RequestMapping 使用@RequestMapping来映射请求的url
一个重要的对象
视图解析器对象 InternalResourceViewResolver
通过springmvc核心配置文件中配置的视图解析器中的prefix+处理器中目标方法的返回值+配置中的suffix这样的方式得到实际的物理视图,然后做转发操作
形如这样的地址/WEB-INF/views/success.jsp
@RequestMapping这个注解可以修饰方法和修饰类 也可以映射方法名 就是请求的url与目标方法的名一样
修饰类的时候:提供初步的请求映射信息。相对于web应用的根目录
修饰方法的时候:提供进一步的细分映射信息
相对于类定义的url,如果类中没有使用@RequestMapping进行定义,那么方法当中的url就相当于web应用的根目录
@PathVariable注解的使用
它是映射url绑定的占位符
通过@PathVariable可以将url中的占位符进行参数绑定到控制器目标方法的参数当中
@RequestMapping("/add/{id})
比如public String add(@PathVariable("id")Integer id){} 中的id值就是请求的url中传递过来的值
@RequestMapping(method) 这个注解方式method的值 就是你请求的方式
请求的方式有多种 用的最多的就是get post delete put请求
怎么发送put delete请求呢?
通过HiddenHttpMethodFilter过滤器来进行使post请求编程put请求和delete请求
比如:<a href="test"></a>
<form >
<input type="hidden" name="_method" value="PUT">
</form>
通过隐藏域的方式进行发送put 和delete请求 把超链接的地址给表单的action属性 delete和put请求的前提是当前请求必须是post请求
@RequestParam(value="username",required=false;default="0")
注解相当于servlet中的getParameter()方法的到请求域中的数据然后赋值给目标方法的参数与他相似的就是
default就是用来当参数不是Integer而是int 时但他又有默认值为0
springmvc会按请求参数名和pojo属性名进行自动匹配,自动为该对象填充属性值。还支持级联属性
目标方法有很多的参数域对象 和io对象
request reponse session java.security.priacipal LocaleInputStream OutputStream Reader Writer
目标方法的返回值还可以是ModelAndView类型
@RequestHeader与@CookieValue与其相似现在就不多做介绍
springmvc会把modelAndView中的model中的数据放入域对象中
四种处理模型数据的方式(就是把数据放入域对象当中)
以ModelAndView 作为返回值类型
Map及Model入参数为它Map和Model类型的参数
@SessionAttribute注解的使用
只能放在类的上面不能放在方法的上面
根据你要放入到session域中的数据类型发到session对象当中
/*@SessionAttributes(value={"list"},types={String.class})*/
产生的异常问题是因为没有@ModelAttribute标记的方法
解决方案:在springmvc核心配置文件当中配置<mvc:default-servlet-handler/>
@ModelAttribute注解的使用
使用场景:在页面当中表单回显不想让密码显示,所以就隐藏,但是使用隐藏域也能看到在网页源代码当中,所以使用这个注解来从数据库中取出这个对象,然后放入域中
注意:放入域对象当中的对象要与目标方法的参数类型的第一个字母小写的字符串保持一致
每次处理请求时都会调用@ModelAttribute标记的方法
配置国际化资源文件
在springmvc核心配置文件当中配置国际化资源文件
ResourceBundleMessageSource
basename
<bean name="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource"> <property name="basename" value="i18n"></property> </bean>
配置直接转发的页面,不需要再经过handler方法 这样会导致handler的url无效这时候要使用<mvc:annotation-drivern>注解
转发和重定向
return “redirect:index.jsp”
return "forward:index.jsp"
<form:form method="' modelAttribute=""></from:form>到有这个页面时要将一个对象放入域对象当中
不能使用<form:hidden>标签name值时_method
springmvc处理静态资源(加载不了那个js css这些静态资源)
解决方案:配置springmvc的配置文件中<mvc:default-servlet-handler>但是配置了这个就要配置<mvc:annotation-drivern> 核心配置
数据绑定流程分析
数字类型分析
数据类型格式化
数据校验
给webDataBinderFactory 创建DataBinder对象
ConversionService进行转换和格式化
validator组件进行校验
最终数据绑定结果bindingData对象
mvc抽取BindingResult中的入参对象和校验错误对象,将他们赋值给目标方法的入参当中
自定义类型转换器
实现converter接口
然后在核心配置文件当中使用自定义类型转换器
ConversionServiceFactoryBean 的converiters属性
package com.jdztc.springmvc.converter; import org.springframework.core.convert.converter.Converter; import org.springframework.stereotype.Component; import com.jdztc.springmvc.entity.Department; import com.jdztc.springmvc.entity.Document; import com.jdztc.springmvc.entity.Gender; @Component public class MyConverter implements Converter<String, Document> { @Override public Document convert(String source) { // TODO Auto-generated method stub if(source!=null){ String[] arr=source.split("-"); if(arr.length!=0&&arr.length==4){ String lastName=arr[0]; String email=arr[1]; Gender gender=new Gender(); gender.setGid(Integer.parseInt(arr[2])); Department department=new Department(); department.setDid(Integer.parseInt(arr[3])); Document document=new Document(null, lastName, email, gender, department); return document; } } return null; } }
<!--这样会导致之前的requestMapping没有作用 需要配置mvc-annotion-driven--> <mvc:annotation-driven conversion-service="conversionService"></mvc:annotation-driven> <!--配置自定义类型转换型 --> <bean name="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean"> <property name="converters"> <set> <bean class="com.jdztc.springmvc.converter.MyConverter"></bean> </set> </property> </bean>
@IninBinder注解的使用
@InitBInder标记的方法对WebDataBinder对象进行初始化 目标方法不能有返回值 参数为WebDataBinder
// @InitBinder // public void initIninBinder(WebDataBinder webDataBinder){ // //设置不需要赋值的javaBean属性 由表单到javaBean的赋值过程中,哪一个值不进行赋值 // webDataBinder.setDisallowedFields("lastName"); // }
@DataTimeFormat注解的使用 实际上作用的对象是JodaDataTimeFormatAnnotationFormatterFactory内部已经注册了
@DateTimeFormat(pattern="yyyy-MM-dd")在javaBean的属性上面进行验证
@NumberFormat注解的使用 实际上作用的对象NumberFormatAnnotationFormatterFactory
在javaBean的属性上使用 @NumberFormat(pattern="#,###,###.#")
如果需要使用自定义类型转换器,又要使用类型转换器注解 就使用FormattingConversionServiceFactoryBean
BindingResult的使用
@RequestMapping(value="/add",method=RequestMethod.POST) //BindingResult结果集 public String add(@Valid Document document,BindingResult result,Map<String,Object> map){ if(result.getErrorCount()>0){ for(FieldError error:result.getFieldErrors()){ System.out.println(error.getField()+"!!"+error.getDefaultMessage()); } Collection<Department> departments=departmentService.getAll(); Collection<Gender> genders=genderService.getAll(); map.put("genders", genders); map.put("departments", departments); return "input"; } System.out.println("document"+document); documentService.add(document); return "redirect:/springmvc/testList"; }
JSR数据校验(验证框架) hibernate.validator
1.怎么验证:注解?
2怎么把错误消息发送到页面,实现国际化
3.发生错误是,跳转到哪个页面
1使用JSR303验证标准
2.加入hibernate-validator验证框架的jar包
3.添加<mvc:annotation-drivern>配置
4.在Bean中添加验证注解
5在目标方法中添加入参实体类中添加@valid注解
注意:目标方法中的入参实体类要与BindingResult要紧挨在一起,中间不能有其他的入参
页面显示错误使用form标签库
<form:errors path="属性名"/>
在国际化文件中实现错误信息的自定义
<!--配置国际化资源文件 --> <bean name="resourceBundleMessageSource" class="org.springframework.context.support.ResourceBundleMessageSource"> <property name="basename" value="i18n"></property> </bean> <!-- 配置自定义错误信息显示要配置的Bean --> <bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"> <property name="validationMessageSource" ref="messageSource"/> </bean> <bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource"> <property name="basename" value="classpath:i18n"/> <property name="fileEncodings" value="utf-8"/> <property name="cacheSeconds" value="120"/> </bean>
国际化文件
NotEmpty.user.userName=用户名不能为空 Pattern.user.password=密码必须包含两种或两种以上字符 NotEmpty.user.phone=联系电话不能为空 Pattern.user.phone=必须是数字
required:必要参数不存在时,作为前缀
typeMIsmatch数据类型不匹配的时候作为前缀
mothodInvocation springmvc调用处理方法时发生了错误时作为前缀
前端回显页面
<form:form action="${pageContext.request.contextPath}/springmvc/adddd" method="post"> document:<input type="text" name="document"/> <input type="submit" value="提交"/> <br/> </form:form> <form:form action="${pageContext.request.contextPath}/springmvc/add" method="post" modelAttribute="document"> <form:errors path="*"></form:errors> <br> <c:if test="${empty document.id}"> LastName:<form:input path="lastName"/> <form:errors path="lastName"></form:errors> </c:if> <br/> <c:if test="${!empty document.id }"> <form:hidden path="id"/> <!-- 对于_method不能使用form:hidden标签,因为modelAttribute对应的bean中没有_method这个属性 --> <input type="hidden" name="_method" value="put"> </c:if> Email:<form:input path="email"/> <form:errors path="email"></form:errors> <br/> Gender:<form:radiobuttons path="gender.gid" items="${genders}" itemLabel="genderName" itemValue="gid"/> <br/> Department: <form:select path="department.did" items="${departments}" itemLabel="DName" itemValue="did"> </form:select> <br/> Birth: <form:input path="birth"/> <form:errors path="birth"></form:errors> <br> salary: <form:input path="salary"/> <form:errors path="salary"></form:errors> <br> <c:if test="${empty document.id}"> <input type="submit" value="提交"/> </c:if> <c:if test="${!empty document.id }"> <input type="submit" value="修改"/> </c:if> </form:form>
处理JSON:使用转换器HttpMessageConverter
把数据转换成json格式的数据
1.导入jacksonjar包
2.在目标方法上加@ResponseBody
3.返回值为你要发送到页面的json数据
@ResponseBody @RequestMapping(value="/testJson") public Collection<Document> testJson(){ return documentService.getAll(); }
文件上传
/* * 文件上传 */ @ResponseBody @RequestMapping(value="/testHttpMessageConverter") public String testHttpMessageConverter(@RequestBody String body){ System.out.println("我的信息"+body); return "上传成功"+new Date(); }
文件下载
@RequestMapping(value="/testResponseEntity") public ResponseEntity<byte[]> testResponseEntity(HttpSession session){ //用于存储读取的数据 byte[] arr=null; //要想下载文件。得到先读取文件,然后写入文件 //得到文件的读取流,获得文件在哪里相关路径 ServletContext context=session.getServletContext(); //根据相关路径得到读取流 InputStream in=context.getResourceAsStream("/file/1.txt"); //将读取到的放入数组当中。 //in.available返回得到的估计字节数 try { arr=new byte[in.available()]; in.read(arr); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } HttpHeaders headers=new HttpHeaders(); headers.add("Content-Disiposition", "attachment;filename=1.txt"); HttpStatus status=HttpStatus.OK; ResponseEntity<byte[]> response=new ResponseEntity<byte[]>(arr, headers, status); return response; }
国际化
什么叫国际化?
1.在页面上根据浏览器设置的语言情况对文本(不是内容),时间,数值进行本地化处理
2.在核心配置文件总可以获取国际化资源文件Local对应的消息
3.可以通过超链接切换Local,而不再依赖于浏览器的语言设置情况
解决方案:
1使用jstl的fmt标签<fmt:message>
2在Bean中注入实例 使用其对应的getMessage
3配置localResolver和localChangeInteceptor
@RequestMapping("/i18n")
public String testi18n(Locale locale){
String val=messageSource.getMessage("i18n.user",null, locale);
System.out.println(val);
return "i18n";
}
<bean name="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource"> <property name="basename" value="i18n"></property> </bean>
<bean name="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver"></bean>
<!-- 配置LocaleChangeInterceptor拦截器-->
<mvc:interceptors>
<bean name="changeInterceptor" class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor"></bean>
</mvc:interceptors>
文件上传
文件上传三部曲:
1表单的enctype属性 multipart/form-data
2配置CommonsMultipartResolver
3接收文件入参数 MultipartFlie flie
<!-- 配置CommonsMultipartResolver -->
<bean name="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="defaultEncoding" value="utf-8"></property>
<property name="maxUploadSize" value="1024000000"></property>
</bean>
拦截器
实现接口 HandlerInterceptor
preHandle()方法 在目标方法之前执行 对他的返回值要特别注意
返回值为true ,就调用之后的拦截器和目标方法
返回值为false,则相反
postHandle()方法 在目标方法调用之后,渲染视图之前被调用 对请求域和视图进行修改
afterCompletion()渲染视图之后,释放资源 打开资源,就要关闭。避免浪费,执行了preHandle就要关闭
拦截器的配置
<mvc:interceptors> <bean class="com.jdztc.springmvc.interceptor.FirstInterceptor"></bean> <mvc:interceptor> <!-- 这个拦截器只作用与这个路径下的链接 --> <mvc:mapping path="/springmvc/testList"/> <bean class="com.jdztc.springmvc.interceptor.SecondInterceptor"></bean> </mvc:interceptor> </mvc:interceptors>
自定义拦截器
package com.jdztc.springmvc.interceptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; //springmvc写拦截器要实现这个接口HandlerInterceptor //Struts2写拦截器继承这个类MethodFilterInterceptor public class FirstInterceptor implements HandlerInterceptor{ /* * (non-Javadoc) * @see org.springframework.web.servlet.HandlerInterceptor#preHandle(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, java.lang.Object) * 在调用目标方法之前调用 * 返回值为true 时之后的拦截器会继续调用,返回值是false时,之后的拦截器不会被调用,目标方法也不会被调用 * * * *如果有多个拦截器 这个方法执行顺序按照在springmvc.xml文件中的配置顺序执行 */ @Override public boolean preHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2) throws Exception { // TODO Auto-generated method stub System.out.println("FirstpreHandle ok"); return true; } /** * 在目标方法执行之后进行调用,但是在渲染视图之前进行调用 * 如果有多个拦截器,这个方法执行顺序按照在springmvc.xml文件中的配置反序执行 */ @Override public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3) throws Exception { // TODO Auto-generated method stub System.out.println("FirstpostHandle ok"); } /** * 在渲染视图之后调用,用于释放资源 * 如果有多个拦截器,这个方法执行顺序按照在springmvc.xml文件中的配置反序执行 */ @Override public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3) throws Exception { // TODO Auto-generated method stub System.out.println("FirstafterCompletion ok"); } } package com.jdztc.springmvc.interceptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; //springmvc写拦截器要实现这个接口HandlerInterceptor //Struts2写拦截器继承这个类MethodFilterInterceptor public class SecondInterceptor implements HandlerInterceptor{ /* * (non-Javadoc) * @see org.springframework.web.servlet.HandlerInterceptor#preHandle(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, java.lang.Object) * 在调用目标方法之前调用 * 返回值为true 时之后的拦截器会继续调用,返回值是false时,之后的拦截器不会被调用,目标方法也不会被调用 */ @Override public boolean preHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2) throws Exception { // TODO Auto-generated method stub System.out.println("SecondpreHandle ok"); //如果一个拦截器的这个方法执行完毕,那么就要关闭资源,如果返回false,那么就不需要关闭资源 return true; } /** * 在目标方法执行之后进行调用,但是在渲染视图之前进行调用 */ @Override public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3) throws Exception { // TODO Auto-generated method stub System.out.println("SecondpostHandle ok"); } /** * 在渲染视图之后调用,用于释放资源 */ @Override public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3) throws Exception { // TODO Auto-generated method stub System.out.println("SecondafterCompletion ok"); } }
异常的处理
springmvc处理异常的为这个接口HandlerExceptionResolver他有3个实现类
ExceptionHandlerExceptionResolver 配置了<mvc:annotation-drivern>
ResponseStatusExceptionResolver
DefaultHandlerExceptonResolver (springmvc的特殊异常进行处理)
第一个实现类:
处理@ExceptionHandler注解的目标方法入参中不能传入Map 如果希望把异常显示在页面上,那么需要使用modelAndView作为返回值 标记的方法有优先级问题 这个注解只能标记方法不能标记类
@ControllerAdvice
如果在当前的Handler中找不到@ExceptionHandler处理的异常,则会到@ControllerAdvice标记的类中来寻找@ExceptionHandler标记的方法来处理异常
第二个实现类:
@ResponseStatus 既可以修饰方法也可以修饰类 HttpStatus
@ResponseStatus(value=HttpStatus.NOT_FOUND,reason="不能除零") // public String testArithmeticException(Exception ex){ // System.out.println("出错了"+ex); // return "error"; // } @ResponseStatus(value=HttpStatus.NOT_FOUND,reason="不能除零") @RequestMapping("/testExceptionExceptionHandlerException") public String testExceptionExceptionHandlerException(@RequestParam("i")int i){ int a=10/i; System.out.println(a); return "success"; } //@ResponseStatus(value=HttpStatus.FORBIDDEN,reason="用户名和密码不匹配 哈哈哈哈哈哈哈") @RequestMapping("/testResponseStatusExceptionResolver") public String testResponseStatusExceptionResolver(@RequestParam("i")int i,RuntimeException re){ if(i==13){ throw new UserPasswordForbiddenException(); } System.out.println("testResponseStatusExceptionResolver ok go"); return "success"; }
第三个实现类:
对springmvc的特殊异常进行处理
还有一个处理异常bean 角标越界异常处理·
在核心配置文件中进行配置
<!-- 配置SimpleMapppingExceptionResolver --> <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver"> <!-- exceptionAttribute的默认值为exception--> <property name="exceptionMappings"> <props> <prop key="java.lang.ArrayIndexOutOfBoundsException">error</prop> </props> </property> </bean>
springmvc运行流程
1.首先根据web.xml中的DispatcherServlet中的url-pattern,根据请求的url 来查看springmvc中是否有这个映射
2.如果有 ,那么将会有HandlerMapping获取HandlerExecutionChain对象(处理器调用链 包含拦截器 处理器 目标方法) 然后获取HandlerAdapter对象 然后就调用拦截器的preHanlder()方法,调用目标方法 得到ModleAndView对象 调用拦截器的postHandler()方法 然后渲染视图 如果这了出现了异常则由HandlerExceptionResolber处理 如果没有异常
那么,真正的渲染视图,将逻辑视图转化为物理视图由视图解析器完成这个功能ViewResolver组件得到View 渲染视图 -调用拦截器的afterCompletion()方法释放资源
3.如果没有,然后他会查看在核心配置文件中是否配置了<mvc:default-servlet-handler>这个,如果配置了,那么就会寻找目标资源,如果没有,则会报错NOMapping 404页面
Spring整合Springmvc
spring整合springmvc会出现一个问题
spring的ioc容器与springmvc的ioc容器有重叠的部分,就会导致有的Bean会被创建2次
解决方案:
然他们扫描不同的包
springmvc只扫描@Controller@ControllerAdvice标注的类
spring则不扫描 借助 exclude-filter 和include-filter来解决
spring核心配置文件中的配置
<context:component-scan base-package="com.jdztc.springmvc" use-default-filters="false"> <!--这样解决springmvc的ioc容器与spring的ioc容器有重合的部分,不然会将bean创建两次 --> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/> <context:exclude-filter type="annotation" expression="org.springframework.web.bind.annotation.ControllerAdvice"/> </context:component-scan>
springmvc核心配置文件中的配置
<!-- 先配置扫描的包--> <context:component-scan base-package="com.jdztc.springmvc" use-default-filters="false"> <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/> <context:include-filter type="annotation" expression="org.springframework.web.bind.annotation.ControllerAdvice"/> </context:component-scan>
springmvc的Ioc容器与spring ioc容器的关系:
springmvc IOC容器中的Bean可以引用spring IOC容器中的Bean 反之不行
Springmvc与Strtus2的对比
1.springmvc的入口是Servlet 而Struts2则是Filer
2.SpringMvc比struts2快 ,因为springmvc是基于方法设计,而Struts2是基于类来设计 例如:每次访问都会创建一个Action对象,还有值栈浪费内存
3springmvc开发更简洁开发效率高 例如支持JSR303 处理ajax请求更方便,表单验证很简单注解就可以实现
4.struts2的ognl表达式使页面的开发效率相比springmvc更高