springboot 动态给注解属性参数赋值

这边是结合最近开发的kafka功能加以示例

yml配置文件

配置映射的Java bean

Controller里使用配置属性进行注解动态设值

 

 

 

 

0

Spring Boot 如何自定义kafka 消费者配置 ContainerFactory

本篇博文主要提供一个在 SpringBoot 中自定义 kafka配置的实践,想象这样一个场景:你的系统需要监听多个不同集群的消息,在不同的集群中topic冲突了,所以你需要分别定义kafka消息配置。

此篇文章会在SpringBoot 提供的默认模板上提供扩展,不会因为你自定义了消费者配置,而导致原生SpringBoot的Kakfa模板配置失效。

1 引入 MAVEN 依赖

版本需要你自己指定

2 引入Java配置类

3 yml模板

4 配置释义

点开 KafkaProperties 这个类,可以看到这个是SpringBoot 自动配置kafka的配置类,引入这个实例,就相当于你拿到了SpringBoot kafka配置模板的参数,就是上述贴的配置,然后再此基础上重新定义你需要改变的配置,这里主要讲消费者配置。

代码中举了个重写监听servers的例子:

5 @KafkaListener 使用 containerFactory

如果在@KafkaListener属性中没有指定 containerFactory 那么Spring Boot 会默认注入 name 为“kafkaListenerContainerFactory” 的 containerFactory。具体源码可跟踪:KafkaListenerAnnotationBeanPostProcessor中的常量:

 

0

spring-oauth-server 数据库表说明

oauth_client_details

