Spring MVC学习 | 拦截器异常处理器

news2024/11/15 11:09:04

文章目录

  • 一、拦截器
    • 1.1 简介
    • 1.2 拦截器的使用
      • 1.2.1 创建
      • 1.2.2 配置
      • 1.2.3 测试
    • 1.3 多个拦截器的执行顺序
      • 1.3.1 preHandle()方法返回true
      • 1.3.2 preHandle()方法返回false
  • 二、异常处理器
    • 2.1 简介
    • 2.2 配置
      • 2.2.1 springmvc.xml中配置
      • 2.2.2 注解配置


学习视频🎥:https://www.bilibili.com/video/BV1Ry4y1574R

一、拦截器

1.1 简介

💬概述:Spring MVC中提供了拦截器HandlerInterceptor,类似于过滤器,可以在执行当前请求对应控制器方法之前对请求进行拦截,同时可以对当前请求进行加工,可以同时设置多个拦截器

🔑三个重要方法

方法名执行时间点说明
boolean preHandle()控制器方法执行前前置处理方法 --> 方法返回值表示拦截和放行(true表示放行,即正常执行控制器方法;返回false表示拦截,即不执行控制器方法)
void postHandle()控制器方法执行后后置处理方法 --> 在控制器方法执行之后执行,如果请求被拦截,即控制器方法不执行,则该方法也不执行
void afterCompletion()处理完视图和模型数据并对视图进行渲染之后完成处理方法 --> 在完成视图和模型数据处理并对视图进行渲染之后执行,如果请求被拦截,即控制器方法不执行,后面的数据处理和视图渲染也不会执行,所以该方法也不执行

🔑从DispatcherServlet源码观察三个方法的执行位置

① 打开DispatcherServlet源码(按两下shift键),找到doDisptch()方法

② 在doDispatch()方法中,找到ModelAndView对象mv,以及ha.handle()方法,由之前的分析可知,ha.handle()方法的执行间接或直接调用了对应的控制器方法,所以handle()方法相当于控制器方法,可以看到该方法前面由一个mappedHandler.applyPreHandle()方法,该方法中调用了preHandle()前置处理方法(可点击方法进入查看),所以applyPreHandle()相当于preHandle()。同理可得,在handle()方法后面有一个mappedHandler.applyPostHandle()方法,而applyPostHandle()也相当于postHandle()interceptor-three-methods

③ 由之前的分析可知,doDispatch()方法中会调用processDispatchResult()方法对请求结果进行处理,然后processDispatchResult()方法中又会调用render()方法对视图进行渲染,进入processDispatchResult()方法中可以看到,在render()方法执行之后,有一个mappedHandler.triggerAfterCompletion()方法,triggerAfterCompletion()方法中同样调用了afterCompletion()方法,所以该方法也就是完成处理方法interceptor-three-methods02

④ 从源码中可以得出拦截器中三个方法的执行顺序为:Ⅰ.preHanlde() --放行–> Ⅱ.控制器方法 --> Ⅲ.postHandle() --完成视图和模型数据处理和渲染视图–> Ⅳ.afterCompletion()

📸拦截器的大致执行过程图interceptor-run
🔑拦截器 🆚 过滤器

参考博文:Spring中的拦截器

  • 相同点
    ① 都有优先处理请求的权利,都可以决定是否将请求转移到请求的实际处理的控制器处
    ② 都可以对请求或者会话当中的数据进行加工

  • 不同点
    ① 过滤器只负责前面的过滤行为;而拦截器可以做前置处理也可以做后置处理,还可以进行完成处理,控制得更加细致
    ② 过滤器优先执行,还是拦截器优先呢? --> 过滤器优先
    ③ 过滤器是servlet规范里面的组件;拦截器都是框架自己而外添加的组件

1.2 拦截器的使用

1.2.1 创建

🔑创建方式:添加interceptor包,在该包下创建一个类TestInterceptor,在类上添加@Component注解(直接交给Spring IOC容器创建对象),然后实现HandleInterceptor接口,并重写实现接口的方法,接口的方法就是拦截器中的三个重要方法——preHandle()postHandle()afterCompletion()

