Spring Bean生命周期以及PostProcessor后置处理器

简介

所谓Bean的生命周期,就是一个 Bean 从创建到销毁,所经历的各种方法调用。

一个Bean的生命周期分为四个阶段:

  1. 实例化(Instantiation):Spring容器负责创建Bean的实例,可以通过构造方法或者无参构造方法进行实例化
  2. 属性赋值(populate):Spring容器通过属性注入的方式为Bean的属性赋值,可以通过setter方法或者无参构造方法进行属性赋值。
  3. 初始化(Initialization):在初始化阶段,Spring容器会调用Bean的初始化方法(例如InitializingBean接口afterPropertiesSet() 方法),以确保Bean的正确初始化。
  4. 销毁(Destruction):在销毁阶段,Spring容器会调用Bean的销毁方法(例如DisposableBean接口destroy() 方法),以便在容器关闭时清理资源。

在整个生命周期中,多个增强接口(后置处理器)贯穿了这四个阶段,这些增强接口为开发者提供了更灵活和强大的功能。

整个过程如下:

img

设计模式——模板方法

上述使用增强接口的处理方式用到了模板方法这种设计模式。

模板方法:方法中固定不变的步骤不动,会变化的部分(刚开始设计时不可以确定的部分)设计成抽象接口,在特定的时机调用抽象方法。

// 利用模板方法这种设计模式,可以实现在属性注入阶段进行动态扩展
public class TestMethodTemplate {public static void main(String[] args) {MyBeanFactory beanFactory = new MyBeanFactory();beanFactory.addBeanPostProcessor(bean -> System.out.println("解析 @Autowired"));beanFactory.addBeanPostProcessor(bean -> System.out.println("解析 @Resource"));beanFactory.getBean();}// 模板方法  Template Method Patternstatic class MyBeanFactory {private final List<BeanPostProcessor> processors = new ArrayList<>();/*** 构造、依赖注入、初始化以及销毁这些方法都是固定的保持不动* 在依赖注入阶段扩展属于会变化的部分,抽象成接口动态添加增强方法*/public Object getBean() {Object bean = new Object();System.out.println("构造 " + bean);System.out.println("依赖注入 " + bean); // @Autowired, @Resource// 会变化的部分for (BeanPostProcessor processor : processors) {processor.inject(bean);}System.out.println("初始化 " + bean);System.out.println("销毁" + bean);return bean;}// 添加增强接口实现public void addBeanPostProcessor(BeanPostProcessor processor) {processors.add(processor);}}/*** 抽象接口*/interface BeanPostProcessor {// 对依赖注入阶段的扩展void inject(Object bean);}
}

自定义BeanPostProcessor

自定义BeanPostProcessor,可以在每个Bean生命周期任意一个阶段执行自定义逻辑

