文档章节

springMVC笔记系列(23)——拦截器及其在乱码问题和登录问题的应用实现

HappyBKs
 HappyBKs
发布于 2016/07/12 22:55
字数 7135
阅读 1754
收藏 60
点赞 3
评论 3

拦截器的概念

A:“什么是拦截器?”

B:“拦截器是通过统一拦截从客户端发往服务器的请求来完成功能的增强。”

A:(一脸懵逼)

B:“说得简单点,拦截器就是在客户端向服务器端发出请求的期间,在请求交友服务器处理之前或之后对请求数据做一些修改或者其他相关的操作。”

A:“能说说具体实现什么功能吗?”

B:“拦截器的使用场景是解决一些共性问题。比如乱码问题、权限验证问题。你可以将这些共性的操作从各个不同业务功能的控制器方法中抽离出来,放在统一的拦截器中执行,这样你的解决乱码问题的代码,或者权限验证的代码不会重复地出现在各个不同业务功能对应的控制器方法执行体中。”

A:“我可以理解为拦截器是看门狗吗?这只狗负责看门,监管进门和出门的人;这只狗也能不同人家的门前做相同的事情——拦截出门或进门的人们。”

B:“能文雅点吗。。。”

A:“springMVC拦截器的出现倒是帮我们在MVC实现时,营造了一种AOP的效果”

B:“spring大法万岁!”

A:“。。。”

好,小段子结束,还是用博客的正常写法来写吧。

 

这里,也许你还要问——拦截器和过滤器有什么区别?

过滤器Filter依赖于Servlet容器,Filter被Servlet容器所管理;基于回调函数;过滤范围大(请求、资源等)

拦截器Interceptor依赖于springMVC框架容器;基于反射机制;只过滤请求

