首页 > 其他分享 >SpringBoot系统启动任务(三十二)的方式

SpringBoot系统启动任务(三十二)的方式

时间:2022-11-10 15:31:35浏览次数:66  
标签:三十二 系统启动 return SpringBoot void private static public String


当我们离开世界的时候,那些人,知道我们来过就好.

上一章简单介绍了SpringBoot通过Cors解决跨域问题(三十一),如果没有看过,​​请观看上一章​​

本章节参考 江南一点雨大神的文章: ​​Spring Boot2 系列教程(十五)定义系统启动任务的两种方式​

一. 系统启动时执行定时任务

一.一 服务启动后执行定时任务

在系统启动时,我们常常需要初始化一些数据,如构造出必要的数据,将数据库中的某些数据提前写入到缓存里面,获取当前服务器的相关信息等。

@SpringBootApplication
public class InitApplication {
public static void main(String[] args) {
SpringApplication.run(InitApplication.class,args);
System.out.println(">>>>>>>项目启动成功");
//执行初始化数据.
initData();
}
public static void initData(){
System.out.println("静态方法初始化数据");
}
}

这样的操作, initData() 并不是系统启动时执行的定时任务,而是系统启动后执行的.

SpringBoot系统启动任务(三十二)的方式_spring

这样写,当不牵扯到组件 @Component 相关的注解方法时,也就是说只有简单的方法处理时,虽然可以进行初始化数据,但不规范,一般不采用这种方式处理.

本章节代码层次结构如下:

SpringBoot系统启动任务(三十二)的方式_数据_02

定义一个接口和一个实现

UserService 接口

public interface UserService {

public void set();

public void get();

}

UserServiceImpl 实现

@Service
public class UserServiceImpl implements UserService {


@Override
public void set() {
System.out.println("UserService--->set值");
}

@Override
public void get() {
System.out.println("UserService--->取出值");
}
}

一.二 Listener 监听器执行定时任务

在 JSP /Servlet 时,我们常常使用 Listener 监听器来初始化数据.

SpringBoot 也支持这种形式.

一.二.一 事件监听器 InitListener

在 listener 包下创建事件监听器

package top.yueshushu.init.listener;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

/**
* @ClassName:InitListener
* @Description 监听器
* @Author zk_yjl
* @Date 2021/11/30 9:00
* @Version 1.0
* @Since 1.0
**/
public class InitListener implements ServletContextListener {
/**
容器启动时
*/
@Override
public void contextInitialized(ServletContextEvent sce) {
//初始化必要的数据. 常于构建缓存,初始化数据.
System.out.println(">>>>Listener,容器启动,初始化必要的数据");
}
/**
容器突然销毁时
*/
@Override
public void contextDestroyed(ServletContextEvent sce) {
//容器关闭了,进行数据备份的处理.
System.out.println(">>>>Listener,容器销毁,将重要数据备份");
}
}

一.二.二 启动添加 Listener

@SpringBootApplication
public class InitApplication {
public static void main(String[] args) {
SpringApplication.run(InitApplication.class,args);
System.out.println(">>>>>>>项目启动成功");
//执行初始化数据.
// initData();
}
/* public static void initData(){
System.out.println("静态方法初始化数据");
}
*/
/**
* 添加Listener
* @date 2021/11/30 9:03
* @author zk_yjl
* @return
*/
@Bean
public ServletListenerRegistrationBean<InitListener> initListenerServletListenerRegistrationBean(){
ServletListenerRegistrationBean<InitListener> registrationBean=new ServletListenerRegistrationBean<>(
new InitListener()
);
return registrationBean;
}
}

SpringBoot系统启动任务(三十二)的方式_任务启动_03

在项目启动之前执行了,初始化 ExecutorService 之前.

二. SpringBoot 系统启动任务

通常使用 CommandLineRunner , ApplicationRunner , 和 @PostConstruct三种方式进行处理

关于参数的使用,可以参考江南一点雨大神的这篇文章.

二.一 CommandLineRunner 方式

如启动时,获取机器码

二.一.一 获取机器码的工具类 HardwareUtil