@Component
public class MyBeanPostProcessor implements InstantiationAwareBeanPostProcessor, DestructionAwareBeanPostProcessor {private static final Logger log = LoggerFactory.getLogger(MyBeanPostProcessor.class);@Overridepublic Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {if (beanName.equals("lifeCycleBean"))log.debug("<<<<<< 实例化之前执行, 这里返回的对象会替换掉原本的 bean");// 返回null不会替换return null;}@Overridepublic boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {if (beanName.equals("lifeCycleBean")) {log.debug("<<<<<< 实例化之后执行, 这里如果返回 false 会跳过依赖注入阶段");
//            return false;}return true;}@Overridepublic PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {if (beanName.equals("lifeCycleBean"))log.debug("<<<<<< 依赖注入阶段执行, 如 @Autowired、@Value、@Resource");return pvs;}@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {if (beanName.equals("lifeCycleBean"))log.debug("<<<<<< 初始化之前执行, 这里返回的对象会替换掉原本的 bean, 如 @PostConstruct、@ConfigurationProperties");return bean;}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {if (beanName.equals("lifeCycleBean"))log.debug("<<<<<< 初始化之后执行, 这里返回的对象会替换掉原本的 bean, 如代理增强");return bean;}@Overridepublic void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException {if (beanName.equals("lifeCycleBean"))log.debug("<<<<<< 销毁之前执行, 如 @PreDestroy");}}

BeanPostProcessor执行顺序

BeanPostProcessor执行顺序可以排序:(无排序按添加顺序执行)

/*bean 后处理的的排序*/
public class TestProcessOrder {public static void main(String[] args) {DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();// 添加一些后置处理器&排序器AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);List<BeanPostProcessor> list = new ArrayList<>();list.add(new P1());list.add(new P2());list.add(new P3());list.add(new P4());list.add(new P5());// bean后置处理器排序 按 order 排序 (order从小到大排序)list.sort(beanFactory.getDependencyComparator());list.forEach(processor -> {processor.postProcessBeforeInitialization(new Object(), "");});/*排序之后:1. 实现了 PriorityOrdered 接口的优先级最高2. 实现了 Ordered 接口与加了 @Order 注解的平级, 按数字升序3. 其它的排在最后*/}@Order(1)static class P1 implements BeanPostProcessor {private static final Logger log = LoggerFactory.getLogger(P1.class);@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {log.debug("postProcessBeforeInitialization @Order(1)");return bean;}}@Order(2)static class P2 implements BeanPostProcessor {private static final Logger log = LoggerFactory.getLogger(P2.class);@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {log.debug("postProcessBeforeInitialization @Order(2)");return bean;}}// 优先级最高(排序的话)static class P3 implements BeanPostProcessor, PriorityOrdered {private static final Logger log = LoggerFactory.getLogger(P3.class);@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {log.debug("postProcessBeforeInitialization PriorityOrdered");return bean;}@Overridepublic int getOrder() {return 100;}}static class P4 implements BeanPostProcessor, Ordered {private static final Logger log = LoggerFactory.getLogger(P4.class);@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {log.debug("postProcessBeforeInitialization Ordered");return bean;}@Overridepublic int getOrder() {return 0;}}static class P5 implements BeanPostProcessor {private static final Logger log = LoggerFactory.getLogger(P5.class);@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {log.debug("postProcessBeforeInitialization");return bean;}}
}

PostProcessor

BeanFactoryPostProcessor 和 BeanPostProcessor 作用及执行时机