@Component
public class TestInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, 
                             HttpServletResponse response, Object handler) throws Exception {

        return false;
    }

    @Override
    public void postHandle(HttpServletRequest request, 
                           HttpServletResponse response, Object handler, ModelAndView modelAndView) 
        throws Exception {
    }

    @Override
    public void afterCompletion(HttpServletRequest request, 
                                HttpServletResponse response, Object handler, Exception ex) 
        throws Exception {
    }
}

1.2.2 配置

🔑配置方式:在springmvc.xml文件添加<mvc:interceptors>标签,用于配置拦截器,在<mvc:interceptors>标签中需要创建拦截器对象,创建方式有三种

<!-- 配置拦截器 -->
<mvc:interceptors>
	<!-- 创建拦截器类对象,并设置拦截路径 -->
</mvc:interceptors>

<bean>标签

  • 使用:在<bean>class属性直接写上拦截器类的全类名
  • 设置拦截路径:无法设置拦截路径,默认为拦截全部请求路径
<bean class="com.key.mvc.interceptor.TestInterceptor"></bean>

💡 如果使用<bean>标签创建拦截器对象,则不用在类上添加@Component注解,因为<bean>本身就是用于创建对象的标签

<ref>标签

  • 使用:在<ref>bean属性直接写上拦截器类对应的bean-id值
  • 设置拦截路径:无法设置具体的拦截路径,默认为拦截全部请求路径
<ref bean="testInterceptor"></ref>

💡 因为<ref>标签本身就是用来导入外部Bean,所以使用<ref>的前提是拦截器类已经创建好对象,因此必须要在拦截器类上添加@Component注解(当然也可以在xml文件配置),此时拦截器类的bean-id值就是第一个字母改成小写后的类名

<mvc:interceptor>标签:在<mvc:interceptors>标签中又有三个标签,用于配置具体的拦截路径和创建拦截器对象

  • 设置拦截路径

    Ⅰ. <mvc:mapping path="拦截路径"/>:在path中设置具体的拦截路径

    💡 如果需要拦截全部请求路径,则设置为/**,而不是/*

    ❓ 为什么是/**而不是/*​:因为/*中的*是指0个或多个字符,类似于Ant风格中的通配符,所以/*仅表示一层路径,如果请求路径为多层,如/a/testInterceptor就不会被拦截;而/**中的有两个**,就表示0或多层路径,所以/**能匹配到所有请求路径

    Ⅱ. <mvc:exclude-mapping path="请求路径"/>:在path是属性中设置不进行拦截的请求路径

  • 创建拦截器对象:使用<bean><ref>标签即可

    <mvc:interceptor>
        <!-- 拦截所有请求资源,除了请求路径为 / 的请求(首页) -->
        <mvc:mapping path="/**"/>
        <mvc:exclude-mapping path="/"/>
    
        <!-- 创建拦截器对象 -->
        <ref bean="testInterceptor"></ref>
    </mvc:interceptor>
    

1.2.3 测试

① 将拦截器类TesInterceptorpreHandle()方法返回值设置为true,并在三个方法中打印测试信息

@Override
public boolean preHandle(HttpServletRequest request, 
                         HttpServletResponse response, Object handler) throws Exception {
    System.out.println("①执行前后处理方法 --> preHandle()");

    return true;
}

@Override
public void postHandle(HttpServletRequest request, 
                       HttpServletResponse response, Object handler, ModelAndView modelAndView) 
    throws Exception {

    System.out.println("③执行后置处理方法 --> postHandle()");
}

@Override
public void afterCompletion(HttpServletRequest request, 
                            HttpServletResponse response, Object handler, Exception ex) 
    throws Exception {
    System.out.println("④执行完成处理方法 --> afterCompletion()");
}

② 配置拦截器,采用第三种方式标识拦截器类,并设置拦截路径为全部路径,除了首页(/

③ 创建对应的测试控制器,控制器中创建测试控制器方法,对应请求路径设置为/**/testInterceptor(Ant风格),方便测试多个不同的路径,同时在方法中打印测试信息