(本文出自happyBKs的博客:http://my.oschina.net/happyBKs/blog/710833)

springMVC拦截器的原理和使用

我们还是先来看一个应用场景:解决乱码问题。

利用SpringMVC的拦截器可以解决乱码问题。但是,你应该会说,我使用Servlet容器中的过滤器也可以完成。是的,现在我们现在一个springMVC项目中通过配置一个过滤器来解决乱码问题。(实际上springMVC拦截器与Servlet容器中的过滤器在实现解决乱码问题时的原理十分相似)

我们在前几篇博客文章的例子的基础上改代码吧,顺便说明一下一个springMVC可以有两个互不干扰的前端控制器配置。在web.xml中,我们在最后追加一个前端控制器viewSpace-dispatcher。可以看到,这个web.xml配置文件中有两个org.springframework.web.servlet.DispatcherServlet。但是拦截的请求是不同的,一个是/,一个是/test2/*。这里请求会自动匹配更具体的一个,关于各种url通配符优先级,改天我专门弄一篇博客来说。

关于web.xml的servlet的url-pattern,我只想插入一个注意事项:

在web.xml文件中,以下语法用于定义映射: 

l. 以”/’开头和以”/*”结尾的是用来做路径映射的。 

2. 以前缀”*.”开头的是用来做扩展映射的。 

3. “/” 是用来定义default servlet映射的。 

4. 剩下的都是用来定义详细映射的。比如: /aa/bb/cc.action 

所以,为什么定义”/*.action”这样一个看起来很正常的匹配会错?因为这个匹配即属于路径映射,也属于扩展映射,导致容器无法判断。

言归正传,下面是web.xml,重点看最后的那个viewSpace-dispatcher:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd " version="2.5">

  <display-name>Archetype Created Web Application</display-name>

  <!-- Spring应用上下文, 理解层次化的ApplicationContext -->
    <context-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>/WEB-INF/configs/spring/applicationContext*.xml</param-value>
    </context-param>

    <listener>
      <listener-class>
        org.springframework.web.context.ContextLoaderListener
      </listener-class>
    </listener>

  <!-- DispatcherServlet, Spring MVC的核心 -->
  <servlet>
    <servlet-name>mvc-dispatcher</servlet-name>
    <servlet-class> org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <!-- DispatcherServlet对应的上下文配置, 默认为/WEB-INF/$servlet-name$-servlet.xml
     -->
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>/WEB-INF/configs/spring/mvc-dispatcher-servlet.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>mvc-dispatcher</servlet-name>
    <!-- mvc-dispatcher拦截所有的请求-->
    <url-pattern>/</url-pattern>
  </servlet-mapping>


  <servlet>
    <servlet-name>viewSpace-dispatcher</servlet-name>
    <servlet-class> org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <!-- DispatcherServlet对应的上下文配置, 默认为/WEB-INF/$servlet-name$-servlet.xml
     -->
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>/WEB-INF/configs/spring/viewSpace-dispatcher-servlet.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>viewSpace-dispatcher</servlet-name>
    <!-- mvc-dispatcher拦截所有的请求-->
    <url-pattern>/test2/*</url-pattern>
  </servlet-mapping>



</web-app>

然后我们编写viewSpace-dispatcher的配置文件:

\WEB-INF\configs\spring\viewSpace-dispatcher-servlet.xml如下:(具体含义可以参加前面的文章)

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <!-- 本配置文件是工名为mvc-dispatcher的DispatcherServlet使用, 提供其相关的Spring MVC配置 -->

    <!-- 启用Spring基于annotation的DI, 使用户可以在Spring MVC中使用Spring的强大功能。 激活 @Required
        @Autowired,JSR 250's @PostConstruct, @PreDestroy and @Resource 等标注 -->
    <context:annotation-config />

    <!-- DispatcherServlet上下文, 只管理@Controller类型的bean, 忽略其他型的bean, 如@Service -->
    <context:component-scan base-package="com.happyBKs.controller">
        <context:include-filter type="annotation"  expression="org.springframework.stereotype.Controller" />
    </context:component-scan>

    <!-- HandlerMapping, 无需配置, Spring MVC可以默认启动。 DefaultAnnotationHandlerMapping
        annotation-driven HandlerMapping -->

    <!-- 扩充了注解驱动,可以将请求参数绑定到控制器参数 -->
    <mvc:annotation-driven />

    <!-- 静态资源处理, css, js, imgs -->
    <mvc:resources mapping="/resources/**" location="/resources/" />


    <!-- 配置ViewResolver。 可以用多个ViewResolver。 使用order属性排序。 InternalResourceViewResolver放在最后。 -->
    <bean
            class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
        <property name="order" value="1"/>
        <!--<property name="mediaTypes">-->
        <!--<map>-->
        <!--<entry key="json" value="application/json"/>-->
        <!--<entry key="xml" value="application/xml"/>-->
        <!--<entry key="htm" value="text/html"/>-->
        <!--</map>-->
        <!--</property>-->

        <property name="defaultViews">
            <list>
                <!-- JSON View -->
                <bean
                        class="org.springframework.web.servlet.view.json.MappingJackson2JsonView">
                </bean>
            </list>
        </property>
        <!--<property name="ignoreAcceptHeader" value="true"/>-->
    </bean>

    <bean
            class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />
        <property name="prefix" value="/WEB-INF/jsps/" />
        <property name="suffix" value=".jsp" />
    </bean>


    <!--200*1024*1024即200M resolveLazily属性启用是为了推迟文件解析,以便捕获文件大小异常 -->
    <bean id="multipartResolver"
          class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <property name="maxUploadSize" value="209715200"/>
        <property name="defaultEncoding" value="UTF-8"/>
        <property name="resolveLazily" value="true"/>
    </bean>

</beans>

之后我们变量一个控制器TestController2 :

package com.happyBKs.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;

/**
 * Created by sunsun on 2016/7/9.
 */

@Controller
@RequestMapping("/test2")
public class TestController2 {

    @RequestMapping("/login")
    public String login(){
        System.out.println("进入控制器的login方法");
        return "login";
    }

    @RequestMapping("/viewAll")
    public ModelAndView viewAll(@RequestParam("name") String name,@RequestParam("pwd") String pwd){
        ModelAndView mv = new ModelAndView();
        System.out.println("进入控制器的viewAll方法");
        System.out.println("name="+name);
        System.out.println("pwd="+pwd);
        mv.setViewName("/hello");
        return mv;
    }


}

这个控制器的两个方法,一个是分发访问登录页面的请求,一个是接收登录请求并将表单数据在控制台输出最终转到hello页面。

登录页面\WEB-INF\jsps\login.jsp:(本项目是之前博客中的项目例子基础上追加的功能,原项目定义了webapp的url名称为mvc,所以表单action的请求路径请注意!)

<%--
  Created by IntelliJ IDEA.
  User: happyBKs
  Date: 2016/7/9
  Time: 13:22
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>登录</title>
</head>
<body>
<form action="/mvc/test2/viewAll" method="post">
    用户名<input type="text" name="name" id="name"><br/>
    密 码<input type="password" name="pwd" id="pwd"><br/>
    <input type="submit" name="登录">
</form>
</body>
</html>

登录后的hello页面:

<%--
  Created by IntelliJ IDEA.
  User: happyBKs
  Date: 2016/7/9
  Time: 14:42
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>成功登录</title>
</head>
<body>
成功登录
</body>
</html>

之后我们发布项目到tomcat,但是请求http://localhost:8080/mvc/test2/login,却提示404错误。

控制台输出:

19680 [http-apr-8080-exec-10] DEBUG org.springframework.web.servlet.DispatcherServlet  - DispatcherServlet with name 'viewSpace-dispatcher' processing GET request for [/mvc/test2/login]
19680 [http-apr-8080-exec-10] DEBUG org.springframework.web.servlet.DispatcherServlet  - DispatcherServlet with name 'viewSpace-dispatcher' processing GET request for [/mvc/test2/login]
19686 [http-apr-8080-exec-10] DEBUG org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping  - Looking up handler method for path /login
19686 [http-apr-8080-exec-10] DEBUG org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping  - Looking up handler method for path /login
19689 [http-apr-8080-exec-10] DEBUG org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping  - Did not find handler method for [/login]
19689 [http-apr-8080-exec-10] DEBUG org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping  - Did not find handler method for [/login]
19690 [http-apr-8080-exec-10] WARN  org.springframework.web.servlet.PageNotFound  - No mapping found for HTTP request with URI [/mvc/test2/login] in DispatcherServlet with name 'viewSpace-dispatcher'
19690 [http-apr-8080-exec-10] WARN  org.springframework.web.servlet.PageNotFound  - No mapping found for HTTP request with URI [/mvc/test2/login] in DispatcherServlet with name 'viewSpace-dispatcher'
19690 [http-apr-8080-exec-10] DEBUG org.springframework.web.servlet.DispatcherServlet  - Successfully completed request
19690 [http-apr-8080-exec-10] DEBUG org.springframework.web.servlet.DispatcherServlet  - Successfully completed request

这是为什么呢?

原因是,当一个请求的全路径通过servlet映射找到所服务的DispatcherServlet后,DispatcherServlet按照url-pattern路径映射的匹配后剩余的路径进一步交给DispatcherServlet,由DispatcherServlet进一步实现剩下路径在其搜索包空间内的控制器类的请求映射的匹配。

回到这个例子中就是:http://localhost:8080/mvc/test2/login中,首先去除了域名和端口/mvc/test2/login,tomcat中配置了应用上下文Application Context为mvc,所以springMVC项目处理的请求为/test2/login。在web.xml中由viewSpace-dispatcher因url-pattern为/test2/**接收请求/test2/login,然后将匹配剩余部分路径/login进一步交给viewSpace-dispatcher所对应的springMVC容器配置文件中定义的控制器类包搜索空间中搜索能够匹配这个请求/login的控制器及其方法。但是这里我们原本希望其能够映射到的控制器类TestController2的@RequestMappping的注解值又配置了一个/test2,原本我们希望的目标方法public String login()的@RequestMappping注解值为/login。这时候实际springMVC是将请求/login尝试与/test2/login进行匹配,当然不可能匹配上,所以才会在控制台输出请求匹配不了,整个请求也只能返回404。

你若不信,当我们这时候请求http://localhost:8080/mvc/test2/test2/login,你会发现尽然正常显示了!!

所以我们就将控制器类TestController2的映射路径改为“/”,这样就可以了。

package com.happyBKs.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;

/**
 * Created by sunsun on 2016/7/9.
 */

@Controller
@RequestMapping("/")
public class TestController2 {

    @RequestMapping("/login")
    public String login(){
        System.out.println("进入控制器的login方法...");
        return "login";
    }

    @RequestMapping("/viewAll")
    public ModelAndView viewAll(@RequestParam("name") String name,@RequestParam("pwd") String pwd){
        ModelAndView mv = new ModelAndView();
        System.out.println("进入控制器的viewAll方法...");
        System.out.println("name="+name);
        System.out.println("pwd="+pwd);
        mv.setViewName("/hello");
        return mv;
    }


}

 

我们来看看运行结果:

请求http://localhost:8080/mvc/test2/login

控制台输出:

提交表单之后,转到http://localhost:8080/mvc/test2/viewAll:

控制台输出:

可以看到中文数据出现了乱码。。。。。。。

springMVC在框架中已经为web的过滤器提供了一个CharacterEncodingFilter类

 在web.xml中,增加一个过滤器:

  <filter>
    <filter-name>viewSpace-filter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
      <param-name>encoding</param-name>
      <param-value>utf8</param-value>
    </init-param>
  </filter>
  <filter-mapping>
    <filter-name>viewSpace-filter</filter-name>
    <url-pattern>/test2/*</url-pattern>
  </filter-mapping>

这个过滤器指定/test2/*符合该通配符的url请求都会被这个过滤器过滤到,然后交由这个CharacterEncodingFilter来处理,将请求参数中的数据编码转换成utf8。

注意:这个不同用/test2,而要使用完整的通配符,否则过滤器会以为你仅仅是要过滤http://localhost:8080/mvc/test2一个url请求,这与前端控制器的url-pattern不同,需要特别注意。

我们按照刚才的方法运行请求:

控制台输出发现参数已经不会再出现中文乱码了。

好,过滤器就像一个检票口,符合特定条件的请求进入者可以经过相应一些手续后进入。过滤器和拦截器在功能上可以说是十分相似的,只是在一些细节上有所不同,下面,我们来看看拦截器如何实现。

拦截器的实现步骤如下:

1. 首先编写一个拦截器类,须要实现HandlerInterceptor接口:

package com.happyBKs.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;

/**
 * Created by happyBKs on 2016/7/9.
 */

@Controller
@RequestMapping("/")
public class TestController2 {

    @RequestMapping("/login")
    public String login(){
        System.out.println("进入控制器的login方法...");
        return "login";
    }

    @RequestMapping("/viewAll")
    public ModelAndView viewAll(@RequestParam("name") String name,@RequestParam("pwd") String pwd){
        ModelAndView mv = new ModelAndView();
        System.out.println("进入控制器的viewAll方法...");
        System.out.println("name="+name);
        System.out.println("pwd="+pwd);
        mv.setViewName("/hello");
        return mv;
    }


}

2.将拦截器注册到springMVC框架中:

注意,在这个前端控制器器配置文件中,必须声明

xmlns:mvc="http://www.springframework.org/schema/mvc"

http://www.springframework.org/schema/mvc/spring-mvc.xsd"

应为拦截器须要用到mvc的名称空间:

我们在\WEB-INF\configs\spring\viewSpace-dispatcher-servlet.xml中追加如下内容:

    <mvc:interceptors>
        <mvc:interceptor>
            <mvc:mapping path="/**"/>
            <bean class="com.happyBKs.interceptor.TestInterceptor"></bean>
        </mvc:interceptor>
    </mvc:interceptors>

注意:这里的,mvc:mapping的path属性配置的路径必须是“/**”,而不能是“/”或者“/*”。


/**的意思是所有文件夹及里面的子文件夹
/*是所有文件夹,不含子文件夹
/是web项目的根目录

然后可以看到请求http://localhost:8080/mvc/test2 结果控制台输出:拦截器的三个方法与控制器方法的执行顺序可以看见了吧。

执行进入preHandle方法
进入控制器的login方法...
执行进入postHandle方法
11705 [http-apr-8080-exec-10] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory  - Invoking afterPropertiesSet() on bean with name 'login'
11705 [http-apr-8080-exec-10] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory  - Invoking afterPropertiesSet() on bean with name 'login'
11705 [http-apr-8080-exec-10] DEBUG org.springframework.web.servlet.DispatcherServlet  - Rendering view [org.springframework.web.servlet.view.JstlView: name 'login'; URL [/WEB-INF/jsps/login.jsp]] in DispatcherServlet with name 'viewSpace-dispatcher'
11705 [http-apr-8080-exec-10] DEBUG org.springframework.web.servlet.DispatcherServlet  - Rendering view [org.springframework.web.servlet.view.JstlView: name 'login'; URL [/WEB-INF/jsps/login.jsp]] in DispatcherServlet with name 'viewSpace-dispatcher'
11714 [http-apr-8080-exec-10] DEBUG org.springframework.web.servlet.view.JstlView  - Forwarding to resource [/WEB-INF/jsps/login.jsp] in InternalResourceView 'login'
11714 [http-apr-8080-exec-10] DEBUG org.springframework.web.servlet.view.JstlView  - Forwarding to resource [/WEB-INF/jsps/login.jsp] in InternalResourceView 'login'
执行进入afterCompletion方法

 

拦截器的方法

下面我们来详细说说拦截器的三个方法:

preHandle:在请求备注里之前进行调用

postHandle:在请求被处理之后进行调用

afterComletion:在请求结束之后才进行调用

 

preHandle方法与其他两个方法相比比较特殊,它是具有返回值的。这个boolean类型的返回值表示这个请求是否能“活命”。

    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("执行进入preHandle方法");
        return true;
    }

返回值为true表示,在preHandle执行完之后请求继续传递下去;为false,请求被拦截后就终止请求了。

如果我们把这个方法的返回值改成false,控制台输出结果和页面显示如下:

看到了请求没有被执行下去,连控制器方法都没有被执行,所以这个请求被拦截下懒之后被拦截器给扼杀在摇篮里了。

这三个方法的形参都具有HttpServletRequest request和HttpServletResponse response,前者包含了所有的请求内容,后者包含了所有的响应内容。

preHandle方法里的Object handler表示的是被拦截的请求目标对象。比如,这个例子中请求拦截的目标就是这个TestController2。

postHandle方法里的独有的参数ModelAndView modelAndView:我们可以通过该参数改变显示的视图,或者修改发往视图的方法。看到了吧,即使你在控制器方法中已经对映射的视图资源做了设定,这里依然可以更改。这个更改既包括这请求映射到那个视图资源,还包括了传递给视图资源的数据等。

这里我们举个例子,加入我们在控制器TestController2中增加一个方法:

    @RequestMapping("/msg")
    public ModelAndView msg(){
        System.out.println("进入控制器的msg方法...");
        ModelAndView mv=new ModelAndView();
        mv.setViewName("/InterTest");
        mv.addObject("msg","从控制器的方法返回的视图数据");
        return mv;
    }

增加一个jsp视图页面\WEB-INF\jsps\InterTest.jsp:

注意:这里msg数据用的EL表达式的形式${msg}。

<%--
  Created by IntelliJ IDEA.
  User: sunsun
  Date: 2016/7/12
  Time: 20:58
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>拦截器测试</title>
</head>
<body>
msg=${msg}
</body>
</html>

然后我们运行请求http://localhost:8080/mvc/test2/msg

 

好,这时候,我们在原先的拦截器类TestInterceptor类的postHandle方法上做个修改:

    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("执行进入postHandle方法");
        //通过modelAndView参数改变显示的视图,或者修改发往视图的方法
        modelAndView.addObject("msg","被拦截器的postHandle方法修改后的视图数据");

    }

我们再次运行方才的项目,请求:http://localhost:8080/mvc/test2/msg

看到了吧,msg的数据已经是被拦截器的postHandle方法修改后的数据了。

并且,控制台输出也表明,执行的各个环节依然还在。

当然,这里的拦截器还可以对请求转发的视图资源做更改。例如,我们继续更改控制器类的postHandle方法:

    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("执行进入postHandle方法");
        //通过modelAndView参数改变显示的视图,或者修改发往视图的方法
        modelAndView.addObject("msg","被拦截器的postHandle方法修改后的视图数据");
        modelAndView.setViewName("/hello");

    }

将modelAndView的视图资源设置为hello页面,即成功登录页面\WEB-INF\jsps\hello.jsp

运行结果变为:

看吧,视图资源也能换。

 

afterCompletion方法有点像C++中的析构函数,afterCompletion方法在请求被响应之后最后执行,用于对一些资源的释放,对我们来说不是很常用。

 

下面我们来说明另外一个问题:如果存在多个拦截器,执行机制是怎么样的?

我们将定义两个拦截器TestController和TestController2

package com.happyBKs.interceptor;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Created by happyBKs on 2016/7/10.
 */
public class TestInterceptor implements HandlerInterceptor {
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("执行进入TestInterceptor的preHandle方法");
        return true;
    }

    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("执行进入TestInterceptor的postHandle方法");
        //通过modelAndView参数改变显示的视图,或者修改发往视图的方法
//        modelAndView.addObject("msg","被拦截器的postHandle方法修改后的视图数据");
//        modelAndView.setViewName("/hello");

    }

    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("执行进入TestInterceptor的afterCompletion方法");
    }
}
package com.happyBKs.interceptor;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Created by happyBKs on 2016/7/10.
 */