  • BeanFactoryPostProcessor解析Bean定义
  • BeanPostProcessor 在初始化Bean阶段进行增强扩展

image-20230710074857991

BeanPostProcessor

用于对象属性赋值

如果不往该容器里添加一些后置处理器的话,将类交给Spring容器管理,获得对象时,对象中的属性一般不会被赋值

添加一些后置处理器:

AutowiredAnnotationBeanPostProcessor:用于解析@Autowired、@Value

CommonAnnotationBeanPostProcessor:用于解析@Resource、@PostConstruct、@PreDestroy

ConfigurationPropertiesBindingPostProcessor:用于解析@ConfigurationProperties,读取配置信息

public class A04 {public static void main(String[] args) {// ⬇️GenericApplicationContext 是一个【干净】的容器 【没有添加任何后置处理器】GenericApplicationContext context = new GenericApplicationContext();// ⬇️用原始方法注册三个 beancontext.registerBean("bean1", Bean1.class);context.registerBean("bean2", Bean2.class);context.registerBean("bean3", Bean3.class);context.registerBean("bean4", Bean4.class);// 设置解析器,否则@Value注解会报错context.getDefaultListableBeanFactory().setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());context.registerBean(AutowiredAnnotationBeanPostProcessor.class); // 解析@Autowired @Valuecontext.registerBean(CommonAnnotationBeanPostProcessor.class); // @Resource @PostConstruct @PreDestroy// 解析@ConfigurationProperties(prefix = "java")ConfigurationPropertiesBindingPostProcessor.register(context.getDefaultListableBeanFactory());// ⬇️初始化容器context.refresh(); // 执行beanFactory后处理器, 添加bean后处理器, 初始化所有单例 (不执行该方法会报错)System.out.println(context.getBean(Bean1.class));System.out.println(context.getBean(Bean4.class));// ⬇️销毁容器context.close();/*学到了什么a. @Autowired 等注解的解析属于 bean 生命周期阶段(依赖注入, 初始化)的扩展功能b. 这些扩展功能由 bean 后处理器来完成*/}
}

AutowiredAnnotationBeanPostProcessor 运行分析 (DI依赖注入)

// AutowiredAnnotationBeanPostProcessor 运行分析
public class DigInAutowired {public static void main(String[] args) throws Throwable {DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();beanFactory.registerSingleton("bean2", new Bean2()); // 创建过程,依赖注入,初始化beanFactory.registerSingleton("bean3", new Bean3());beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver()); // @ValuebeanFactory.addEmbeddedValueResolver(new StandardEnvironment()::resolvePlaceholders); // ${} 的解析器// 1. 查找哪些属性、方法加了 @Autowired, 这称之为 InjectionMetadata
//        AutowiredAnnotationBeanPostProcessor processor = new AutowiredAnnotationBeanPostProcessor();
//        processor.setBeanFactory(beanFactory);//        Bean1 bean1 = new Bean1();
//        System.out.println(bean1);
//        processor.postProcessProperties(null, bean1, "bean1"); // 执行依赖注入 @Autowired @Value
//        System.out.println(bean1);// 反射调用 findAutowiringMetadata 方法找到类中带有@Autowired、@Value注解的属性信息
//        Method findAutowiringMetadata = AutowiredAnnotationBeanPostProcessor.class.getDeclaredMethod("findAutowiringMetadata", String.class, Class.class, PropertyValues.class);
//        findAutowiringMetadata.setAccessible(true);
//        InjectionMetadata metadata = (InjectionMetadata) findAutowiringMetadata.invoke(processor, "bean1", Bean1.class, null);// 获取 Bean1 上加了 @Value @Autowired 的成员变量,方法参数信息
//        System.out.println(metadata);// 2. 调用 InjectionMetadata 来进行依赖注入, 注入时按类型查找值
//        metadata.inject(bean1, "bean1", null);
//        System.out.println(bean1);// 3. 如何按类型查找值 反射通过属性名或者方法名找到属性值Field bean3 = Bean1.class.getDeclaredField("bean3");// true找不到属性对象直接报错,false找不到属性对象返回null,然后使用反射将属性对象设置给bean对象DependencyDescriptor dd1 = new DependencyDescriptor(bean3, false);Object o = beanFactory.doResolveDependency(dd1, null, null, null);System.out.println(o);Method setBean2 = Bean1.class.getDeclaredMethod("setBean2", Bean2.class);DependencyDescriptor dd2 =new DependencyDescriptor(new MethodParameter(setBean2, 0), true);Object o1 = beanFactory.doResolveDependency(dd2, null, null, null);System.out.println(o1);Method setHome = Bean1.class.getDeclaredMethod("setHome", String.class);DependencyDescriptor dd3 = new DependencyDescriptor(new MethodParameter(setHome, 0), true);Object o2 = beanFactory.doResolveDependency(dd3, null, null, null);System.out.println(o2);}
}

BeanFactoryPostProcessor

用于解析Bean定义

image-20230710225709410

ConfigurationClassPostProcessor:用于解析@ComponentScan、@Bean、@Import、 @ImportResource,将类解析成一个一个bean注册到Spring容器中

MapperScannerConfigurer:Mybatis自定义后置处理器,相当于@MapperScan,将一个个Mybatis Mapper接口解析成MapperFactoryBean对象注册到BeaFactory容器中

/*BeanFactory 后处理器的作用*/
public class A05 {public static void main(String[] args) throws IOException {// ⬇️GenericApplicationContext 是一个【干净】的容器GenericApplicationContext context = new GenericApplicationContext();context.registerBean("config", Config.class);// Spring自带后置处理器context.registerBean(ConfigurationClassPostProcessor.class); // @ComponentScan @Bean @Import @ImportResource// 三方自定义后置处理器context.registerBean(MapperScannerConfigurer.class, bd -> { // @MapperScanbd.getPropertyValues().add("basePackage", "com.lkl.spring.chapter5.mapper");});// ⬇️初始化容器context.refresh();for (String name : context.getBeanDefinitionNames()) {System.out.println(name);}Mapper1 mapper1 = context.getBean(Mapper1.class);Mapper2 mapper2 = context.getBean(Mapper2.class);System.out.println("-->" + mapper1);System.out.println("-->" + mapper2);// ⬇️销毁容器context.close();/*学到了什么a. @ComponentScan, @Bean, @Mapper 等注解的解析属于核心容器(即 BeanFactory)的扩展功能b. 这些扩展功能由不同的 BeanFactory 后处理器来完成, 其实主要就是补充了一些 bean 定义*/}
}

Config配置类:

@Configuration
@ComponentScan("com.lkl.spring.chapter5.component")
public class Config {@Beanpublic Bean1 bean1() {return new Bean1();}@Beanpublic SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource) {SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();sqlSessionFactoryBean.setDataSource(dataSource);return sqlSessionFactoryBean;}@Bean(initMethod = "init")public DruidDataSource dataSource() {DruidDataSource dataSource = new DruidDataSource();dataSource.setUrl("jdbc:mysql://localhost:3306/test");dataSource.setUsername("root");dataSource.setPassword("root");return dataSource;}/*    @Beanpublic MapperFactoryBean<Mapper1> mapper1(SqlSessionFactory sqlSessionFactory) {MapperFactoryBean<Mapper1> factory = new MapperFactoryBean<>(Mapper1.class);factory.setSqlSessionFactory(sqlSessionFactory);return factory;}@Beanpublic MapperFactoryBean<Mapper2> mapper2(SqlSessionFactory sqlSessionFactory) {MapperFactoryBean<Mapper2> factory = new MapperFactoryBean<>(Mapper2.class);factory.setSqlSessionFactory(sqlSessionFactory);return factory;}*/
}

自定义简易PostProcessor,解析@ComponentScan注解

public class ComponentScanPostProcessor implements BeanDefinitionRegistryPostProcessor {@Override // context.refreshpublic void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {}@Overridepublic void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanFactory) throws BeansException {try {ComponentScan componentScan = AnnotationUtils.findAnnotation(Config.class, ComponentScan.class);if (componentScan != null) {for (String p : componentScan.basePackages()) {System.out.println(p);// com.lkl.spring.chapter5.component -> classpath*:com/lkl/spring/chapter5/component/**/*.class 文件夹及子文件夹下所有类String path = "classpath*:" + p.replace(".", "/") + "/**/*.class";System.out.println(path);CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();// 根据路径寻找资源Resource[] resources = new PathMatchingResourcePatternResolver().getResources(path);AnnotationBeanNameGenerator generator = new AnnotationBeanNameGenerator();for (Resource resource : resources) {// System.out.println(resource);// 解析类的元数据MetadataReader reader = factory.getMetadataReader(resource);// System.out.println("类名:" + reader.getClassMetadata().getClassName());AnnotationMetadata annotationMetadata = reader.getAnnotationMetadata();// System.out.println("是否加了 @Component:" + annotationMetadata.hasAnnotation(Component.class.getName()));// System.out.println("是否加了 @Component 派生:" + annotationMetadata.hasMetaAnnotation(Component.class.getName()));if (annotationMetadata.hasAnnotation(Component.class.getName())|| annotationMetadata.hasMetaAnnotation(Component.class.getName())) {// 创建BeanDefinition注册到Spring中AbstractBeanDefinition bd = BeanDefinitionBuilder.genericBeanDefinition(reader.getClassMetadata().getClassName()).getBeanDefinition();// 生成beanNameString name = generator.generateBeanName(bd, beanFactory);beanFactory.registerBeanDefinition(name, bd);}}}}} catch (IOException e) {e.printStackTrace();}}
}

自定义简易PostProcessor,解析@Bean注解

public class AtBeanPostProcessor implements BeanDefinitionRegistryPostProcessor {@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {}@Overridepublic void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanFactory) throws BeansException {try {CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();MetadataReader reader = factory.getMetadataReader(new ClassPathResource("com/lkl/spring/chapter5/Config.class"));Set<MethodMetadata> methods = reader.getAnnotationMetadata().getAnnotatedMethods(Bean.class.getName());for (MethodMetadata method : methods) {System.out.println(method);String initMethod = method.getAnnotationAttributes(Bean.class.getName()).get("initMethod").toString();BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();builder.setFactoryMethodOnBean(method.getMethodName(), "config");// 设置自动装配的类型 构造器builder.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);if (initMethod.length() > 0) {builder.setInitMethodName(initMethod);}AbstractBeanDefinition bd = builder.getBeanDefinition();beanFactory.registerBeanDefinition(method.getMethodName(), bd);}} catch (IOException e) {e.printStackTrace();}}
}

自定义简易PostProcessor,解析@Mapper注解

public class MapperPostProcessor implements BeanDefinitionRegistryPostProcessor {@Overridepublic void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanFactory) throws BeansException {try {PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();Resource[] resources = resolver.getResources("classpath:com/lkl/spring/chapter5/mapper/**/*.class");AnnotationBeanNameGenerator generator = new AnnotationBeanNameGenerator();CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();for (Resource resource : resources) {MetadataReader reader = factory.getMetadataReader(resource);ClassMetadata classMetadata = reader.getClassMetadata();if (classMetadata.isInterface()) {// 创建 MapperFactoryBean BeanDefinitionAbstractBeanDefinition bd = BeanDefinitionBuilder.genericBeanDefinition(MapperFactoryBean.class).addConstructorArgValue(classMetadata.getClassName()).setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE).getBeanDefinition();// 根据 Mapper className 生成 BeanDefinition 从而生成 beanNameAbstractBeanDefinition bd2 = BeanDefinitionBuilder.genericBeanDefinition(classMetadata.getClassName()).getBeanDefinition();String name = generator.generateBeanName(bd2, beanFactory);beanFactory.registerBeanDefinition(name, bd);}}} catch (IOException e) {e.printStackTrace();}}@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {}
}

注册自定义后置处理器,与上方效果一致

public class A05 {public static void main(String[] args) throws IOException {// ⬇️GenericApplicationContext 是一个【干净】的容器GenericApplicationContext context = new GenericApplicationContext();context.registerBean("config", Config.class);context.registerBean(ComponentScanPostProcessor.class); // 解析 @ComponentScancontext.registerBean(AtBeanPostProcessor.class); // 解析 @Beancontext.registerBean(MapperPostProcessor.class); // 解析 Mapper 接口// ⬇️初始化容器context.refresh();for (String name : context.getBeanDefinitionNames()) {System.out.println(name);}Mapper1 mapper1 = context.getBean(Mapper1.class);Mapper2 mapper2 = context.getBean(Mapper2.class);System.out.println("-->" + mapper1);System.out.println("-->" + mapper2);// ⬇️销毁容器context.close();/*学到了什么自定义后置处理器解析注解,封装为BeanDefinition 注册到Spring容器中*/}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/18252.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

VMWare在Ubuntu系统下无法启动问题

项目场景&#xff1a; 在Ubuntu系统安装了VMWare虚拟机&#xff0c;已开始还能打开VMWare&#xff0c;能进入正常VM界面。最近怎么进入不了VM主界面。启动虚拟机发现报错&#xff1a;Unable to install all modules. See ****; 问题描述 1、启动VMware&#xff1a;提示内核需…

QT检测USB HID设备的拔插

网上的参考代码很多&#xff0c;比如下面这个&#xff1a; QT 检测hid设备拔插打印设备信息_qt hid打印机_研知电子的博客-CSDN博客 但是&#xff0c;参考了很多人的代码&#xff0c;写出来的发现检测不到USB HID设备的拔插。 明明其他人都可以正常使用&#xff0c;那问题应…

哈达玛矩阵乘法

哈达玛矩阵乘法 作者: 赵晓鹏时间限制: 1S章节: 递归与分治 输入说明 : 见问题描述。 输出说明 : 见问题描述。 输入范例 : 1 4 -6 输出范例 : -2 10 #include <iostream> #include <vector> using namespace std; vector<int>res; void cal(int len…

RocketMQ下载安装、集群搭建保姆级教程

目录 1.下载安装 2.配置 3.测试 4.集群配置 4.1.规划 4.2.环境准备 4.3.节点配置 4.3.1.master1 4.3.2.slave2 4.3.3.master2 4.3.4.slave1 4.4.启动 4.5.测试 1.下载安装 前置条件&#xff1a; JDK环境 下载地址&#xff1a; 下载 | RocketMQ 博主下载的是4.…

如何在Docker和Kubernetes中使用代理IP?

Docker和Kubernetes是目前非常流行的容器化技术&#xff0c;这些技术被广泛用于开发、部署和管理应用程序。在某些情况下&#xff0c;需要使用代理IP来访问特定的网络资源。本文将介绍如何在Docker和Kubernetes中使用代理IP&#xff0c;并提供详细的举例说明。 一、在Docker中使…

excel 复制出来的数据内容自动带上空格

在excel中批量处理完了公式&#xff0c;想复制到navicat 或者文本编辑框里&#xff0c;发现都会自动带上双引号&#xff0c;但是excel 里是没有&#xff0c;查找了半天。 在excel里的文本如下所示 拷贝出来的结果如下所示&#xff1a; 经过检查发现原文中只要带有回车或者换行…

目标检测常用的评价指标

目标检测常用的评价指标 1 IoU&#xff08;Intersection over Union&#xff09;2 GIoU&#xff08;Generalized IoU&#xff09;3 DIoU&#xff08;Distance-IoU&#xff09;4 CIoU&#xff08;Complete-IoU&#xff09;5 EIoU&#xff08;Efficient-IoU&#xff09;6 SIoU7 W…

微服务网关SpringCloudGateway实战

目录 微服务网关SpringCloudGateway 1.概述 2.核心概念 快速入门 1.微服务开发 2.网关配置创建一个Gateway服务&#xff0c;引入以下依赖&#xff1a; 微服务网关SpringCloudGateway 1.概述 Spring cloud gateway是spring官方基于Spring 5.0、Spring Boot2.0和Project R…

一种简单的数据库性能测试方法

这两天接到一个任务&#xff0c;要测试一个服务器的性能&#xff0c;客户要求向数据库内 1000/s&#xff08;每插入一千条数据&#xff09; 的处理能力&#xff0c;当时脑子赌赛了&#xff0c;想的是用LR来进行&#xff0c;由于LR接触不深&#xff0c;只知道LR实现参数化的时候…

巧用word 邮件合并批量输出报告

在实际调查中&#xff0c;往往遇到很多统计信息要单独生成调查报告。word 邮件合并就能很好的帮助我们快速实现批量产出报告。 具体案例如下&#xff1a; 目前入河排污口调查正在如火如荼开展&#xff0c;我们排查收集了大量信息&#xff0c;整理为表格。 要将这些表格输出为…

Kubernetes中Pod的调度策略

Kubernetes中Pod的调度策略 1、Pod调度 在 Kubernetes 平台上&#xff0c;我们很少会直接创建一个 Pod&#xff0c;在大多数情况下会通过 RC、Deployment、 DaemonSet、Job 等控制器完成对一组 Pod 副本的创建、调度及全生命周期的自动控制任务。 在最早的 Kubernetes 版本…

idea 添加类库

打开项目中的独立环境文件夹&#xff0c;右键打开终端输入安装类库的命令&#xff1a; pip install requests pip3 install BeautifulSoup4 检查这里是否把类库加进来了&#xff0c;加进来就完成&#xff01;