@RequestMapping("/**/testInterceptor")
public String showTestInterceptor() {
    System.out.println("②执行控制器方法 --> showTestInterceptor()");
    return "success";
}

④ 在首页添加测试超链接

<a th:href="@{/testInterceptor}">测试拦截器</a>

⑤ 测试不同请求路径,观察控制器打印情况test-interceptor-result

1.3 多个拦截器的执行顺序

1.3.1 preHandle()方法返回true

💬概述:如果配置的每一个拦截器中的preHandle()方法都返回true,即每一个拦截器都采取放行的操作,此时拦截器中的三个方法的执行顺序只与拦截器在springmvc.xml文件中配置的顺序有关

preHandle()方法:每一个拦截器中的preHandle()都能被执行,且执行顺序与拦截器的在springmvc.xml中配置的顺序一致
postHandle()方法:每一个拦截器中的postHandle()都能被执行,且执行顺序与拦截器的在springmvc.xml中配置的顺序相反
afterComplaetion()方法:每一个拦截器中的afterCompletion()都能被执行,且执行顺序与拦截器的在springmvc.xml中配置的顺序相反

🔑测试

① 创建两个拦截器,在springmvc.xml文件中的配置顺序为——Ⅰ.FirstInterceptor、Ⅱ.SecondInterceptor,两个拦截器中的preHandle()方法返回值都为true

<mvc:interceptors>
	<!-- 拦截器① -->
    <ref bean="firstInterceptor"></ref>
    <!-- 拦截器② -->
    <ref bean="secondInterceptor"></ref>
</mvc:interceptors>

② 运行服务器后,点击测试链接,查看控制台打印情况interceptor-run-order

🔑从源码分析拦截器的执行顺序

❓ applyPreHandle()、applyPostHandle()和triggerAfterCompletion()三个方法关系:三个方法都是HandlerExecutionChain类中的方法,而在doDispatch()方法中的mappedHandler对象就是HandlerExecutionChain类型的对象

preHandle()方法

  • 进入DispatcherServlet前端控制器的doDispatch()方法中,找到mappedHandler.applyPreHandle()方法,之前已经介绍过,该方法内部调用了前置处理方法——preHandle()

  • 进入applyPreHandle()方法中,可以看到方法中有一个interceptorList——拦截器集合,该集合封装了全部拦截器(集合的元素对应每一个拦截器)applyPreHandle()

  • 通过debug模式启动,并在applyPreHanlde()方法上打上断点,查看拦截器集合的详情,可以看到集合中有三个元素,分别为ConversionServiceExposingInterceptorFirstInterceptorSecondInterceptor,索引分别为0、1、2,由此可见,越先配置的拦截器,在集合中的索引值越小,即位置越靠前interceptorList

    interceptorList中三个拦截器的来源

    Ⅰ. ConversionServiceExposingInterceptor:Spring MVC中创建的拦截器,无需我们创建和配置,所以它肯定是最先被配置的,因此它在拦截器集合中肯定是第一个元素(索引值为0)
    Ⅱ. FirstInterceptorSecondInterceptor:后面两个拦截器都是我在测试中创建和配置的,配置的顺序为同上,因此它们在集合中的索引值分别为1、2

  • 继续看到applyPreHandle()方法,方法中采用for循环正序遍历InterceptorList集合(从第一个元素遍历到最后一个元素),循环中获取集合中每一个拦截器对象,然后调用每一个拦截器的preHandle()方法,因为是正序循环遍历,因此每一个拦截器的preHandle()方法的执行顺序就跟集合中拦截器的顺序一致,即跟每一个拦截器的配置顺序一致applyPreHandle-for

  • 在for循环中可以看到,当前获取的拦截器对象的preHandle()返回值为true时,for循环才会继续执行,而且applyPreHandle()方法才会返回true;如果有一个拦截器的preHandle()返回false,则直接结束for循环,并返回false(下面会讨论返回false的情况),因此只有每一个拦截器的preHandle()方法都返回true时,每一个preHandle()才能都被执行applyPreHandle()才能返回true

    💡 由上面测试中的打印结果可知,测试的两个拦截器的preHandle()方法都被执行了,因此可以得出Spring MVC创建的ConversionServiceExposingInterceptor拦截器中的preHandle()返回值肯定也是true

  • 在循环还可以看到一个interceptorIndex变量,即拦截器索引,初始值为-1,当执行完本次循环获取的拦截器对象中的preHandle()方法后,如果循环还能继续(preHandle()返回true),则interceptorIndex加一,所以该变量是用来记录拦截器集合中第一个preHandle()返回false的拦截器的前一个拦截器在集合中的索引值,如果集合中没有preHandle()返回false的拦截器,则interceptorIndex记录的是最后一个拦截器在集合中的索引值,interceptorIndex会在triggerAfterCompletion()方法中使用到applyPreHandle-for-interceptorIndex

