入门程序
1. 创建maven项目需要选择webapp骨架(org.apache.maven.archetypes:maven-archetype-webapp),还有添加额外配置(archetypeCatalog internal),防止创建maven的web项目慢的问题。
2. 引入依赖(修改maven的jdk编译环境为1.8)
spring-context
spring-web
spring-webmvc
servlet-api(scope--->provide)
jsp-api(scope--->provide)
3. 核心配置文件的配置(mvc和context约束)
1. 开启注解扫描 <context:component-scan base-package />
2. 视图解析器对象 <bean class="InternalResourceViewResolver"> 拼接字符串的前面 和 拼接字符串的后面的两个set </bean>
3. 开启SpringMVC框架注解支持 <mvc:annotation-driven/>
4. web.xml的配置
1. 配置前端控制器,加载核心配置文件
<servlet>
<servlet-name>dispatcherServlet</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>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
5. 编写Controller和jsp页面进行测试
1. 使用@Controller注解和jsp配置视图解析器进行测试
2. 使用@RestController进行测试(推荐,相对简单)
MVC的执行流程
1. 配置文件的加载:
启动项目,加载web.xml文件
创建Servlet(前端/核心控制器-->指挥中心)的对象
springmvc.xml的加载
注解扫描加载SpringIOC的bean(@Controller)
视图解析器
SpringMVC注解支持(@RequestMapping)
2. 请求响应:
客户端发送请求------> 核心控制器(进入SpringMVC的请求流程)----->
调用HandlerMapping(处理器映射器),匹配被@Controller注解注解的类中的@RequestMapping ----->
调用HandlerAdapter(处理器适配器)执行handle(Controller中声明的方法),handle返回结果(ModelAndView,模型视图,封装结果和视图)给HandlerAdapter ------->
HandlerAdapter返回MOdelAndView给核心控制器,核心控制器拿到结果去找视图解析器(ViewResolver)进行视图解析----->
视图解析器进行物理拼接形成具体的路径(View),返回给核心控制器 ------>
核心控制器进行视图渲染,填充数据到reuqest域中,response响应返回给客户端
3. 组件介绍
前端/核心控制器: 中央调度,协调作用
处理器映射器(HanderMapping): 根据请求的url查找Handler
处理器适配器(HanderAdapter): 执行Handle
ModelAndView: 模型及视图,封装结果视图
ViewResolver: 视图解析器,解析逻辑视图
Handle: 程序员编写业务逻辑处理,也就是Controller的方法
请求参数绑定
1. 基本数据类型绑定:
原则: 请求后拼接的参数与handle的参数名保持一致
2. JavaBean对象的绑定
1. 简单Bean直接使用form表单,name属性的值为Bean对象的属性名
2. Bean内包含另一个Bean,name的值使用属性.属性的方式
3. list使用属性下标点属性 ------> list[0].name
4. map使用属性key属性的方式 -----> list['one'].name
3. 中文乱码问题:
post乱码可以在web.xml中配置Spring提供的CharacterEncodingFilter
get乱码:
tomcat8之前,在tomcat的配置文件中配置URIEncoding="utf-8"
new String(username.getBytes("ios-8859-1"),"UTF-8")的方式
4. 日期类型转换:
1. 自定义类实现Converter<S,T>接口,使用DateFormat进行格式的转换.
2. 在springmvc中配置自定义转换器(ConversionServiceFactoryBean).
* 注意是context包下的转换器
* 配置converters属性
* 注入自定义转换器的bean
SpringMVC使用原生的Servlet
Handler方法的形参设置HttpServletRequest和HttpServletResponse就可以获取到所有的原生Servlet对象了
注解介绍
1. @RequestMapping:
* 作用位置:
1. 类上: 窄化请求路径,业务隔离
2. 方法上: url请求映射
* 属性:
1. value: 指定请求路径,与path属性一样
2. method: 指定请求方式,值有GET,POST,DELETE等等(枚举类型,RequestMethod)
3. params: 用于指定限制请求参数条件
4. headers: 用于指定请求头
2. @RequestParam:
* 作用 : 在页面传递的参数名与后台不一致的时候需要使用该注解,作用于参数之前
* 属性 :
1. name/value : 指定页面传递的参数名
2. required : 是否为必须,默认为true,表示该参数必须传递
3. defaultValue : 给定参数一个默认值,当有默认值的时候required属性感觉就无效了
3. @RequestBody
* 作用: 用于获取请求体的内容.(get请求不适用,POST请求适用)
4. @PathVariable(基于Restful风格的url支持)
* 作用在参数之前,目的是获取请求url中的占位符(参数)的信息
/test/{sid} ----> 请求路径
@PathVaribale(name="sid") Integer id ---> 方法的参数
/test/10 ----> 客户端的访问路径
* Restful的理解:
通过请求参数决定对哪个资源进行操作
通过请求方式决定采用何种操作(post,delete,put,get)
5. HiddenHttpMethodFilter:
浏览器的form表单只支持get和post请求,要洗那个发送put或delete请求,需要借助SpringMVC提供的HiddenHttpMethodFilter.
使用:
1. 在web中配置HiddenHttpMethodFilter过滤器
2. 在表单中添加隐藏域,<input type="hidden" name="_method" value="PUT"/>
3. 定义handle,声明method为RequestMethod.PUT
6. @RequestHeader
用于获取请求消息头,作用在方法参数之前
value属性可以指定获取哪个请求头信息(User-Agent比较常用)
7. @CookieValue
* 获取指定名称cookie的值
* value属性指定cookie名称(JSESSIONID)
8. @ModelAttribute
* 作用位置:
1. 方法上: 被@ModelAttribute注解修饰的方法表示该方法会在当前Controller的人格一个方法执行前先执行
应用在表单提交实体数据不完整的情况下,为实体数据添加内容.
方法有返回值(直接返回user),springmvc会将方法执行的返回值,存放在Model对象,最终会存放在Request中:
考虑在修改的时候根据id获取实体,方法返回实体,前台只需要传递修改的内容就可以了.
方法无返回值,springmvc会将方法形参中的map中的数据存放在Model对象,最终会存放在Request中:
在方法中使用map保存数据,在Handler的参数前使用@ModelAttribute(name="key")获取
2. 形式参数前(需制定value(key)属性):
获取request中获取对应的value的数据
9. @SessionAttributes(只能作用于类上,指定属性)
* 用于多个Handler间的参数共享(将数据存放在session域)
* 使用Model对象的addAttribute方法存放数据
* 使用ModelMap对象的get方法来获取session数据
* 使用setComplete()方法清空session域的数据(使用的是SessionStatus对象)
Handler返回值类型
1. 字符串:简单字符串是逻辑视图名称
* 使用返回的字符串,经过视图解析器,拼接前缀后缀,跳转到真实路径
2. void ===> 原始的Servlet操作
* 请求路径默认是: 前缀 + handle访问路径 + 后缀
* 使用原生Servlet进行跳转和携带参数,但是注意这个时候不会执行ViewResolver,所以必须写全路径,这个需要注意.在handle最后加个return;防止handle跳转方法完成后继续执行后续的代码.
* 还有需要注意重定向是无法访问WEB-INF目录下的东西.
* 直接响应数据: response.getWriter().print("content");注意处理乱码问题
3. ModelAndView
* ModelAndView对象: ModelAndView mv = new ModelAndView();
* 可以存放数据到对象,使用mv.addObject("key","value");实际还是存放在Model中,最后还是request域中
* 存放跳转的页面: 使用mv.setViewName("success");
4. forward和redirect关键字的用法(返回类型还是String):
* 返回的写法不一样,有了forward关键字后就不能调用视图解析器.
return "forward:/WEB-INF/pages/success.jsp"
* return "redirect:/index.jsp"; 重定向不能直接跳转到WEB-INF的目录下,并且redirect关键字不用添加项目目录
* return "forward:testString"; 转发到指定的的Handle
return "redirect:/user/testString";重定向到指定的的Handle
响应JSON数据
1. 在springmvc配置文件中配置静态资源不拦截(因为前端控制器会拦截所有资源):
<mvc:resources mapping="/js/**" location="/js/" />,表示js文件下面的所有文件都不拦截
<mvc:resources mapping="/css/**" location="/css/" />
<mvc:resources mapping="/images/**" location="/images/" />
> 注意静态资源放在web-app下
上面的配置在mapping的后面也一定要加**,否则会失败(在我电脑上是这样),如果还是失败,可以试试在web.xml中配置:
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.js</url-pattern>
</servlet-mapping>
2. ajax请求返回json数据:
* $.ajax({
url:"user/testAjax",
type:"POST",
contentType:"application/json;charset=utf-8",
data:'{"username":"hehe","password":"123456","age":24}',
dataType:"json",
success:function(data){
alert(data);
}
})
3. 自动转换,将json字符串转换为对象,需要额外依赖jackson的jar包:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.7</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.9.7</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.9.7</version>
</dependency>
接收json数据需要使用 ==@RequestBody==
获取请求中的数据
如果请求的参数是json格式,使用pojo接受,但是注意参数名和实体属性名一致
响应json数据需要使用 ==@ResponseBody==
直接将方法执行的返回值响应给客户端
如果是pojo,将pojo对象转化为json字符串
传统方式文件上传(了解):
1. 表单的enctype属性取值必须是: multipart/form-data
method属性值必须是post
提供你文件选择域: <input type="file" />
2. 三方组件实现,引入额外依赖:
commons-fileupload
commons-io
3. 传统方式文件上传(后台代码实现):
创建文件的上传路径(path),使用request.getSession().getServletContext().getRealPath("/uploads");
核心解析器:
DiskFileItemFactory ---> 磁盘文件的工厂对象
ServletFileUpload ---> 文件上传的核心解析器,传入工厂对象
解析request对象
List<FileItem> list = ServletFileUpload.parseRequest(request);
文件上传:
遍历list,判断是否为普通表单项(isFormFiled),不是则进行上传
获取文件上传名,修改上传名为唯一上传名(考虑使用UUID或者日期)
fileItem.write(new File(path,filename));path表示父级目录,filename表示修改后的文件名.
最后使用fileItem.delete(); 用来删除临时文件
/**
* 文件上传
* @return
*/
@RequestMapping("/fileupload1")
public String fileuoload1(HttpServletRequest request) throws Exception {
System.out.println("文件上传...");
// 使用fileupload组件完成文件上传
// 上传的位置
String path = request.getSession().getServletContext().getRealPath("/uploads/");
// 判断,该路径是否存在
File file = new File(path);
if(!file.exists()){
// 创建该文件夹
file.mkdirs();
}
// 解析request对象,获取上传文件项
DiskFileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload upload = new ServletFileUpload(factory);
// 解析request
List<FileItem> items = upload.parseRequest(request);
// 遍历
for(FileItem item:items){
// 进行判断,当前item对象是否是上传文件项
if(item.isFormField()){
// 说明普通表单向
}else{
// 说明上传文件项
// 获取上传文件的名称
String filename = item.getName();
// 把文件的名称设置唯一值,uuid
String uuid = UUID.randomUUID().toString().replace("-", "");
filename = uuid+"_"+filename;
// 完成文件上传
item.write(new File(path,filename));
// 删除临时文件
item.delete();
}
}
return "success";
}
SpringMVC的文件上传(掌握)
1. springmvc.xml配置文件上传解析器:
类为CommonsMultipartResolver
id为multipartResolver,这个不能改变(这个很重要)
属性为:maxUploadSize,值为10485760
<!--配置文件上传解析器-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="10485760"/>
</bean>
2. handler的形参: (HttpServletRequest request,MultipartFile upload)
表单的隐藏域的name属性必须和handler的形参MultipartFile的对象的名字一样.
<form action="/file/fileupload" method="post" enctype="multipart/form-data">
文件上传:<input type="file" name="upload">
<input type="submit" value="提交">
</form>
3. handler的步骤:
1. 创建文件上传目录path
2. 获取文件名,修改文件名为唯一名字
3. 使用upload.transferTo(new File(newFileName))就完成了
@RequestMapping("fileupload")
public String fileUpload(HttpServletRequest request, MultipartFile upload) throws Exception {
// 创建文件上传目录
String realPath = request.getSession().getServletContext().getRealPath("/uploads/");
// 创建File
File file = new File(realPath);
// 判断目录是否存在
if(!file.exists()){
// 创建该文件夹
file.mkdirs();
}
// 获取文件名,并修改文件名为唯一
String name = upload.getOriginalFilename();
// 生成uuid
String replace = UUID.randomUUID().toString().replace("-", "");
// 唯一文件名
String fileName = replace + "_" + name;
// 拼接最后的路径
String newPath = realPath + fileName;
// 进行文件的上传
upload.transferTo(new File(newPath));
return "hello";
}
SpringMVC跨服务器方式的文件上传
1. 搭建图片服务器
1. 修改tomcat端口号(修改3个地方)
2. 修改tomcat的conf下的web.xml,修改readonly属性为false
3. 在webapps下新建uploads目录
2. 加入依赖:
jersey-core
jersey-client
3. 步骤:
创建客户端的对象Client.create()
和图片服务器建立连接(client.resource())
上传文件(webResource.put(new File(newFileName)))
@RequestMapping("/fileupload3")
public String fileuoload3(MultipartFile upload) throws Exception {
System.out.println("跨服务器文件上传...");
// 定义上传文件服务器路径
String path = "http://localhost:9090/uploads/";
// 说明上传文件项
// 获取上传文件的名称
String filename = upload.getOriginalFilename();
// 把文件的名称设置唯一值,uuid
String uuid = UUID.randomUUID().toString().replace("-", "");
filename = uuid+"_"+filename;
// 创建客户端的对象
Client client = Client.create();
// 和图片服务器进行连接
WebResource webResource = client.resource(path + filename);
// 上传文件
webResource.put(upload.getBytes());
return "success";
}
SpringMVC的异常处理
1. 不处理异常造成的问题:
* 用户体验差
* 系统安全性容易出现问题
2. 处理:
1. 编写自定义异常处理类,继承Exception
public class MyException extends Exception {
private String msg;
public MyException(String msg) {
this.msg = msg;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
}
2. 编写异常处理器,实现HandlerExceptionResolver
public class MyExceptionResolver implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse,
Object o,
Exception e) {
// 创建自定义异常类
MyException myException = null;
// 转换
if(e instanceof MyException){
myException = (MyException)e;
} else{
myException = new MyException("程序遇到错误,但不是自定义异常类");
}
// 创建ModelAndView
ModelAndView mv = new ModelAndView();
mv.addObject("errormsg",myException.getMsg());
mv.setViewName("error");
return mv;
}
}
3. 配置异常处理器,SpringMVC中配置异常处理器(也就是声明Bean)
<!--配置异常处理器 -->
<bean id="myExceptionResolver" class="com.itheima.exception.MyExceptionResolver"></bean>
4. Controller如何进行编写呢?
@RequestMapping("exce")
public String testException() throws MyException {
try {
int i = 1 / 0;
} catch (Exception e) {
e.printStackTrace();
throw new MyException("程序错误");
}
return "hello";
}
SpringMVC的拦截器
1. 拦截器: 对处理器进行预处理和后处理(AOP的思想)
2. 拦截器链: 多个拦截器按一个次序形成一个链,执行顺序考虑栈的结构,前置代码是入栈,后置代码是出栈
3. 过滤器(filter)与拦截器(interceptor):
过滤器什么都可以拦截,并且是属于servlet的,任何项目都可以用
拦截器属于SpringMVC的,只有在SpringMVC的项目中使用,并且只能拦截控制器的访问
4. 使用拦截器的步骤:
1. 编写拦截器: 类实现HandlerInterceptor,实现3个方法:
boolean preHandler(): 预处理,handler执行前执行
true: 放行,如果有多个拦截器,放行到下一个拦截器,没有后续拦截器,执行Handler方法
false: 不放行, 到此为止,不继续向下执行
postHandler(): 后处理,handler之后,跳转页面之前执行
afterComletion(): 最后执行的方法,也就是成功返回页面后执行的代码
public class MyInterceptor implements HandlerInterceptor {
/**
* Handler执行之前执行
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle=======================");
return true;
}
/**
* Handler执行之后,跳转页面之前
* @param request
* @param response
* @param handler
* @param modelAndView
* @throws Exception
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle=======================");
}
/**
* 跳转页面之后执行
* @param request
* @param response
* @param handler
* @param ex
* @throws Exception
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion=============================");
}
}
2. 配置拦截器:
<mvc:interceptors>
<!--可以配置对个interceptor,表示多个拦截器-->
<mvc:interceptor>
<!--拦截的具体方法-->
<mvc:mapping path="/**"/> <!--所有的方法都拦截-->
<!--不要拦截的具体方法,和上面那个配置一个就好-->
<!-- <mvc:exclude-mapping path=""/>-->
<!--配置拦截器对象-->
<bean class=""/>
</mvc:interceptor>
</mvc:interceptors>
<!--配置拦截器-->
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="com.itheima.interceptor.MyInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
3. 前端:
<% System.out.println("hello.jsp================"); %>
执行效果:
preHandle=======================
interceptor===================
postHandle=======================
hello.jsp================
afterCompletion=============================
5. 拦截器的作用:
登录拦截器=======> 在prehandler()方法中获取session,判断有无用户,无用户跳转到登录.