package top.yueshushu.init.util;
import lombok.extern.log4j.Log4j2;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.io.InputStream;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.List;
import java.util.Scanner;
@Log4j2
public class HardwareUtil {

private static final String[] getCpuIdWindows = {"wmic", "cpu", "get", "ProcessorId"};
private static String cpuId;

public static String getCpuId() {
try{
String osName = getOsName();
String cpuId = null;

if (osName.toLowerCase().contains("linux")) {
cpuId = getSerialNumber("dmidecode -t processor | grep 'ID'", "ID", ":");
} else if (osName.toLowerCase().contains("windows")) {
cpuId = getWindowsCpuId();
}
return !StringUtils.hasText(cpuId)?"1020304050607080":cpuId;
}catch (Exception e){
log.error("生成CpuId错误",e);
return "1020304050607080";
}
}

private static String getWindowsCpuId() {
try {
Process process = Runtime.getRuntime().exec(getCpuIdWindows);
process.getOutputStream().close();
Scanner sc = new Scanner(process.getInputStream());
String property = sc.next();
String serial = sc.next();
log.info(property + ": " + serial);
return serial;
} catch (IOException e) {
// TODO Auto-generated catch block
log.error("发生异常", e);
}

return null;
}

public static String getMac() {
NetworkInterface networkInterface = getRealNetworkInterface();
try {
if (networkInterface == null) {
return null;
}
byte[] mac = networkInterface.getHardwareAddress();
return buildMac(mac);
} catch (SocketException e) {
log.error("发生异常{}", e);
}
return null;
}

public static String getServerId() {
return buildServerId(getIpAddress());
}

private static String getIpAddress() {
NetworkInterface networkInterface = HardwareUtil.getRealNetworkInterface();
return networkInterface.getInterfaceAddresses().get(0).getAddress().getHostAddress();
}

private static NetworkInterface getRealNetworkInterface() {
try {
//枚举本机所有的网络接口
Enumeration<NetworkInterface> allNetInterfaces = NetworkInterface.getNetworkInterfaces();
while (allNetInterfaces.hasMoreElements()) {
NetworkInterface netInterface = allNetInterfaces.nextElement();

// 去除回环接口,子接口,未运行和接口
if (netInterface.isLoopback() || netInterface.isVirtual() || !netInterface.isUp()) {
continue;
}
return netInterface;
}
} catch (SocketException e) {
System.err.println("Error when getting host ip address" + e.getMessage());
}
return null;
}

private static String buildServerId(String address) {
List<String> ips = Arrays.asList(address.split("\\."));
return ips.stream().mapToInt(Integer::parseInt).boxed().map(Object::toString).reduce("", String::concat);
}

private static String buildMac(byte[] mac) {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < mac.length; i++) {
if (i != 0) {
sb.append("-");
}
String s = Integer.toHexString(mac[i] & 0xFF);
sb.append(s.length() == 1 ? 0 + s : s);
}
return sb.toString().toUpperCase();
}

private static String getOsName() {
return System.getProperty("os.name");
}


private static String getSerialNumber(String cmd, String record, String symbol) {
String execResult = executeLinuxCmd(cmd);
String[] infos = execResult.split("\n");

for (String info : infos) {
info = info.trim();
if (info.contains(record)) {
info.replace(" ", "");
String[] sn = info.split(symbol);
return sn[1];
}
}

return null;
}

private static String executeLinuxCmd(String cmd) {
try {
log.debug("got cmd job : " + cmd);
Runtime run = Runtime.getRuntime();
Process process;
process = run.exec(cmd);
InputStream in = process.getInputStream();
// BufferedReader bs = new BufferedReader(new InputStreamReader(in));
StringBuffer out = new StringBuffer();
byte[] b = new byte[8192];
for (int n; (n = in.read(b)) != -1; ) {
out.append(new String(b, 0, n));
}

in.close();
process.destroy();
return out.toString();
} catch (Exception e) {
log.error("发生异常", e);
}
return null;
}
}

二.一.二 实现 CommandLineRunner 的 run 方法