public class TestInterceptor2 implements HandlerInterceptor {
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("执行进入TestInterceptor2的preHandle方法");
        return true;
    }

    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("执行进入TestInterceptor2的postHandle方法");
        //通过modelAndView参数改变显示的视图,或者修改发往视图的方法
//        modelAndView.addObject("msg","被拦截器的postHandle方法修改后的视图数据");
//        modelAndView.setViewName("/hello");

    }

    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("执行进入TestInterceptor2的afterCompletion方法");
    }
}

之后在springMVC前端控制器配置文件\WEB-INF\configs\spring\viewSpace-dispatcher-servlet.xml中配置:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd">


    <!-- 本配置文件是工名为mvc-dispatcher的DispatcherServlet使用, 提供其相关的Spring MVC配置 -->

    <!-- 启用Spring基于annotation的DI, 使用户可以在Spring MVC中使用Spring的强大功能。 激活 @Required
        @Autowired,JSR 250's @PostConstruct, @PreDestroy and @Resource 等标注 -->
    <context:annotation-config />

    <!-- DispatcherServlet上下文, 只管理@Controller类型的bean, 忽略其他型的bean, 如@Service -->
    <context:component-scan base-package="com.happyBKs.controller">
        <context:include-filter type="annotation"  expression="org.springframework.stereotype.Controller" />
    </context:component-scan>

    <!-- HandlerMapping, 无需配置, Spring MVC可以默认启动。 DefaultAnnotationHandlerMapping
        annotation-driven HandlerMapping -->

    <!-- 扩充了注解驱动,可以将请求参数绑定到控制器参数 -->
    <mvc:annotation-driven />


    <!-- 静态资源处理, css, js, imgs -->
    <mvc:resources mapping="/resources/**" location="/resources/" />


    <!--<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping">-->
        <!--<property name="alwaysUseFullPath" value="true"></property>-->
    <!--</bean>-->

    <bean
            class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />
        <property name="prefix" value="/WEB-INF/jsps/" />
        <property name="suffix" value=".jsp" />
    </bean>


    <mvc:interceptors>
        <mvc:interceptor>
            <mvc:mapping path="/**"/>
            <bean class="com.happyBKs.interceptor.TestInterceptor"></bean>
        </mvc:interceptor>
        <mvc:interceptor>
            <mvc:mapping path="/**"/>
            <bean class="com.happyBKs.interceptor.TestInterceptor2"></bean>
        </mvc:interceptor>
    </mvc:interceptors>

