2.学习ActiveMQ-安装与启动

2020-05-06   龙德   ActiveMQ   ActiveMQ  

安装

从官网 http://activemq.apache.org 可以下载 ActiveMQ,现在最新版本是 5.x,我们下载 Windows 平台的。

image

image

image

ActiveMQ 各个目录的作用:

bin:ActiveMQ的启动脚本activemq.bat,注意分32、64位。

conf:配置文件,重点关注的是activemq.xml、jetty.xml、jetty-realm.properties。在登录ActiveMQ Web控制台需要用户名、密码信息;在JMS CLIENT和ActiveMQ进行何种协议的连接、端口是什么等这些信息都在上面的配置文件中可以体现。

data:ActiveMQ进行消息持久化存放的地方,默认采用的是kahadb,当然我们可以采用leveldb,或者采用JDBC存储到MySQL,或者干脆不使用持久化机制。

webapps:ActiveMQ自带Jetty提供Web管控台。

lib:ActiveMQ为我们提供了分功能的JAR包,当然也提供了activemq-all-5.14.4.jar。

启动

进入 bin/win64 目录,直接运行 activemq.bat,就可以启动 ActiveMQ。

image

访问首页:http://localhost:8161/index.html

image

看到这个页面就说明已经成功启动 ActiveMQ 了。

访问控制台:http://localhost:8161/admin/index.jsp

访问控制台需要输入账号和密码,账号和密码配置在 conf 目录下的 jetty-realm.properties 文件里。

image

端口号配置在 jetty.xml 文件里。

image

输入账号密码就可以访问控制台了。

image

至此,ActiveMQ 的安装和启动就完成了。

1.学习ActiveMQ-关于JMS

2020-05-05   龙德   ActiveMQ   ActiveMQ  

JMS的概念

百度百科的描述:

JMS即Java消息服务(Java Message Service)应用程序接口,是一个Java平台中关于面向消息中间件(MOM)的API,用于在两个应用程序之间,或分布式系统中发送消息,进行异步通信。

看到 “接口” 两个字,下意识就想到了规范。没错,JMS 和 JDBC、JPA 一样,只是一个规范,也就是只给出了接口,具体的内容由各个厂商去实现。

image

JMS的组成

image

从上图可以看出,JMS 的组成有4个部分,它们分别是:

  • JMS provider:实现 JMS 接口规范的消息中间件,也就是 MQ 服务器
  • JMS producer:消息生产者,创建和发送JMS消息的客户端应用
  • JMS consumer:消息消费者,接收和处理JMS消息的客户端应用
  • JMS message:消息头、消息属性、消息体

JMS的实现产品

MQ 产品有很多种,以下是最常见的 4 种:

image

我们接下来学习的 ActiveMQ 就是 apache 出品的一个非常流行的消息中间件。

解析前后端交互的多种数据类型

2020-05-01   龙德   Java   Content-Type  

在 HTTP 协议的请求头中,有一个很重要的属性:Content-Type,它的作用是:

  • 作为请求:告诉服务器,客户端发送的数据类型。

  • 作为响应:告诉客户端,服务器响应的数据类型。

客户端或者服务器拿到数据后,通过该数据类型,就可以正确的解析数据。

由于 GET 请求不存在请求体部分,它的参数都是拼接在 URL 尾部,浏览器把数据转换成一个字符串(key1=value1&key2=value2…),然后把这个字符串追加到 URL 后面,用 ? 分割,因此 GET 请求的请求头不需要设置 Content-Type 字段。

所以下面我们所讲的都是针对 POST 请求而言。

Content-Type 属性的值有很多种,下面列举几种最常见的:

application/x-www-form-urlencoded

这是最常见的数据类型,前端表单默认使用的就是这种类型。

比如有这么一个登录表单:

<form action="login.do" enctype="application/x-www-form-urlencoded" method="post">
    <input type="text" name="username" />
    <input type="password" name="password" />
    <button type="submit">登录</button>
</form>

注意 “enctype” 属性,该属性规定在发送到服务器之前,浏览器应该如何对表单数据进行编码。默认是 “application/x-www-form-urlencoded”。

当我们点击 “登录” 按钮时,浏览器会将所有内容进行编码,拼接成 “key=value” 的格式,也就是键值对的格式。多个键值对之间用 “&” 分割。

