SpringMVC
MVC的概念
- Model1 模式
- Model1:JSP页面和JavaBean相结合的方式,不用java的Servlet形式写前后台交互,几乎全部由jsp负责
- Model2 模式
-
M:Model 模型(JavaBean)负责数据逻辑部分,包括业务逻辑、访问数据库、保存数据状态
- Model2:JSP+Servlet+JavaBean结合方式,实现一定程度上解耦
-
V:View 视图(Jsp等客户端)负责数据显示部分
-
C:Controller 控制器(Servlet)负责应用程序中用户交互部分
- 三层 模式
- 在Model2的基础上,将Model层细分成dao层,entity层,service层
SpringMVC执行流程
SpringMVC执行时涉及以下几种组件
-
前端控制器(DispatcherServlet)
负责接收客户端的请求,由它调用其他组件处理用户请求,降低了组件间的耦合性
-
处理器映射器(HandlerMapping)
负责根据用户请求找到对应的Handler处理请求。
-
处理器适配器(HandleAdapter)
通过它对处理器进行执行
-
处理器(Handler)
处理请求,返回逻辑视图
-
视图解析器(View Resolver)
负责将逻辑视图解析成物理视图地址(即:页面地址),再生成View视图,对View进行渲染后展示在客户端
-
视图(View)
客户端页面
执行流程
DispatcherServlet 接收请求 --> HandlerMapping 映射出对应Handler --> HandleAdapter 通过适配器适配真正的处理器进行请求处理 --> Handler 处理请求,返回ModelAndView --> DispatcherServlet接受ModelAndView --> View Resolver 处理视图,得到逻辑视图,解析成物理视图 --> DispatcherServlet请求渲染视图 --> **View进行渲染视图 ** --> DispatcherServlet响应回客户端
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-I70Ayuis-1584420553015)(img\SpringMVC原理.png)]
SpringMVC使用
pom.xml依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.4.RELEASE</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
配置web.xml
在导入依赖后,先在web.xml中配置前端控制器组件
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<!-- 程序入口 加载spring的xml配置 -->
<web-app>
<!-- 监听器放在顶部时,web-app有错误,可写下述web-app
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
-->
<display-name>Archetype Created Web Application</display-name>
<!-- 加载 Spring 相关配置文件 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-*.xml</param-value>
</context-param>
<!-- spring其他配置:不要放在顶部,如果有过滤器,放在过滤器后面 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- 配置SpringMVC的配置:DispatcherServlet-->
<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:spring-mvc.xml</param-value>
</init-param>
<!-- 配置DispatcherServlet优先加载 -->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
前端控制器配置注意点
注意还要设置DispatcherServlet的contextConfigLocation
如果不设置该属性,则Spring MVC会自动的在WEB-INF下查找**[servlet-name]-servlet.xml文件来作为SpringMVC的配置文件**
如果没有配置,需要在/WEB-INF下新建一个名字为:[servlet-name]-servlet.xml的文件
**[servlet-name]指的内容 **
XML方式
配置spring-mvc.xml
spring-mvc.xml(此项可自定义xml名字,符合web上下文配置classpath即可)
-
InternalResourceViewResolver:视图解析器, 该属性里可以分别配置前缀和后缀,为了保证程序的安全性,可以将页面放在/WEB-INF/文件名/下。
- prefix(前缀):不配置该属性,则前缀为/,代表的是webapp目录
- suffix(后缀):可以根据项目需要设置为.jsp或者.html
- <context:annotation-config />:可以不配置传统的声明,主要为了减少**@Autowired**,@PersistenceContext,@Required,@Resource、@ PostConstruct、@ PreDestroy等注解的bean声明。如果有context:component-scan配置,就不用此配置了。
- mvc:default-servlet-handler:缺省servlet处理器,该配置可以保证Spring MVC项目可以直接访问静态资源,比如:可以直接访问index.html
- mvc:annotation-driven:注解驱动器,该配置使得当前项目可以使用注解来完成配置。如:@Controller
- context:component-scan:上下文的包扫描组件,使用该配置,可以使得该basePackage所对应的包下的所有在类之上添加**@Component,@Repository,@Service, @Controller @RestController, @ControllerAdvice, and @Configuration**组件直接被扫描出来使用
<!-- 配置视图解析器 -->
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/pages/"/>
<property name="suffix" value=".jsp"/>
</bean>
<!-- 配置缺省的servlet处理器,静态资源可以直接被访问;若不配置,无法访问静态页面 -->
<mvc:default-servlet-handler/>
<!--前端控制器,哪些静态资源不拦截-->
<mvc:resources location="/css/" mapping="/css/**"/>
<mvc:resources location="/images/" mapping="/images/**"/>
<mvc:resources location="/js/" mapping="/js/**"/>
<!-- xml方式的SpringMVC
bean的id属性值不能包含特殊字符
name可以,所以路径需要使用name来标识一个控制器的路径
此处name是客户端访问的url路径,访问后将会跳转到相应的class中
-->
<bean name="/productInput" class="com.qf.controller.ProductInputController"/>
<bean name="/saveProduct" class="com.qf.controller.SaveProductController"/>
例子
ProductInputController实现类1
public class ProductInputController implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
// 将会返回一个逻辑视图
return new ModelAndView("index");
}
}
WEB-INF/pages/index.jsp
<form method="post" action="saveProduct">
pid:<input type="text" name="pid" /><br />
pname:<input type="text" name="pname" /><br />
price:<input type="text" name="price" /><br />
<input type="submit" value="submit" /><br />
</form>
SaveProductController实现类2
public class SaveProductController implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest req, HttpServletResponse resp) throws Exception {
String pid = req.getParameter("pid");
String pname = req.getParameter("pname");
String price = req.getParameter("price");
Product p = new Product();
p.setPid(pid);
p.setPname(pname);
p.setPrice(price == null? 0.0:Double.parseDouble(price));
ModelAndView mv = new ModelAndView("success","p",p);
// 返回逻辑视图,并将对象传递回前端;
//相当于req.getRequestDispatcher().forward()和req.setAttribute()
return mv;
}
}
success.jsp
<h3>pId: ${p.pid}</h3>
<h3>pId: ${p.pname}</h3>
<h3>pId: ${p.price}</h3>
注解方式
配置spring-mvc.xml
spring-mvc.xml(此项可自定义xml名字,符合web上下文配置classpath即可)
<!-- 配置视图解析器 -->
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/pages/"/>
<property name="suffix" value=".jsp"/>
</bean>
<!-- 配置缺省的servlet处理器,静态资源可以直接被访问;若不配置,无法访问静态页面 -->
<mvc:default-servlet-handler/>
<!-- 开启SringMVC注解 -->
<mvc:annotation-driven />
常用注解
@RequestMapping
-
作用:建立url和控制器处理方法之间的对应关系
- 作用在类上
- 作用在方法上
-
属性(可不加 /)
- path:指定请求路径的url
- value:value属性和path属性是一样的
- mthod:指定该方法的请求方式
-
代码:
@RequestMapping("anno") public class RequestController { @RequestMapping("/testRequestBody") public String testRequestBody(@RequestBody String body){ System.out.println(body);// username=root&uid=123 return "success"; } @RequestMapping("/testModelAndView") public ModelAndView testModelAndView(){ ModelAndView mav = new ModelAndView(); User user = new User(); user.setUname("远方"); user.setUid("55"); mav.addObject("user",user); mav.setViewName("show"); return mav; } }
@RequestParam
-
作用:将请求中指定名称的参数传递给控制器中的形参(请求名称可与控制器形参名称不同)
-
属性
- value:请求参数的名称
- required:请求参数是否必须提供此参数,默认为true(必须提供)
- defaultvalue:设置默认值
-
代码
// 如果参数是基本数据类型和String,可以不加@RequestParam // @RequestParam:客户端传递的参数与后台参数名不同时,可以使用该注解 // @RequestParam(required = false)被修饰的参数可以不传值,默认是必须传参数 public String testRequestParam(@RequestParam(name = "name") String uname){ System.out.println("这是测试RequestParam()-->name:"+uname);// 这是测试RequestParam()-->name:哈哈 return "success"; } public String testRequestParam( String name){}
@RequestBody
-
作用:用于获取请求体的内容(注意:get方法不可以)
-
属性
- required:是否必须有请求体,默认值是true
-
代码
@RequestMapping("/testRequestBody") public String testRequestBody(@RequestBody String body){ System.out.println(body);// username=root&uid=123 return "success"; }
@GetMapping
-
作用:接受get方式的请求,不接受post数据
-
属性:
- value:请求参数的名称
-
代码
@GetMapping("/saveStu") public ModelAndView saveStu(){ return new ModelAndView("validate","student",new Student()); }
@PostMapping
-
作用:接受post方式请求,可接受数据
-
属性:
- value:请求参数的名称
-
代码
@PostMapping("/saveStu") public String saveStu(@ModelAttribute Student student, BindingResult errors, Model model) { StuValidate sv = new StuValidate(); sv.validate(student, errors); // 如果错误集合中存在错误信息 if (errors.hasErrors()) { // 将前端输入的信息再返回前端 model.addAttribute("student", student); return "validate"; } return "success"; }
@RestController
用于标注一个bean,同@Component作用相同,一般用于前后端分离的一种注解,当返回数据时,返回的数据会被自动转换成json格式。使用此注解后,相当于使用了**@Controller+@ResponseBody**,但@Controller属于前后端不分离。
@RequestMapping("/del/{id}")
public boolean delBook(@PathVariable Integer id){
// 将boolean转成json字符串格式后传回前端
return bookService.delBookService(id);
}
@RequestMapping("/list")
public List<Book> listBook(){
// 将list集合转成json字符串格式后传回前端
return bookService.detailBookService();
}
@Controller
用于标注一个bean,同@Component作用相同,一般用在前后端不分离的情况,只有当与 @ResponseBody使用时,返回的数据才自动转换成json格式。
不加@ResponseBody
@RequestMapping("/getAdmin")
public String getAdmin(HttpSession session,Model model){
String admin = (String)session.getAttribute("admin");
model.addAttribute("list",adminService.getAdminByNameService(admin));
// 返回的是一个逻辑视图
return "adminUdp.jsp";
}
加@ResponseBody
@RequestMapping("/list")
@ResponseBody
public List<Admin> adminList(HttpSession session){
String admin = (String)session.getAttribute("admin");
// 此处返回的是一个json字符串形式的list数据
return adminService.getAdminByNameService(admin);
}
文件上传
在spring-mvc.xml中配置
<!-- 配置 文件上传 解析器对象 -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!--限制文件上传大小-->
<property name="maxUploadSize" value="10485760"/>
</bean>
Controller
@RequestMapping("/testFileUpload")
public String testFileUpload(@RequestParam String name, MultipartFile upload, HttpServletRequest req) throws IOException {
System.out.println("开始上传");
// 上传位置
String path = req.getSession().getServletContext().getRealPath("/upload");
// 判断路径是否存在
File file = new File(path);
if(!file.exists()){
file.mkdirs();
}
// 获取上传文件名
String filename = upload.getOriginalFilename();
String uuid = UUID.randomUUID().toString().replace("-", "");
filename = uuid+"_"+filename;
// 完成上传
upload.transferTo(new File(file,filename));
System.out.println(name);
return "show";
}
自定义异常
在spring-mvc.xml中配置
<!--配置异常处理器-->
<bean id="exceptionResolver" class="com.qf.exception.MyExceptionResolver"/>
自定义异常类
public class MyException extends Exception{
private String message;
public MyException(String message) {
this.message = message;
}
@Override
public String getMessage() {
return super.getMessage();
}
public void setMessage(String message) {
this.message = message;
}
}
异常实现类
public class MyExceptionResolver implements HandlerExceptionResolver {
/**
*
* @param req
* @param resp
* @param handler
* @param ex 自定义异常
* @return
*/
@Override
public ModelAndView resolveException(HttpServletRequest req, HttpServletResponse resp, Object handler, Exception ex) {
MyException e = null;
if (ex instanceof MyException) {
e = (MyException) ex;
// e = new MyException("系统崩溃,请稍后再访问!");
}else{
e = new MyException("系统崩溃,请稍后再访问!");
}
ModelAndView mav = new ModelAndView();
mav.addObject("errMsg",e.getMessage());
mav.setViewName("error");
return mav;
}
Controller
@RequestMapping("/testExcpetion")
public String testExcpetion() throws MyException {
System.out.println("testExcpetion....");
try {
int i = 10 / 0;
} catch (Exception e) {
e.printStackTrace();
throw new MyException("发生异常");
}
return "error";
}
自定义拦截器
在spring-mvc.xml中配置
<!-- 配置自定义拦截器 -->
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/demo/*"/>
<bean id="myInterceptor" class="com.qf.interceptor.MyInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
自定义拦截器类
/**
会对xml配置的路径下的指定方法进行拦截
*/
public class MyInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle预处理,controller执行前");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle后处理,controller执行后");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion页面执行后 ");
}
}
事务
pom.xml
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>com.springsource.org.aspectj.weaver</artifactId>
<version>1.6.8.RELEASE</version>
</dependency>
spring-mybatis.xml
<!--
配置事务管理器
-->
<bean id="dtx" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="ds" />
</bean>
<!--
声明事务的实现方式
以这些关键字开头的方法分别设置事务的隔离级别以及出错后的操作
-->
<tx:advice transaction-manager="dtx" id="tx">
<tx:attributes>
<tx:method name="save*" propagation="REQUIRED" isolation="DEFAULT" rollback-for="Exception"/>
<tx:method name="insert*" propagation="REQUIRED" isolation="DEFAULT" rollback-for="Exception"/>
<tx:method name="update*" propagation="REQUIRED" isolation="DEFAULT" rollback-for="Exception"/>
<tx:method name="delete*" propagation="REQUIRED" isolation="DEFAULT" rollback-for="Exception"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="mpt" expression="execution(* com.qfedu.service.*.*(..))" />
<aop:advisor advice-ref="tx" pointcut-ref="mpt" />
</aop:config>