</beans>

然后我们运行,请求http://localhost:8080/mvc/test2/login

控制台输出结果:

执行进入TestInterceptor的preHandle方法
执行进入TestInterceptor2的preHandle方法
进入控制器的login方法...
执行进入TestInterceptor2的postHandle方法
执行进入TestInterceptor的postHandle方法
执行进入TestInterceptor2的afterCompletion方法
执行进入TestInterceptor的afterCompletion方法

是不是有点糊涂,不要紧,看看这个执行机制的示意图:

打个比方说:比如你从上海去北京出差,有两个收费口,我们先经过,收费口1,然后经过2,达到北京,然后回来时先经过2,在经过1,并且,回来的过程中我告诉收费口把发票寄到上海家里,于是收费口2的发票先寄出,然后收费口1的再寄出。收费口就是拦截器,去北京的路上的收费口执行preHandle方法,到北京执行控制器方法,返程中收费口执行postHandle方法,开发票就是afterCompletion方法。

最后,在拦截器的实现方法上再补充一点,拦截器除了可以通过实现HandleInterceptor接口来完成,还有一种接口也可以——接口WebRequestInterCeptor。但是这种方法的preHandle方法没有返回值,因此不具有终止请求的功能。所以我还是推荐通过实现HandleInterceptor接口来完成。

 

 

 