当我们提交时,在 Chrome 浏览器中,可以看到请求体的数据格式是这样子的:

image

可以看到浏览器帮我们拼接成了键值对的格式,key 就是输入框的 name,而 value 就是我们输入的内容。

当服务器接收到数据后,根据 Content-Type 属性获取到数据类型,就能正确的解析数据。

接下来是后台代码:

@RequestMapping(value = "/login.do", method = RequestMethod.POST)
public String login(@RequestParam(value = "username") String username,
          @RequestParam(value = "password") String password) {
  return "index.jsp";
}

当 Spring MVC 收到客户端发送来的数据时,根据 Content-Type 声明的数据类型,通过使用 HandlerAdapter 配置的 HttpMessageConverters 来解析请求体中的数据,然后绑定到相应的 bean 上,bean 的类型、个数和顺序跟我们方法声明的参数是一样的。这样后台就接收到了数据。

image

application/json

这也是一种很常见的数据类型,这种类型的数据是序列化后的 JSON 字符串。

比如有这么一个登录表单:

<form>
    <input type="text" name="username" />
    <input type="password" name="password" />
    <button type="button">登录</button>
</form>

后台代码如下:

@RequestMapping(value = "/login.do", method = RequestMethod.POST)
@ResponseBody
public String login(@RequestBody User user) {
  return "login success.";
}

处理 application/json 编码的数据时,必须使用 @RequestBody 注解,Spring MVC 通过使用 HandlerAdapter 配置的 HttpMessageConverters 来解析请求体中的数据,然后绑定到 @RequestBody 注解的对象上。

AJAX 代码如下:

var username = $("input[name='username']").val();
var password = $("input[name='password']").val();
var data = {
    username: username,
    password: password
}
$.ajax({
  type: "post",
  url: "/login.do",
  contentType: "application/json",
  data: JSON.stringify(data),
  success: function(data){
    
  },
  error: function(data){

  }
});

JQuery 的 AJAX 默认传递的是 application/x-www-form-urlencoded 类型的数据,如果想传递 JSON 类型,则需指定 contentType: application/json。

由于 application/json 类型的数据是序列化后的 JSON 字符串,所以我们也必须手动把 JSON 对象序列化成字符串,我们可通过 JSON.stringify(data) 方法进行序列化。

通过 Chrome 浏览器可以看到请求体的数据格式是这样子的:

image

multipart/form-data

在最初的 http 协议中,没有上传文件方面的功能。后来为了支持文件上传,提高二进制文件的传输效率,新增了 multipart/form-data 数据类型。

比如有这么一个登录表单:

<form action="login.do" enctype="multipart/form-data" method="post">
    <input type="text" name="username" />
    <input type="password" name="password" />
    <input type="file" name="avatar">
    <button type="submit">登录</button>
</form>

表单的 enctype 属性必须指定为 multipart/form-data,否则不能上传文件。

后台代码如下:

@RequestMapping(value = "/login.do", method = RequestMethod.POST)
public String login(String username, String password, MultipartFile avatar) {
  return "index.jsp";
}

MultipartFile 这个类是用来接受前台传过来的文件。

重点是 multipart/form-data 数据类型的请求体,这种数据类型的请求体跟前面两个完全不一样。

通过 Chrome 浏览器我们可以看到请求体的数据格式,请看下图:

image

首先先看 Content-Type 属性,它的值是:multipart/form-data; boundary=—-WebKitFormBoundaryxJ5HRAtPAwUo1RsG

multipart/form-data 是我们在表单里指定的数据类型,这个是必须的。

那么这个 boundary=—-WebKitFormBoundaryxJ5HRAtPAwUo1RsG 又是什么呢?

boundary 是边界符,作用是用来分割多个表单项和文件。它是浏览器自动帮我们加上的,边界符的值不是固定的,可以随便取,边界符也是必须的。

通过上图,我们来分析一下请求体的数据结构。

首先先看一个公式:

分割符 = "--" + 边界符

结束符 = "--" + 边界符 + "--"

根据上面的公式,我们可以得出分割符和结束符的值:


分割符:------WebKitFormBoundaryxJ5HRAtPAwUo1RsG

结束符:------WebKitFormBoundaryxJ5HRAtPAwUo1RsG--

先分析第一个表单项:

------WebKitFormBoundary8vr5fnZ5TkqDW6kZ
Content-Disposition: form-data; name="username"

zhangsan

第一行是分割符,但是仅仅有分割符是不够的,这样的数据格式还是不对,服务器还是无法解析数据。通过查资料得知,分割符必须以回车符(\r)+换行符(\n)结尾。

也就是说,第一行的内容其实是:分割符 + 回车符 + 换行符。

------WebKitFormBoundaryxJ5HRAtPAwUo1RsG\r\n

接下来看第二行,第二行的作用是告诉服务器对应字段(表单)的相关信息,第二行也必须以 “回车符 + 换行符” 结尾。所以第二行的内容其实是:

Content-Disposition: form-data; name="username"\r\n

接下来看第三行,第三行虽然没有显示任何内容,但第三行的内容其实是 “回车符 + 换行符”,只是不显示而已。

也就是说第三行的内容其实是:

\r\n

接下来看第四行,第四行是我们输入的表单值了,同样必须以 “回车符 + 换行符” 结尾。

也就是说第四行的内容其实是:

zhangsan\r\n

综上所述,一个完整的文本格式的数据结构应该是这样的:

------WebKitFormBoundary8vr5fnZ5TkqDW6kZ\r\nContent-Disposition: form-data; name="username"\r\n\r\nzhangsan\r\n

接下来再分析一下文件的内容:

------WebKitFormBoundary8vr5fnZ5TkqDW6kZ
Content-Disposition: form-data; name="avatar"; filename=""
Content-Type: application/octet-stream


文件只比文本多了一行 “Content-Type: application/octet-stream” 内容,但其实两者的数据结构其实是一样的:

------WebKitFormBoundary8vr5fnZ5TkqDW6kZ\r\nContent-Disposition: form-data; name="avatar"; filename=""\r\nContent-Type: application/octet-stream\r\n\r\n(文件的二进制数据)\r\n

最后一行是结束符,结束符也必须以 “回车符 + 换行符” 结尾。

所以最后一行的内容其实是:

------WebKitFormBoundary8vr5fnZ5TkqDW6kZ--\r\n

综上所述,请求体的完整数据结构如下图:

image

Java中比较特殊的三个浮点数Infinity、-Infinity、NaN

2020-04-27   龙德   Java   Java  

学过 JavaScript 的应该都知道,在 js 中的数值型 number 类型中有几个特殊的数,一个正无穷大、一个负无穷大、一个不是一个数 NaN。

后来无意中发现 java 中也有这三个数,不过这三个数是浮点数,不是整数。只有在浮点数中这这三个数才成立。看图分析:

正无穷 Infinity

image

负无穷 -Infinity

image

不是一个数 NaN

image

无穷乘以 0,结果为 NaN。

System.out.println(Float.POSITIVE_INFINITY * 0); // NaN
System.out.println(Float.NEGATIVE_INFINITY * 0); // NaN

无穷除以 0,结果还是无穷。

System.out.println(Float.POSITIVE_INFINITY / 0); // Infinity
System.out.println(Float.NEGATIVE_INFINITY / 0); // -Infinity

无穷做任何运算(除了乘以 0),结果还是无穷。

System.out.println(Float.POSITIVE_INFINITY + 10000); // Infinity
System.out.println(Float.POSITIVE_INFINITY - 10000); // Infinity
System.out.println(Float.POSITIVE_INFINITY * 10000); // Infinity
System.out.println(Float.POSITIVE_INFINITY / 10000); // Infinity

System.out.println(Float.NEGATIVE_INFINITY + 10000); // -Infinity
System.out.println(Float.NEGATIVE_INFINITY - 10000); // -Infinity
System.out.println(Float.NEGATIVE_INFINITY * 10000); // -Infinity
System.out.println(Float.NEGATIVE_INFINITY / 10000); // -Infinity

判断一个浮点数是否为无穷,可用 isInfinite() 方法。

System.out.println(Float.isInfinite(Float.POSITIVE_INFINITY)); // true
System.out.println(Float.isInfinite(Float.NEGATIVE_INFINITY)); // true

判断一个浮点数是否为 NaN,可用 isNaN() 方法。

System.out.println(Float.isNaN(Float.NaN)); // true
System.out.println(Double.isNaN(Double.NaN)); // true

