SpringMVC:
1.jar
spring-aop.jar
spring-bean.jar
spring-context.jar
spring-core.jar
spring-web.jar
spring-webmvc.jar
如果报错NoClassDefFoundError,说明项目缺少jar包NoClassDefFoundError,说明项目缺少jar包
2.第一个SpringMVC程序
Servlet -> SpringMVC
jsp -> Servlet(SpringMVC) ->jsp
SpringMVC配置文件springmvc.xml
选中常用的命名空间:beans aop context mvc
普通的servlet的流程:
请求- url-patten - 交给对应的servlet,如何告知程序?-如何让springmvc介入程序:
需要配置一个springmvc自带的servlet
通过以下配置,拦截所有请求,交给SpringMVC处理:
<servlet>
<servlet-name>springDispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>springDispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
其中:
<url-pattern>/</url-pattern>
/:一切请求,注意请求不是/*
/user:拦截以 /user开头的请求
/user/abc.do :只拦截该请求
.action:只拦截.action结尾的请求
项目中同时兼容 springMVC和Servlet
<servlet-mapping>
<servlet-name>springDispatcherServlet</servlet-name>
<url-pattern>.action</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>springDispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 初始化参数,告诉MVC的配置参数在哪里 -->
<!--如果MVC的配置地址与该web.xml在同一目录下,可以省去此配置,但是MVC的配置文件名必须为springDispatcherServlet-servlet.xml-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springMVC.xml</param-value>
</init-param>
通过该配置指定springmvc配置文件的路径,如果要省略,必须放到默认路径:
/WEB-INF/springDispatcherServlet-servlet.xml
<!-- 初始化参数, 项目启动时,以第一的身份自动加载,配置自动生效-->
<load-on-startup>1</load-on-startup>
</servlet>
映射是去匹配@RequestMapping()注解
通过method指定 请求方式(get post delete put)
@RequestMapping(value="welcome",method=RequestMethod.POST)//映射
设置name="xxxx"的情况
params= {"name2=zs","age!=23"}
name2:必须有name="name2"参数
age!=23: a.如果有name="zs",则age值不能是23】
b.没有age
!name2: 参数中不能含有name=!name2
ant风格的请求路径:
? 单字符
* 任意个字符(0或)
** 任意目录
例子:
@RequestMapping(value="welcome/**/test")
接受实例: <a href="welcome/aba/syz/123/test"></a>
@PathVariable获取动态参数
REST风格:软件编程风格
Springmvc:
GET: 查
POST: 增
DELETE: 删
PUT: 改
普通浏览器只支持GET和POST方式,其他请求方式如delete和put请求是通过过滤器新加入的支持
过滤的条件:
1.<input type="hidden" name="_method" value="delete/put">
2.请求方式为post
Springmvc实现:put/post请求方式的步骤
a.增加过滤器
<!-- 增加HiddenHttpMethodFilter过滤器:目的是给普通浏览器增加put/delete请求方式 -->
<filter>
<filter-name>HiddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>HiddenHttpMethodFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
b.表单
<form action="SpringMVCHandler/testDelete/1234" method="post">
<input type="hidden" name="_method" value="DELETE"/>
<input type="submit" value="删">
</form>
i:必须是post方式
ii:通过隐藏域的value值设置实际的请求方式DELETE|PUT
c.控制器
@RequestMapping(value="testDelete/{id}",method=RequestMethod.DELETE)//映射
public String testDelete(@PathVariable("id")Integer id) {
System.out.println("delete:删"+id);
return "success";// /view/success.jsp 默认使用了请求中转发的跳转方式
//success:会增加解析器中prefix,suffix
}
通过 , method=RequestMethod.DELETE 匹配具体的请求方式
此外,可以发现,当映射相同时@RequestMapping(value="testRest"),可以通过method处理不同的请求
过滤器中 处理put|delete请求的部分源码:
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
HttpServletRequest requestToUse = request;
if ("POST".equals(request.getMethod()) && request.getAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE) == null) {
String paramValue = request.getParameter(this.methodParam);
if (StringUtils.hasLength(paramValue)) {
requestToUse = new HttpMethodRequestWrapper(request, paramValue);
}
}
filterChain.doFilter(requestToUse, response);
}
原始请求:request,该请求默认只支持get post header
但是如果是"POST" 并且有隐藏域 <input type="hidden" name="_method" value="DELETE"/>
则,过滤器将原始的请求request加入新的请求方式DELETE,并将原始请求转为requestToUser 请求(request+delete请求)
最后将requestToUser 放入请求链中,后续再事情request时 实际就使用改造后的requestToUser
@RequestParam("uname") String name,@RequestParam(value="uage",required=false,defaultValue="23")
@RequestParam("uname"):接受前台传递的值,等价于request.getParameter("uname");
required =false:该属性 不是必须的。
defaultValue = "23":默认值23
获取请求头信息 @RequestHeader
public String testPut(@RequestHeader("Accept-Language")String al){}
通过@RequestHeader("Accept-Language") String al 获取请求头中的Accept-Language值,并将值保存在al变量中
通过mvc获取cookie值(JSESSIONID)
@CookieValue
(前置知识:服务器在接受客户端第一次请求时,会给该客户端分配一个session(该session包含一个sessionId)), 并且服务器端会在第一次响应客户端时,
将该sessionId赋值给JSESSIONID并传递给客户端cookie中
小结:
SpringMVC处理各种参数的流程/逻辑:
请求:前段发送请求a->@RequestMapping("a")
处理请求中的参数xyz
@RequestMapping("a")
public String aa(@Xxx注解("xyz") xyz){
}
使用对象(实体类Student)接受请求参数
在SpringMVC中使用原生态的Servlet API :HttpServletRequest:直接将 servlet-api中的类、接口等写在springMVC所映射的方法参数中即可。
处理模型数据
如果跳转时需要带数据:V,M则可以使用以下方式
ModelAndView、ModelMap、Map、Model 数据放在了request作用域
@SessionAttributes、@ModelAttribute
示例:
public String testModel(Model model | Map<String,Object> m | ModelMap mm) {
m.put(x,"..");就会将x对象放入request域中
}
如何将上述数据放入session中?加上@SessionAttributes()
@ModelAttribute
i.经常在更新时候使用
ii.在不改变原有代码的基础上插入一个新的方法
通过@ModelAttribute修饰的方法,会在每次请求前先执行,
并且该方法的参数map.put()可以将对象放入即将查询的参数中。
必须满足的约定:
map.put(k.v); 其中的k必须是即将查询的方法参数的首字母小写
testModelAttribute(Student xxx),即student;
如果不一致,需要通过@ModelAttribute声明。如下
@ModelAttribute//在任何一次请求钱,都会先执行@ModelAtttibute修饰的方法
public void queryStudentById(Map<String ,Object> map) {
//模拟调用三层查询数据库的操作
Student student = new Student();
student.setId(31);
student.setName("zs");
student.setAge(21);
//map.put("student",student);//约定:map的key就是方法参数类型的首字母小写
map.put("stu",student);
}
//修改
@RequestMapping(value="testModelAttribute")
public String testModelAttribute(@ModelAttribute("stu")Student student) {
student.setName(student.getName());//将名字zs改为ls
System.out.println(student.getId()+","+student.getName()+","+student.getAge());
return "success2";
}
一个servlet对应一个功能;
增删改查 对应于 4个Servlet
查: Servlet - SpringMVC的Controller
@ModelAttribute
public void query(){
}
修改
public String update(){
}
@ModelAttribute 会在该类的每个方法执行前均被执行一次,因此使用时需要注意
2.视图、视图解析器
Controller返回值
String(sussess)
View(success) ===》 ModelAndView ==》 视图解析器 ==> 视图
ModelAndView ViewResolver View (渲染)(Jsp/PDF/Excel)
视图的顶级接口:View
视图解析器的顶级接口:ViewResolver
常见的视图和解析器:
InternalResourceView、InternalResourceViewResolver
public class JstlView extends InternalResourceView:
springMVC解析jsp时,会默认使用InternalResourceView,如果发现jsp中包含jstl语言,则自动转为JstlView
JstlView可以解析jstl\实现国际化操作
国际化:针对不同地区、不同国家,进行不同的显示
具体实现国际化步骤:
a.创建资源文件
基名_语言_地区.properties
基名_语言.properties
b.配置springmvc.xml 加载资源文件
<!-- 加载国际化资源文件
1.将ResourceBundleMessageSource在程序加载时,加入springmvc:pringmvc在启动时,会自动查找一个id="messageSource"的bean,如果有则自动加载
2.配置了ResourceBundleMessageSource,则该类会在程序相应的时介入
-->
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value="i18n"></property>
</bean>
ResourceBundleMessageSource会在springmvc响应程序时介入(解析国际化资源文件)
c.通过jstl使用国家化
jstl.jar standard.jar
springmvc在启动时,会自动查找一个id="messageSource"的bean,如果有则自动加载
i18n_zh_CH.properties
resource.welcome=你好
resource.exist=退出
i18n.properties
1.<mvc:view-controller..>
InternalResourceViewResolver
index.jsp -> Controller(@RequestMapping) -> succes.jsp
要用SpringMVC实现:index.jsp -> succes.jsp
<mvc:view-controller path="SpringMVCHandler/welcome" view-name="success">
以上注解,会让所有的请求 转入<mvc..>中匹配映射地址,而被忽略掉@RequestMapping();
如果想让@RequestMapping("a")和<mvc:...>共存,则需要加入一个注解:<mvc:annotation-driven></mvc:annotation-driven>
2.指定请求方式
指定跳转方式:return "forward:/views/success.jsp";
forward: 、 redirect: ,需要注意此种方式,不会被视图解析器加上前缀(/views)、后缀(.jsp)
forward:请求转发 redirect:重定向
2.处理静态资源:html css js 图片 视频
可以与用户交互,因为时间/地点的不同而结果不同的内容:动态(百度:天气)
在springMVC中,如果直接访问静态资源:404 原因:之前将所有的请求通过通配符"、"拦截,进而小哥女Springmvc的入口DispatcherServlet去处理:找该请求映射对应的@RequesMapping
http://localhost:8080/SpringMVCProject/img.png
@RequestMapping("img.png")
解决:如果是需要mvc处理的,则交给@RequesMapping("img.png")处理,如果不需要springmvc处理,则使用
tomcat默认的Servlet去处理:如果有对应的请求拦截,则交给相应的servlet去处理,如果没有相应的servlet,则直接访问。
tocmcat默认的Servlet在哪里?在tomcat配置文件\conf\web.xml中
<servlet>
<servlet-name>abc</servlet-name>
<servlet-class>xxx.xxx.xx.ABCServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>abc</servlet-name>
<url-pattern>/abc</url-pattern>
</servlet-mapping>
解决静态资源方案:如果有springmvc对应的@requestMapping则交给spring处理,如果没有对应的@requestMapping,则交给服务器Tomcat默认的servlet去处理:实现方法,只需要增加两个注解即可 springmvc.xml:
<mvc:default-servlet-handler></mvc:default-servlet-handler>
<mvc:annotation-driven></mvc:annotation-driven>
总结:要让springmvc访问静态资源,只需要加入以下2个注解:
<mvc:default-servlet-handler></mvc:default-servlet-handler>
<mvc:annotation-driven></mvc:annotation-driven>
4.类型转换
a.Spring自带一些常见的类型转换器:
public String testDelete(@PathVariable("id") String id) . 既可以接受int类型数据id,也可以接受String类型数据
a.可以自定义类型转换器
i.编写自定义类型转换器的类(实现Converter接口)
public class MyConverter implements Converter<String,Student>{
@Override
public Student convert(String source) {//source:2-zs-23
//cource接收前端传来的String:2-zs-23
String[] studentStrArr = source.split("-");
Student student = new Student();
student.setId(Integer.parseInt(studentStrArr[0]));
student.setName(studentStrArr[1]);
student.setAge(Integer.parseInt(studentStrArr[2]));
return student;
}
2.配置:将MyConverter加入到springmvc中
<!-- 1将自定义转换器纳入SpringIOC容器 -->
<bean id="myConverter" class="org.lanqiao.converter.MyConverter"></bean>
<!-- 2将myConverter再纳入SpringMVC提过的转换器bean -->
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<set>
<ref bean="myConverter"/>
</set>
</property>
</bean>
<!-- 3将 conversionService注册到annotation-driven中-->
<!-- 此配置是SpringMVC的基础配置,很多功能都需要通过该注解来协调 -->
<mvc:annotation-driven conversion-service="conversionService"></mvc:annotation-driven>
5.数据格式化
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
SpringMVC提供了很多的注解 ,方便我们数据格式化
实现步骤:
a.配置
<!-- 配置数据格式化 注解 所依赖的bean -->
<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean"></bean>
b.通过注解使用
@DateTimeFormat(pattern="yyyy-MM-dd")
@NumberFormat(pattern="###,#")
错误消息:
public String testDateTimeFormat(Student student,BindingResult result,Map<String,Object> map){
需要验证的数据是Student中的birthday,SpringMVC要求如果校验失败则将错误信息自动放入该对象之后紧挨的BindingResult中。
即Student student,BindingResult result之间不能有其他参数。
如果要讲控制台的错误信息传到jsp中显示,则可以将错误信息对象放入request域中,然后在jsp中从request中获取。
2.数据校验
JSR303
Hibernate Validator
使用Hibernate Validator步骤:
a.jar
hibernate-validator.jar --注意版本问题(可能出现不兼容)
classmate.jar
jboss-logging.jar
validatetion-api.jar
hibernate-validator-annotation-processor.jar
b.配置
<mvc:annotation-driven></mvc:annotation-driven>
此时mvc:annotation-driven的作用,要实现Hibernate Validator/JSR303 校验(或者其他各种校验),必须实现SpringMVC提供的一个接口:ValidatorFactory
LocalValidatorFactoryBean是ValidatorFactory的一个实现类
<mvc:annotation-driven></mvc:annotation-driven>会在springmvc容器中自动加载一个LocalValidatorFactoryBean类,因此可以直接实现数据校验
c.直接使用注解
public class Student{
@Past//当前时间以前
private Date birthday;
}
在校验的Controller中,给校验的对象前增加@Valid
public String testDateTimeFormat(@Valid Student student,BindingResult,Map<String,Object> map){...}
3.Ajax请求SpringMVC,并且JSON格式的数据
a.jar
jackson-annotations.jar
jackson-core.jar
jackson-databind.jar
b.
@ResponseBody修饰的方法,会将该方法的返回值以一个json数组的形式返回给前台
============================================================
SpringMVC实现文件上传:
和Servlet方式的本质一样,都是通过commons-fileupload.jar和commons-io.jar
SpingMVC可以简化文件上传的代码,但是必须满足条件,实现MultipartResolver接口:
而该接口的实现类SpringMVC也已经提供CommonsMultipartResolver
具体步骤:(直接使用CommonsMultipartResolver实现上传)
a.jar
commons-fileupload.jar
commons-io.jar
b.配置CommonsMultipartResolver
将其加入SpringIOC容器
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="defaultEncoding" value="UTF-8"></property>
<!-- 上传单个文件的最大值,单位Byte;如果-1,表示无限值 -->
<property name="maxUploadSize" value="102400"></property>
</bean>
c.处理方法
@RequestMapping(value="testUpload")
public String testUpload(@RequestParam("desc")String desc,@RequestParam("file")MultipartFile file) throws IOException {
System.out.println("文件描述信息:"+desc);
//jsp中上传的文件:file
InputStream input = file.getInputStream();//IO
String fileName = file.getOriginalFilename();
OutputStream out = new FileOutputStream("e:\\"+fileName);//IO
byte[] bs = new byte[1024];
int len = -1;
while((len=input.read(bs))!=-1){
out.write(bs,0,len);
}
out.close();
input.close();
//将file上传到服务器中的某一个 硬盘文件中
System.out.println("上传成功");
return "success2";
}
<form action="SpringMVCHandler/testUpload" method="post" enctype="multipart/form-data">
<input type="file" name="file"/>
描述:<input name="desc" type="text"/>
<input type="submit" value="上传">
</form>
2.拦截器
原理与过滤器相同
SpringMVC:要想实现拦截器,必须实现一个接口HandlerInterceptor
a.编写拦截器implements HandlerInterceptor
b.配置:将自己写的拦截器配置到springmvc中(spring)
如果有多个拦截器,则每个拦截器的preHandle postHandle 都会在相应时机各被触发一次;但是afterCompletion只在最后一次拦截被触发进行渲染
3.异常处理
SpringMVC: HandlerExceptionResolver接口,
该接口的每个实现类都是异常的一种处理方式:
a.
ExceptionHandlerExceptionResolver:主要提供了@ExceptionHandler注解,并通过该注解处理异常
//该方法 可以捕获本类中 抛出的ArithmeticException异常
@ExceptionHandler({ArithmeticException.class,ArrayIndexOutOfBoundsException.class})
public String handlerArithmeticException(Exception e){
System.out.println(e);
return "error";
}
@ExceptionHandler标识的方法的参数必须在异常类型(Throwable或其子类),不能包含其他类型参数
异常处理路径:最短优先
@ExceptionHandler默认只能捕获当前类中的异常方法。
如果发现异常的方法和处理异常的方法不在同一个类中:@ControllerAdvice
总结:如果一个方法用于处理异常,并且只处理当前类中的异常:@ExceptionHandler
如果一个方法用于处理异常,并且处理所有类中的异常:类前加@ControllerAdvice、处理异常的方法前加@ExceptionHandler
b.
ResponseStatusExceptionResolver:自定义异常显示界面@ResponseStatus
@ResponseStatus(value=HttpStatus.FORBIDDEN,reson="数组越界")
public class MyArrayIndexOutOfBoundsException extends Exception{ //自定义异常
}
@ResponseStatus也可以标志在方法前面
视频学习地址:https://ke.qq.com/course/list/DT