[笔记]SpringBoot实战(5)-Spring Boot 企业级开发

系列笔记:

Spring 定时任务

Spring Boot中,默认的定时任务线程池是只有一个线程的,所以如果在一堆定时任务中,有一个发生了延时或者死循环之类的异常,很大可能会影响到其他的定时任务。

  • 方案一:异步执行

在方法上使用@Async注解,让这个任务异步执行。
不过,如果在定时任务中真的发生了死循环,那么使用异步执行则会带来灾难性的后果。。
每次添加进异步线程池的任务因为死循环而一直占用着线程资源。随着时间的增加异步线程池的所有线程资源都会被死循环的任务占据,导致其他服务全部阻塞。

  • 方案二:自定义定时任务线程池数量
@Configuration
public class GlobalConfiguration {  
    @Bean
    public TaskScheduler schedule() {
        return new ConcurrentTaskScheduler(new ScheduledThreadPoolExecutor(2));
    }
}

可参考SpringBoot源码解析-Scheduled定时器的原理

Spring Boot 企业级开发

Spring Security

在Spring中使用Spring Security,只需在一个配置上注解@EnableWebSecurity,并让这个类继承WebSecurityConfigurerAdapter即可。可以通过重写configure方法来配置相关的安全配置。
自定义实现 UserDetailService 或 UserDetailPasswordService 接口,可以实现自定义的用户和权限获取方式。其实Spring内置的内存和JDBC获取用户、权限方式也是 UserDetailService 的实现。

import org.springframework.security.core.userdetails.UserDetails;  
import org.springframework.security.core.userdetails.UserDetailsService;  
import org.springframework.security.core.userdetails.UsernameNotFoundException;  
public class CustomUserService implements UserDetailsService { //1  
    @Autowired
    SysUserRepository userRepository;
    @Override
    public UserDetails loadUserByUsername(String username) {        
        SysUser user = userRepository.findByUsername(username); 
        if(user == null){
            throw new UsernameNotFoundException("用户名不存在");
        }       
        return user; //3
    }

}

除此之外我们还需要注册这个CustomUserServic,代码如下:

@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter{  
    @Bean
    UserDetailsService customUserService(){
        return new CustomUserService(); 
    }
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(customUserService());   
    }
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests() // 开始请求权限配置
                        .anyRequest().authenticated()//开始请求权限配置
                        .and()
                        .formLogin()//定制登录操作
                            .loginPage("/login")//定制登录页面的访问地址
                            .failureUrl("/login?error")//指定登录失败后转向的页面
                            .permitAll()
                        .and()
                        .logout().permitAll();
    }
}

Spring Boot 对 Security 的支持

主要通过SecurityAutoConfiguration和SecurityProperties来完成配置,SecurityAutoConfiguration自动导入了SpringBootWebSecurityConfiguration和WebSecurityEnablerConfiguration。
在SpringBootWebSecurityConfiguration配置中,我们可以获得如下自动配置:

  • 1) 自动配置了一个内存中的用户,账号为user,密码在程序启动时出现。
  • 2) 忽略/css/**、/js/**、/images/**/**/favicon.ico等静态文件的拦截。
  • 3) 自动配置的securityFilterChainRegistration的Bean。

SecurityProperties 使用以"spring.security"为前缀的属性配置 Spring Security 相关的配置。

Spring Boot 为我们做了如此多的配置,当我们需要自己扩展的配置时,只需配置类继承WebSecurityConfigurerAdapter类即可,无须使用@EnableWebSecurity注解,例如:

@Configuration
public class Websecurityconfig extends WebSecurityConfigurerAdapter {  
    // xxx
}

spring security 默认拦截了post put delete 请求的解决方案

使用 spring security 做的安全框架时,会自动拦截 post,put,delete 等请求,这时因为spring security 开启了防止跨域访问攻击(CSRF)的配置,可以这么解决:

使用thymeleaf在网页的head中添加

<meta name="_csrf" th:content="${_csrf.token}"/>  
<meta name="_csrf_header" th:content="${_csrf.headerName}"/>  

然后在js全局的方法中添加:

$(function () {
    var token = $("meta[name='_csrf']").attr("content");
    var header = $("meta[name='_csrf_header']").attr("content");
    $(document).ajaxSend(function (e, xhr, options) {
        xhr.setRequestHeader(header, token);
    });
});

这样就可以正常使用 post, put, delete 请求了。

批处理 Spring Batch

具体请参考书中 9.2 章。。。。。。。。。。。。。。。。。。。

异步消息

异步消息主要目的是为了系统与系统之间的通信。所调异步消息即消息发送者无须等待消息接收者的处理及返回,甚至无须关心消息是否发送成功。

在异步消息中有两个很重要的概念,即消息代理(message broker)和目的地(destination)。当消息发送者发送消息后,消息将由消息代理接管,消息代理保证消息传递到指定的目的地。

异步消息主要有两种形式的目的地:队列(queue)主题(topic)。队列用于点对点式(point-to-point)的消息通信;主题用于发布/订阅式(publish/subscribe)的消息通信。

  • 1.点对点式

    当消息发送者发送消息,消息代理获得消息后将消息放进一个队列(queue)里,当有消息接收者来接收消息的时候,消息将从队列里取出来传递给接收者,这时候队列里就没有了这条消息。
    点对点式确保的是每一条消息只有唯一的发送者和接收者,但这并不能说明只有一个接收者可以从队列里接收消息。因为队列里有多个消息,点对点式只保证每一条消息只有唯一的发送者和接收者。

  • 2.发布/订阅式

    和点对点击不同,发布门阅式是消息发送者发送消息到主题(topic),而个消息接收者监听这个主题。此时的消息发送者和接收者分别叫做发布者订阅者

企业级消息代理

JMS(Java Message Service)即Java消息服务,是基于JVM消息代理的规范。而ActiveMQ、HometQ是一个JMS消息代理的实现。

AMOP (Advanced Message Queuing Protocol)也是一个消息代理的规范,但它不仅兼容JMS,还支持跨语言和平台。AMQP的主要实现有RabbitMQ。

Spring 的支持

Spring对JMS和AMOP的支持分别来自于spring-jms和Spring-rabbito。
Sprine Boot 对JMS的自动配置支持位于 org.springframework.boot.autoconfigure.jms下。
具体可查看书中 9.3 章

系统集成 Spring Integration

Spring Ingegration 提供了基于 Spring 的 EIP(Enterprise Integration Patterns,企业集成模式)的实现。Spring Integration主要解决的问题是不同系统之间交互的问题,通过异步消息驱动来达到系统交互时系统之间的松稠合。可以使用Java配置、注解以及Spring Integration Java DSL来使用Spring Integration。
Spring Integratin 主要由以下三个概念组成。

  • Message : Message 是用来在不同部分之间传递的数据。Message由两部分组成:消息体(payload)与消息头(header)。消息体可以是任何数据类型(如XML、JSON,Java对象);消息头表示的元数据就是解释消息体的内容。
  • Channel:在消息系统中,消息发送者发送消息到通道(Channel),消息收受者从通道(Channel)接收消息。
  • Message EndPoint:消息端点(Message Endpoint)是真正处理消息的(Message)组件,它还可以控制通道的路由。

具体可查看书中 9.4 章