postHandle()方法

  • 继续回到前端控制器的doDispatch()方法中,找到mappedHandler.applyPostHandle()方法,该方法内部调用了后置处理方法postHandle()

  • 进入applyPostHandle()方法中,可以看到方法中和applyPreHandle()同样有拦截器集合interceptorList,也同样采用for循环遍历拦截器集合,for循环中也同样获取了每一个拦截器对象并调用其postHandle()方法,不一样的是,applyPostHandle()中的for循环是反序遍历(从集合的最后一个元素遍历到第一个元素),因此在循环中,是先调用集合索引值较大(即位置靠后)的拦截器中的postHandle()方法,所以每一个拦截器的postHandle()方法的执行顺序是跟集合中拦截器的顺序相反,即跟拦截器的配置顺序相反,也是跟preHandle()的执行顺序相反applyPostHandle()

afterComplaetion()方法

  • 回到前端控制器的doDispatch()方法中,找到processDispatchResult(),进入该方法可以找到mappedHandler.triggerAfterCompletion()方法,该方法中就调用了完成处理方法afterCompletion()

  • 进入triggerAfterCompletion()中,可以看到interceptorIndex变量,该变量就是applyPreHandle()方法中的拦截器索引变量,因为applyPreHandle()triggerAfterCompletion()之前执行,所以在triggerAfterCompletion()方法中的interceptorIndex的值为拦截器集合中第一个preHandle()返回false的拦截器的前一个拦截器在集合中的索引值triggerAfterCompletion-interceptorIndex

  • triggerAfterCompletion()方法中与前两个方法类似,同样有一个for循环,同样是遍历拦截器集合,同样在循环中获取每一个拦截器对象并调用器afterCompletion()方法,不一样的是,triggerAfterCompletion()中的for循环的是反序遍历且起始索引为interceptorIndex,而所有拦截器的preHandle()方法都返回true的情况下,interceptorIndex的值就是最后一个拦截器在集合中的索引,因此在triggerAfterCompletion()方法中,所有拦截器的afterCompletion()方法都会被执行triggerAfterCompletion()

1.3.2 preHandle()方法返回false

💬概述:如果配置的多个拦截器中,有一个或者多个拦截器的preHandle()方法返回值为false,即采取不放行的操作,此时拦截器中的三个方法的执行顺序不仅与拦截器的配置顺序有关,也与第一个preHandle()返回false的拦截器在拦截器集合中的位置有关

preHandle()方法:在拦截器集合中,第一个preHandle返回false的拦截器以及它之前的拦截器中的preHandle()都能被执行(包括preHandle()返回false的拦截器),且执行顺序与拦截器的在springmvc.xml中配置的顺序一致,之后的都不会被执行
postHandle()方法:所有拦截器中的postHandle()都不会被执行
afterComplaetion()方法:在拦截器集合中,第一个preHandle返回false的拦截器之前的拦截器中的afterCompletion()都能被执行(不包括preHandle()返回false的拦截器),且执行顺序与拦截器的在springmvc.xml中配置的顺序相反,之后的都不会被执行

🔑测试

① 创建并配置三个测试拦截器——firstInterceptorsecondInterceptorThirdInterceptor,配置顺序为Ⅰ.FirstInterceptor、Ⅱ.SecondInterceptor、Ⅲ.ThirdInterceptorsecondInterceptorpreHandle()方法返回值设置为false,其他两个拦截器的preHandle()方法返回值都是true