SpringMVC拦截器的使用场景

使用原则:处理所有请求中的共性问题

1. 解决乱码问题

我们将控制器TestInterceptor重新整理一下,对preHandle方法中的request参数设置一下编码。

package com.happyBKs.interceptor;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Created by happyBKs on 2016/7/10.
 */
public class TestInterceptor implements HandlerInterceptor {
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("执行进入TestInterceptor的preHandle方法");
        request.setCharacterEncoding("utf-8");
        return true;
    }

    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("执行进入TestInterceptor的postHandle方法");
    }

    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("执行进入TestInterceptor的afterCompletion方法");
    }
}

原先项目web.xml中的过滤器我们注释掉。

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd " version="2.5">

  <display-name>Archetype Created Web Application</display-name>

  <!-- Spring应用上下文, 理解层次化的ApplicationContext -->
    <context-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>/WEB-INF/configs/spring/applicationContext*.xml</param-value>
    </context-param>

    <listener>
      <listener-class>
        org.springframework.web.context.ContextLoaderListener
      </listener-class>
    </listener>

   <!--DispatcherServlet, Spring MVC的核心-->
  <!--<servlet>-->
    <!--<servlet-name>mvc-dispatcher</servlet-name>-->
    <!--<servlet-class> org.springframework.web.servlet.DispatcherServlet</servlet-class>-->
    <!--&lt;!&ndash; DispatcherServlet对应的上下文配置, 默认为/WEB-INF/$servlet-name$-servlet.xml-->
     <!--&ndash;&gt;-->
    <!--<init-param>-->
      <!--<param-name>contextConfigLocation</param-name>-->
      <!--<param-value>/WEB-INF/configs/spring/mvc-dispatcher-servlet.xml</param-value>-->
    <!--</init-param>-->
    <!--<load-on-startup>1</load-on-startup>-->
  <!--</servlet>-->
  <!--<servlet-mapping>-->
    <!--<servlet-name>mvc-dispatcher</servlet-name>-->
    <!--&lt;!&ndash; mvc-dispatcher拦截所有的请求&ndash;&gt;-->
    <!--<url-pattern>/</url-pattern>-->
  <!--</servlet-mapping>-->


  <servlet>
    <servlet-name>viewSpace-dispatcher</servlet-name>
    <servlet-class> org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <!-- DispatcherServlet对应的上下文配置, 默认为/WEB-INF/$servlet-name$-servlet.xml
     -->
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>/WEB-INF/configs/spring/viewSpace-dispatcher-servlet.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>viewSpace-dispatcher</servlet-name>
    <!-- mvc-dispatcher拦截所有的请求-->
    <url-pattern>/test2/*</url-pattern>
  </servlet-mapping>


<!--  <filter>
    <filter-name>viewSpace-filter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
      <param-name>encoding</param-name>
      <param-value>utf8</param-value>
    </init-param>
  </filter>
  <filter-mapping>
    <filter-name>viewSpace-filter</filter-name>
    <url-pattern>/test2/*</url-pattern>
  </filter-mapping>-->

</web-app>

springMVC前端控制器配置文件\WEB-INF\configs\spring\viewSpace-dispatcher-servlet.xml也只保留一个拦截器注册:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd">


    <!-- 本配置文件是工名为mvc-dispatcher的DispatcherServlet使用, 提供其相关的Spring MVC配置 -->

    <!-- 启用Spring基于annotation的DI, 使用户可以在Spring MVC中使用Spring的强大功能。 激活 @Required
        @Autowired,JSR 250's @PostConstruct, @PreDestroy and @Resource 等标注 -->
    <context:annotation-config />

    <!-- DispatcherServlet上下文, 只管理@Controller类型的bean, 忽略其他型的bean, 如@Service -->
    <context:component-scan base-package="com.happyBKs.controller">
        <context:include-filter type="annotation"  expression="org.springframework.stereotype.Controller" />
    </context:component-scan>

    <!-- HandlerMapping, 无需配置, Spring MVC可以默认启动。 DefaultAnnotationHandlerMapping
        annotation-driven HandlerMapping -->

    <!-- 扩充了注解驱动,可以将请求参数绑定到控制器参数 -->
    <mvc:annotation-driven />


    <!-- 静态资源处理, css, js, imgs -->
    <mvc:resources mapping="/resources/**" location="/resources/" />


    <!--<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping">-->
        <!--<property name="alwaysUseFullPath" value="true"></property>-->
    <!--</bean>-->

    <bean
            class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />
        <property name="prefix" value="/WEB-INF/jsps/" />
        <property name="suffix" value=".jsp" />
    </bean>





    <mvc:interceptors>
        <mvc:interceptor>
            <mvc:mapping path="/**"/>
            <bean class="com.happyBKs.interceptor.TestInterceptor"></bean>
        </mvc:interceptor>
<!--        <mvc:interceptor>
            <mvc:mapping path="/**"/>
            <bean class="com.happyBKs.interceptor.TestInterceptor2"></bean>
        </mvc:interceptor>-->
    </mvc:interceptors>

</beans>

运行后请求http://localhost:8080/mvc/test2/login:

提交后控制输出:无乱码

 

执行进入TestInterceptor的afterCompletion方法
197415 [http-apr-8080-exec-10] DEBUG org.springframework.web.servlet.DispatcherServlet  - Successfully completed request
197415 [http-apr-8080-exec-10] DEBUG org.springframework.web.servlet.DispatcherServlet  - Successfully completed request
206359 [http-apr-8080-exec-1] DEBUG org.springframework.web.servlet.DispatcherServlet  - DispatcherServlet with name 'viewSpace-dispatcher' processing POST request for [/mvc/test2/viewAll]
206359 [http-apr-8080-exec-1] DEBUG org.springframework.web.servlet.DispatcherServlet  - DispatcherServlet with name 'viewSpace-dispatcher' processing POST request for [/mvc/test2/viewAll]
206360 [http-apr-8080-exec-1] DEBUG org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping  - Looking up handler method for path /viewAll
206360 [http-apr-8080-exec-1] DEBUG org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping  - Looking up handler method for path /viewAll
206360 [http-apr-8080-exec-1] DEBUG org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping  - Returning handler method [public org.springframework.web.servlet.ModelAndView com.happyBKs.controller.TestController2.viewAll(java.lang.String,java.lang.String)]
206360 [http-apr-8080-exec-1] DEBUG org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping  - Returning handler method [public org.springframework.web.servlet.ModelAndView com.happyBKs.controller.TestController2.viewAll(java.lang.String,java.lang.String)]
206360 [http-apr-8080-exec-1] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory  - Returning cached instance of singleton bean 'testController2'
206360 [http-apr-8080-exec-1] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory  - Returning cached instance of singleton bean 'testController2'
执行进入TestInterceptor的preHandle方法
206381 [http-apr-8080-exec-1] DEBUG org.springframework.web.cors.DefaultCorsProcessor  - Skip CORS processing: request is from same origin
206381 [http-apr-8080-exec-1] DEBUG org.springframework.web.cors.DefaultCorsProcessor  - Skip CORS processing: request is from same origin
进入控制器的viewAll方法...
name=马云
pwd=123
执行进入TestInterceptor的postHandle方法
206404 [http-apr-8080-exec-1] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory  - Invoking afterPropertiesSet() on bean with name '/hello'
206404 [http-apr-8080-exec-1] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory  - Invoking afterPropertiesSet() on bean with name '/hello'
206405 [http-apr-8080-exec-1] DEBUG org.springframework.web.servlet.DispatcherServlet  - Rendering view [org.springframework.web.servlet.view.JstlView: name '/hello'; URL [/WEB-INF/jsps//hello.jsp]] in DispatcherServlet with name 'viewSpace-dispatcher'
206405 [http-apr-8080-exec-1] DEBUG org.springframework.web.servlet.DispatcherServlet  - Rendering view [org.springframework.web.servlet.view.JstlView: name '/hello'; URL [/WEB-INF/jsps//hello.jsp]] in DispatcherServlet with name 'viewSpace-dispatcher'
206405 [http-apr-8080-exec-1] DEBUG org.springframework.web.servlet.view.JstlView  - Forwarding to resource [/WEB-INF/jsps//hello.jsp] in InternalResourceView '/hello'
206405 [http-apr-8080-exec-1] DEBUG org.springframework.web.servlet.view.JstlView  - Forwarding to resource [/WEB-INF/jsps//hello.jsp] in InternalResourceView '/hello'
执行进入TestInterceptor的afterCompletion方法

看到了吧,拦截器方法可以对请求的数据做很多设置和修改,同样,也可以对响应的数据的编码等做修改。

 

2. 解决权限验证问题

比如,我们现在需要一个拦截器,专门用来做权限验证,拦截器会在preHandle方法检查服务服务器session是否有该用户的会话,如果有则继续执行,如果没有则将响应重定向到登录页面。

这个部分因为在大部分业务模块中都需要先行完成,如果把这样一个共性的东西添加到各个业务模块,整个系统的代码质量和可维护性可想而知有多糟,这正是拦截器的用武之地和使用原则。

好,我们就试着写个代码示例,在preHandle方法中:

package com.happyBKs.interceptor;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Created by happyBKs on 2016/7/10.
 */