package top.yueshushu.init.line;

import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.util.Base64Utils;
import org.springframework.util.StringUtils;
import top.yueshushu.init.util.HardwareUtil;
@Component
@Order(1)
public class InitCommandLineRunner implements CommandLineRunner {

@Override
public void run(String... args) {
//args 获取传入的参数。
//容器启动时,进行的操作。 如获取计算机的 机器码
String cpuId = HardwareUtil.getCpuId();
if(!StringUtils.hasText(cpuId)){
System.out.println(">>>没有机器码");

}
String singleSignature = Base64Utils.encodeToString(cpuId.getBytes());
System.out.println(">>>机器码是:"+singleSignature);
}
}

@Order(1) 表示顺序。 可以定义多个启动方法, 数据越小,越先启动。

参数 String … args 对应的就是 main 方法里面的参数,获取参数的方式也是相同的.

二.一.三 测试

SpringBoot系统启动任务(三十二)的方式_PostConstruct_04

是在服务启动之后,进行处理的. 这个时候,已经是拥有 @Component, @Controller,@Service,@Repository 相关的实例了

二.一.四 使用组件

@Component
@Order(2)
public class InitCommandLineRunner2 implements CommandLineRunner {

@Autowired
private UserService userService;

@Override
public void run(String... args) {
userService.set();
userService.get();
}
}

启动服务

SpringBoot系统启动任务(三十二)的方式_系统任务启动_05

二.二 ApplicationRunner

将 InitCommandLineRunner 和 InitCommandLineRunner2 去掉 @Order 和 @Component 注解

二.二.一 实现 ApplicationRunner 的 run 接口

@Component
@Order(3)
public class InitApplicationRunner implements ApplicationRunner {

@Autowired
private UserService userService;
@Override
public void run(ApplicationArguments args) throws Exception {
//在运行时,进行的操作.
Random random=new Random();
System.out.println(">>>输出值:"+random.nextInt(100));

userService.set();
userService.get();
}
}

参数使用的是 ApplicationArguments args

  • args.getNonOptionArgs();`可以用来获取命令行中的无key参数(和CommandLineRunner一样)
  • args.getOptionNames();`可以用来获取所有key/value形式的参数的key
  • ​args.getOptionValues(key);​​可以根据key获取key/value 形式的参数的value
  • ​args.getSourceArgs();​​则表示获取命令行中的所有参数

二.二.二 测试

SpringBoot系统启动任务(三十二)的方式_PostConstruct_06

二.三 @PostConstruct 注解

@PostConstruct 注解,用于在依赖关系注入完成之后,需要执行的方法,用于执行初始化,只执行一次。

也就是说, 是在 @Autowired/@Resource 之后执行的注解

二.三.一 不需要继承接口

@Component
@Order(4)
public class InitMethod {
@Autowired
private UserService userService;
@PostConstruct
public void initName(){
System.out.println(">>>>初始化名称模块");
}
@PostConstruct
public void initSet(){
userService.set();
}
@PostConstruct
public void initGet(){
userService.get();
}
}

二.三.二 测试

SpringBoot系统启动任务(三十二)的方式_PostConstruct_07

在 ExecutorService 服务之前执行的。

与 init() 方法相同.

二.三.三 定义静态类

在实际项目中,一个相对完整的业务链,会有很多个 相关的 Service 。

通常会将相应的几个 Service进行封装,封装成一个工具类,对外提供方法.

先将 InitMethod 类上的 @Component 注解去掉

二.三.三.一 不使用 @PostConstructor 注解

  1. 定义工具类
@Component
public class MyUserServiceUtil {
@Autowired
private UserService userService;

public void handler(){
//先设置值
userService.set();
//再获取值
userService.get();
}
}
  1. 使用工具类

先注入,再使用

@SpringBootTest
public class PostTest {
//先注入
@Autowired
private MyUserServiceUtil myUserServiceUtil;
@Test
public void test(){
System.out.println(">>>先注入,再使用");
//再使用
myUserServiceUtil.handler();
}
}

SpringBoot系统启动任务(三十二)的方式_数据_08

