SpringBoot-Controller和前后端交互相关

一、Controller和模板引擎

1.Controller用到的注解

@GetMapping – 处理get请求的路径映射

@GetMapping("emp/{id}")

@PutMapping – 处理put请求的路径映射

@PutMapping("emp/{id}")

@PathVariable – 获取请求参数中的’id’

@PathVariable("id")

给前端返回一个bean

model.setAttribute("emp",employee);

2.页面重用(thymeleaf)

2.1 标签实现重用(新增页面和编辑页面)

th:value="${emp!=null}?${emp.lastName}"//如果回显对象不为空就直接回显值
th:if="${emp!=null}"//如果回显对象有值才生成当前元素
th:checked="${emp!=null}?${emp.gender==1}"//条件成立则选中
th:selected="${emp!=null}?${emp.deptId==dept.id}"//条件成立则选中
th:text="${emp!=null}?'编辑':'添加'"//三元判断
th:action="@{/emp/}+${emp.id}"//@{}代表当前项目路径下,后面用拼接的方式拼接id

2.2 简化列表每一列按钮请求方式
上面发送delete请求的代码中,每一个删除按钮都有一个单独的form表单,这样的格式显得代码十分的臃肿,现在用另一种方法来轻量化表单。
以删除按钮为例:

  1. 用th:attr来设置自定义属性值
//删除按钮上定义当前记录的id
<button th:attr="del_uri=@{/emp/}+${emp.id}" class="btn btn-sm btn-danger deletebtn">删除</button>
  1. form表单-只写一个
<form id="deleteEmpForm"  method="post">
	<input type="hidden" name="_method" value="delete" >
</form>
  1. 删除按钮绑定事件
<script>
//绑定删除按钮的点击事件
//动态设置删除form表单的action值,并提交。
$(".deletebtn").click(function(){
	$("#deleteEmpForm").attr("action",$(this).attr("del_uri")).submit();
	return false;
});
</script>

3.发送自定义请求(RESTful)

SpringBoot已经在WebMvcAutoConfiguration.java中自动配置了HiddenHttpMethodFilter过滤器,作用就是把请求转换为指定的请求格式。
可以指定put、get、delete、post

<!--1.页面创建一个post方式的表单-->
<!--2.创建一个hidden,这个hidden的name="_method" ,value就是需要过滤器转换的目标请求方式。(hidden只在编辑时生成)-->
<form th:action="@{/emp/}+${emp.id}" method="post">
	<input type="hidden" name="_method" value="delete" >
</form>

二、错误处理原理&定制错误页面

1.SpringBoot默认错误处理原理

默认错误处理配置类:ErrorMvcAutoConfiguration 配置类中配置了以下几个组件:
ErrorPageCustomizer 当系统出现错误以后来到error请求进行处理
BasicErrorController:处理默认/error请求,去哪儿个页面由DefaultErrorViewResolver解析得到
DefaultErrorViewResolver:找到error/(状态码)对应的页面,如果有模板引擎就去模板引擎下找,否则就去静态资源文件夹下找,都没有就返回代码中定义的一段视图代码。
DefaultErrorAttributes:帮我们在页面共享信息,获取页面展示或者json返回的数据。
步骤
一但系统出现4xx或者5xx之类的错误;ErrorPageCustomizer就会生效(定制错误的响应规则);它会发送/error请求,而error请求会被BasicErrorController处理;BasicErrorController中又通过DefaultErrorViewResolver解析得到该去哪儿个页面

@Override
	public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status,
			Map<String, Object> model) {
		ModelAndView modelAndView = resolve(String.valueOf(status), model);
		if (modelAndView == null && SERIES_VIEWS.containsKey(status.series())) {
			modelAndView = resolve(SERIES_VIEWS.get(status.series()), model);
		}
		return modelAndView;
	}

	private ModelAndView resolve(String viewName, Map<String, Object> model) {
        //默认SpringBoot可以去找到一个页面?  error/404
		String errorViewName = "error/" + viewName;
        
        //模板引擎可以解析这个页面地址就用模板引擎解析
		TemplateAvailabilityProvider provider = this.templateAvailabilityProviders
				.getProvider(errorViewName, this.applicationContext);
		if (provider != null) {
            //模板引擎可用的情况下返回到errorViewName指定的视图地址
			return new ModelAndView(errorViewName, model);
		}
        //模板引擎不可用,就在静态资源文件夹下找errorViewName对应的页面   error/404.html
		return resolveResource(errorViewName, model);
	}

2.定制错误页面

  1. 有模板引擎的情况下;error/状态码
    只要将将错误页面命名为:错误状态码.html 放在模板引擎文件夹里面的 error文件夹下
    发生此状态码的错误就会来到对应的页面;同时,们可以使用4xx和5xx作为错误页面的文件名来匹配这种类型的所有错误(优先寻找精确的 状态码.html)
    页面能获取的信息:
    timestamp:时间戳
    status:状态码
    error:错误提示
    exception:异常对象
    message:异常消息
    errors:JSR303数据校验的错误都在这里
  2. 没有模板引擎(模板引擎找不到这个错误页面),就会去静态资源文件夹下找;
  3. 如果模板和静态资源文件夹下都没有错误页面,就返回代码中定义的一段视图代码;