<mvc:interceptors>
    <!-- 拦截器① -->
    <ref bean="firstInterceptor"></ref>
    <!-- 拦截器② -->
    <ref bean="secondInterceptor"></ref>
    <!-- 拦截器③ -->
    <ref bean="thirdInterceptor"></ref>
</mvc:interceptors>

② 运行服务器后,点击测试链接,查看控制台打印情况interceptor-run-false-order

③ 查看浏览器页面的显示结果interceptor-run-false-page

🔑从源码分析拦截器的执行顺序

preHandle()方法

  • 进入doDispatch()方法中,同样打开applyPreHandle()方法,打上断点,debug模式启动,查看此时的拦截器集合interceptorList-false

  • applyPreHandle()方法中可以看到如果for循环中调用的某个拦截器中的preHandle()方法返回值为false时,就会直接执行triggerAfterCompletion()方法,执行完后就返回false,此时for循环结束,applyPreHandle()方法也执行结束,返回值就是falseapplyPreHandle-false

    💡 因为preHandle()triggerAfterCompletion()在同一个类中,所以在preHandle()可以直接调用this.triggerAfterCompletion(),无需创建对象来调用

  • 但在第一个preHandle返回false的拦截器之前的拦截器,还是能正常遍历到,而且preHandle()方法也正常执行,并且interceptorIndex也正常的加一,直到遇到第一个preHandle返回false的拦截器时,interceptorIndex就不会在加一(因为for循环已经结束),因此它记录就是第一个preHandle()返回false的拦截器的前一个拦截器在集合中的索引值applyPreHandle-false-interceptorIndex

  • 回到doDispatch()中可以看到,当applyPreHandle()方法返回false时,直接就return,即结束doDispatch()的执行,mappedHandler.applyPreHandle()后面的代码就不会再执行,因此控制器方法以及拦截器中的applyPostHandle()方法也就不会再执行,而applyPostHandle()方法不执行就会导致所有拦截器的postHandle()都不会被执行dispatch-false

afterComplaetion()方法

  • 由上述分析可知,在applyPreHandle()方法中,如果遇到preHandle()返回false的拦截器,就直接调用triggerAfterCompletion()方法,由此可得,即使有拦截器的preHandle()返回false,拦截器中的afterCompletion()方法还是能正常执行

  • 进入triggerAfterCompletion()方法中,由于applyPreHandle()方法同样执行在triggerAfterCompletion()之前,所以interceptorIndex的值同样是拦截器集合中第一个preHandle()返回false的拦截器的前一个拦截器在集合中的索引值,因此在循环中,是先调用索引值为interceptorIndex的拦截器中的afterCompletion()方法,然后往索引值减小的方向遍历,因此interceptorIndex索引值之前(包括interceptorIndex)的拦截器中的afterCompletion()方法都能被执行,且执行顺序与集合中拦截器的顺序相反,而interceptorIndex索引值之后的拦截器中的afterCompletion()方法都不会被执行triggerAfterCompletion-false-interceptorIndex


二、异常处理器

2.1 简介

💬概述:SpringMVC提供了一个异常处理器接口——HandlerExceptionResolver,该接口是Spring MVC中最底层的接口
🔑作用:异常处理器可以处理控制器方法在执行过程中遇到的所有异常,在遇到异常时也可以自定义跳转的视图页面
🔑两个主要的实现类

  1. DefaultHandlerExceptionResolver:Spring MVC中默认使用的异常处理器,无需我们配置
  2. SimpleMappingExceptionResolver:Spring MVC中提供的一个可以自定义异常处理的异常处理器,即可以对每一个异常设置指定的视图名,当遇到对应异常时就跳转到指定的视图,需要我们在配置文件中配置或使用注解配置

2.2 配置

2.2.1 springmvc.xml中配置

🔑配置方式

<!-- 配置异常处理器,自定义异常对应的视图 -->
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
    <!-- 属性① -->
    <property name="exceptionMappings">
        <!-- 通过<props>标签注入 -->
        <props>
            <!-- 映射关系写在<prop>标签中 -->
            <prop key="java.lang.ArithmeticException">error</prop>
        </props>
    </property>

    <!-- 属性② ,request域中异常信息对应的键为ex-->
    <property name="exceptionAttribute" value="ex"></property>