每次都需要先注入,再使用。

如果能够定义成相关的静态方法,就好了.

二.三.三.二 使用 @PostConstructor 注解处理成静态类

@Component
public class MyUserServiceUtil2 {

//执行的顺序,先 Component, 再Autowired,后 PostConstruct
//内部注入
@Autowired
private UserService userService;

//定义代理的静态属性
private static UserService userServiceImpl;

@PostConstruct
public void setComponent(){
//赋予属性值
userServiceImpl=userService;
}
//方法是静态的,那么 调用者必须是静态的.
public static void handler(){
//先设置值
userServiceImpl.set();
//再获取值
userServiceImpl.get();
}
}

在使用时,直接用静态方法的形式即可.

@SpringBootTest
public class PostTest {
//先注入
@Autowired
private MyUserServiceUtil myUserServiceUtil;
@Test
public void test(){
System.out.println(">>>先注入,再使用");
//再使用
myUserServiceUtil.handler();
}


@Test
public void test2(){
System.out.println("静态方法,直接使用");
MyUserServiceUtil2.handler();
}
}

运行 test2() 方法

SpringBoot系统启动任务(三十二)的方式_PostConstruct_09

这种形式,在开发中很常用,一定要记住.

这就是SpringBoot 系统启动任务的常用方法.

本章节的代码放置在 github 上:

​https://github.com/yuejianli/springboot/tree/develop/SpringBoot_Init​



谢谢您的观看,如果喜欢,请关注我,再次感谢 !!!


标签:三十二,系统启动,return,SpringBoot,void,private,static,public,String
From: https://blog.51cto.com/u_13420484/5841801

相关文章

  • Springboot项目部署到docker
    Manve项目部署到docker第一步:将springboot项目打包Maven打包SpringBoot项目报错(repackagefailed:Unabletofindmainclass),排除寻找Main方法,一般用于被依赖的公用......
  • SpringBoot启动报错The APR based Apache Tomcat Native..
    SpringBoot项目启动报错TheAPRbasedApacheTomcatNativel...一、报错信息2022-11-1009:50:53org.apache.catalina.core.AprLifecycleListenerinit信息:TheAP......
  • 记录一次springboot 集成 openfeign 实现模块间调用异常
    记录一次springboot集成openfeign实现模块间调用异常 问题背景product 服务作为服务端,提供了一个对外通信Fegin接口ProductClient,放在了com.imooc.product.clie......
  • springboot整合项目-商城项目展示购物车勾选到支付页面并展示功能
    显示勾选的购物车数据1.持久层1.规划sql语句用户在购物车列表页面通过随机勾选相关的商品,在点击结算俺就后,跳转到结算页面,在这个页面需要展示用户在上个页面说勾选的购......
  • springboot启动直接退出显示Process finished with exit code 1
    SprintBoot项目启动后直接退出(未打印有关SP的日志)一、解决思路尝试在main方法前面加了一行打印语句:System.out.println(“SpringBootStart…”);//正常运行的打印......
  • 基于Docker部署Springboot工程
    主要参考文章:https://blog.csdn.net/qq_33285112/article/details/109726538https://www.cnblogs.com/linnuo/p/15699121.html一、首先基于Alpine构建Java最小运行环境......
  • springboot文件上传大小限制设置
    一般的web系统基本都会有文件上传功能,文件上传必然涉及到一个问题,就是文件大小,太大的文件不仅传输速度慢,而且对服务器压力巨大,后期的下载和保存都是一种考验。所以有了文......
  • 01-SpringBoot注解
    SpringBoot注解Spring常用注解配置注解含义@Configuration定义一个类是Spring配置类@Bean配置自定义的Bean,如DruidDataSource@Componen......
  • SpringBoot 01: JavaConfig + @ImportResource + @PropertyResource
    springboot的前置知识:通过注解创建对象和读取配置文件1.JavaConfig设计思想使用java类作为xml配置文件的替代,是配置spring容器的纯java的方式可以创建java对象并把......
  • springBoot实现全局跨域
    importlombok.extern.slf4j.Slf4j;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importorg.s......