3.定制错误json

方法:

  1. 完全来编写一个ErrorController的实现类【或者是编写AbstractErrorController的子类】,放在容器中;(比较繁琐)
  2. 页面上能用的数据,或者是json返回能用的数据默认都是通过容器中 DefaultErrorAttributes.getErrorAttributes() 获取的;可以重写这个方法来实现自定义。
//给容器中加入我们自己定义的ErrorAttributes
@Component
public class MyErrorAttributes extends DefaultErrorAttributes {

//重写getErrorAttributes方法,里面对数据进行处理
    @Override
    public Map<String, Object> getErrorAttributes(RequestAttributes requestAttributes, boolean includeStackTrace) {
        Map<String, Object> map = super.getErrorAttributes(requestAttributes, includeStackTrace);
        map.put("company","atguigu");
        return map;
    }
}

三、嵌入式Servlet容器

SpringBoot默认的Servlet容器是Tomcat

1.修改Servlet容器的相关配置(配置文件)

ServerProperties类中规定了嵌入式Servlet容器的一些配置,如果想要修改默认配置只需在配置文件中重新配置对应的属性即可。

server.port=8081
server.context-path=/crud
server.tomcat.uri-encoding=UTF-8

//通用的Servlet容器设置
server.xxx
//Tomcat的设置
server.tomcat.xxx

2.添加定制器的方法来实现修改Servlet容器配置

编写一个方法,给容器中注入EmbeddedServletContainerCustomizer(Servlet容器定制器)

@Bean  //一定要将这个定制器加入到容器中
public EmbeddedServletContainerCustomizer embeddedServletContainerCustomizer(){
    return new EmbeddedServletContainerCustomizer() {
        //定制嵌入式的Servlet容器相关的规则
        @Override
        public void customize(ConfigurableEmbeddedServletContainer container) {
            container.setPort(8083);//设置属性
        }
    };
}

3.切换其他嵌入式Servlet容器

只需要在pom文件中把别的容器的依赖去掉,只引入要使用的容器就可以了。

Tomcat (默认就是使用嵌入式的Tomcat作为Servlet容器)

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-web</artifactId>
</dependency>

Jetty

<dependency>
   <artifactId>spring-boot-starter-jetty</artifactId>
   <groupId>org.springframework.boot</groupId>
</dependency>

Undertow

<dependency>
   <artifactId>spring-boot-starter-undertow</artifactId>
   <groupId>org.springframework.boot</groupId>
</dependency>

4.嵌入式Servlet容器自动配置原理

嵌入式Servlet容器自动配置原理
步骤:

  1. SpringBoot根据pom文件中导入的依赖情况,给容器中添加相应的容器工厂对象EmbeddedServletContainerFactory【比如tomcat容器就对应TomcatEmbeddedServletContainerFactory工厂对象】
  2. 只要是嵌入式的Servlet容器工厂要创建对象就会惊动后置处理器EmbeddedServletContainerCustomizerBeanPostProcessor
  3. 后置处理器会从容器中获取所有的EmbeddedServletContainerCustomizer 定制器,并遍历调用定制器的定制方法来给创建出的对象赋予属性值。

5.嵌入式Servlet容器启动原理

嵌入式Servlet容器启动原理 获取嵌入式Servlet容器的过程:

  1. SpringBoot应用启动运行run()方法
  2. refreshContext(context)—SpringBoot刷新IOC容器
    【创建IOC容器对象,并初始化容器,创建容器中的每一个组件】;
    如果是web应用创建AnnotationConfigEmbeddedWebApplicationContext
    否则:AnnotationConfigApplicationContext
  3. refresh(context) 刷新刚才创建好的ioc容器;
  4. onRefresh(); web的ioc容器重写了onRefresh方法
  5. webioc容器会创建嵌入式的Servlet容器;createEmbeddedServletContainer();
  6. 获取嵌入式的Servlet容器工厂
//从ioc容器中获取EmbeddedServletContainerFactory 组件;
EmbeddedServletContainerFactory containerFactory = getEmbeddedServletContainerFactory(); 

TomcatEmbeddedServletContainerFactory创建对象时,后置处理器一看是这个对象,就获取所有的定制器来先定制Servlet容器的相关配置;

  1. 使用容器工厂获取嵌入式的Servlet容器
containerFactory.getEmbeddedServletContainer(getSelfInitializer());
  1. 嵌入式的Servlet容器创建对象并启动Servlet容器;先启动嵌入式的Servlet容器,再将ioc容器中剩下没有创建出的对象获取出来;

总结IOC容器启动时创建嵌入式的Servlet容器