NaN 表示非数字,它与任何值都不相等,甚至不等于它自己。

System.out.println(Float.NaN == Float.NaN); // false
System.out.println(Double.NaN == Double.NaN); // false

参考资料:

https://www.cnblogs.com/zhizhixiaoxia/p/10756317.html

https://www.cnblogs.com/zhisuoyu/archive/2016/03/24/5314541.html

SpringSecurity用户认证的流程

2020-04-02   龙德   SpringSecurity   SpringSecurity  

认证流程如下图所示:

image

根据流程图实现的伪代码:

1.认证拦截器

/**
     * 用于处理来自表单提交的认证拦截器,Spring Security 会默认提供
     * <p> 父类已经实现了 doFilter() 方法,我们只需要实现 attemptAuthentication() 方法,
     * 进行身份认证就可以了
     * 
     */
class MyUsernamePasswordAuthenticationFilter extends AbstractAuthenticationProcessingFilter {

    protected MyUsernamePasswordAuthenticationFilter(String defaultFilterProcessesUrl) {
        super(defaultFilterProcessesUrl);
    }

    // 执行实际的身份认证
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
            throws AuthenticationException, IOException, ServletException {
        
        // 客户端提交的用户名
        String username = request.getParameter("username");
        // 客户端提交的密码
        String password = request.getParameter("password");
        
        // 封装成一个未认证的 Authentication 对象
        UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
                username, password);
        // 调用认证管理器进行认证,返回一个已经认证的 Authentication 对象
        return this.getAuthenticationManager().authenticate(authRequest);
    }
    
}

2.认证管理器

/**
 * 认证管理器,Spring Security 会默认提供
 */
class MyAuthenticationManager implements AuthenticationManager {

    // 认证提供者(可以有多个)
    private List<AuthenticationProvider> providers = Collections.emptyList();
    
    public MyAuthenticationManager(List<AuthenticationProvider> providers) {
        this.providers = providers;
    }

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        Class<? extends Authentication> toTest = authentication.getClass();
        Authentication result = null;
        for (AuthenticationProvider provider : providers) {
            if (!provider.supports(toTest)) {
                continue;
            }
            // 调用认证提供者进行认证,如果 result 不为 null ,说明认证通过
            result = provider.authenticate(authentication);
            if (result != null) {
                break;
            }
        }
        if (result == null) {
            throw new ProviderNotFoundException("ProviderManager.providerNotFound");
        }
        return result;
    }

}

3.认证提供者

/**
 * 认证提供者,对用户的信息进行认证,并返回一个完整的 Authentication 对象,Spring Security 会默认提供
 * 
 * <p>认证提供者需要实现 AuthenticationProvider 接口,该接口定义了一个认证的方法 authenticate()。
 * <p>由于 Spring Security 提供了一个抽象类 AbstractUserDetailsAuthenticationProvider,
 * 并且它已经实现了 authenticate() 方法,所以我们不需要自己实现,直接继承这个抽象类即可。
 * 
 */
class MyAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {

    // 认证的 Service
    private UserDetailsService userDetailsService;
    
    // 密码认证处理器
    private PasswordEncoder passwordEncoder;
    
    public MyAuthenticationProvider(UserDetailsService userDetailsService, PasswordEncoder passwordEncoder) {
        this.userDetailsService = userDetailsService;
        this.passwordEncoder = passwordEncoder;
    }

    /**
     * 这个方法用于添加额外的检查功能,我们不需要添加,所以空着即可
     */
    @Override
    protected void additionalAuthenticationChecks(UserDetails userDetails,
            UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
        
    }

    /**
     * 这个方法很重要,用于认证用户提供的信息是否正确,
     * 并且返回一个 UserDetails 对象,父类的 authenticate() 方法会用到这个对象
     */
    @Override
    protected UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication)
            throws AuthenticationException {
        // 调用认证服务接口,加载 UserDetails 对象
        UserDetails userDetails = userDetailsService.loadUserByUsername(username);
        if (userDetails == null) {
            throw new UsernameNotFoundException(username);
        }
        // 判断用户名和密码是否正确,如果正确直接返回
        if (userDetails.getUsername().equals(authentication.getPrincipal().toString()) 
                && passwordEncoder.isPasswordValid(userDetails.getPassword(), authentication.getCredentials().toString(), null)) {
            return userDetails;
        }
        throw new BadCredentialsException("username: " + username + ", credentials: " + authentication.getCredentials());
    }

}

