学习笔记:
SpringMVC 框架
Spring Web MVC 是一种基于 Java 实现了 MVC 设计模式的请求驱动型的轻量级 Web 框架,即使用了 MVC 架构模式的思想,将 web 层进行职责解耦,基于请求驱动指的就是使用请求-响应模型,框架的目的就是帮助简化开发,Spring Web MVC 也是要简化日常 Web 开发的。
HelloWorld 程序
注意 Spring Framework 6 将基于 Java 17 和 Jakarta EE 9。确保升级到 Tomcat 10、
Jetty 11 或 Undertow 2.2.14。如果使用 JDK1.8 则采用 5.3.23
1、SpringMVC 依赖的 jar 包 spring-context-support 和 spring-webmvc
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.10.1</version>
<configuration>
<source>17</source>
<target>17</target>
<encoding>UTF-8</encoding>
2、添加 Web.xml 配置文件中关于 SpringMVC 的配置
<servlet> 对应配置文件为 yan-servlet.xml
<servlet-name>yan</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
<servlet-mapping>
<servlet-name>ma</servlet-name>
<url-pattern>/</url-pattern>
3.在 src 下添加/WEB-INF/yan-servlet.xml 配置文件,名字头部是 Servlet 的名称
<context:component-scan base-package="com.ma.action"/>自动组件扫描,利用注解避免控制器的配置
<mvc:default-servlet-handler /> 默认 Servlet 处理器
<mvc:annotation-driven />注解驱动
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">视图解析器,用于将控
制器返回的逻辑视图名称转换为物理地址
<property name="prefix" value="/WEB-INF/content/" />前缀
<property name="suffix" value=".jsp" />后缀
4、在 WEB-INF 文件夹下创建名为 content 的文件夹,用来存放 jsp 视图。创建一个 hello.jsp,在 body 中添加 Hello World 字符串
5、创建 Controller 类
@Controller
public class HelloController {
@RequestMapping("hello")
public String sayHello() {
return "hello"; } 默认返回的是视图名称,需要通过视图解析器将逻辑地址名转换为物
理地址
1、DispatcherServlet 是前置控制器,配置在 web.xml 文件中的。拦截匹配的请求,Servlet 拦截匹配规则要自已定义,把拦截下来的请求,依据相应的规则分发到目标 Controller 来处理,是配置 spring MVC 的第一步。
2、InternalResourceViewResolver 视图名称解析器,将控制器返回的逻辑视图名转换为物理视图
3、相应的注解
@Controller 负责注册一个 bean 到 spring 上下文中<context:component-scan
base-package="com.ma.action"/>
@RequestMapping 注解为控制器指定可以处理哪些 URL 请求.
Spring MVC 是一个基于 Java 的实现了 MVC 设计模式的请求驱动类型的轻量级 Web 框架,通过把 Model、View和 Controller 分离,将 web 层进行职责解耦,把复杂的 web 应用分成逻辑清晰的三部分,简化开发,减少出错,方便组内开发人员之间的配合。
MVC 全名是 Model View Controller,是模型 model、视图 view 和控制器 controller 的缩写,一种软件设计典范,用一种业务逻辑、数据、界面显示分离的方法组织代码。
优点:
1、可以支持各种视图技术,而不仅仅局限于 JSP
2、与 Spring 框架集成,如 IoC 容器、AOP 等
3、清晰的角色分配:前端控制器 dispatcherServlet、请求到处理器映射 handlerMapping、处理器适配器
HandlerAdapter 和视图解析器 ViewResolver。
主要组件
1、前端控制器 DispatcherServlet,不需要程序员开发。作用:接收请求、响应结果,相当于转发器,有了DispatcherServlet 就减少了其它组件之间的耦合度。
2、处理器映射器 HandlerMapping,不需要程序员开发。作用:根据请求的 URL 来查找 Handler
3、处理器适配器 HandlerAdapter。注意在编写 Handler 时要按照 HandlerAdapter 要求的规则去编写,这样适配器 HandlerAdapter 才可以正确的去执行 Handler。
4、处理器 Handler,需要程序员开发。
5、视图解析器 ViewResolver,不需要程序员开发。作用:进行视图的解析,根据视图逻辑名解析成真正的视图 view
6、视图 View,需要程序员开发 jsp。View 是一个接口,它的实现类支持不同的视图类型,例如 jsp、freemarker和 pdf 等
运行流程
1、用户发送请求至前端控制器,DispatcherServlet 收到请求调用 HandlerMapping 处理器映射器。
2、处理器映射器根据请求 url 找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet。
3、DispatcherServlet 通过 HandlerAdapter 处理器适配器调用处理器;执行处理器 Controller,也叫后端控制器。
4、Controller 执行完成返回 ModelAndView,HandlerAdapter 将 controller 执行结果 ModelAndView 返回给DispatcherServlet
5、DispatcherServlet 将 ModelAndView 传给 ViewReslover 视图解析器,ViewReslover 解析后返回具体 View
6、DispatcherServlet 对 View 进行渲染视图(即将模型数据填充至视图中),DispatcherServlet 响应用户。
DispatcherServlet 有接收请求、响应结果和转发等作用。有了 DispatcherServlet 后可以减少组件之间的耦合度。springMVC 和 struts2 区别
1、springmvc 的入口是一个 servlet 即前端控制器 DispatchServlet,而 struts2 入口是一个 filter 过虑器StrutsPrepareAndExecuteFilter。
2、springmvc 是基于方法开发,一个 url 对应一个方法,请求参数传递到方法的形参,可以设计为单例或多例,建议单例;struts2 是基于类开发,传递参数是通过类的属性,只能设计为多例。
3、Struts 采用值栈存储请求和响应的数据,通过 OGNL 存取数据;springmvc 通过参数解析器是将 request 请求内容解析,并给方法形参赋值,将数据和视图封装成 ModelAndView 对象,最后又将 ModelAndView 中的模型数据通过 reques 域传输到页面。Jsp 视图解析器默认使用 jstl。
SpringMVC 常用注解
@Controller 注解类型用于指示当前类是一个控制器。 Spring 使用扫描机制查找应用程序中所有基于注解的控
制器类,分发器会扫描使用该注解类的方法,并检测方法是否使用了@RequestMapping 注解,只用使用了
@RequestMapping 注解的方法才能用来处理请求。
为了保证 spring 能找到控制器,需要完成两件事情:
1、在 spring mvc 的配置文件的头文件中引入 spring-context。
2、在 spring mvc 的配置文件中使用<context:component-scan/>
,该元素的功能是启动包扫描功能以便注册
有@Controller、@Service、@Repository、@Component 等注解的类成为 spring 的 bean。
@Controller //@Component
/* 最大的区别点在于语义
@Controller 一般用于控制器
@Service 一般用于业务类
@Repository 一般用于 DAO 的定义
@Component 一般用于不能明确区分用途的场景
*/
public class Test1Controller {
}
@RequestMapping 注解类型指示 spring 用哪一个类或方法来处理请求动作,可以用于类或方法。
1、value 用来映射一个请求和一个方法,是其默认属性,如果在使用@RequestMapping 时只有这个属性,则可以省略关键字 value
@Controller //@Component
@RequestMapping("/users") //相当于给当前类中的所有请求映射添加了一个名空间
public class Test1Controller {
@RequestMapping("/login.do") //访问路径为/users/login.do
public String login(){
return "";
}
}
2、method 属性用来指定该方法仅仅处理哪些 HTTP 请求的处理方式,例如 GET、POST。
@Controller //@Component
@RequestMapping("/users") //相当于给当前类中的所有请求映射添加了一个名空间
public class Test1Controller {
@RequestMapping(value = "/login.do", method = {RequestMethod.GET,RequestMethod.POST})
//定义了 method 时,value=则不能省略,method 用于定义对应的请求方法,例如 get\post
public void login() {
System.out.println("login....");
}
}
可以使用 Postman 来模拟非 get 请求场景
在 具 体 的 应 用 中 SpringMVC 又 提 供 了 @GetMapping 代 替
@RequestMapping(method =
RequestMethod.GET) 、@PostMapping 代替 @RequestMapping(method = RequestMethod.POST) ,还有@DeleteMapping 和@PutMapping 等
3、consumes 用来指定处理请求提交内容的类型。例如使用前端技术提交 JSON 字串
4、produces 用来指定返回的内容类型,返回的内容类型必须是 request 请求头 Accept 中包含的类型。
5、params 指明 request 中必须包含哪些参数时,才让该方法处理
@RequestMapping(value = "/login.do",params = "username")//要求请求参数中必须有 username,否则
不由这个方法处理请求
@RequestMapping(value = "/login.do",params = "!username")//要求请求参数中必须没有 username,否
则不由这个方法处理请求
@RequestMapping(value = "/login.do",params = "username=zhangsan")// 要 求 请 求 参 数 中 必 须 有
username,而且 username 对应的值必须为 zhangsan,否则不由这个方法处理请求
@RequestMapping(value = "/login.do",params = "username!=zhangsan")// 要 求请 求参 数中 必须有
username,而且 username 对应的值必须不为 zhangsan,否则不由这个给方法中的形参。
方法处理请求@RequestParam 注解将指定的请求参数赋值
1、name 绑定参数在 Request 中的名称。用于形参名称和实际提交参数的名称不一致
2、value 是 name 属性的别名。
3、required 参数是否必须绑定。public void login(@RequestParam(value=“username”,required = true) String
name),实际上 required 默认值为 true
4、defaultValue 如果没有传递参数而使用的默认值。public void
login(@RequestParam(value=“username”,defaultValue = “zhangsan”) String name)
@RequestMapping(value = "/login.do")
public void login(String name) { //默认情况下要求请求参数应该和形参名称一致;如果不一致则请求参数丢
失
System.out.println("login...."+name);
}
//如果请求参数不能修改,名称就是 username,同时不想修改形参名称,可以使用 RequestParam 设置参数之
//间的对应关系
@RequestMapping(value = "/login.do")
public void login(@RequestParam("username") String name) { //将请求中的 username 参数对应的值赋值给name 形参
System.out.println("login...."+name);
}
3、@PathVariable 只支持一个属性 value,类型为 String,表示绑定的名称,如果省略则表示绑定同名参数
@DeleteMapping("/{id}") //请求路径为/users/12
public void remove(@PathVariable("id") Long id){
System.out.println("delete:"+id);
}
@ModelAttribute 只有一个属性 value,类型为 String,表示绑定数据类型的名称。其使用方法以及表现形式比较多变。主要用来修饰方法,被其修饰的方法会在 Controller 中每个方法执行前被执行,因此在一个 Controller类要映射多个 URL 时,要谨慎使用。
@Controller 负责注册一个 bean 到 spring 上下文中。
@RequestMapping 注解为控制器指定可以处理哪些 URL 请求。
@RequestBody 注解用于读取 Request 请求的 body 部分数据,使用系统默认配置 HttpMessageConverter 进行解析,然后把相应的数据绑定到要返回的对象上 ,再把 HttpMessageConverter 返回的对象数据绑定到controller 中方法的参数上。
@ResponseBody 注解用于将 Controller 的方法返回的对象,通过适当的HttpMessageConverter 转换为指定格式后,写入到 Response 对象的 body 数据区。
@ModelAttribute 注解
在方法定义上使用@ModelAttribute 注解则 Spring MVC 在调用目标处理方法前,会先逐个调用在方法级上标注了@ModelAttribute 的方法。
在方法的入参前使用@ModelAttribute 注解可以从隐含对象中获取隐含的模型数据中获取对象,再将请求参数–绑定到对象中,再传入入参将方法入参对象添加到模型中。
@RequestParam 在处理方法入参处使用@RequestParam 可以把请求参数传递给请求方法。
@PathVariable 绑定 URL 占位符到入参。
@ExceptionHandler 注解到方法上出现异常时会执行该方法。
@ControllerAdvice 使一个 Contoller 成为全局的异常处理类,类中用@ExceptionHandler 方法注解的方法可以处理所有 Controller 发生的异常。
方法参数
1、直接把提交的参数写在 Controller 相应的方法的形参中
简单数据类型或者 Integer、Boolean、Double 等等简单数据类型都是支持的,会自动进行数据类型转换,当然如果转换失败则报错。【参照 Struts2 叫做属性驱动】
@GetMapping(“/show.do”)如果在请求参数中使用简单类型,没有提交该数据则报错,因为 null 不能赋值给简单类型;如果引用类型,没有提交该参数,则形参值为 null
public void show(String name,int age,Boolean sex){
System.out.println(name+"-->"+age+"-->"+sex);
}
如果在应用中使用了原生类型,例如 int,但是该参数可有可无,则请使用@RequestParam 设置一个defaultValue数组类的参数,一般用于使用一个名称传递多个值的情况下,例如 checkbox 复选框或者上传多个文件
@GetMapping("/show.do")
public void show(String[] names) {
System.out.println("names:");
for (String tmp : names)
System.out.print(tmp + "\t");
}
值 bean 参数,【参照 struts2 叫做模型驱动】
@Data
public class User {
private Long id;
private String username;
private String password;
}
@PostMapping("/add.do")
public void add(User user) {
//按照提交参数的名称和 User 属性名称一致的规则进行注入,同时会自动进行数
//据类型转换,如果转换失败则报错
System.out.println(user);
}
级联属性
@Data
public class User {
private Long id;
private String username;
private Date birth;
// 复合类型,其中包含 year、month 和 date 等属性,提交数据时的格式为 birth.year
}
注意:接受请求参数的方法不互斥
@PostMapping("/add.do")
public void add(User user,String username) {
//请求参数中有一个名称为 username 的参数,user 中可以接受到,同时 username 也可以接收到
System.out.println(user);
System.out.println(username);
}
2、通过类似 HttpServletRequest 的 Servlet API 以及 SpringMVC 提供的 ServletAPI 包装类接收
@PostMapping("/add.do")
public void add(HttpServletRequest request, HttpServletResponse response, HttpSession session) {
System.out.println(request.getParameter("username"));
System.out.println(session);
}
@RequestHeader 将请求头信息区数据映射到处理方法上
name 指定请求头绑定的名称。
value 是 name 属性的别名。
required 参数是否必须绑定。
defaultValue 如果没有传递参数而使用的默认值。
@PostMapping("/add.do")
public void add(@RequestHeader("User-Agent") String userAgent) throws Exception{
System.out.println(userAgent);
}
跟踪用户 4 大方法:隐藏域、URL 重写、客户端跟踪 cookie、服务器端跟踪 session
Cookie[] cks= request.getCookies();
for(Cookie tmp:cks){
System.out.println(tmp.getName()+"--->"+tmp.getValue()+"-->"+tmp.getMaxAge());
}
// JSESSIONID--->node038xt1l3qsqnk49d119qq5tpu0.node0-->-1
@CookieValue 将请求的 Cookie 数据映射到功能处理方法参数上
name 指定请求头绑定的名称。
value 是 name 属性的别名。
required 参数是否必须绑定。
defaultValue 如果没有传递参数而使用的默认值。
@PostMapping("/add.do")
public void add(@CookieValue("JSESSIONID") String sessionId) throws Exception{
System.out.println(sessionId);
}
获取 session 中的数据
session.getAttribute/setAttribute/removeAttribute
@SessionAttribute 注解允许有选择的将指定 Model 中的哪些属性转存到 HttpSession 对象中。其只能声明在类上。
names 指定 model 中属性的名称,即存储在 HttpSession 中的属性
value 是 names 属性的别名
types 是参数值的类型
自定义编程的方式:存储数据
@GetMapping("/login.do") 登录成功后经常会在 session 中存储一个标识值,用于记录当前用户的登录状态
public void login(String name,HttpSession session){
session.setAttribute("username",name);
}
可以使用注解的方式自动实现数据存储
3、通过@PathVariable 获取路径中的参数
4、使用@ModelAttribute 注解获取 POST 请求的 FORM 表单数据如果需要向页面通过 request 的 attribute 传递数据,并不一定非要使用 request 对象,添加一个方法参数 Model即可
@Controller
@RequestMapper("/books")
public class BookController{
@GetMapping("/add")
public String add(Model model){
Book book = new Book();
model.addAttribute("book",book);
return "add";
}
}
方法返回值
1、如果是前后端不分离的开发,大部分情况下返回 ModelAndView,即数据模型+视图
@Controller
public class HelloController {
@RequestMapping(value="/hello.do")
//将处理方法和请求地址建立对应关系,由浏览器发送该地址的请求触发方法的执行
//针对方法先使用最古老的定义方法,方法参数可以分为 4 大类,一般可以使用属性驱动或者类似于
//struts2 的模型驱动方式接受请求数据
public ModelAndView sayHello(String name){ //可以接受请求种 name 参数,例如
//hello.do?name=zhangsan
if(name==null || name.trim().length()<1)
name="Spring MVC";
String res="Hello "+name+"!";
ModelAndView mv=new ModelAndView();//ModelAndView 对象种包含 2 部分数据,视图 view 和需
//要显示的数据 model
mv.setViewName("hello"); //逻辑视图名,需要依靠视图解析器将逻辑视图名转换为物理视图地址
mv.addObject("msg",res);//model 数据,是默认存储在 request 的 attribute 种
return mv;
}
}
2、没有返回值,并不一定真的没有返回值,只是方法的返回值为 void
自定义使用 response 生成响应,例如使用 response.sendRedirect 或者response.getWriter 写出 html 文档
@PostMapping("/add.do")
public void add(HttpServletRequest request, HttpServletResponse response, HttpSession session) throws
Exception{
response.setContentType("text/html;charset=utf8");
PrintWriter writer = response.getWriter();
Enumeration<String> names = request.getHeaderNames();//获取所有的请求头参数名称
while(names.hasMoreElements()){
String name=names.nextElement();
String value=request.getHeader(name);
writer.write(name+"-->"+value+"<br/>");
}
writer.close();
}
如果逻辑视图名和映射路径一致,也可以使用。例如请求路径为 login.do,而逻辑视图名为 login,也可以使用这个方法
3、返回字符串,逻辑视图名
可以有前缀 forward:表示请求转发,redirect:表示重定向