6.使用外部的Servlet容器&JSP支持

1.嵌入式Servlet容器:(应用jar方式打包)
​优点:简单、便携。
缺点:默认不支持JSP、优化定制比较复杂。

2.使用外置的Servlet容器(应用war方式打包)
步骤

  1. 必须创建一个war项目(创建时利用idea创建好目录结构和web.xml)
  2. 将嵌入式的Tomcat指定为provided;表示打包时不把tomcat打进包里,使用外部的Servlet容器。
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-tomcat</artifactId>
   <scope>provided</scope>
</dependency>
  1. 必须编写一个SpringBootServletInitializer的子类,并调用configure方法(此步骤原理见下一节)
public class ServletInitializer extends SpringBootServletInitializer {

   @Override
   protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
       //传入SpringBoot应用的主程序
      return application.sources(SpringBoot04WebJspApplication.class);
   }

}
  1. 启动服务器就可以使用

7.外部Servlet容器启动SpringBoot应用原理

1.使用外部Servlet容器的启动过程

SpringBoot打成jar包的执行过程

  1. 执行SpringBoot主类的main方法
  2. 启动ioc容器
  3. 创建嵌入式的Servlet容器

SpringBoot打成war包的执行过程

  1. 启动服务器
  2. 服务器启动SpringBoot应用【SpringBootServletInitializer】
  3. 启动ioc容器

war包的执行具体流程

  1. 启动Tomcat(先启动Servlet容器)
  2. 服务器启动时(web应用启动时)会创建当前web应用里面每一个jar包里面ServletContainerInitializer实例。ServletContainerInitializer的实现放在jar包的META-INF/services文件夹下,有一个名为javax.servlet.ServletContainerInitializer的文件,内容就是ServletContainerInitializer的实现类的全类名。
  3. 使用@HandlesTypes,在应用启动的时候加载我们感兴趣的类。SpringServletContainerInitializer将@HandlesTypes(WebApplicationInitializer.class)标注的所有这个类型的类都传入到onStartup方法的Set<Class<?>>;为这些WebApplicationInitializer类型的类创建实例;
  4. 每一个WebApplicationInitializer都调用自己的onStartup,相当于我们的SpringBootServletInitializer的类会被创建对象,并执行onStartup方法;
  5. SpringBootServletInitializer实例执行onStartup的时候会createRootApplicationContext;创建IOC容器

总结启动Servlet容器,再启动SpringBoot应用,最后创建IOC容器

四、注册Servlet三大组件

由于SpringBoot默认是以jar包的方式启动嵌入式的Servlet容器来启动SpringBoot的web应用,没有web.xml文件,所以可以通过三个类来分别往容器中添加servlet、filter、listener。

  1. ServletRegistrationBean:往容器中添加Servlet组件
//注册Servlet
@Bean
public ServletRegistrationBean myServlet(){
	//有参构造中传的第一个参数为要添加的Servlet,第二个参数为此Servlet映射的路径
    ServletRegistrationBean registrationBean = new ServletRegistrationBean(new MyServlet(),"/myServlet");
    return registrationBean;
}
  1. FilterRegistrationBean:往容器中添加过滤器组件
//注册过滤器
@Bean
public FilterRegistrationBean myFilter(){
	//可以使用(Filter,ServletRegistrationBean)的构造器,也可以使用无参构造创建对象,然后...
	//设置过滤器和要过滤的url
    FilterRegistrationBean registrationBean = new FilterRegistrationBean();
    registrationBean.setFilter(new MyFilter());
    registrationBean.setUrlPatterns(Arrays.asList("/hello","/myServlet"));
    return registrationBean;
}
  1. ServletListenerRegistrationBean:往容器中添加监听器组件
//注册监听器
@Bean
public ServletListenerRegistrationBean myListener(){
	//有参构造传入要注册的监听器
    ServletListenerRegistrationBean<MyListener> registrationBean = new ServletListenerRegistrationBean<>(new MyListener());
    return registrationBean;
}

PS:当SpringBoot帮我们自动配置SpringMVC的时
自动的注册SpringMVC的前端控制器:DIspatcherServlet就是使用ServletRegistrationBean来注册的。

DispatcherServletAutoConfiguration中:

@Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
@ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public ServletRegistrationBean dispatcherServletRegistration(
      DispatcherServlet dispatcherServlet) {
   ServletRegistrationBean registration = new ServletRegistrationBean(
         dispatcherServlet, this.serverProperties.getServletMapping());
    //默认拦截:/ --所有请求;包静态资源,但是不拦截jsp请求;   
    //  	  /*	--会拦截jsp
    //可以通过server.servletPath来修改SpringMVC前端控制器默认拦截的请求路径
    
   registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
   registration.setLoadOnStartup(
         this.webMvcProperties.getServlet().getLoadOnStartup());
   if (this.multipartConfig != null) {
      registration.setMultipartConfig(this.multipartConfig);
   }
   return registration;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值