字段名 字段说明
client_id 主键,必须唯一,不能为空. 用于唯一标识每一个客户端(client); 在注册时必须填写(也可由服务端自动生成). 对于不同的grant_type,该字段都是必须的. 在实际应用中的另一个名称叫appKey,与client_id是同一个概念.
resource_ids 客户端所能访问的资源id集合,多个资源时用逗号(,)分隔,如: “unity-resource,mobile-resource”. 该字段的值必须来源于与security.xml中标签‹oauth2:resource-server的属性resource-id值一致. 在security.xml配置有几个‹oauth2:resource-server标签, 则该字段可以使用几个该值. 在实际应用中, 我们一般将资源进行分类,并分别配置对应的‹oauth2:resource-server,如订单资源配置一个‹oauth2:resource-server, 用户资源又配置一个‹oauth2:resource-server. 当注册客户端时,根据实际需要可选择资源id,也可根据不同的注册流程,赋予对应的资源id.
client_secret 用于指定客户端(client)的访问密匙; 在注册时必须填写(也可由服务端自动生成). 对于不同的grant_type,该字段都是必须的. 在实际应用中的另一个名称叫appSecret,与client_secret是同一个概念.
scope 指定客户端申请的权限范围,可选值包括read,write,trust;若有多个权限范围用逗号(,)分隔,如: “read,write”. scope的值与security.xml中配置的‹intercept-url的access属性有关系. 如‹intercept-url的配置为‹intercept-url pattern=”/m/**” access=“ROLE_MOBILE,SCOPE_READ”/>则说明访问该URL时的客户端必须有read权限范围. write的配置值为SCOPE_WRITE, trust的配置值为SCOPE_TRUST. 在实际应该中, 该值一般由服务端指定, 常用的值为read,write.
authorized_grant_types 指定客户端支持的grant_type,可选值包括authorization_code,password,refresh_token,implicit,client_credentials, 若支持多个grant_type用逗号(,)分隔,如: “authorization_code,password”. 在实际应用中,当注册时,该字段是一般由服务器端指定的,而不是由申请者去选择的,最常用的grant_type组合有: “authorization_code,refresh_token”(针对通过浏览器访问的客户端); “password,refresh_token”(针对移动设备的客户端). implicit与client_credentials在实际中很少使用.
web_server_redirect_uri 客户端的重定向URI,可为空, 当grant_type为authorization_code或implicit时, 在Oauth的流程中会使用并检查与注册时填写的redirect_uri是否一致. 下面分别说明:当grant_type=authorization_code时, 第一步 从 spring-oauth-server获取 ‘code’时客户端发起请求时必须有redirect_uri参数, 该参数的值必须与 web_server_redirect_uri的值一致. 第二步 用 ‘code’ 换取 ‘access_token’ 时客户也必须传递相同的redirect_uri. 在实际应用中, web_server_redirect_uri在注册时是必须填写的, 一般用来处理服务器返回的code, 验证state是否合法与通过code去换取access_token值.在spring-oauth-client项目中, 可具体参考AuthorizationCodeController.java中的authorizationCodeCallback方法.当grant_type=implicit时通过redirect_uri的hash值来传递access_token值.如:http://localhost:7777/spring-oauth-client/implicit#access_token=dc891f4a-ac88-4ba6-8224-a2497e013865&token_type=bearer&expires_in=43199然后客户端通过JS等从hash值中取到access_token值.
authorities 指定客户端所拥有的Spring Security的权限值,可选, 若有多个权限值,用逗号(,)分隔, 如: “ROLE_
access_token_validity 设定客户端的access_token的有效时间值(单位:秒),可选, 若不设定值则使用默认的有效时间值(60 * 60 * 12, 12小时). 在服务端获取的access_token JSON数据中的expires_in字段的值即为当前access_token的有效时间值. 在项目中, 可具体参考DefaultTokenServices.java中属性accessTokenValiditySeconds. 在实际应用中, 该值一般是由服务端处理的, 不需要客户端自定义.refresh_token_validity 设定客户端的refresh_token的有效时间值(单位:秒),可选, 若不设定值则使用默认的有效时间值(60 * 60 * 24 * 30, 30天). 若客户端的grant_type不包括refresh_token,则不用关心该字段 在项目中, 可具体参考DefaultTokenServices.java中属性refreshTokenValiditySeconds. 在实际应用中, 该值一般是由服务端处理的, 不需要客户端自定义.
additional_information 这是一个预留的字段,在Oauth的流程中没有实际的使用,可选,但若设置值,必须是JSON格式的数据,如:{“country”:“CN”,“country_code”:“086”}按照spring-security-oauth项目中对该字段的描述 Additional information for this client, not need by the vanilla OAuth protocol but might be useful, for example,for storing descriptive information. (详见ClientDetails.java的getAdditionalInformation()方法的注释)在实际应用中, 可以用该字段来存储关于客户端的一些其他信息,如客户端的国家,地区,注册时的IP地址等等.create_time 数据的创建时间,精确到秒,由数据库在插入数据时取当前系统时间自动生成(扩展字段)
archived 用于标识客户端是否已存档(即实现逻辑删除),默认值为’0’(即未存档). 对该字段的具体使用请参考CustomJdbcClientDetailsService.java,在该类中,扩展了在查询client_details的SQL加上archived = 0条件 (扩展字段)
trusted 设置客户端是否为受信任的,默认为’0’(即不受信任的,1为受信任的). 该字段只适用于grant_type=”authorization_code”的情况,当用户登录成功后,若该值为0,则会跳转到让用户Approve的页面让用户同意授权, 若该字段为1,则在登录后不需要再让用户Approve同意授权(因为是受信任的). 对该字段的具体使用请参考OauthUserApprovalHandler.java. (扩展字段)
autoapprove 设置用户是否自动Approval操作, 默认值为 ‘false’, 可选值包括 ‘true’,‘false’, ‘read’,‘write’. 该字段只适用于grant_type=”authorization_code”的情况,当用户登录成功后,若该值为’true’或支持的scope值,则会跳过用户Approve的页面, 直接授权. 该字段与 trusted 有类似的功能, 是 spring-security-oauth2 的 2.0 版本后添加的新属性. 在项目中,主要操作oauth_client_details表的类是JdbcClientDetailsService.java, 更多的细节请参考该类. 也可以根据实际的需要,去扩展或修改该类的实现.

oauth_client_token

字段名 字段说明
create_time 数据的创建时间,精确到秒,由数据库在插入数据时取当前系统时间自动生成(扩展字段)
token_id 从服务器端获取到的access_token的值.
token 这是一个二进制的字段, 存储的数据是OAuth2AccessToken.java对象序列化后的二进制数据.
authentication_id 该字段具有唯一性, 是根据当前的username(如果有),client_id与scope通过MD5加密生成的. 具体实现请参考DefaultClientKeyGenerator.java类.
user_name 登录时的用户名
client_id

该表用于在客户端系统中存储从服务端获取的token数据, 在spring-oauth-server项目中未使用到.
对oauth_client_token表的主要操作在JdbcClientTokenServices.java类中, 更多的细节请参考该类.

oauth_access_token

字段名 字段说明
create_time 数据的创建时间,精确到秒,由数据库在插入数据时取当前系统时间自动生成(扩展字段)
token_id 该字段的值是将access_token的值通过MD5加密后存储的.
token 存储将OAuth2AccessToken.java对象序列化后的二进制数据, 是真实的AccessToken的数据值.
authentication_id 该字段具有唯一性, 其值是根据当前的username(如果有),client_id与scope通过MD5加密生成的. 具体实现请参考DefaultAuthenticationKeyGenerator.java类.
user_name 登录时的用户名, 若客户端没有用户名(如grant_type=“client_credentials”),则该值等于client_id
client_id
authentication 存储将OAuth2Authentication.java对象序列化后的二进制数据.
refresh_token 该字段的值是将refresh_token的值通过MD5加密后存储的. 在项目中,主要操作oauth_access_token表的对象是JdbcTokenStore.java. 更多的细节请参考该类.

oauth_refresh_token

字段名 字段说明
create_time 数据的创建时间,精确到秒,由数据库在插入数据时取当前系统时间自动生成(扩展字段)
token_id 该字段的值是将refresh_token的值通过MD5加密后存储的.
token 存储将OAuth2RefreshToken.java对象序列化后的二进制数据.
authentication 存储将OAuth2Authentication.java对象序列化后的二进制数据.

在项目中,主要操作oauth_refresh_token表的对象是JdbcTokenStore.java. (与操作oauth_access_token表的对象一样);更多的细节请参考该类.
如果客户端的grant_type不支持refresh_token,则不会使用该表.

oauth_code

字段名 字段说明
create_time 数据的创建时间,精确到秒,由数据库在插入数据时取当前系统时间自动生成(扩展字段)
code 存储服务端系统生成的code的值(未加密).
authentication 存储将AuthorizationRequestHolder.java对象序列化后的二进制数据.

在项目中,主要操作oauth_code表的对象是JdbcAuthorizationCodeServices.java. 更多的细节请参考该类.
只有当grant_type为”authorization_code”时,该表中才会有数据产生; 其他的grant_type没有使用该表.

表结构

 

0

mybatis-plus-generator 自动代码生成

mybatis-plus-generator 自动代码生成

新建一个springboot的maven工程。

pom依赖

代码生成类

 

 

 

0

SpringBoot + websocket 定时任务完成实时数据(加密)推送前端

一、业务场景

登录数,与游客数,实时推送至前端,完成看板统计功能的页面动态展示

二、实现步骤

1.websocket

完成实时推送我使用的是websocket.
每当使用SpringBoot进行Weboscket开发时,最容易想到的就是spring-boot-starter-websocket(或spring-websocket)。它可以让我们使用注解,很简单的进行Websocket开发,让我们更多的关注业务逻辑。它底层使用的是Tomcat,且不说把整个Tomcat放进一个WebSocket服务中是否会太重,但在大数据量高并发的场景下,它的表现并不是非常理想。

Netty一款高性能的NIO网络编程框架,在推送量激增时,表现依然出色。(关于性能与表现的讨论,网上很多,这里不过多说明。)很多流行开源项目都在使用Netty,如:Dubbo、Storm、Spark、Elasticsearch、Apache Cassandra等,这得益于Netty的并发高、传输快、封装好等特点。

但是,要在SpringBoot项目中整合Netty来开发WebSocket不是一件舒服的事,这会让你过多的关注非业务逻辑的实现。那么,是否有一款框架,能使得在SpringBoot项目中使用Netty开发WebSocket变得简单,甚至优雅,并且可以从使用spring-boot-starter-websocket开发的项目无缝的迁移过来呢?

netty-websocket-spring-boot-starter

这是个开源的框架。通过它,我们可以像spring-boot-starter-websocket一样使用注解进行开发,只需关注需要的事件(如OnMessage)。并且底层是使用Netty,当需要调参的时候只需要修改配置参数即可,无需过多的关心handler的设置。

添加依赖

2.SpringBoot定时任务(基于注解(@Scheduled))

基于注解@Scheduled默认为单线程,开启多个任务时,任务的执行时机会受上一个任务执行时间的影响。

1.创建定时器

3.传输加密

websocket一般在传输过程中加密,我采用的是Aes加密方法,具体加密代码请移步:

后台Java代码

依赖的maven包

前端js代码

4.代码分析

 

0

Spring 的 @Lookup 注解使用

最近工作中重构代码,需要将prototype类型的组件注入到singleton类型的组件中,但是只是声明scope是prototype,并使用@autowire注入的话,并不能满足需要。个人理解是项目启动的时候已经组装好了,导致单例bean中的原型bean也是同一个。

1. 介绍

我们来看一下如何通过方法级的@Lookup注解完成依赖注入。

2. 为什么用@Lookup?

一个有@Lookup注解的方法会告诉Spring,当我们调用这个方法时,Spring会返回一个方法返回值类型的实例。
实质上,Spring会父爱我们的注解方法并使用我们方法的返回值和参数作为BeanFactory#getBean方法的入参。
@Lookup注解的使用情景:

1) 向一个单例bean注入一个原型bean(prototype-scoped bean)(类似于Provider)
2) Injecting dependencies procedurally(暂译为程序依赖注入,有不妥请指正)
@Lookup相当于XML元素中的lookup-method

3. 如何使用@Lookup

3.1 向一个单例bean注入原型bean

当我们决定使用一个原型bean时,我们必须面对的问题就是我们的单例bean要如何使用这些原型bean呢?
当然可以使用Provider,但是@Lookup在某些方面更合适一些。
首先,我们先创建一个之后需要注入到单例bean的原型bean。

之后我们使用@Lookup创建一个单例bean:

使用@Lookup,我们能在单例bean中得到一个SchoolNotification实例。

注意,在StudentServices中,我们将getNotification方法设置成了一个空方法(we left the getNotification method as a stub)。
这是因为,spring通过调用beanFactory.getBean(StudentNotification.class)重写了这个方法,所以我们能将它置空。
3.2 部分就是说使用@Lookup注入的话,还可以通过构造器传参,其实就是spring调用beanFactory.getBean(class, name)实现的。

示例

下面看一个例子:




可以看到,使用@lookup时,两次的MyHandlerImpl不是同一个对象,而使用autowired注入的话,则是同一个对象。
需要注意的是,被@lookup注解的方法需要是public的哦!

 

0

springboot整合logback日志框架

一、添加依赖

但是呢,实际开发中我们不需要直接添加该依赖,你会发现spring-boot-starter其中包含了 spring-boot-starter-logging,该依赖内容就是 Spring Boot 默认的日志框架 Logback+SLF4J。而 spring-boot-starter-web 包含了spring-boot-stater,所以我们只需要引入web组件即可:

二、默认配置

默认情况下Spring Boot将日志输出到控制台,不会写到日志文件。如果要编写除控制台输出之外的日志文件,则需在application.properties中设置logging.file或logging.path属性

三、logback-spring.xml

可以看到上面那种方式配置简单,但是能实现的功能也非常有限,如果想要更复杂的需求,就需要下面的定制化配置了。

Spring Boot官方推荐优先使用带有-spring的文件名作为你的日志配置(如使用logback-spring.xml,而不是logback.xml),命名为logback-spring.xml的日志配置文件,将xml放至 src/main/resource下面。

 

 

0

如何配置Spring Cloud应用使用指定IP或忽略某张网卡?

问题说明

分布式应用部署到服务上,由于服务器可能存在多张网卡,造成IP地址不准的问题。

解决方法

1、直接添加忽略某张网卡的配置:

正则:

2、指定默认IP:

3、除了这些配置,还有以下的这些配置:

ignored-interfaces和preferred-networks这两个配置。这两个配置决定了spring cloud应用在启动的时候所使用的网卡和IP地址。ignored-interfaces接收一个正则表达式数组,配置名字虽然是ignored-interfaces,忽略的网卡,但是因为其接收的是正则表达式,所以我们可以任意的选择和反选本机的网卡。preferred-networks是指倾向于使用的IP地址,接收一个正则表达式数组,用于选择Spring Cloud应用使用的本机的IP地址。通过这两个配置,我们可以任意指定Spring Cloud应用使用的网卡和IP地址。

0

Spring5.X编译的踩坑记录

1.Spring5.0

Spring5.X编译的踩坑记录Spring 5.0是在2013年发布Spring 4后的第一个大版本.该次升级也更新了不少的新特性. 基本可以归为如下几类:

  • JDK版本升级
  • Core框架修订,核心容器更新
  • Kotlin函数式编程
  • 响应式编程模型
  • 测试改进
  • 额外库支持
  • 停止维护一些特性

2.编译源码的原因

如果想要深入理解Spring的特性并学习优秀开源项目的编程范式,必然要到源码中分析实现细节.但仅通过view代码的形式是无法明晰某些复杂逻辑的. Spring的源码中提供了很多单元测试,我们可以通过Debug单元测试的形式来加深对组件的理解.

3.踩坑细节

1.版本控制工具Gradle

Spring5.X编译的踩坑记录Gradle虽然特性优于Maven,且有完善的task体系.但终究是个战未来的工具,对于大多数人来说Gradle还是一个相对陌生的东西. 但我们的目的是编译Spring源码,对于技术栈中没有Gradle的人来说Gradle就成了一道绕不过的坎.

我使用的Gradle版本为5.5.1. 在运行build.gradle的过程中,由于连接的依赖仓库为外网仓库,所以网速惨不忍睹.我build了两个小时+都没有完成. 这时有两个解决方案.

  1. 修改中央仓库地址
    build.gradle中的maven中央仓库地址更换为阿里云的私服

    依赖中有些内容是私服中所没有的,解决方案是先使用私服把能下载的依赖都下载了,再切回中央仓库将剩余依赖下载,可以节省很多时间.

  2. 使用代理
    有梯子的小伙伴可以直接使用代理下载依赖,速度会快很多.

2.spring-core核心代码报错

spring-core编译时CoroutinesUtils找不到.

运行build.gradle后,会在spring-core-coroutines包中生成build文件夹,将buildlibs的内容导入到spring-core的依赖中即可解决该问题.

3.build顺序问题

由于spring-framework内的组件大部分都引用了spring-oxmspring-core.所以需要提前先编译这两个包 ,否则直接编译其他包会显示缺依赖而直接失败.

运行这两个包内的compileTestJavaTask.
Spring5.X编译的踩坑记录运行完成后,就可以继续编译我们常用的组件,例如:spring-beansspring-context了.

4.idea中kotlin插件冲突问题

Spring5.X编译的踩坑记录

直接关闭idea的kotlin插件即可.

5.关于spring-aspects的问题

由于引用了IntelliJ IDEA未知的方面类型,因此spring-aspects不会编译.可以从项目中排除’spring-aspects’以避免编译错误.

4.reference

0

Spring 里 @Lazy 和 @DependsOn 注解作用是什么?

Spring 里 @Lazy 和 @DependsOn 注解作用是什么?

lazy是是否懒加载,如果一个类不是启动就需要的就可以设置为懒加载,用的时候再初始化。DependsOn是说该类的初始化依赖于另外一个类的初始化,也就是只有另外一个类初始化了,这个类才会初始化。

0