4.认证服务

/**
 * 认证服务,一般需要我们提供
 * 
 * <p>认证服务需要实现 UserDetailsService 接口,该接口只定义了一个方法 loadUserByUsername()
 * <p>这里就是我们的认证逻辑了,可以根据 username 去数据库查找用户的信息,角色以及权限,然后包装成 UserDetails 对象,
 * 供后续使用
 */
class MyUserDetailsService implements UserDetailsService {

    /**
     * 这里应该查数据库来组装 UserDetails 对象,
     * 为了演示方便我直接写死了
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        // 存放用户的角色或者权限
        List<GrantedAuthority> authorities = new ArrayList<>();
        
        // 角色的名字以 "ROLE_" 开头
        SimpleGrantedAuthority authority1 = new SimpleGrantedAuthority("ROLE_ADMIN");
        // 权限的名字没有限制,起啥都行
        SimpleGrantedAuthority authority2 = new SimpleGrantedAuthority("read");
        
        // 添加角色
        authorities.add(authority1);
        // 添加权限
        authorities.add(authority2);
        
        // 构造 User 对象并返回,构造参数的含义注释都有写
        return new org.springframework.security.core.userdetails.User(username, "64c8b1e43d8ba3115ab40bcea57f010b", true, true, true,
                true, authorities);
        
    }
    
}

5.密码认证处理器

/**
 * 密码认证处理器,如果你数据库里的密码是加密的话,还需要提供一个密码认证处理器
 * 
 * <p>构造方法接收一个 String 类型的参数,用来指定加密的类型
 * <p>也就是说你数据库里的密码是用什么加密的,这里就传入什么,后面的认证提供者会用到
 */
class MyMessageDigestPasswordEncoder extends MessageDigestPasswordEncoder {

    public MyMessageDigestPasswordEncoder(String algorithm) {
        super(algorithm);
    }