public class TestInterceptor implements HandlerInterceptor {
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("执行进入TestInterceptor的preHandle方法");
        request.setCharacterEncoding("utf-8");
        //对用户是否登录进行判断
        if(request.getSession().getAttribute("user")==null){
            //如果用户没有回话,即没有登录,就终止请求,并发送到登录页面
            request.getRequestDispatcher("/test2/login").forward(request,response);//发送到登录页面
            return false;//终止请求
        }
        return true;
    }

    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("执行进入TestInterceptor的postHandle方法");
        //通过modelAndView参数改变显示的视图,或者修改发往视图的方法
//        modelAndView.addObject("msg","被拦截器的postHandle方法修改后的视图数据");
//        modelAndView.setViewName("/hello");

    }

    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("执行进入TestInterceptor的afterCompletion方法");
    }
}

好,我们尝试运行,并随便请求一个能给该拦截器拦截的url,如http://localhost:8080/mvc/test2/msg。这时候我们是没有登录过的,看看会发生什么。我们的预想是preHandle方法将验证到我们sessioin会话为空,然后跳转到登录页面。然而,恐怖的一幕发生了,页面死住,控制台开始疯狂套异常和死循环。。。

这是为什么呢?原来,我们的登录页面url /test2/login也在拦截器的拦截返回内,当我们请求http://localhost:8080/mvc/test2/msg,拦截器preHandle方法检查到了我们会话为空没有登录,然后请求被终止的同时转而请求同样在拦截器作用返回内的/test2/login,这时候死循环的故事就开始了,懂了吧。因此,拦截器的使用需要十分留心,拦截器的方法中在对请求进行转发时尤为要注意,请求转发不能是该拦截器,否则就会出现死循环。当然,还有一种情况,就是多个拦截器作用下的多个url相互转发请求,造成多个拦截器之间的死循环。