</bean>
  • 在Spring MVC核心配置文件springmvc.xml中添加<bean>标签,将SimpleMappingExceptionResolver实现类的全类名导入,即创建SimpleMappingExceptionResolver实现类的对象

  • 然后在<bean>标签中添加<property>标签,为类注入属性,SimpleMappingExceptionResolver有两个重要属性

    ① exceptionMappings(异常映射):该属性是Proerties类型,用于将异常类与对应需要跳转的视图名建立映射关系;属性值用<props>标签书写,<props>标签中再添加<prop>标签,<prop>标签中添加需要处理的异常全类名以及与之建立映射关系的视图名

    🔺 <prop>标签介绍

    • key:<prop>标签的一个属性,属性值为异常对应的的全类名
    • 标签体中的内容:标签体中内容就是与异常建立映射关系的视图名,该视图名的书写与控制器方法中返回值一样(改变视图名的前缀同样可以改变视图对象的类型)

    ② exceptionAttribute:该属性用于将当前出现的异常信息(异常全类名和对应的错误类型)存储到request域中进行共享,对应的页面上就可以从request域中获取出异常信息并打印到页面上

    🔺exceptionAttribute的属性值exexceptionAttribute的属性值ex对应此时request域中存储异常信息的数据名,即键(数据值就是异常信息),在对应页面上直接通过ex获取request域中存储的异常信息(request域中数据无需使用内置对象获取)

🔑测试

① 创建一个测试控制器方法,方法中自定义一个数学异常(1/0)

@RequestMapping("/testEx")
public String showTestEx() {

    int i = 1/0;
    return "success";
}

② 在springmvc.xml中配置异常处理器SimpleMappingExceptionResolver,其中添加数学异常ArithmeticException,与异常建立映射关系的视图名为error,并将异常信息存储到request域中

创建异常对应的页面error.html,获取request域中的异常信息并打印到页面上

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>异常处理</title>
</head>
<body>
    <p>不好意思!出现异常了!</p><br/>

    <!-- 打印异常信息 -->
    <p th:text="${ex}"></p>
</body>
</html>

测试结果exception-xml

2.2.2 注解配置

🔑配置方式

  • 创建一个类,在类上添加@ControllerAdvice注解,然后创建处理异常的方法,该方法与控制器方法类似,返回值类型为String,返回值同样也对应一个视图名,但这里设置的是与异常建立映射关系的视图名
  • 然后在方法上添加@ExceptionHandler注解,注解中的value属性值对应的是要处理的异常类的class对象
  • 方法形参上添加一个异常对象ex以及可以处理request域对象数据的对象,如model对象(ModelMapMap都可),Spring MVC会自动识别出形参上异常对象ex,并将当前的异常信息存储到该对象中,而形参上的model对象则用来将ex存储到request域
@ControllerAdvice
public class ExceptionController {

    /**
     * 处理异常的方法
     */
    @ExceptionHandler(ArithmeticException.class)
    public String showExPage(Exception ex, Model model) {
        // 将异常对象存储到request域中
        model.addAttribute("ex", ex);

        return "error";
    }
}

❓ 异常处理器中使用的两个注解

  1. @ControllerAdvice:作用于类上,将对应类标识为异常处理器组件,该注解中包含了@Component注解,因此被@ControllerAdvice注解标识的类在一定程度上也是一个控制器,而且在该类里创建的方法与控制器方法也类似
  2. @ExceptionHandler:作用于异常处理器类里的方法上,注解中的value属性是一个数组,因此在@ExceptionHandler注解中可以配置多个异常类,但方法中只能返回一个视图名,说明一个视图名(即一个页面)可以对应多个异常

🔑测试

① 创建测试控制器方法,添加一个数学异常(同上)

② 创建异常处理器类,类上用@ControllerAdvice标识,类里创建处理异常的方法,方法上添加@ExceptionHandler注解,注解中添加两个异常类的class对象(数学异常和空指针),方法返回值即视图名设置为error