    /**
     * @param encPass 数据库密码(通常是加密的)
     * @param rawPass 前端传送过来的密码(通常是明文的)
     * @param salt 盐值
     */
    @Override
    public boolean isPasswordValid(String encPass, String rawPass, Object salt) {
        // 此处应该根据你自己的加密规则来校验密码
        
        // 为了演示方便,我这里只简单的进行比较
        return Objects.equals(encPass, rawPass);
    }
    
}

伪代码执行的流程图如下:

image

调用堆栈如下:

image

SpringSecurity认证失败后,转发到登录页面,造成无限循环

2020-03-17   龙德   SpringSecurity   SpringSecurity  

SpringSecurity 踩坑了,遇到了无限循环认证的问题。具体是认证失败后,我想转发到登录页面,并且返回一些提示信息,结果就踩坑了。

我是 SpringMVC 4.1.7,SpringSecurity 4.0.4

这是 xml 配置

<security:form-login login-page="/admin/login"
    login-processing-url="/admin/login"
    username-parameter="username"
    password-parameter="password" />

我配置了登录页面是 /admin/login,登录表单提交的地址是 /admin/login

当认证失败时,SpringSecurity 默认的失败处理器是 SimpleUrlAuthenticationFailureHandler 类,它默认采用重定向的方式,重定向到 login-page 设置的 URL,也就是重定向到登录页面。

然后我想在认证失败后,根据失败信息返回给客户端一些友好的提示。但是采用重定向的方式,我的提示信息就丢失了,客户端无法收到提示信息。所以我想将重定向的方式改成转发的方式。

这是我改后的 xml 配置

<security:form-login login-page="/admin/login"
    login-processing-url="/admin/login"
    username-parameter="username"
    password-parameter="password" 
    authentication-failure-handler-ref="simpleUrlAuthenticationFailureHandler"/>
    
<bean id="simpleUrlAuthenticationFailureHandler" 
    class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">
    <constructor-arg name="defaultFailureUrl" value="/admin/login"></constructor-arg>
    <property name="useForward" value="true" />
</bean>

我手动配置了 SimpleUrlAuthenticationFailureHandler 类,将 useForward 属性设置为 true,表示启用转发的方式。defaultFailureUrl 属性设置为 /admin/login,表示转发到登录页面。这里说明一下,defaultFailureUrl 属性是认证失败后,重定向或者转发的 URL。如果不设置,默认是 login-page 的值。

重启后发现果然采用了转发的方式,但是这样又遇到了一个问题,转发到 /admin/login 之后,又进入了认证的方法,认证失败后又转发到 /admin/login,然后又进入认证的方法,一直无限循环下去。。。

仔细想了一下,应该是我的配置出了问题

我配置的登录页是 /admin/login,请求方法是 GET,登录表单提交的地址是 /admin/login,请求方式是 POST,也就是说登录页面和登录提交的地址都是同一个,唯一的区别是请求方法不同。

转发之前的地址是 admin/login,请求方法是 POST,由于转发是同一个请求,它的请求方法不会变,所以转发的请求方法也是 POST。又由于我设置的转发 URL 是admin/login,所以转发之后又会进入认证的方法,相当于重新提交了登录表单。所以无论如何都不会进入到登录页面,所以就会造成无限循环。

这里也特别说明一下,为什么默认的重定向不会造成无限循环。因为重定向是客户端的行为,相当于客户端重新发起请求,并且重定向肯定是 GET 请求,所以会正常进入到登录页面,不会造成无线循环。

最后的 xml 配置如下:

<security:form-login login-page="/admin/login.jsp"
    login-processing-url="/admin/login"
    username-parameter="username"
    password-parameter="password" 
    authentication-failure-handler-ref="simpleUrlAuthenticationFailureHandler"/>
    
<bean id="simpleUrlAuthenticationFailureHandler" 
    class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">
    <constructor-arg name="defaultFailureUrl" value="/admin/login.jsp"></constructor-arg>
    <property name="useForward" value="true" />
</bean>

结论:login-pagelogin-processing-url 最好不要设置相同的 URL

SpringMVC集成SpringSecurity报错-java.lang.NoSuchMethodError-org.springframework.uti.AntPathMatcher.setCaseSensitive(Z)V

2020-03-17   龙德   SpringSecurity   SpringSecurity  

我的 SpringMVC 版本是 4.1.7,SpringSecurity 版本是 4.1.5

在 SpringMVC 里集成 SpringSecurity 后,运行出错:

Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'securityFilterChainRegistration' defined in class path resource [org/springframework/boot/autoconfigure/security/SpringBootWebSecurityConfiguration.class]: Unsatisfied dependency expressed through constructor argument with index 0 of type [javax.servlet.Filter]: : Error creating bean with name 'org.springframework.security.filterChainProxy': Cannot resolve reference to bean 'org.springframework.security.filterChains' while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.security.filterChains': Cannot resolve reference to bean 'org.springframework.security.web.DefaultSecurityFilterChain#0' while setting bean property 'sourceList' with key [0]; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.security.web.DefaultSecurityFilterChain#0': Cannot create inner bean '(inner bean)#3da73b75' of type [org.springframework.security.web.util.matcher.AntPathRequestMatcher] while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name '(inner bean)#3da73b75': Bean instantiation via constructor failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.security.web.util.matcher.AntPathRequestMatcher]: Constructor threw exception; nested exception is java.lang.NoSuchMethodError: org.springframework.util.AntPathMatcher.setCaseSensitive(Z)V; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.security.filterChainProxy': Cannot resolve reference to bean 'org.springframework.security.filterChains' while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.security.filterChains': Cannot resolve reference to bean 'org.springframework.security.web.DefaultSecurityFilterChain#0' while setting bean property 'sourceList' with key [0]; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.security.web.DefaultSecurityFilterChain#0': Cannot create inner bean '(inner bean)#3da73b75' of type [org.springframework.security.web.util.matcher.AntPathRequestMatcher] while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name '(inner bean)#3da73b75': Bean instantiation via constructor failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.security.web.util.matcher.AntPathRequestMatcher]: Constructor threw exception; nested exception is java.lang.NoSuchMethodError: org.springframework.util.AntPathMatcher.setCaseSensitive(Z)V
    at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:749)
    at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:464)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1119)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1014)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:504)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:476)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:303)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:299)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
    at org.springframework.boot.context.embedded.ServletContextInitializerBeans.getOrderedBeansOfType(ServletContextInitializerBeans.java:176)
    at org.springframework.boot.context.embedded.ServletContextInitializerBeans.addServletContextInitializerBeans(ServletContextInitializerBeans.java:80)
    at org.springframework.boot.context.embedded.ServletContextInitializerBeans.<init>(ServletContextInitializerBeans.java:68)
    at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.getServletContextInitializerBeans(EmbeddedWebApplicationContext.java:216)
    at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext$1.onStartup(EmbeddedWebApplicationContext.java:202)
    at org.springframework.boot.context.embedded.tomcat.ServletContextInitializerLifecycleListener.lifecycleEvent(ServletContextInitializerLifecycleListener.java:64)
    at org.apache.catalina.util.LifecycleSupport.fireLifecycleEvent(LifecycleSupport.java:117)
    at org.apache.catalina.util.LifecycleBase.fireLifecycleEvent(LifecycleBase.java:90)
    at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5095)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1409)
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1399)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:748)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.security.filterChainProxy': Cannot resolve reference to bean 'org.springframework.security.filterChains' while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.security.filterChains': Cannot resolve reference to bean 'org.springframework.security.web.DefaultSecurityFilterChain#0' while setting bean property 'sourceList' with key [0]; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.security.web.DefaultSecurityFilterChain#0': Cannot create inner bean '(inner bean)#3da73b75' of type [org.springframework.security.web.util.matcher.AntPathRequestMatcher] while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name '(inner bean)#3da73b75': Bean instantiation via constructor failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.security.web.util.matcher.AntPathRequestMatcher]: Constructor threw exception; nested exception is java.lang.NoSuchMethodError: org.springframework.util.AntPathMatcher.setCaseSensitive(Z)V
    at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:359)
    at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:108)
    at org.springframework.beans.factory.support.ConstructorResolver.resolveConstructorArguments(ConstructorResolver.java:634)
    at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:140)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1139)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1042)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:504)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:476)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:303)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:299)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1120)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1044)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:942)
    at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:813)
    at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:741)
    ... 25 more
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.security.filterChains': Cannot resolve reference to bean 'org.springframework.security.web.DefaultSecurityFilterChain#0' while setting bean property 'sourceList' with key [0]; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.security.web.DefaultSecurityFilterChain#0': Cannot create inner bean '(inner bean)#3da73b75' of type [org.springframework.security.web.util.matcher.AntPathRequestMatcher] while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name '(inner bean)#3da73b75': Bean instantiation via constructor failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.security.web.util.matcher.AntPathRequestMatcher]: Constructor threw exception; nested exception is java.lang.NoSuchMethodError: org.springframework.util.AntPathMatcher.setCaseSensitive(Z)V
    at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:359)
    at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:108)
    at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveManagedList(BeanDefinitionValueResolver.java:382)
    at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:157)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1477)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1222)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:537)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:476)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:303)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:299)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194)
    at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:351)
    ... 41 more
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.security.web.DefaultSecurityFilterChain#0': Cannot create inner bean '(inner bean)#3da73b75' of type [org.springframework.security.web.util.matcher.AntPathRequestMatcher] while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name '(inner bean)#3da73b75': Bean instantiation via constructor failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.security.web.util.matcher.AntPathRequestMatcher]: Constructor threw exception; nested exception is java.lang.NoSuchMethodError: org.springframework.util.AntPathMatcher.setCaseSensitive(Z)V
    at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveInnerBean(BeanDefinitionValueResolver.java:313)
    at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:129)
    at org.springframework.beans.factory.support.ConstructorResolver.resolveConstructorArguments(ConstructorResolver.java:634)
    at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:140)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1139)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1042)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:504)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:476)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:303)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:299)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194)
    at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:351)
    ... 53 more
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name '(inner bean)#3da73b75': Bean instantiation via constructor failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.security.web.util.matcher.AntPathRequestMatcher]: Constructor threw exception; nested exception is java.lang.NoSuchMethodError: org.springframework.util.AntPathMatcher.setCaseSensitive(Z)V
    at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:275)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1139)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1042)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:504)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:476)
    at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveInnerBean(BeanDefinitionValueResolver.java:299)
    ... 65 more
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.security.web.util.matcher.AntPathRequestMatcher]: Constructor threw exception; nested exception is java.lang.NoSuchMethodError: org.springframework.util.AntPathMatcher.setCaseSensitive(Z)V
    at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:163)
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:122)
    at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:267)
    ... 70 more