好,这里我们增加一个公共jsp页面\loginPub.jsp:页面内容不再详述

拦截器代码改为:

public class TestInterceptor implements HandlerInterceptor {
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("执行进入TestInterceptor的preHandle方法");
        request.setCharacterEncoding("utf-8");
        //对用户是否登录进行判断
        if(request.getSession().getAttribute("user")==null){
            //如果用户没有回话,即没有登录,就终止请求,并发送到登录页面
            //request.getRequestDispatcher("/test2/login").forward(request,response);//发送到登录页面
            request.getRequestDispatcher("/loginPub.jsp").forward(request,response);//发送到登录页面
            return false;//终止请求
        }
        return true;
    }

运行结果:

请求http://localhost:8080/mvc/test2/msg:

控制台输出显示,执行到了拦截器的preHandle方法之后就没有了,因为拦截器检测到没有登录,所以讲请求转发到了登录页面\loginPub.jsp

 

 

 

 

 

© 著作权归作者所有

共有 人打赏支持
HappyBKs

HappyBKs

粉丝 614
博文 244
码字总数 467954
作品 0
浦东
程序员
加载中

评论(3)

SVD
SVD
666
小九酒
小九酒
two
大掌柜的
大掌柜的
79
后台开发常问面试题集锦(问题搬运工,附链接)

Java基础问题 String的’+’的性能及原理 java之yield(),sleep(),wait()区别详解-备忘笔记 深入理解Java Stream流水线 抽象 & abstract关键字 Java final 修饰符知识点总结(必看篇) Java中的...

大黄有故事 ⋅ 2017/11/18 ⋅ 0

JAVA学习笔记22——SpingMVC框架第二章

JAVA学习笔记22——SpingMVC框架第二章 Harries Blog™2017-12-210 阅读 ACESpringAppbeancatActionAjax 昨天讲解了 SpringMVC 的入门,今天来讲讲进阶一点的知识,话不多说,请看下文。 今天...

Harries Blog™ ⋅ 2017/12/21 ⋅ 0

Java系列文章(全)

