SpringBoot整合Nacos⾃动刷新配置
⽬的
Nacos作为SpringBoot服务的注册中⼼和配置中⼼。
在NacosServer中修改配置⽂件,在SpringBoot不重启的情况下,获取到修改的内容。
本例将在配置⽂件中配置⼀个 cml.age=100 的配置项,程序中编写⼀个⽅法读取配置⽂件,并通过 Get--->/test/age 接⼝提供给浏览器访问。
若配置⽂件中的 age 修改为 200 ,不⽤重新启动程序,直接访问 /test/age 接⼝,将获取到最新的值 200
若配置⽂件中没有age 的配置项,或⼲脆没有 cml 的配置项,访问 /test/age 接⼝将返回默认的值 18
环境
SpringCloud:2020.0.3
SpringCloudAlibaba:2021.1
SpringBoot:2.5.2
pom
pom中引⼊ nacos 相关配置:discovery,config,bootstrap
⽹上有⼈说,需要引⼊ actuator ,其实不⽤。也没有 SpringSecurity 拦截的问题,NacosClient 端定时从 NacosServer 端拉取最新配置,以http⽅式请求。
<dependencyManagement>
spring怎么读取配置<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring.cloud.dependencies}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring.cloud.alibaba.dependencies}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- nacos-discovery -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--nacos config -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!--bootstrap-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
</dependencies>
配置⽂件
server:
port: 9556
spring:
application:
name: app
profiles:
active: test
nacos:
discovery:
username: nacos
password: nacos
server-addr: 192.168.1.61:8848
config:
server-addr: 192.168.1.61:8848
file-extension: yaml
app-dev.yaml
此配置指 NacosServer 中的配置⽂件 app-dev.yaml ,仅截取 cml.age 部分
cml:
age: 100
代码
RefreshScope注解:必须加在 controller 上⾯,加在主启动内上⾯不好使。哪些资源需要⾃动刷新配置就在该controller上⾯添加此注解,可封装⼀个 BaseController 。
@Value("${cml.age:18}"):读取配置⽂件中的 cml.age 配置项值,赋给变量 age ,默认值为 18
getAge:获取年龄接⼝
/test/age接⼝需要添加到 Security.permitAll
问题:RefreshScope注解为什么⼀定要添加在 controller 上⾯?为什么在主启动类上⾯添加不⽣效
@RefreshScope
@Api(tags = "测试 - api")
@Slf4j
@RestController
@RequestMapping("/test")
public class TestController {
/**
* 获取配置⽂件中的 cml.age 内容,若未获取到,默认值为18
*/
@Value("${cml.age:18}")
private String age;
@ApiOperation(value = "获取年龄 - 测试配置⾃动刷新", notes = "获取年龄 - 测试配置⾃动刷新")
@GetMapping("/age")
public String getAge() {
return age;
}
}
⽇志
开启 nacos-refresh ⽇志,打印配置内容更新情况
logging:
level:
com.alibaba.fresh: debug
打印的⽇志:
2022-01-28 13:43:30.574 [com.alibaba.nacos.client.Worker.longPolling.fixed-192.168.1.61_8848-zjrkm-admin] DEBUG com.alibaba. fresh.NacosContextRefresher.innerReceive:136 - Refresh Nacos config group=DEFAULT_GROUP,dataId=identityQrCodeAdmin-service-cml-test.y aml,configInfo=spring:
application:
name:
.
.
.
.
.
测试
在不重启SpringBoot服务的情况,多次在 NacosServer 中修改 cml.age 配置项的值,然后通过浏览器访问 /test/age 接⼝,发现每次都可以获取到最新的 age 值。
特殊的情况
当项⽬集成了 SpringSecurity 的时候,如果在 UserDetailsService 实现类上添加 RefreshScope 注解,并在类中直接使⽤ @Value 获取配置⽂件的内容,会导致⽤户在登录的时候不到该类,从⽽⽆法调⽤loadUserByUsername ⽅法做⽤户名密码校验,进⽽登录功能⽆法使⽤。
解决⽅案是:
新建⼀个配置类(假设类名为 Account ),使⽤ ConfigurationProperties 将需要的配置注⼊到类属性;并在类上添加 RefreshScope 注解将 Account 类注⼊容器,UserDetailsService 实现类中将 Account 类 Autowired 进来再使⽤
核⼼代码:
Account 类:
import lombok.Data;
import lombok.ToString;
import org.springframework.beans.factory.annotation.Value;
import org.t.properties.ConfigurationProperties;
import org.t.config.annotation.RefreshScope;
import org.springframework.stereotype.Component;
import java.io.Serializable;
@RefreshScope
@Component
@ConfigurationProperties(prefix = "cml.admin")
@Data
@ToString
public class Account  implements Serializable {
private  String defaultUserName;
private  String defaultPassword;
private  String defaultUserName1;
private  String defaultPassword1;
}
UserDetailsService 实现类:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.t.config.annotation.RefreshScope;
import org.authority.AuthorityUtils;
import org.userdetails.UserDetails;
import org.userdetails.UserDetailsService;
import org.userdetails.UsernameNotFoundException;
import org.pto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
@Service
public class UserService implements UserDetailsService {
@Autowired
private PasswordEncoder passwordEncoder;
@Autowired
private Account account;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
if (DefaultUserName().equals(username)) {
String password = DefaultPassword());
return new User(username, password, AuthorityUtilsmaSeparatedStringToAuthorityList("admin"));
}
if (DefaultUserName1().equals(username)) {
String password = DefaultPassword1());
return new User(username, password, AuthorityUtilsmaSeparatedStringToAuthorityList("admin1"));
}
return null;
}
}

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。