Caused by: java.lang.NoSuchMethodError: org.springframework.util.AntPathMatcher.setCaseSensitive(Z)V
    at org.springframework.security.web.util.matcher.AntPathRequestMatcher$SpringAntMatcher.createMatcher(AntPathRequestMatcher.java:291)
    at org.springframework.security.web.util.matcher.AntPathRequestMatcher$SpringAntMatcher.<init>(AntPathRequestMatcher.java:275)
    at org.springframework.security.web.util.matcher.AntPathRequestMatcher$SpringAntMatcher.<init>(AntPathRequestMatcher.java:268)
    at org.springframework.security.web.util.matcher.AntPathRequestMatcher.<init>(AntPathRequestMatcher.java:134)
    at org.springframework.security.web.util.matcher.AntPathRequestMatcher.<init>(AntPathRequestMatcher.java:101)
    at org.springframework.security.web.util.matcher.AntPathRequestMatcher.<init>(AntPathRequestMatcher.java:87)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
    at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:147)
    ... 72 more

原因是 spring-core-4.1.x 不存在 AntPathMatchersetCaseSensitive() 方法,请参阅 4.1.X.RELEASE 上的 AntPathMatcher4.2.X.RELEASE 上的 AntPathMatcher

解决方法:使用 spring-core-4.2.x 版本,或者降低 SpringSecurity 的版本。

