SpringMVC
1.导包
spring-aop-4.0.9.RELEASE.jar
spring-beans-4.0.9.RELEASE.jar
spring-context-4.0.9.RELEASE.jar
spring-core-4.0.9.RELEASE.jar
spring-expression-4.0.9.RELEASE.jar
spring-web-4.0.9.RELEASE.jar
spring-webmvc-4.0.9.RELEASE.jar
2.SpringMVC配置文件 SpringMVC.xml
选中命名空间:beans aop context mvc
3.普通的servlet流程:
请求 -> url -> pattern -> 交给对应的servlet去处理
*SpringMVC工作原理|工作流程|执行原理: 请求 -> 前端控制器 (DispatchServlet) -> handlerMapping -> handlerAdapter(适配器|处理器|控制器) -> service&dao ->ModelAndView -> viewResolver -> jsp
|
如果现在想用SpringMVC,而不是普通的servlet需要配置一个SpringMVC自带的servle
通过以下配置,拦截所有请求
在web项目的web.xml中配置SpringMVC拦截器: <servlet> <servlet-name>springDispatcherServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!-- 告诉服务器MVC配置文件在哪里 contextConfigLocations是DispatcherServlet中的一个属性--> <init-param> <param-name>contextConfigLocation<param-name> <param-value>classpath:SpringMVC.xml</param-value> </init-param> <!-- 设置启动服务器时自动生效 --> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <!-- 拦截所有"/"请求交给springDispatcherServlet --> <servlet-name>springDispatcherServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> |
4.创建servlet层
1.创建普通类 org.***.serlvet | org.***.controller | org.***.handler | org.***.action 包下
2.在类上加 @Controller注解
3.有注解必须将注解所在的包扫描到配置文件中
在SpringMVC.xml中配置扫描器
<!-- 扫描org.***.handler包 --> <context:component-scan base-package="org.***.handler"/>
|
4.配置视图解析器InternalResourceViewResolver
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <!-- 给所有的响应地址加前缀(文件夹名) --> <property name="prefix" value="views"/> <!-- 给所有的响应地址加后缀(文件格式) --> <property name="suffix" value=".jsp"/> </bean> |
5.在方法上加拦截注解 @RequestMapping("url")
请求的路径是什么 注解中的url就是什么
总结:
<url-pattern>/</url-pattern>中的 ” / ”
映射是去匹配 @RequestMapping("url")注解,可以和方法名、类名不一致
设置POST|GET请求接收方式
@RequestMapping(value="url",method=RequestMethod.POST,params={"name","age!=23",....}) |
value属性:设置请求路径,如果没有其他参数可以不设置,直接写路径即可
method属性:设置接收方式RequestMethod.POST | GET | DELETE | PUT
params属性:设置接接收参数,可以是一个,也可以用{}中加入多个参数,也可以加入表达式
其本质是约束比如 age!=23 ,那么age如果为23则不接收其请求,
如果参数设置了"name" 那么请求中必须携带name参数 否则不接收,
如果参数设置了"name=zs"那么请求中必须携带name而且其值为zs否则不接收等等类Java的表达式.
headers属性:这是请求头的约束
SpringMVC默认为单例;设置SpringMVC为多例:类上加@scope="prototype";
5.ant风格请求路径:
? | 任意单字符 |
* | 任意字符 0个或多个 |
** | 任意目录 |
1.浏览器get传值通过@PathVariable动态获取参数
例: jsp发送一个请求 userServlet?name=zs -> userServlet/zs MVC接收请求和参数 @RequestMapping(value="userServlet/{name}") public String welccome(@PathVariable("name")String name){ } |
REST风格: 一种软件编程风格
GET | 查 |
POST | 增 |
DELETE | 删 |
PUT | 改 |
普通浏览器只支持GET | POST 并不支持其他 这种情况可以使用过滤器进行拦截改变请求方式
原本的servlet过滤器是需要手写的,在使用MVC框架以后,框架提供了过滤器HiddenHttpMethodFilter
实现方法: 在WEB-INF目录下的web.xml中 <!--映射MVC过滤器--> <filter> <filter-name>HiddenHttpMethodFilter</filter-name> <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class> </filter> <filter> <filter-name>HiddenHttpMethodFilter</filter-name> <!--拦截所有请求--> <url-pattern>/*</url-pattern> </filter>
|
在表单中如果要发送PUT|DELETE请求而浏览器不支持就可以增加一个隐藏域
from中的method请求方式必须是post <input type="hidden" name="_method" value="PUT|DELETE"> |
2.表单get提交传值
在方法参数中定义@RequestParam("result")默认参数是必须接收的,如果前端没有传过来此参数,则会报错.在参数中加入required=false属性解决此问题
也可以给参数加上默认值defaultValue="default" 若前端未传此参数,则使用默认参数
例: public String getName(@RequestParam("name")String name) |
3.获取请求头信息 @RequestHeader
例:public String getName(@RequestHeader("Accept-Language")String Language) |
4.获取Cookie信息 @CookieValue
例:public String getSessionId(@CookieValue("JSESSIONID")String jSessionId) |
小结:
SpringMVC处理各种参数的流程/逻辑
请求: 前端发送请求a -> @RequestMapping("a")获取参数
6.使用实体类接收参数
form表单中的input-> name值 必须与实体类中的属性值一样,级联查询使用属性对象.对象中的属性名
例: <form> <input type="text" name="name"> <input type="text" name="age"> <input type="text" name="book.bookName"> </form> public String addStudent(Student student){}
|
在SpringMVC中使用原生态的Servlet API 比如HttoServletRequest 就直接将servletAPI中的类或接口等 直接写在参数中即可使用 对象直接交给SpringMVC管理
例: @RequestMapping(value="testServletAPI") public String testServletAPI(HttpServletRequst request,HttpServletResponse response){ } |
7.响应参数
响应参数放入Request域 : RequestAttribute
1.返回ModelAndView
public ModelAndView testModelAndView(){ ModelAndView modelAndView = new ModelAndView(ShowStudent.jsp); //构造函数参数->响应页面 Student stu = new Student("张三",19); modelAndView.addObjecg(stu);//addObject方法将响应的数据放入request域中 return modelAndView; } jsp页面接收响应用EL表达式${requestScope.stu.name}
|
2.返回ModelMap
public String testModelMap(ModelMap modelMap){ Student stu = new Student("张三",19); modelMap.put("Student",stu);//键值对形式将响应数据放入request域中 return ShowStudent;//转发请求形式响应视图 } jsp页面接收响应用EL表达式${requestScope.stu.name}
|
3.返回Map
public String testModelMap(Map<"String",Object> map){ Student stu = new Student("张三",19); map.put("Student",stu);//键值对形式将响应数据放入request域中 return ShowStudent;//转发请求形式响应视图 } jsp页面接收响应用EL表达式${requestScope.stu.name} |
4.返回Model
public String testModelMap(Model model){ Student stu = new Student("张三",19); model.addAttribute("Student",stu);//键值对形式将响应数据放入request域中 return ShowStudent;//转发请求形式响应视图 } jsp页面接收响应用EL表达式${requestScope.stu.name} |
5.响应参数放入session域 :
@SessionAttributes("")放在类名上
1.单个对象@SessionAttributes(value="student")放入对象名
1.2.放入多个参数@SessionAttributes(value={"student","book"})
2.放一个类型的对象@SessionAttributes(types=Student.class)
2.2.放入多个对象@SessionAttributes(types={Student.class,Book.class})
6.更新请求:
@ModleAttrribute此注解标注的方法在所有方法前执行
例: @ModleAttribute public void getStudent(@RequestParam("stuId")Integer stuId,Map map){ Student student = StudentServiceImpl.getStudent(stuId);//调用三层获取Student对象 //假设 student的id=12,name=zhangsan,age=19, map.put("student",student); //通过map放入request域中 放入的就是key值就是对象自动映射首字母大写,如果想改名, //那么调用处也要用@modelAttribute()来标注 比如map.put("stu",student)那么 //调用处fu(@ModelAttribute("stu")Student student); }
@RequestMapping("updateStudent") public String queryStudentById(@RequestParam("stuName")String stuName,Student student,Map map){ //request域中的student自动映射参数中的Student(映射的是类型与形参无关) student.setName(stuName); map.put("student",student); return "index"; } |
常用场景:
1.经常使用在更新之前
2.想要不修改本身代码 再增加功能
注意事项:
本身controller的设计理念是一个controller处理一个请求,所以加@ModelAttribute非常合理
但是因为SpringMVC本身太强大灵活,所以经常将各种方法写在一个类(controller)里,
所以使用@ModelAttribute时需要慎重,因为执行每一个请求前都要执行一遍,会使程序执行效率降低或产生干扰
8. 视图解析器
1.视图解析器处理流程:
Controller返回值 -> | ModelAndView -> | 视图解析器 -> | 视图 |
String(ssucess) | 无论返回什么 | ViewResolver | View |
View(ssucess) | 都会返回 |
| jsp/HTML/PDF/Excel |
ModelAndView | ModelAndView |
|
|
2.常见的视图解析器:
InternalresourceView、InternalResourceViewResolver
Public clas JstlView extends InternalResourceView 可以解析jstl/实现国际(i18n/internationalization/base)化操作
SpringGMVC解析jsp时 会默认使用InternalResourceView,如果发现jsp中包含了jstl语言,则自动转为jstlView
3.实现国际化的步骤:
1.创建资源文件 -> 基名_语言_地区.properties | 基名_语言properties
2.参数就是k/v对
例:你好=hello
3.具体实现国际化步骤
a.创建资源文件
b.配置springmvc.xml加载资源文件
<bean id="messageSource" class="prg.springframework.context.support.ResourceBundleMessageSource"> <property name="basename" value="i18n"/> </bean> ResourceBundleMessageSource会再SpringMVC响应时介入(解析国际化资源文件) |
c.通过JSTL使用国际化
jstl.jar standar.jar
springmvc在启动时.会自动查找一个id="messageSource"的bean.如果有则自动加载
9.pringMVC视图解析器常见功能
1.实现jsp直接访问jsp
SpringMVC:index.jsp -> succes.jsp :
<mvc:view-controller path="index" view-name="success"/> |
此时客户端的一切请求会将Controller(@RequestMapping())的一切请求屏蔽掉.都在<mvc:-controller>中查找
加入<mvc:annotation-driven></mvc:annotation-driven>(此标签是SpringMVC的基础配置,很多功能都需要通过该注解来协调)实现两种方式的共存
2.请求转发 改成 重定向
转发:forward: 重定向:redirect
例: public void goIndexfun(){ return index; } -> public void goIndexfun(){ return forward:/WEB-INF/jsp/index.jsp; } 或 public void goIndexfun(){ return redirect:/WEB-INF/jsp/index.jsp; } |
注意:如果使用此请求方法,则请求地址不会被视图解析器加上前缀和后缀,就需要自己加前缀和后缀,否则请求访问404
3.处理静态资源:
(静态资源:html css js 图片 视频等)
可以与用户交互的资源为动态资源(如百度搜索:天气)
在SpringMVC中如果直接访问静态资源,结果会是404原因如下:
之前所有的请求,通过通配符/拦截,进而交给SpringMVC的入口DispatcherServlet去处理;找该请求映射对应的@RequestMapping
例:http://localhost:8080/SpringMVCProject/img.png
@RequestMapping("img.png") return sucess; 所以找不到
解决:如果是需要mvc处理的,交给@RequestMapping去处理,如果不需要SpringMVC处理则使用tomcat默认的servlet去处理.
例:如果有对应的请求拦截:<url-pattern>*.png</url-pattern>则被拦截交servlet处理,如果没有就直接访问.
兼容两种方式值需要增加一个配置:
在springMVC中加入标签<mvc:default-servlet-handler></mvc:default-servlet-handler> 此标签就可以让没有对应的@RequestMapping直接访问 |
注意:要在加入了<mvc:annotation-driven></mvc:annotation-driven>的基础上使用,两个标签都需要
4.类型转换
a.Spring内置有一些常见的类型转换器
b.也可以自定义类型转换器
i.编写自定义类型转换器的类
约定:实现Converter接口
ii.将自己编写的转换器加入到SpringMVC配置中
- 将自定义转换器纳入SpringIOC中
<bean id="myConverter" class="org.langiao.converter.Myconverter"></bean> |
- 将myConverter再纳入SpringMVC提供的转换器Bean中,所有的转换器都需要再spring工厂中注册,所以将自定义的转换器set进去就可以了
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean"> <property name="converters"> <set> <ref bean="myConverter"/> </set> </property> </bean> |
- 将conversionService注册到annotation-driven中来协调一下
<mvc:annotation-driven conversion-service="conversionService"></mvc:annotation-driven> |
10.数据格式化
例:SimpleDateForamt date= new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"); |
SpringMVC提供了很多注解,方便我们数据格式化:
- 在SpringMVC配置文件中加入配置数据格式化所以来的注解所需要的bean
<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean"> </bean> |
- 通过注解来使用//将前端传过来的数据进行格式约定方便映射接收
例:在pojo类中 @DateTimeFormmat(pattern="yyyy-MM-dd") private Date birthday |
3.fu(Student stu,BinndingResult result);
//如果格式化出错(传参出错) 会将错误信息放入BindingResult中 此为约定参数
其中有一个方法getErrorCont()返回错误信息数组,getDefaultMessage()方法返回单个错误信息
本身具有容错性 如果使用 控制台是可以输出错误信息 但是前段页面是不会报错的,解决此类问题:
将错误信息放入request域中, 在前端页面拿出展示即可
例:map.put("errors",result.getFieldErrors())返回错误信息集合
4.将类型转换的bean myConverter 放入格式化中 就能实现既格式化 又类型转换
注:BindingResult是一个约定参数,必须放在要接收的错误参数的下一个 暨:fu(Student stu,BindingResult result,Map map);否则不会生效
11. 校验
1.准备校验时使用的JAR
validation-api-1.0.0.GA.jar:JDK的接口;
hibernate-validator-4.2.0.Final.jar是对上述接口的实现;如果版本较高可能与aip冲突
jobss-jogging.jar
classmate.jar
hibernate-validator-annotation-processor.jar
log4j、slf4j、slf4j-log4j
2.需要<mvc:annotation-driven></mvc:annotation-driven>支持 要实现各种校验,必须实现一个接口:ValidatorFactory
org.springframework.validation.beanvalidation.LocalValidatorFactoryBean 是一个SpringMVC实现校验的标准接口实现类
而annotation-driven会自动加载LocalValidatorFactoryBean这个类.
3.在需要的类中的属性前加入jsr注解,在conctroller需要校验的方法参数前加@Valid注解
例:
@Past
private Date birthday
fn(@Valid Student stu)
JSR 303 基本的校验
空检查
@Null 验证对象是否为null
@NotNull 验证对象是否不为null, 无法查检长度为0的字符串
@NotBlank 检查约束字符串是不是Null还有被Trim的长度是否大于0,只对字符串,且会去掉前后空格.
@NotEmpty 检查约束元素是否为NULL或者是EMPTY.
Boolean检查
@AssertTrue 验证 Boolean 对象是否为 true
@AssertFalse 验证 Boolean 对象是否为 false
长度检查
@Size(min=, max=) 验证对象(Array,Collection,Map,String)长度是否在给定的范围之内
@Length(min=, max=) Validates that the annotated string is between min and max included.
日期检查
@Past 验证 Date 和 Calendar 对象是否在当前时间之前,验证成立的话被注释的元素一定是一个过去的日期
@Future 验证 Date 和 Calendar 对象是否在当前时间之后 ,验证成立的话被注释的元素一定是一个将来的日期
@Pattern 验证 String 对象是否符合正则表达式的规则,被注释的元素符合制定的正则表达式,regexp:正则表达式 flags: 指定 Pattern.Flag 的数组,表示正则表达式的相关选项。
数值检查
建议使用在Stirng,Integer类型,不建议使用在int类型上,因为表单值为“”时无法转换为int,但可以转换为Stirng为”“,Integer为null
@Min 验证 Number 和 String 对象是否大等于指定的值
@Max 验证 Number 和 String 对象是否小等于指定的值
@DecimalMax 被标注的值必须不大于约束中指定的最大值. 这个约束的参数是一个通过BigDecimal定义的最大值的字符串表示.小数存在精度
@DecimalMin 被标注的值必须不小于约束中指定的最小值. 这个约束的参数是一个通过BigDecimal定义的最小值的字符串表示.小数存在精度
@Digits 验证 Number 和 String 的构成是否合法
@Digits(integer=,fraction=) 验证字符串是否是符合指定格式的数字, Integer指定整数精度,fraction指定小数精度。
@Range(min=, max=) 被指定的元素必须在合适的范围内
@Range(min=10000,max=50000,message=”range.bean.wage”)
@Valid 递归的对关联对象进行校验, 如果关联对象是个集合或者数组,那么对其中的元素进行递归校验,如果是一个map,则对其中的值部分进行校验.(是否进行递归验证)
@CreditCardNumber信用卡验证
@Email 验证是否是邮件地址,如果为null,不进行验证,算通过验证。
@ScriptAssert(lang= ,script=, alias=)
@URL(protocol=,host=, port=,regexp=, flags=)
12.SpringMVC通过Ajax处理json数据@Response
1.导入jar包 - 最好是同一版本 否则容易出冲突
jackson-annotations.jar
jackson-core.jar
jackson-databind.jar
- Ajax请求SpringMVC.并且返回json格式的数据
请求Ajax 例: $(document).reday(function(){ $(“#testAjaxJson”).click(function(){ //通过ajax请求springMVC $.post( “handler/testAjaxJson”,//服务器地址 {“name”:”zs”} function(){//服务端处理完毕后的回调函数} ) }) })
|
如何在ajax中获取回调函数:在contrller的方法中加入@ResponseBody注解之后就可以在ajax中调json对象了
例: //告诉springMVC此时的返回不是一个view页面,而是一个ajax返回值以json格式 @ResponseBody fu(){} |
13.SpringMVC实现文件上传
与servlet本质一样 都是通过组件实现的
commons-io.jar
commons-fileupload.jar
SpringMVC可以简化文件上传的代码.但是必须满足条件:实现MultipartreSolver接口
SpringMVC实现文件上传
与servlet本质一样 都是通过组件实现的
commons-io.jar
commons-fileupload.jar
SpringMVC可以简化文件上传的代码.但是必须满足条件:实现MultipartreSolver接口
- 配置XML文件
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <property name="defaultEncoding" value="UTF-8" /><!--编码格式--> <property name="maxUploadSize" value="-1" /> <!--上传单个文件的最大值,单位byte 如果配-1表示无限制--> </bean>
|
- 编写前端代码
<form action="handler/testUpload" Method="post" enctype="multipart/form-data"> <input type="file" name="fileUpload"/> <button type="submit">提交</button> </form> |
- 编写controller
//文件上传处理方法 @RequestMapping("handler") public class TestUpload { @RequestMapping("testUpload") public String testUpload(@RequestParam("fileUpload")MultipartFile file , HttpServletRequest request){ //获取项目路跟目录路径 String path = request.getSession().getServletContext().getRealPath("upload/"); System.out.println(path); try { InputStream input = file.getInputStream(); //拿到流 抛异常 String fileName = file.getOriginalFilename();//获取上传文件名 OutputStream out = new FileOutputStream(path+fileName); byte[] bs = new byte[1024]; int len= -1; while((len=input.read(bs)) != -1){ out.write(bs,0,len); } out.close(); input.close(); } catch (IOException e) { // TODO Auto-generated catch block System.out.println("进入异常"); } return "/hello"; } }
|
14.SpringMVC拦截器
a.编写拦截器 public class MyInterceptor implements HandlerInterceptor
重写方法:
prehandle();拦截请求
postHandle();拦截响应
afterCompletion();渲染完毕触发
b.配置:将自己写的拦截器配置到springmvc中 默认拦截全部请求
//如果默认拦截全部 不配置<mvc:interceptor>则放在<mvc:interceptors>中 <mvc:interceptors> <!--配置具体的拦截路径--> <mvc:interceptor> <!--指定拦截的路径,基于ant风格--> <mvc:mapping path="/**"> <!--指定不拦截的路径二者取交集--> <mvc:exclude-mapping path=""/> <bean class"MyInterceptor的全名"></bean> </mvc:interceptor> </mvc:interceptors>
|
c:拦截器链
拦截器1 -> 拦截器2 -> 执行方法 -> 拦截器2 -> 拦截器1 -> 渲染展示 ->
最终渲染只会用一次 谁排最后就用谁
15.异常处理:
SpringMVC:必须实现HandlerExceptionResolver接口,该接口的实现类 都是异常的各种处理方式
- ExceptionHandlerExceptionResolver,主要提供了@ExceptionHandler注解,并且通过该注解处理异常
//该方法就可以捕获本类中抛出的ArithmeticException异常 @ExceptionHandler({ArithmeticException.class}) //参数中只能有一个异常参数,有其他参数此方法就不会捕获异常了 public ModelAndView handlerArithmeticException(ArithmeticException e){ ModelAndView mv = new ModelAndView("error"); System.out,println(e); mv.addObject("er",e); return mv }
|
异常处理路径:最短优先
如果有方法抛出一个ArithmeticException异常,而该类中 有2个对应的异常处理方法则按照就近原则
@Exception默认只能捕获当前类中的异常方法
如果发生异常的方法,和处理异常的方法不在同一个类中: 加一个注解@ControllerAdvice即可
例子: @ControllerAdvice public class MyExceptiongHandler(){//用于处理异常的类 fun(); } |
总结:如果一个 方法用于处理异常,并且只处理当前类的异常:则只在类中加一个
@ExceptiongHandler修饰的方法
如果一方法用于处理异常,并且处理所有类中的异常:新建一个@ControllerAdvice修饰的类,在类中加一个处理异常的@ExceptiongHandler修饰的方法
b.ResponseStatusExceptionResolver:自定义异常显示页面@ResponseStatus(value="HttpStatus.FORBIDDEN",reason="数组越界")
//value错误信息码,枚举类型,reason错误信息提示
继承Exception编程自定义异常类
例:@ResponseStatus(value="HttpStatus.FORBIDDEN",reason="数组越界") public class MyArrayIndexOutOfBoundsExcption extends Exception(){} 自定义异常类,该注解也可以直接放到方法前 |
通过过配置方式处理异常 <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver"> <!--如果发生异常 相当于将错误信息e放入request域中--> <!--如果不写下面这个声明 就会将异常变量放入默认值Exception--> <property name ="exceptionAttribute" value="e" > </property> <property name="exceptionMappings"> <props> <!--相当于catch (NullPointerException e){跳转:error}--> <prop key="java.lang.NullPointerException"> error </prop> </props> </property> </bean> |