@ExceptionHandler(
    value = {ArithmeticException.class, NullPointerException.class}
)
public String showExPage(Exception ex, Model model) {
    // 将异常对象存储到request域中
    model.addAttribute("ex", ex);

    return "error";
}

③ 创建异常对应的页面error.html,页面上打印出对应的异常信息(同上👆)

④ 测试结果:结果同上👆

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/98803.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

编码与解码总结

标准ASC||字符集&#xff1a; ASC||&#xff1a;美国信息交换标准代码&#xff0c;包括了英文、数字等标准ASC||使用一个字节存储一个字符&#xff0c;首位是0&#xff0c;总供可以表示128个字符 GBK&#xff08;汉字内码扩展规范&#xff0c;国标&#xff09; 汉字编码字符集…

Apache Shiro,这一篇就够了

Apache Shiro&#xff0c;这一篇就够了1.Shiro实现登录拦截2.登录认证操作3.Shiro整合Mybatis4.用户授权操作5.Shiro授权6.Shiro整合Thymeleaf1.Shiro实现登录拦截 前期环境准备 准备添加Shiro的内置过滤器&#xff1a; Bean public ShiroFilterFactoryBean shiroFilterFact…

《美国职业橄榄球大联盟》:NFL·橄榄1号位

基本装备 NFL橄榄球是一项过程极为激烈的比赛&#xff0c;阻挡、拦截与冲撞都是比赛不可或缺的一部分&#xff0c;这也可以说是橄榄球的一大特色。为了保护球员的安全&#xff0c;避免因为球员受伤而耽误球赛&#xff0c; NFL与NCAA都要求所有球员必须“穿戴合适且合法的护具”…

IfcOpenShell正确设置几何体的坐标

在之前的文章中&#xff0c;我们使用 IfcOpenShell (IOS) 读取 ifc 几何并将其转换为 brep。 当我们读取 wikilab.ifc文件时&#xff0c;一切似乎都是正确的&#xff0c;但真的如此吗&#xff1f; 当你在项目中使用 BIM 时&#xff0c;坐标始终是正确讨论的主题。 就此而言&am…

Android开发如何使用Docker为Jenkins持续集成助力

Android开发如何使用Docker为持续集成助力 为什么使用Docker 我为啥要使用到Docker呢&#xff1f;其实也是被动的&#xff0c;因为公司的项目托管在Coding上面&#xff0c;然后Jenkins集成也用的是Coding的&#xff0c;Coding默认提供了Android-29&#xff0c;JDK-8的构建环境…

【JAVA进阶】多态,内部类

&#x1f4c3;个人主页&#xff1a;个人主页 &#x1f525;系列专栏&#xff1a;JAVASE基础 目录 一、多态 1.多态的概述 2.多态的优势 3.类型转换问题 二、内部类 1.内部类概述[了解] 2.静态内部类[了解] 3.成员内部类[了解] 4.匿名内部类概述[重点] 一、多态 1.多态…

树状数组经典例题

目录 1.数星星 2.小朋友排队 3.求逆序对 1.数星星 题目描述 天空中有一些星星,这些星星都是在不同的位置,每个星星都有一个坐标。 如果一个星星的左下方(包含正左和正下)有k颗星星,就说这颗星星是k级的。 例如,上图中星星5是3级的(1,2,4在它的左下),星星2,4是1级的。…

idea中推送本地仓库和远程仓库后代码回退

本地仓库代码提交后回退 提交到本地仓库后 点击提交后会保存在本地仓库 本地仓库的回撤 找到git的提交记录 右键选择撤销还原 撤销还原后会出现提交文件&#xff0c;成功将本地仓库的文件移除&#xff0c;但是本地的错误代码仍然存在 如果想撤销提交到本地仓库的错误文…

SOLID 设计原则 - 这篇最容易消化

面向对象设计原则 SOLID 应该是职业程序员必须掌握的基本原则&#xff0c;每个程序员都应该了然于胸&#xff0c;遵守这 5 个原则可以帮助我们写出易维护、易拓展的高内聚低耦合的代码。 它是由罗伯特C马丁(知名的 Rob 大叔)21世纪初期 (准确来说&#xff0c;2000年在他的论文…

