当前位置:   article > 正文

读书笔记《Spring Boot+Vue全栈开发实战》(下)_springboot为什么易扩展

springboot为什么易扩展

在这里插入图片描述


读书笔记《Spring Boot+Vue全栈开发实战》(下)

前言

接上一篇 《Spring Boot+Vue全栈开发实战》(上) 点击查看

第九章 Spring Boot缓存

缓存核心思路是对方法的缓存将方法的参数和返回值作为key-value缓存起来,再次调用该方法时从缓存获取 但Spring并未提供缓存的实现而是提供一套缓存API
目前Spring Boot支持的缓存有 JCache、EhCache2.x、Hazelcast、Infinispan、Couchbase、Redis、Caffeine、Simple
Spring早已将缓存领域同一 无论使用哪种缓存开发使用的注解都是一致的

Redis缓存
添加缓存依赖spring-boot-starter-cache

<dependency>
<groupId>org.springframewokr.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
  • 1
  • 2
  • 3
  • 4

在项目入口类添加@EnableCaching开启缓存

@SpringBootApplication
public class CacheApplication {
public static void main(String[] args) {
SpringApplication.run(CacheApplication.class, args);
}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

创建BookDao

@Repository
@CacheConfig(cacheNames = "book_cache")
public class BookDao {
@Cacheable
public Book getBookById(Integer id) {
System.out.println("getBookById");
Book book = new Book();
book.setId(id);
book.setName("三国演义");
return book;
}
@CachePut(key = "#book.id")
public Book updateBookById(Book book) {
System.out.println("updateBookById");
book.setName("三国演义2");
return book;
}
@CacheEvict(key = "#id")
public void deleteBookById(Integer id) {
System.out.println("deleteBookById");
}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

在BookDao上添加@CacheConfig注解指明使用的缓存的名字 该配置可选 如果不使用@CacheConfig注解则直接在@Cacheable注解中指明缓存名字
@Cacheable注解中指明缓存名字 默认情况下缓存的key是方法参数 value是方法返回值 其他类中调用该方法时首先会根据调用参数看是否命中缓存 有缓存则不执行该方法直接返回值 否则执行该方法并在成功后缓存返回值 但若在当前类中调用该方法则缓存不会生效
@Cacheable注解中属性condition来描述缓存执行时机 如@Cachebale(condition = “#id%2==0”)表示当id对2取模为0时才进行缓存
@CachePut更新缓存 @CacheEvict失效缓存
如果不想使用默认key可以设置key="#book.id"自定义key
除了使用参数定义key外Spring还提供一个root对象来生成key

如果这些key还不能满足需求可自定义缓存key生成器 只需实现KeyGenerator
如果是集群Reids还需要额外配置RedisCacheConfig

第十章 Spring Boot安全管理

Java领域常见的安全框架有Shiro和Spring Security
Shiro是一个轻量级安全管理框架 提供认证、授权、会话管理、密码管理、缓存管理等
Spring Security是个相对复杂的安全管理框架 功能更强大 权限控制细粒度更高 对OAuth2的支持也更友好而且与Spring框架无缝整合 特别在Spring Boot中提供了自动化配置方案
Spring Security基本配置

<dependency>
<groupId>org.springframewokr.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
  • 1
  • 2
  • 3
  • 4

添加依赖后所有资源都会被保护起来
默认用户名user 密码在项目启动时随机生成输出到控制台
配置用户名和密码并具有角色
spring.security.user.name=sang
spring.security.user.password=123
spring.security.user.roles=admin
基于内存的认证
自定义类集成WebSecurityConfigurerAdapter
HttpSecurity

public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Bean
    PasswordEncoder passwordEncoder() {
        return NoOpPasswordEncoder.getInstance();
    }
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
                .withUser("root").password("123").roles("ADMIN", "DBA")
                .and()
                .withUser("admin").password("123").roles("ADMIN", "USER");
    }
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/admin/**")
                .hasRole("ADMIN")
                .antMatchers("/user/**")
                .access("hasAnyRole('ADMIN', 'USER')")
                .antMatchers("/db/**")
                .access("hasRole('ADMIN') and hasRole('DBA')")
                .anyRequest()
                .authenticated()
                .and()
                .formLogin()
                .loginProcessingUrl("/login")
                .permitAll()
                .and()
                .csrf()
                .disable();
    }
}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

authorizeRequests方法开启HttpSecurity配置
anyRequest+authenticated表示除前面定义的URL模式之外的其他URL都必须认证后访问
permitAll设置login相关接口不需要认证即可访问
csrf+disable关闭csrf
根据上文配置
"/admin/hello"接口root和admin有访问权限
"/user/hello"接口root admin和user有访问权限
"/db/hello"接口需要同时admin和dba权限或root有访问权限
也可以实现多个自定义WebSecurityConfigurerAdapter通过@Order控制执行顺序

密码加密
数据库中的用户密码也应该加密以防止数据库泄露导致明文密码泄露
加密方式常用散列函数MD5摘要算法、安全散列算法等 为了增加安全性还要加盐
Spring Security提供多种密码加密方案 官方推荐BCryptPasswordEncoder使用BCrypt强哈希函数 strength值越大密码迭代次数越多 迭代次数为2^strength 取值4-31之间默认10

@Bean
PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(10);
}
  • 1
  • 2
  • 3
  • 4

方法安全(注解鉴权)

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnable = true)
public class WebSecurityConfig {}
  • 1
  • 2
  • 3

prePostEnabled注解解锁@PreAuthorize和@PostAuthorize 前者在方法执行前进行验证 后者在方法执行后进行验证
securedEnabled注解解锁@Secured

@Service
public class MethodService {
    @Secured("ROLE_ADMIN")
    public String admin() { return "hello admin"; }
    @PreAuthorize("hasRole('ADMIN') and hasRole('DBA')")
    public String dba() { return "hello dba"; }
    @PreAuthorize("hasAnyRole('ADMIN','DBA','USER')")
    public String user() { return "hello user"; }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

注意@Secured注解 角色默认有前缀"ROLE_"

基于数据库的认证
设计三张表 用户表、角色表、用户角色关联表
user表 id,username,password,enabled,locked
role表 id,name,nameZh
user_role表 id,uid,rid
注意role.name有默认前缀"ROLE_"如value(1,ROLE_dba,数据库管理员)
用户类需事先UserDetails接口并实现7个方法
getAuthorities(); 获取当前用户的角色
getPassword(); 获取当前用户的密码
getUsername(); 获取当前用户的用户名
isAccountNonExpired(); 获取当前用户是否未过期
isAccountNonLocked(); 获取当前用户是否未锁定
isCredentialsNonExpired(); 获取当前用户密码是否未过期
isEnabled(); 获取当前用户是否可用

创建UserService
服务类实现UserDetailsService

@Service
public class UserService implements UserDetailsService {
@Autowired
UserMapper userMapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userMapper.loadUserByUsername(username);
if (user == null) {
throw new UsernameNotFoundException("账户不存在");
}
user.setRoles(userMapper.getUserRolesByUid(user.getId()));
return user;
}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

配置Spring Security

@Configuration
public class WebSecurity extends WebSecurityConfigurerAdapter {
@Autowired
UserService userService;
@Bean
PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService);
}
@Override
protected void configure(HttpSecurity http) throw Exception {...}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

这里没有配置内存用户而是将刚创建好的UserService配置到AuthenticationManagerBuilder中

角色继承
ROLE_user是公共角色 admin继承user dba继承admin 只需提供RoleHierarchy

@Bean
RoleHierarchy roleHierarchy() {
RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl();
String hierarchy = "ROLE_dba > ROLE_admin ROLE_admin > ROLE_user";
roleHierarchy.setHierarchy(hierarchy);
return roleHierarchy;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

配置之后ROLE_dba用户就可以访问所有资源了 admin也可以访问user可以访问的资源

动态配置权限
使用HttpSecurity配置认证授权规则不够灵活 要实现动态配置URL权限自定义权限配置
数据库设计上在增加一张资源表(路由表)和资源角色关联表
menu表 id,pattern
menu_role表 id,mid,rid
自定义FilterInvocationSecurityMetadataSource 实现getAttributes方法来确定一个qingqiu需要哪些角色 可查库后用AntPathMatcher进行URL匹配
自定义AccessDecisionManager 当请求走完FilterInvocationSecurityMetadataSource中的getAttributes方法后会来到AccessDecisionManager实现类中进行角色信息对比
最后在Spring Security中配置这两个自定义类 通过@Bean注入 经过这些配置实现动态配置

OAuth2
OAuth是一个开放标准 该标准允许用户让第三方应用访问该用户在某一网站上存储的私密资源(如头像、照片、视频等)而无须将用户名和密码提供给第三方应用,典型如通过QQ登录知乎。OAuth2是更新版本但不向下兼容OAuth1.0 更加关注客户端开发简易性并为各种非Web端提供专门认证流程

OAuth2角色
资源所有者:即用户,具有头像、照片、视频等资源
客户端:即第三方应用,如上文的知乎
授权服务器:用来验证用户提供的信息是否正确,并返回一个令牌给第三方应用
资源服务器:提供给用户资源的服务器
一般来说 授权服务器和资源服务器可以是同一台机器
OAuth2授权流程图
客户端(第三方应用)向用户请求授权
用户点击客户端所呈现的服务授权页面上的同意授权按钮后 服务端返回一个授权许可凭证给客户端
客户端拿着授权许可凭证去授权服务器申请令牌
授权服务器验证信息无误后 发放令牌给客户端
客户端拿着令牌去资源服务器访问资源
资源服务器验证令牌无误后开放资源

OAuth2授权模式
OAuth协议的授权模式共分为4种
授权码模式:authorization code授权码模式是功能最完整、流程最严谨的授权模式 他的特点是通过客户端的服务器与授权服务器进行交互 国内常见的第三方登录功能基本都是使用这种模式
简化模式:不需要客户端服务器参与,直接在浏览器中向授权服务器申请令牌,一般若网站是纯静态页面,则可以采用这种方式
密码模式:用户把用户密码直接告诉客户端 客户端使用这些信息向授权服务器申请令牌 这需要用户对客户端高度信任 例如客户端应用和服务提供商是同一家公司
客户端模式:客户端使用自己的名义而不是用户的名义向服务提供者申请授权 严格来说这不能算作OAuth协议要解决问题的一种解决方案 但对开发而言在前后分离应用或移动端提供的认证授权服务器上使用这种模式非常方便

实践
本案例是前后分离应用认证 使用密码模式

<dependency>
<groupId>org.springframewokr.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframewokr.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<version>2.3.3.RELEASE</version>
</dependency>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

Spring Boot中的OAuth协议是在Spring Security的基础上完成的所以需要添加依赖
令牌可以存储在Redis缓存服务器上,同时Redis就过期等功能很适合令牌存储

配置OAuth2授权服务器

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
@Autowired
AuthenticationManager authenticationManager;
@Autowired
RedisConnectionFactory redisConnectionFactory;
@Autowired
UserDetailsService userDetailsService;
@Bean
PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
public void configure(ClientDetailsServiceConfigure clients) throw Exception {
clients.inMemory()
.withClient("password")
.authorizedGrantTypes("password", "refresh_token")
.accessTokenValiditySeconds(1800)
.resourceIds("rid")
.scopes("all")
.secret("$2a$10$RMuFXGQ5Atxxx")
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenStore(new RedisTokenStore(redisConnectionFactory))
.authenticationManager(authenticationManager)
.userDetailsService(userDetailsService);
}
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security.allowFormAuthenticationForClients();
}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

自定义AuthorizationServerConfigurerAdapter完成对授权服务器的配置 然后通过@EnableAuthorizationServer注解开启授权服务器
注入AuthenticationManager对象用来支持password模式
注入RedisConnectionFactory对象用来完成Redis缓存 将令牌信息存储到Redis缓存中
配置password授权模式 withClient(“password”) authorizedGrantTypes表示OAuth2中的授权模式为"password"和"refresh_token"两种 在标准OAuth2协议中授权模式并不包括"refresh_token" 但Spring Security的实现将其归为一种 因此如果要实现access_token的刷新就需要添加这样一种授权模式 同时accessTokenValiditySeconds方法配置了access_token的过期时间 resourceIds配置了资源id secret方法配置了加密后的密码
authenticationManager和userDetailsService主要用于支持password模式以及令牌的刷新
allowFormAuthenticationForClients表示支持client_id和client_secret做登录认证

配置OAuth2资源服务器

@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
@Override
public void configure(ResourceServerSecurityConfigurer resources) throw Exception {
resources.resourceId("rid").stateless(true);
}
@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/admin/**").hasRole("admin")
.antMatchers("/user/**").hasRole("user")
.anyRequest().authenticated();
}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

自定义ResourceServerConfigurerAdapter并添加@EnableResourceServer注解开启资源服务器配置
配置资源id 这里的资源id和授权服务器中的资源id一致 然后设置这些资源仅基于令牌认证

配置OAuth2的Security

@Configuration
public class WebSecurityConfig extends WebSecurityConfigureAdapter {
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Bean
@Override
protected UserDetailsService userDetailsService() {
return super.userDetailsService();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("admin")
.password("$2a$10$RMuFXGQ5Atxxx")
.roles("admin")
.and()
.withUser("sang")
.password("$2a$10$RMuFXGQ5Atxxx")
.roles("user");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/oauth/**").authorizeRequests()
.antMatchers("/oauth/**").permitAll()
.and().csrf().disable();
}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

和之前配置Spring Security基本一致 但多了两个Bean 注入授权服务器配置类中使用
在Spring Security配置和资源服务器配置中共涉及两个HttpSecurity 其中Spring Security中的配置优先级高于资源服务器中的配置 请求地址先经过Spring Security的HttpSecurity再经过资源服务器的HttpSecurity
请求示例(实际使用POST)

http://localhost:8080/oauth/token?username=sang&password=123&grant_type=password&client_id=password&scope=all&client_secret=123

{
"access_token": "918f7927-6144-xxx",
"token_type": "bearer",
"refresh_token": "330355cd-a1a1-xxx",
"expires_in": 1799,
"scope": "all"
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

请求包含用户名、密码、授权模式、客户端id、scope及客户端密码
返回包括获取资源的令牌、token_type、刷新令牌、过期时间、scope

http://localhost:8080/oauth/token?grant_type=refresh_token&refresh_token=330355cd-a1a1-xxx&client_id=password&client_secret=123
  • 1

设置授权模式为fresh_token用refresh_token获取新的access_token 返回结果access_token和expire_in会发生变化
本案例是前后分离应用常用的password模式 整体来说 OAuth2使用还是比较复杂的 但在Spring Boot项目中使用Spring Security整合要更加方便

Spring Boot整合Shiro (thymeleaf为例 略)
Apache Shiro是一个开源轻量级Java安全框架 提供身份验证、授权、密码管理及会话管理等功能 Shiro更加直观易也能提供健壮的安全性 传统SSM框架中手动整合Shiro配置较多 Shiro官方提供了shiro-spring-boot-web-starter用来简化Shiro在Spring Boot中的配置

第十一章 Spring Boot整合WebSocket

为什么需要WebSocket
在HTTP协议中 请求都是由客户端发起而无法服务端推送消息 传统解决方案有轮询、长轮询、Applet和Flash等
轮询 最简单 客户端固定间隔请求服务端 服务端要处理大量无效请求 高并发场景下严重影响性能和资源浪费
长轮询 轮询的升级版 服务端不立即响应客户端而是等有最新数据时才响应请求 这在一定程度上节省网络资源和服务器资源 但如果响应之前浏览器要发送新数据则又要新建一个并发请求或尝试先断开当前请求再请求 并且TCP和HTTP规范中有连接超时 长轮询不能一致持续 这又增大了开发量且是非主流方案
Applet和Flash 这两项技术除了让HTML更加绚丽之外还可以解决消息推送问题 通过创建只有一个像素点大小的透明Applet或Flash 用其来模拟全双工通信 在Applet或Flash代码中创建一个Socket连接进行双向通信 这种连接方式消除了HTTP协议中的诸多限制 但浏览器必须能运行Java或Flash 且无论Applet或Flash都存在安全问题 随着HTML5标准被各浏览器支持 Flash已经下架(Adobe 2020)

WebSocket
WebSocket是一种在单个TCP连接上进行全双工通信的协议 已被定为W3C标准 在WebSocket协议中浏览器和服务器只需要完成一次握手
WebSocket使用了HTTP/1.1的协议升级特性 一个WebSocket请求首先使用非正常的HTTP请求以特定的模式访问一个URL(ws/wss)
在请求头中有"Connection:Upgrade"和"Upgrade:websocket" 表示客户端请求升级协议为WebSocket协议

WebSocket主要特点
WebSocket使用时要先创建连接 这使得WebSocket成为一种有状态的协议 在之后通信中可以省略部分状态信息 如身份认证等
WebSocket连接在端口80/ws和443/wss上创建 与HTTP端口相同基本不会防火墙阻止
WebSocket使用HTTP协议进行握手 因此可低成本集成到浏览器和HTTP服务器中
心跳消息(ping和pong)将被反复发送 进而保持WebSocket连接一直处于活跃状态
使用该协议当消息启动或者到达时 服务端和客户端都可以知道
WebSocket连接关闭时将发送一个特殊的关闭消息
WebSocket支持跨域 可以避免Ajax的限制
HTTP规范要求浏览器将并发连接数限制为每个主机名两个连接 但是当我们使用WebSocket的时候 当握手完成后该限制就不存在了 因为此时的连接已经不再是HTTP连接
WebSocket协议支持扩展 用户可以扩展协议 实现部分自定义的子协议
更好的二进制支持及更好的压缩效果

Spring Boot整合WebSocket
添加依赖spring-boot-starter-websocket
Spring框架提供了基于WebSocket的STOMP支持 是一个简单的可互操作协议,通常被用于通过中间服务器在客户端之间进行异步消息传递

注:
STOMP:Simple (or Streaming) Text Orientated Messaging Protocol,即简单(流)文本定向消息协议。最初是为脚本语言(如Ruby、Python和Perl)创建的,用于连接到企业消息代理,它被设计用于处理常用消息传递模式的最小功能子集,STOMP可以用于任何可靠的双向流网络协议,如TCP和WebSocket,虽然STOMP是一个面向文本的协议,但消息payload可以是文本或二进制。就像HTTP在TCP套接字之上添加了请求-响应模型层一样,STOMP 在 WebSocket 之上提供了一个基于帧的线路格式(frame-based wire format)层,用来定义消息的语义。STOMP是一种为MOM(Message Oriented Middleware,面向消息的中间件)设计的简单文本协议,它提供一个可互操作的连接格式,允许客户端与任意STOMP消息代理(Broker)进行交互,用于client之间进行异步消息传输的简单文本协议。
对于STOMP协议来说, client分为消费者client与生产者client两种. server是指broker, 也就是消息队列的管理者。STOMP协议并不是为websocket设计的, 它是属于消息队列的一种协议, 和amqp, jms平级。只不过由于它的简单性恰巧可以用于定义websocket的消息体格式。STOMP协议很多mq都已支持, 比如rabbitmq, activemq。很多语言也都有STOMP协议的解析client库
  • 1
  • 2
  • 3

Spring Boot中WebSocket还是非常方便的 通过@MessageMapping注解配置消息接口 通过@SendTo或SimpMessagingTemplate进行消息转发 通过简单几行配置就能实现点对点、店对面的消息发送

第十二章 消息服务

消息队列(Message Queue)是一种进程间或者线程间的异步通信方式 分为消息生产者、消息队列、消息消费者 可以有效实现服务解耦并提高系统可靠性和扩展性 开源的消息服务非常多如Apache ActiveMQ、RabbitMQ等 这些即常说的消息中间件

JMS
JMS(Java Message Service)即Java消息服务 大部分消息中间件提供对JMS支持 JMS包括两种消息模型 点对点和订阅/发布 JMS仅支持Java平台
JMS是一套标准 Spring Boot整合JMS加具体某一实现 本案以ActiveMQ为例
ActiveMQ完全支持JMS1.1规范 支持多种语言多种协议如OpenWire、REST、STOMP、WS-Notification、MQTT、XMPP以及AMQP

application
@Bean
Queue queue() { return new ActiveMQQueue("amp") }
  • 1
  • 2
  • 3
@Component
public class JmsComponent {
@Autowired
JmsMessaingTemplate messagingTemplate;
@Autowired
Queue queue;
public void send(Message msg) {
messagingTemplate.convertAndSend(this.queue, msg);
}
@JmsListener(destination = "amq")
public void receive(Message msg) {
System.out.println("receive:" + msg);
}
}
class Message {...}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

AMQP
AMQP(Advanced Message Queuing Protocal 高级消息队列协议)是一个线路层的协议规范,而不是API规范(例如JMS) 因此具有跨平台性
本例具体实现以RabbitMQ为例 RabbitMQ消息中间件实现了AMQP 使用高性能的Erlang编写 可靠、支持多种协议、高可用、支持消息集群
在RabbitMQ中 所有生产者提交的消息都会交由Exchange再根据不同策略分发到不同Queue中 四种Exchange策略分别是Direct、Fanout、Topic以及Header
Direct策略 将消息队列绑定到一个DirectExchange上 当一条消息到达DirectExchange时会被转发到与该条消息routing key相同的Queue上
Fanout策略 把所有到达FanoutExchange的消息转发给所有与它绑定的Queue而routingkey不起任何作用
Topic策略 是比较复杂也灵活的一种路由策略 Queue通过routingkey绑定到TopicExchange上 消息到达后根据routingkey将消息路由到一个或多个Queue上
Header策略 使用较少 根据消息的Header将消息路由到不同Queue上 和routingkey无关

第十三章 企业开发

邮件发送
Sun提供JavaMail但配置繁琐 Spring提供JavaMailSender并且Spring Boot中提供MailSenderAutoConfiguration做了进一步简化
QQ邮箱为例 首先申请开通POP3/SMTP服务或IMAP/SMTP服务
SMTP(Simple Mail Transfer Protocal)简单邮件传输协议 定义了邮件客户端与SMTP服务器之间、SMTP服务器与SMTP服务器之间的通信规则 即a@qq.com用户将邮件投递到SMTP服务器、腾讯SMTP服务器将邮件投递到网易的SMTP服务器
POP3(Post Office Protocal3)邮局协议 定义了邮件客户端与POP3服务器之间的通信规则 登录服务器查看邮件的时候使用该协议 IMAP协议则是对POP3协议的扩展 作用类似功能更强

定时任务
@Scheduled由Spring提供的定时任务注解
需要在项目启动类添加@EnableScheduling注解开启定时任务
@Scheduled(fixedDelay = 1000) 表示当前任务执行结束后1秒开启另一个任务
@Scheduled(fixedRate = 2000) 表示当前任务开始2秒后开启另外一个任务
initialDelay = 1000 表示首次执行的延迟时间
@Scheduled(cron = “0 * * * * * ?”) cron表达式 每分钟执行一次

Quartz
功能丰富的开源作业调度库 由Java写成 支持集成在任何Java程序 支持cron表达式 支持数据库、集群、插件及邮件 具有极高灵活性
主要提供三个Bean:JobDetail、Trigger、SchedulerFactory

批处理
Spring Batch是一个开源的、全面的、轻量级的批处理框架 可以实现强大的批处理应用程序的开发 还提供记录/跟踪、事务管理、作业处理统计、作业重启以及资源管理等功能 可以结合定时任务发挥更大作用
Spring Batch提供ItemReader、ItemProcessor和ItemWriter来完成数据的读取、处理和写出 并可以将批处理执行状态持久化到数据库

Swagger2
Swagger2是一个开源软件框架 前后端分离开发中构建文档、维护、测试

数据校验
一般为了提高系统运行效率都会在前端进行数据校验 但可能会有接口非法请求所以后端也要进行数据校验 添加依赖 spring-boot-starter-validation
项目创建成功后查看LocalValidatorFactoryBean类源码 默认ValidationMessageSource是resources目录下的ValidationMessages.properties文件 因此创建该文件

user.name.size=用户名长度介于5到10字符
user.address.notnull=用户地址不能为空
user.age.size=年龄输入不正确
user.email.pattern=邮箱格式不正确
  • 1
  • 2
  • 3
  • 4

创建User类 配置校验数据

public class User {
private Integer id;
@Size(min = 5, max = 10, message = "{user.name.size}")
private String name;
@NotNull(message = "{user.address.notnull}")
private String address;
@DecimalMin(value = "1", message = "{user.age.size}")
@DecimalMax(value = "200", message = "{user.age.size}")
private Integer age;
@Email(message = "{user.email.pattern}")
private String email;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

给接口参数添加@Validated注解 表示对该参数校验 BindingResult保存出错信息

@PostMapping("/user")
public String addUser(@Validated User user, BindingResult result) {
if (result.hasErrors()) {
List<ObjectError> allErrors = result.getAllErrors();
for (ObjectError error : allErrors) {
error.getDefaultMessage();
}
}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

分组校验
有时候一个实体类定义了很多校验规则 但某次业务处理不需要这么多校验

public interface ValidationGroup1 {}
public interface ValidationGroup2 {}
  • 1
  • 2
@NotNull(message = "{user.address.notnull}", groups = ValidationGroup2.class)
  • 1
@PostMapping("/user")
public String addUser(@Validated(ValidationGroup2.class) User user, BindingResult result) {}
  • 1
  • 2

校验注解
AssertFalse Boolean、boolean 必须为false
AssertTrue Boolean、boolean 必须为true
DecimalMax BigDeciaml、BigInteger、CharSequence、byte、short、int、long及包装类
DecimalMin BigDeciaml、BigInteger、CharSequence、byte、short、int、long及包装类
Max BigDeciaml、BigInteger、byte、short、int、long及包装类
Min BigDeciaml、BigInteger、byte、short、int、long及包装类
Digits BigDeciaml、BigInteger、CharSequence、byte、short、int、long及包装类 被注解元素必须是数字
Email CharSequence
Future java.util.Date、java.util.Calendar、java.time包下时间类 被注解值为未来日期
Past java.util.Date、java.util.Calendar、java.time包下时间类 被注解值为过去日期
PastOrPresent 被注解值为过去或当前日期
FutureOrPresent 被注解值为未来或当前日期
Negative 必须是负数
NegativeOrZero 必须是负数或0
Positive 必须是正数
PositiveOrZero 必须是正数或0
NotBlank CharSequence 部位null且最少一个非空字符
NotEmpty CharSequence、Collection、Map、Array 不为null或空字符串 集合不为空
NotNull 任意类型 不为null
Null 任意类型 为null
Pattern CharSequence 必须符合指定正则表达式
Size CharSequence、Collection、Map、Array 字符串长度或集合长度在指定范围

第十四章 应用监控

Spring Boot提供了actuator帮助开发中获取应用程序的实施运行数据 开发者可使用HTTP端点或JMX来管理和监控应用程序 获取运行数据包括健康状况、应用信息、内存使用等
端点配置
Spring Boot中开启应用监控只需添加springboot-starter-actuator依赖即可
开发者可以使用actuator(执行器)中的端点(EndPoints)对应用进行监控或者与应用进行交互
监控信息可视化
邮件报警

第十五章 项目构建与部署

Spring Boot项目可以内嵌Servlet容器 可直接打包成可执行JAR包也可像传统Java Web应用打包成WAR包运行
JAR
使用spring-boot-maven-plugin插件可以创建一个可执行的JAR应用
可以创建可依赖的JAR 需要配置Maven插件生成一个单独artifact
配置文件多又不需要的时候可配置Maven插件 打包时排除配置文件
mvn package 进行打包 java -jar x.jar 运行应用
WAR
一些特殊情况下 开发者需要将Spring Boot打包成WAR包 传统方式部署
修改pom.xml文件 将项目打包成WAR包

<packaging>war</packaging>
  • 1

修改pom.xml文件 将内嵌容器的依赖标记为provided

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
  • 1
  • 2
  • 3
  • 4
  • 5

提供一个Spring BootServletInitializer的子类并覆盖其configure方法 完成初始化

public class ServletInitializer extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder app) {
return app.sources(WarApplication.class);
}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

mvn package 打包成功后在targer目录下生成WAR包 复制到Tomcat的webapps目录下 启动Tomcat即可

第十六章 微人事项目实战

Spring Boot+Vue前后端分离项目 完整代码地址为 https://github.com/lenve/vhr
项目介绍
人事管理系统是常见的企业后台管理系统
技术架构
后端采用Spring Boot 前端采用Vue+ElementUI来构建SPA(Single-Page Application 单页应用) SPA应用通过动态重写当前页面来与用户交互而非传统的从服务器加载整个新页面 前端通过Ajax与后端通信 只有一个HTML页面 所有页面的跳转都通过路由进行导航 前后分离的另一个好处是一个后端可以对应多个前端 各端都通过JSON交互数据
Vue简介
是一套用户构建用户界面的渐进式框架 被设计为可以自底向上逐层应用
Element简介
前端页面组件库 还有Vux、iView、mint-ui等 基本都是MD风格 差异不是很大

项目构建
Vue项目使用webpack来构建 先确保本地安装NodeJS然后CMD执行 创建名vuehr的项目

npm install -g vue-cli
vue init webpack vuehr
cd vuehr
npm run dev
  • 1
  • 2
  • 3
  • 4

在执行"vue init webpack vuehr"时会要求输入项目的基本信息
当"npm run dev"执行后 浏览器输入http://localhost:8080查看页面
后端Spring Boot项目添加web依赖即可
此处需要自己动手多练习一下

至此上篇更新本书共16章节全部更新完成,更多笔记,敬请关注~

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小小林熬夜学编程/article/detail/444009
推荐阅读
相关标签
  

闽ICP备14008679号