概念
Spring 框架提供了构建 Web 应用程序的全功能 MVC 模块
使用 Spring 可插入的 MVC架构,从而在使用Spring进行WEB开发的
也可以选择使用Spring的SpringMVC框架或集成其他MVC开发框架(如Struts2老技术等)
SpringMVC就是基于MVC设计模式来实现的。
设计思想
通用的软件编程思想, 在MVC设计模式中认为, 任何软件都可以分三层: MVC(Model模型、View视图、Control控制层),将软件进行分层达到松耦合的效果
Model: 数据处理模型,也就是各种实体类对象,一般存在pojo文件夹下(自己封装的类)
Controller: 控制层,实现接收和响应前端数据(对象默认会处理成json对象)
View: 视图层就是模板,对应存在templates这个文件夹下(一般储存的时jsp,或者html)
分层的好处
- 软件保证了上面这三部分相互独立,互不干扰,每一个部分只负责自己擅长的部分。
- 分层的结构,这样代码分离结构清晰,各层代码,各司其职,易于开发大型项目。
- 如果某一个模块发生变化,应该尽量做到不影响其他两个模块。提高代码的可读性,实现程序间的松耦合、提高代码复用性。
工作原理
- 前端发来一条请求,后端首先会被前端控制器(DispatcherServlet)接收到消息
- 前端控制器会把URL交给处理器映射器(HandlerMapping),根据URL和XML或注解来生成处理器对象(自已的Controller方法)及处理器拦截器(如果有则生成)一并返回给前端控制器。
- 前端控制器把请求中找到的类和方法交给处理器适配器(HandlerAdapter)调用具体的处理器(自定义的Controller层,也叫后端控制器),当方法执行完返回给前端控制器 ModelAndView对象(模型和视图)
- 前端控制器在把ModelAndView交给ViewReslover(视图解析器)解析后返回具体的View(html,jsp)交给前端控制器
- DispatcherServlet根据View进行渲染视图(模型数据填充至视图中)DispatcherServlet响应用户
简单点说就是:前端先请求MVC,接收浏览器发来的请求,解析数据,给浏览器响应数据
工作原理2(深入版)
SpringMVC程序启动概念
- Spring容器(内存中的一大块空间)由于IOC/DI的机制,可以作为第三方的管理者 所以作为父级
- SpringMVC容器,其中只负责Controller层的相关的对象的管理
- 当SpringMVC容器启动时,提前将SpringMVC中的所有请求路径方法方法完成映射.
SpringMVC运行流程
-
用户发起请求时,第一步经过前端控制器,
-
但是前端控制器 只负责请求的转发和响应.不做任何业务处理.将请求转发给处理器映射器.
-
处理器映射器接收到前端控制器的请求之后,查询自己维护的服务列表信息.如果服务列表中没有这个URL的key. 该程序不能处理用户的请求,则返回特定数据,前端控制器接收之后响应用户404.如果服务列表中有该URL key 则说明请求可以正常执行. 将该方法的对象返回给前端控制器.
-
前端控制器将返回的方法进行接收,但是由于前端控制器只负责转发和响应,不能直接执行该方法.所以交给处理器适配器执行.
-
处理器适配器根据方法的类型(xml配置文件/注解/其他方法),处理器适配器在自己的处理器库中挑选一个最为合适的处理器去执行该方法. 当处理器执行该方法时标识业务开始. 将最终的处理的结果通过ModelAndView对象进行包裹,返回给前端控制器.ModelAndView: Model: 代表服务器返回的业务数据 View: 服务器端返回的页面的名称
-
视图解析器 将View中的数据进行解析 拼接一个完整的页面路径 前缀/hello后缀
-
视图渲染: 将数据与页面进行绑定. 这样用户就可以在页面中看到具体的数据.
-
由于现在流行前后端分离. 所以SpringMVC省略了视图解析和视图渲染.只有前5步. 核心注解: 核心注解: @ResponseBody 省略6-7步
SpringMVC与Tomcat的执行链
Tomcat执行链
Spring mvc执行链
get的两种请求方式
RestFul方式: 获取包含url地址的值–http; //localhost/car/get/value1/value2
优点:
- 数据相对get而言安全,是用来优化、简化get提交数据的写法
- 简化普通get提交数据的写法后可以传更多的数据了
普通get方式:
优点:
- 数据都在地址栏拼接
缺点:
- 数据都被明文拼接在地址栏中,以及对应的名字
获取请求的参数需知道
注意客户端发送请求时:
- 参数的名字必须和方法参数的名字相同,否则无法完成赋值
- 参数类型最好写包装类,即使不穿参数也可以正常访问(包装类属性有默认值)
- 前端以多选框内容传给后端时可以直接使用数组,MVC会自动解析
注意实体类和对象传参类型
- 实体类或者controller层参数的接收类型,最好都使用包装类型的参数类型(对象的方式)
- 原因是:包装类型默认为null,而基本类型数据默认值为:0、0.0、false 等!
- 当使用参数调用方法时会将默认参数类型0、0.0、false 等传过去,结果就是数据不安全
实体类一般会实现序列化接口
因为后期如果使用spring cloud时可能会出现两个服务之间对象的共享问题
拦截器
说明:
左测的的是java ee规范的过滤器
右侧是基于MVC基于java ee的规范实现的拦截器
作用
可以实现用户访问前要做什么操作,访问之后做什么操作
实现代码:自定义MVC的拦截器
//MVC 拦截器的实现 必须实现HandlerInterceptor接口
public class TokenInterceptor implements HandlerInterceptor {
//该方法的作用:在用户访问前,执行什么操作
//handler:目标是用户请求的Controller
//return:true表示用户可以访问 false表示表示请求不在继续传递,前端直接显示空
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return true;
}
// 该方法的作用:在用户处理完controller业务时,执行什么操作
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
}
实现代码:将自定义MVC的拦截器配置到MVC
//注意:必须被@Configuration注解,且实现WebMvcConfigurer 接口
//定义Spring Web MVC 配置类
@Configuration
public class SpringWebConfig implements WebMvcConfigurer {
//将拦截器添加到Spring MVC 的执行链中
//registry :此对象提供了一个List集合,可以将拦截器添加到集合中
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new TokenInterceptor())//将自定义的拦截器添加到Spring MVC 的执行链中
.addPathPatterns("/retrieve");//配置要拦截的URL
}
}
跨域过滤器
@Configuration
public class CorsFilterConfig {
//服务端过滤器层面的跨域设计
@Bean
public FilterRegistrationBean<CorsFilter> filterFilterRegistrationBean() {
//1.对此过滤器进行配置(跨域设置-url,method)
UrlBasedCorsConfigurationSource configSource = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.addAllowedHeader("*");//所有请求头信息
config.addAllowedMethod("*");//所有请求方式,post,delete,get,put,....
config.addAllowedOrigin("http://127.0.0.1:8848");//所有请求参数
config.setAllowCredentials(true);//所有认证信息,例如cookie
//2.注册过滤器并设置其优先级
configSource.registerCorsConfiguration("/**", config);
FilterRegistrationBean<CorsFilter> fBean = new FilterRegistrationBean(new CorsFilter(configSource));
//设置此过滤器的优先级最高
fBean.setOrder(Ordered.HIGHEST_PRECEDENCE);
return fBean;
}
}
补充小知识(实现对象转为json)
由于spring-boot-starter-web里面自动包含的jackson主件(专门转换Json的工具)
所以我们可以直接使用它的方法实现对象与Json之间的互相转换
String json = new ObjectMapper().writeValueAsString(java对象);
JAVAEE规范中的三大组件
listener(监听器:启动监听器)
- 监听服务的启动,服务器启动时可以读取配置文件,可以加载配置类,并实现初始化
//这里的监听器用于监听服务的启动和销毁
public class DemoListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println("servlet服务启动了");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("servlet服务销毁了");
}
}
filter(过滤器)
- 对请求进行过滤,在Servlet之前执行
/**
* javaee规范中的过滤器,对请求和响应数据进行过滤
* 1)统一数据的编码
* 2)统一数据格式校验 (今日头条的灵犬系统)
* 3)统一身份认证
*/
public class DemoFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("==doFilter==");
servletRequest.setCharacterEncoding("UTF-8");
String id= servletRequest.getParameter("id");
System.out.println("id="+id);
filterChain.doFilter(servletRequest,servletResponse);
}
}
servlet
- 处理请求,然后做请求转发和重定向
//JavaEE规范中的Servlet
public class DemoServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("==doGet==");
resp.getWriter().println("hello client");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
}
注册三大组件
@Configuration
public class ComponentConfig {
//注册servlet
@Bean
public ServletRegistrationBean servletRegistrationBean(){
ServletRegistrationBean bean=new ServletRegistrationBean();//Servlet注册对象
bean.setServlet(new DemoServlet());//设置Servlet服务
bean.addUrlMappings("/hello");//设置服务访问地址
return bean;
}
//注册过滤器
@Bean
public FilterRegistrationBean filterRegistrationBean(){
//创建过滤器注册器,并添加自定义过滤器
FilterRegistrationBean bean = new FilterRegistrationBean(new DemoFilter());
bean.addUrlPatterns("/hello");//对哪个请求进行处理
return bean;
}
//注册监听器
@Bean
public ServletListenerRegistrationBean servletListenerRegistrationBean(){
//创建注册Servlet监听器对象,并设置自定义监听器
ServletListenerRegistrationBean bean = new ServletListenerRegistrationBean(new DemoListener());
return bean;
}
}