DevOps实战系列【第十章】:详解Jenkins Pipeline基本概念和语法

个人亲自录制全套DevOps系列实战教程 &#xff1a;手把手教你玩转DevOps全栈技术 流水线基本概念 官方中文手册&#xff1a; https://www.jenkins.io/zh/doc/book/pipeline 我们最好在结合英文文档去看&#xff0c;因为翻译过来的中文比较乱。 Jenkins pipeline是一套插件&…

MySQL 日志,难怪被模仿

一.前言 日志是mysql数据库的重要组成部分&#xff0c;记录着数据库运行期间各种状态信息。mysql日志主要包括错误日志、查询日志、慢查询日志、事务日志、二进制日志几大类。 通过分析日志&#xff0c;我们可以优化数据库性能&#xff0c;排除故障&#xff0c;甚至能够还原数…

中文文献检索网站

1. 中国知网 网址&#xff1a; https://www.cnki.net/ 中国知网被称众多科研人称国内文献论文最全最权威的中文文献库&#xff0c;知网提供中国学术文献、外文文献、学位论文、报纸、会议、年鉴、工具书等各类资源统一检索、统一导航、在线阅读和下载服务。 2. 掌桥科研 网址…

25.Django大型电商项目之地址管理——如何使用三级联动菜单数据加载地址、保存数据、动态获取数据、设置默认值

1. 地址管理基本页面 1.1 概述 1.2 流程 修改templates的跳转链接center.html <ul><li><a href"/userapp/address/">地址管理</a></li> </ul>templates {% extends base.html %} {% block title %}用户中心{% endblock %} {…

东北大学数据结构第八周(排序)

7-1 快速排序 作者 朱允刚 单位 吉林大学 给定包含n个元素的整型数组a[1],a[2],…,a[n]&#xff0c;利用快速排序算法对其进行递增排序&#xff0c;请输出排序过程&#xff0c;即每次Partition之后的数组。每次选择所处理的子数组的第一个元素作为基准元素。 输入格式: 输入为…

MapReduce 序列化案例

文章目录MapReduce 序列化案例一、案例需求二、案例分析map 阶段Reduce 阶段三、代码实现1、编写流量统计的Bean对象2、Mapper阶段代码MapReduce 序列化案例 一、案例需求 1、需求&#xff1a; 统计每一个手机号耗费的总上行流量&#xff0c;下行流量&#xff0c;总流量 2、输…

二、ZFNet可视化卷积神经网络——可解释性机器学习(DataWhale组队学习)

目录引言ZFNet的网络结构可视化反卷积反池化反激活反卷积训练细节特征可视化特征演化特征不变性局部遮挡测试敏感性分析相关性分析消融实验宽度影响深度影响迁移学习能力有效性分析总结引言 纽约大学ZFNet&#xff0c;2013年ImageNet图像分类竞赛冠军模型。对AlexNet进行改进的…

AOP 操作

AOP 操作AOP 操作&#xff08;准备&#xff09;1. Spring 框架一般是基于 AspectJ 实现 AOP 操作&#xff08;1&#xff09;什么是 AspectJ2. 基于 AspectJ 实现 AOP 操作3. 在项目工程里面引入 AOP 先关的依赖4. 切入点表达式举例1&#xff1a;对 com.fairykunkun.dao.UserDao…

谈谈自己对依赖注入的理解

1. 絮絮叨叨 1.1 想学习Google Guice 在工作的过程中&#xff0c;发现有名的大数据组件Presto大量使用Google Guice实现各种Module的构建 很多bind(interface).to(implementClass).in(scope)语句&#xff0c;实现接口与实现类的绑定&#xff0c;并指定实现类是单例还是多例 /…

Service的绑定过程

前言 Service的绑定过程将分为两个部分来进行讲解&#xff1b;分别是Contextlmpl到AMS的调用过程和Service的绑定过程。 frameworks/base/core/java/android/content/ContextWrapper.javapublic boolean bindService(Intent service, ServiceConnection conn,int flags) {ret…

计算机毕设Python+Vue-新型冠状病毒防控咨询网站(程序+LW+部署)

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…