JVM JVM系列:类装载器的体系结构 JVM系列:Class文件检验器 JVM系列:安全管理器 JVM系列:策略文件 Java垃圾回收机制 深入剖析Classloader(一)--类的主动使用与被动使用 深入剖析Classloader(二...

www19 ⋅ 2017/07/04 ⋅ 0

Spring AOP 日志拦截器的事务管理

如果要在方法执行前或后或抛出异常后加上一个自己的拦截器,或者一个环绕拦截器,在拦截器中执行一些操作,比如执行一些数据库操作,记录一些信 息,这些操作通过调用一个服务类的方法来执行...

哲别0 ⋅ 05/18 ⋅ 0

【spring boot 系列】spring security 实践 + 源码分析

前言 本文将从示例、原理、应用3个方面介绍 spring data jpa。 以下分析基于spring boot 2.0 + spring 5.0.4版本源码 概述 Spring Security 是一个能够为基于 Spring 的企业应用系统提供声明...

java高级架构牛人 ⋅ 06/06 ⋅ 0

早前学习Java记录

Spring 对 iBATIS 的支持】 Spring 通过 DAO 模式,提供了对 iBATIS 的良好支持。 SqlMapClient:是 iBATIS 中的主要接口,通过 xml 配置文件可以让 Spring 容器来管理 SqlMapClient 对象的创...

大风厂蔡成功 ⋅ 2016/07/10 ⋅ 0

SpringMvc基础知识

1.SpringMvc是什么 Spring Web MVC是一种基于Java的实现了Web MVC设计模式的请求驱动类型的轻量级Web框架,即使用了MVC架构模式的思想,将web层进行职责解耦,基于请求驱动指的就是使用请求-...

王念博客 ⋅ 2016/03/27 ⋅ 0

JAVA学习笔记21——SpingMVC框架第一章

JAVA学习笔记21——SpingMVC框架第一章 Harries Blog™2017-12-210 阅读 ACESpringAppcatapachebeanAOPAction 前面学习了MyBatis的相关知识,今天带大家一起来学习一下关于表现层的另一个框架...

Harries Blog™ ⋅ 2017/12/21 ⋅ 0

springMVC笔记系列(18)——配置文件细节详解

本文着重说说springMVC项目中各个配置文件的一些细节。 web.xml文件是web应用的部署描述。 在上一节的springMVC示例中 ,idea下的Maven-webapp项目自动生成了web.xml文件,用的是webapp2.3的...

HappyBKs ⋅ 2016/06/14 ⋅ 0

springboot学习(二)——springmvc配置使用

以下内容,如有问题,烦请指出,谢谢 上一篇讲解了springboot的helloworld部分,这一篇开始讲解如何使用springboot进行实际的应用开发,基本上寻着spring应用的路子来讲,从springmvc以及web...

嘻哈开发者 ⋅ 04/26 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

OSChina 周三乱弹 —— 这样的女人私生活太混乱了

Osc乱弹歌单(2018)请戳(这里) 【今日歌曲】 @ 胖达panda :你经历过体验到人生的大起大落吗?我一朋友在10秒内体验了,哈哈。@小小编辑 请点一首《almost lover》送给他。 《almost love...

小小编辑 ⋅ 48分钟前 ⋅ 7

自己动手写一个单链表

文章有不当之处,欢迎指正,如果喜欢微信阅读,你也可以关注我的微信公众号:好好学java,获取优质学习资源。 一、概述 单向链表(单链表)是链表的一种,其特点是链表的链接方向是单向的,对...

公众号_好好学java ⋅ 53分钟前 ⋅ 0

Centos7重置Mysql 8.0.1 root 密码

问题产生背景: 安装完 最新版的 mysql8.0.1后忘记了密码,向重置root密码;找了网上好多资料都不尽相同,根据自己的问题总结如下: 第一步:修改配置文件免密码登录mysql vim /etc/my.cnf 1...

豆花饭烧土豆 ⋅ 今天 ⋅ 0

熊掌号收录比例对于网站原创数据排名的影响[图]

从去年下半年开始,我在写博客了,因为我觉得业余写写博客也还是很不错的,但是从2017年下半年开始,百度已经推出了原创保护功能和熊掌号平台,为此,我也提交了不少以前的老数据,而这些历史...

原创小博客 ⋅ 今天 ⋅ 0

LVM讲解、磁盘故障小案例

LVM LVM就是动态卷管理,可以将多个硬盘和硬盘分区做成一个逻辑卷,并把这个逻辑卷作为一个整体来统一管理,动态对分区进行扩缩空间大小,安全快捷方便管理。 1.新建分区,更改类型为8e 即L...

蛋黄Yolks ⋅ 今天 ⋅ 0

Hadoop Yarn调度器的选择和使用

一、引言 Yarn在Hadoop的生态系统中担任了资源管理和任务调度的角色。在讨论其构造器之前先简单了解一下Yarn的架构。 上图是Yarn的基本架构,其中ResourceManager是整个架构的核心组件,它负...

p柯西 ⋅ 今天 ⋅ 0

uWSGI + Django @ Ubuntu

创建 Django App Project 创建后, 可以看到路径下有一个wsgi.py的问题 uWSGI运行 直接命令行运行 利用如下命令, 可直接访问 uwsgi --http :8080 --wsgi-file dj/wsgi.py 配置文件 & 运行 [u...

袁祾 ⋅ 今天 ⋅ 0

JVM堆的理解

在JVM中,我们经常提到的就是堆了,堆确实很重要,其实,除了堆之外,还有几个重要的模块,看下图: 大 多数情况下,我们并不需要关心JVM的底层,但是如果了解它的话,对于我们系统调优是非常...

不羁之后 ⋅ 昨天 ⋅ 0

推荐:并发情况下:Java HashMap 形成死循环的原因

在淘宝内网里看到同事发了贴说了一个CPU被100%的线上故障,并且这个事发生了很多次,原因是在Java语言在并发情况下使用HashMap造成Race Condition,从而导致死循环。这个事情我4、5年前也经历...

码代码的小司机 ⋅ 昨天 ⋅ 2

聊聊spring cloud gateway的RetryGatewayFilter

序 本文主要研究一下spring cloud gateway的RetryGatewayFilter GatewayAutoConfiguration spring-cloud-gateway-core-2.0.0.RC2-sources.jar!/org/springframework/cloud/gateway/config/G......

go4it ⋅ 昨天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部