我采用的是第二种方法,将 4.1.5 改成 4.0.4,这是我修改后的 pom.xml

<dependency> 
    <groupId>org.springframework.security</groupId> 
    <artifactId>spring-security-web</artifactId> 
    <version>4.0.4.RELEASE</version> 
</dependency> 
<dependency> 
    <groupId>org.springframework.security</groupId> 
    <artifactId>spring-security-config</artifactId> 
    <version>4.0.4.RELEASE</version > 
</dependency>

MyBatis删除数据,不报错,SQL能执行,但是数据并没有删除

2020-03-15   龙德   MyBatis   MyBatis  

遇到一个诡异的问题,MyBatis 根据主键 ID 删除数据,不报错,SQL 也正常打印出来,但是数据并没有删除。拎 SQL 到数据库中执行,却能正常执行,能把数据删除。

MyBatis 打印的 SQL,好像没啥问题

image

但是数据还在,并没有删除

image

拎 SQL 到数据库中执行,却能正常执行,能把数据删除。

image

这真是太奇怪了

一开始想的会不会是 MyBatis 没有 commit,但是仔细想,MyBatis 不是自动 commit 的吗,insert 和 update 都能正常执行,怎么 delete 反而没效果呢?

debug 看看

image

差点吐血。。。原来是 ID 前面多了一个空格。。。

改了之后果然能删除了

image

image

这种错误真的是低级而又不容易发现

SpringBoot解决form表单提交中文乱码

2020-02-28   龙德   SpringBoot   乱码  

form 表单提交数据映射到后台对象,中文出现乱码

image

解决方法如下:

1.application.properties 文件配置字符编码

spring.banner.charset=UTF-8
spring.messages.encoding=UTF-8
spring.http.encoding.charset=UTF-8
spring.http.encoding.force=true
spring.http.encoding.enabled=true
server.tomcat.uri-encoding=UTF-8

2.配置 CharacterEncodingFilter

之前 SpringMVC 是在 web.xml 里配置的

<filter>
	<description>字符集过滤器</description>
    <filter-name>encodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
    	<description>字符集编码</description>
        <param-name>encoding</param-name>
        <param-value>UTF-8</param-value>
    </init-param>
    <init-param>
		<param-name>forceEncoding</param-name>
		<param-value>true</param-value>
	</init-param>
</filter>
<filter-mapping>
    <filter-name>encodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

SpringBoot 的话直接注入 bean 即可

@Configuration
public class CharacterEncodingFilterConfig {

	@Bean
    public FilterRegistrationBean filterRegistrationBean() {
        FilterRegistrationBean registrationBean = new FilterRegistrationBean();
        CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
        characterEncodingFilter.setForceEncoding(true);
        characterEncodingFilter.setEncoding("UTF-8");
        registrationBean.setFilter(characterEncodingFilter);
        return registrationBean;
    }

}

重启之后,重新提交表单,中文就可以正常显示了

image