当前位置: 首页 > news >正文

Spring中常见的BeanFactory后处理器

常见的BeanFacatory后处理器

先给出没有添加任何BeanFactory后处理器的测试代码

public class TestBeanFactoryPostProcessor {public static void main(String[] args) {GenericApplicationContext context = new GenericApplicationContext();context.registerBean("config", Config.class);context.refresh();for (String beanDefinitionName : context.getBeanDefinitionNames()) {System.out.println(beanDefinitionName);}context.close();}
}

在配置类中我们编写了如下信息

@Configuration
@ComponentScan("com.zmt.test5")
public class Config {@Beanpublic Bean2 bean2(){return new Bean2();}@Beanpublic SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource){SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();sqlSessionFactoryBean.setDataSource(dataSource);return sqlSessionFactoryBean;}@Beanpublic DruidDataSource dataSource(){DruidDataSource dataSource = new DruidDataSource();dataSource.setUrl("jdbc:mysql://localhost:3306/test");dataSource.setUsername("root");dataSource.setPassword("123456");return dataSource;}
}

同时还有一个Bean1添加了@Component注解并且能够被扫描到,所以理论上来讲,我们可以观察到五个beanName,那么执行测试代码观察输出结果

可以看到,这里只输出了一个beanName,我们可以推测出其他注解没有生效,那么接下来我们将常用的BeanFactory后处理器也注册到BeanFactory后,观察输出结果

public class TestBeanFactoryPostProcessor {public static void main(String[] args) {GenericApplicationContext context = new GenericApplicationContext();context.registerBean("config", Config.class);//添加BeanFactory后处理器context.registerBean(ConfigurationClassPostProcessor.class);//用来解析 @ComponentScan @Bean @Import @ImportResource注解context.registerBean(MapperScannerConfigurer.class,bd -> {bd.getPropertyValues().add("basePackage","com.zmt.test.mapper");});//扫描Mapper,相当于@MapperScan注解context.refresh();for (String beanDefinitionName : context.getBeanDefinitionNames()) {System.out.println(beanDefinitionName);}context.close();}
}

添加Spring提供的BeanFactory后处理器之后,可以正常将Bean对象添加到context容器当中了,执行结果如下

接下来我们模拟实现在BeanFactory后处理器当中具体如何解析这些注解。 

模拟实现组件扫描

首先是模拟实现@ComponentScan注解是如何扫描包,获取类资源的

public class TestBeanFactoryPostProcessor {public static void main(String[] args) throws IOException {GenericApplicationContext context = new GenericApplicationContext();context.registerBean("config", Config.class);//模拟ConfigurationPostProcessor处理器中如何解析@ComponentScan注解ComponentScan componentScan = AnnotationUtils.findAnnotation(Config.class, ComponentScan.class);if (componentScan != null) {for (String s : componentScan.basePackages()) {//将com.zmt.test5.bean转化格式为classpath*:com/zmt/test5/bean/**/*.classString path = "classpath*:"+s.replace(".", "/")+"/**/*.class";//创建出一个元数据读取工厂,用来读取类资源信息CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();//通过getResource方法获取到path中的所有类资源Resource[] resources = context.getResources(path);for (Resource resource : resources) {//读取类资源信息MetadataReader reader = factory.getMetadataReader(resource);System.out.println("类名:"+reader.getClassMetadata());System.out.println("是否添加了@Component注解:"+reader.getAnnotationMetadata().hasAnnotation(Component.class.getName()));System.out.println("是否添加了@Component的派生注解:"+reader.getAnnotationMetadata().hasMetaAnnotation(Component.class.getName()));}}}context.refresh();for (String beanDefinitionName : context.getBeanDefinitionNames()) {System.out.println(beanDefinitionName);}context.close();}
}

运行结果如下,可以正常识别类上是否添加了@Component注解或是派生注解

能够扫描到类上添加的注解之后,我们是需要将注解添加到BeanDefinitionMap当中去的,那么继续完善我们的测试方法

public class TestBeanFactoryPostProcessor {public static void main(String[] args) throws IOException {GenericApplicationContext context = new GenericApplicationContext();context.registerBean("config", Config.class);//模拟ConfigurationPostProcessor处理器中如何解析@ComponentScan注解ComponentScan componentScan = AnnotationUtils.findAnnotation(Config.class, ComponentScan.class);if (componentScan != null) {for (String s : componentScan.basePackages()) {//将com.zmt.test5转化格式为classpath*:com/zmt/test5/**/*.classString path = "classpath*:" + s.replace(".", "/") + "/**/*.class";//创建出一个元数据读取工厂,用来读取类资源信息CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();//通过getResource方法获取到path中的所有类资源Resource[] resources = context.getResources(path);//创建Bean名称生成器AnnotationBeanNameGenerator generator = new AnnotationBeanNameGenerator();for (Resource resource : resources) {//读取类资源信息MetadataReader reader = factory.getMetadataReader(resource);System.out.println("类名1:" + reader.getClassMetadata());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())) {//如果该类添加了注解,需要添加到BeanDefinitionMap当中去,生成BeanDefinition对象AbstractBeanDefinition bd = BeanDefinitionBuilder.genericBeanDefinition(reader.getClassMetadata().getClassName()).getBeanDefinition();DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory();//生成Bean名称String name = generator.generateBeanName(bd,beanFactory);System.out.println("name:"+name);//将BeanDefinition注册到beanFactorybeanFactory.registerBeanDefinition(name,bd);}}}}context.refresh();for (String beanDefinitionName : context.getBeanDefinitionNames()) {System.out.println(beanDefinitionName);}context.close();}
}

我们通过打断点查看beanFactory中是否将BeanDefinition信息注册到BeanDefinitionMap当中去,结果如下

至此已经实现了组件扫描,但是目前我们的实现是在容器初始化 [refresh()方法] 之前就做好了,我们应该将这些实现抽取到一个BeanFactory后处理器当中,等待refresh()方法回调,因此我们将这些实现代码放入一个组件扫描后处理器。

public class ComponentScanPostProcessor implements BeanFactoryPostProcessor {@Override //在执行context.refresh()方法时回调该方法public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {try {ComponentScan componentScan = AnnotationUtils.findAnnotation(Config.class, ComponentScan.class);if (componentScan != null) {for (String s : componentScan.basePackages()) {//将com.zmt.test5转化格式为classpath*:com/zmt/test5/**/*.classString path = "classpath*:" + s.replace(".", "/") + "/**/*.class";//创建出一个元数据读取工厂,用来读取类资源信息CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();//通过getResource方法获取到path中的所有类资源Resource[] resources = new PathMatchingResourcePatternResolver().getResources(path);//创建Bean名称生成器AnnotationBeanNameGenerator generator = new AnnotationBeanNameGenerator();for (Resource resource : resources) {//读取类资源信息MetadataReader reader = factory.getMetadataReader(resource);System.out.println("类名1:" + reader.getClassMetadata());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())) {//如果该类添加了注解,需要添加到BeanDefinitionMap当中去,生成BeanDefinition对象AbstractBeanDefinition bd = BeanDefinitionBuilder.genericBeanDefinition(reader.getClassMetadata().getClassName()).getBeanDefinition();if (configurableListableBeanFactory instanceof DefaultListableBeanFactory){DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) configurableListableBeanFactory;String name = generator.generateBeanName(bd,beanFactory);System.out.println("name:"+name);beanFactory.registerBeanDefinition(name,bd);}}}}}} catch (IOException e) {e.printStackTrace();}}
}

修改此时的测试代码,将我们自定义的BeanFactory后处理器注册到context当中 

public class TestBeanFactoryPostProcessor {public static void main(String[] args) throws IOException {GenericApplicationContext context = new GenericApplicationContext();context.registerBean("config", Config.class);//模拟ConfigurationPostProcessor处理器中如何解析@ComponentScan注解context.registerBean(ComponentScanPostProcessor.class);context.refresh();for (String beanDefinitionName : context.getBeanDefinitionNames()) {System.out.println(beanDefinitionName);}context.close();}
}

模拟实现@Bean注册

public class TestBeanFactoryPostProcessor {public static void main(String[] args) throws IOException {GenericApplicationContext context = new GenericApplicationContext();context.registerBean("config", Config.class);//模拟@Bean注解解析//@Bean注解的解析与组件扫描不太一样,@Bean的解析是将配置类当作工厂类,而被Bean注解修饰的方法被当作工厂方法//用来读取配置类信息CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();MetadataReader reader = factory.getMetadataReader(Config.class.getName());//获取被注解修饰的方法中被Bean注解修饰的方法Set<MethodMetadata> methods = reader.getAnnotationMetadata().getAnnotatedMethods(Bean.class.getName());for (MethodMetadata method : methods) {System.out.println(method);//因为是通过Config调用那些方法,因此这里不用指定类的名称,而是通过我们手动set设置bd的名称BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();//指定工厂类Config通过该类获取工厂方法,这里给工厂类的beanName即可builder.setFactoryMethodOnBean(method.getMethodName(),"config");AbstractBeanDefinition bd = builder.getBeanDefinition();//将BeanDefinition加入到BeanFactorycontext.registerBeanDefinition(method.getMethodName(),bd);}context.refresh();for (String beanDefinitionName : context.getBeanDefinitionNames()) {System.out.println(beanDefinitionName);}context.close();}
}

表面上以上代码就可以实现@Bean注解的解析,但实际上运行会出现如下错误

在创建sqlSessionFactoryBean时失败,这是因为在创建该对象时,需要注入一个参数,那么我们需要在创建BeanDefinition时需要指定Bean的装配模式

但是还是存在一个问题,那就是目前无法解析@Bean注解中的属性,比如说dataSource方法上的Bean注解中指定的初始化方法我们没有办法解析执行,因此,我们接着完善上面的代码,同时我们将这些实现代码同样抽取到一个BeanFactory后处理当中,那么具体的实现代码如下

public class AtBeanPostProcessor implements BeanFactoryPostProcessor {@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {try {CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();MetadataReader reader = factory.getMetadataReader(Config.class.getName());//获取被注解修饰的方法中被Bean注解修饰的方法Set<MethodMetadata> methods = reader.getAnnotationMetadata().getAnnotatedMethods(Bean.class.getName());for (MethodMetadata method : methods) {System.out.println(method);//获取方法上的注解属性信息,得到map集合后通过get方法获取初始化方法的方法名String initMethod = method.getAnnotationAttributes(Bean.class.getName()).get("initMethod").toString();//因为是通过Config调用那些方法,因此这里不用指定类的名称,而是通过我们手动set设置bd的名称BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();//指定工厂类Config通过该类获取工厂方法,这里给工厂类的beanName即可builder.setFactoryMethodOnBean(method.getMethodName(),"config");//如果是工厂方法的参数需要自动装配那么需要指定装配模式builder.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);if (initMethod.length()>0){//说明当前@Bean注解添加了initMethod属性builder.setInitMethodName(initMethod);}AbstractBeanDefinition bd = builder.getBeanDefinition();//将BeanDefinition加入到BeanFactoryDefaultListableBeanFactory context = (DefaultListableBeanFactory) beanFactory;context.registerBeanDefinition(method.getMethodName(),bd);}} catch (IOException e) {e.printStackTrace();}}
}

模拟实现MapperScan

在实现MapperScan之前,我们先通过配置类了解一下Spring底层是如何将接口创建对象并加入beanFactory当中的

@Configuration
@ComponentScan("com.zmt.test5.bean")
public class Config {//...上面已经展示过的内容@Beanpublic MapperFactoryBean<Mapper1> mapper1(SqlSessionFactory sqlSessionFactory){MapperFactoryBean<Mapper1> factoryBean = new MapperFactoryBean<>(Mapper1.class);factoryBean.setSqlSessionFactory(sqlSessionFactory);return factoryBean;}@Beanpublic MapperFactoryBean<Mapper2> mapper2(SqlSessionFactory sqlSessionFactory){MapperFactoryBean<Mapper2> factoryBean = new MapperFactoryBean<>(Mapper2.class);factoryBean.setSqlSessionFactory(sqlSessionFactory);return factoryBean;}
}

这样添加Mapper存在一个问题,就是无法批量添加,只要存在一个Mapper接口就需要编写一个@Bean注解。因此我们需要实现类似于ComponentScan的注解一样,实现Mapper注解的扫描,这里我们就直接将实现代码写在BeanFactory后处理器当中了

public class MapperPostProcessor implements BeanDefinitionRegistryPostProcessor {@Overridepublic void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanFactory) throws BeansException {try {//首先实现扫描包PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();Resource[] resources = resolver.getResources("classpath*:com/zmt/test5/mapper/*.class");//获取类信息CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();AnnotationBeanNameGenerator generator = new AnnotationBeanNameGenerator();for (Resource resource : resources) {MetadataReader reader = factory.getMetadataReader(resource);ClassMetadata classMetadata = reader.getClassMetadata();if (classMetadata.isInterface()) {//判断该类是否是接口,如果是接口那么再进行下一步//生成bd,我们是通过MapperFactoryBean获取接口对象的,因此这里指定MapperFactoryBean类名AbstractBeanDefinition bdFactoryBean = BeanDefinitionBuilder.genericBeanDefinition(MapperFactoryBean.class)//设置MapperFactoryBean构造方法中的需要生成的bean对象.addConstructorArgValue(classMetadata.getClassName())//需要注入sqlSessionFactory对象,这里选择根据类型注入.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE).getBeanDefinition();//生成bean名称需要依赖genericBeanDefinition中的指定的通用Bean的className//因为是根据MapperFactoryBean一个类生成所有的Mapper对象,因此采用MapperFactoryBean的bd生成beanName时//会导致生成的都是同一个名称,从而导致后者bd覆盖前者bd,所以我们再次生成一个bd,这里保存的ClassName是各个Mapper类名//在生成beanName时,bd选择各个Mapper接口生成的bdAbstractBeanDefinition bd = BeanDefinitionBuilder.genericBeanDefinition(classMetadata.getClassName()).getBeanDefinition();String name = generator.generateBeanName(bd, beanFactory);//注册到beanFactory中的还是bdFactoryBeanbeanFactory.registerBeanDefinition(name,bdFactoryBean);}}} catch (IOException e) {e.printStackTrace();}}@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {}
}

通过断点观察,我们可以知道beanName是各个mapper接口类名,但是beanClass还是MapperFactoryBean

需要注意的是,我们在创建Mapper的bd时,需要注入sqlSessionFactory对象,因此,我们需要确保Spring中存在该对象,因此,我们需要将上文中的AtBeanPostProcessor处理器也要执行

相关文章:

Spring中常见的BeanFactory后处理器

常见的BeanFacatory后处理器 先给出没有添加任何BeanFactory后处理器的测试代码 public class TestBeanFactoryPostProcessor {public static void main(String[] args) {GenericApplicationContext context new GenericApplicationContext();context.registerBean("co…...

FPGA LCD1602驱动代码 (已验证)

一.需求解读 1.需求 在液晶屏第一行显示“HELLO FPGA 1234!” 2. 知识背景 1602 液晶也叫 1602 字符型液晶,它是一种专门用来显示字母、数字、符号等的点阵 型液晶模块。它由若干个 5X7 或者 5X11 等点阵字符位组成,每个点阵字符位都可以显示一 个字符,每位之间有一个点距的…...

c++编程要养成的好习惯

1、缩进 你说有缩进看的清楚还是没缩进看的清楚 2、i和i i运行起来和i更快 3、 n%20和n&1 不要再用n%20来判断n是不是偶数了&#xff0c;又慢又土&#xff0c;用n&10&#xff0c;如果n&10就说明n是偶数 同理&#xff0c;n&11说明n是奇数 4、*2和<<…...

后台管理项目的多数据源方案

引言 在互联网开发公司中&#xff0c;往往伴随着业务的快速迭代&#xff0c;程序员可能没有过多的时间去思考技术扩展的相关问题&#xff0c;长久下来导致技术过于单一。为此最近在学习互联网思维&#xff0c;从相对简单的功能开始做总结&#xff0c;比如非常常见的基础数据的…...

视频美颜SDK趋势畅想:未来发展方向与应用场景

当下&#xff0c;视频美颜SDK正不断演进&#xff0c;本文将深入探讨视频美颜SDK的发展趋势&#xff0c;探讨未来可能的方向和广泛的应用场景。 1.深度学习与视频美颜的融合 未来&#xff0c;我们可以期待看到更多基于深度学习算法的视频美颜SDK&#xff0c;为用户提供更高质量…...

C++ const 限定符的全面介绍

C const 限定符的全面介绍 1. const 修饰基本数据类型 定义 const 修饰的基本数据类型变量&#xff0c;值不可改变。 语法 const type variable value;特点 不可变性&#xff0c;增加代码可读性。 作用 定义不可修改的常量。 使用场景 全局常量、配置项。 注意事项…...

Vue 中的 ref 与 reactive:让你的应用更具响应性(上)

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…...

华为云CCE-集群内访问-根据ip访问同个pod

华为云CCE-集群内访问-根据ip访问同个pod 问题描述&#xff1a;架构如下&#xff1a;解决方法&#xff1a; 问题描述&#xff1a; 使用service集群内访问时&#xff0c;由于启用了两个pod&#xff0c;导致请求轮询在两个pod之间&#xff0c;无法返回正确的结果。 架构如下&am…...

Kasada p.js (x-kpsdk-cd、x-kpsdk-ct、integrity)

提供x-kpsdk-cd的API服务 详细请私信~ 可试用~ V:zhzhsgg 一、简述 integrity是通过身份验证Kasada检测机器人流量后获得的一个检测结果&#xff08;数据完整性&#xff09; x-kpsdk-cd 是经过编码计算等等获得。当你得到正确的解决验证码值之后&#xff0c;解码会看到如下图…...

Thinkphp 5框架学习

TP框架主要是通过URL实现操作 http://servername/index.php/模块/控制器/操作/参数/值.. index.php 为入口文件&#xff0c;在 public 目录内的 index.php 文件; 模块在 application 目录下默认有一个 index 目录&#xff0c;这就是一个模块; 而在 index 目录下有一个 contro…...

麒麟云增加计算节点

一、安装基座系统并配置好各项设置 追加的计算节点服务器&#xff0c;安装好系统&#xff0c;把主机名、网络网线&#xff08;网线要和其他网线插的位置一样&#xff09;、hosts这些配置好&#xff0c;在所有节点的/etc/hosts里面添加信息 在控制节点添加/kylincloud/multinod…...

使用Redis进行搜索

文章目录 构建反向索引 构建反向索引 在Begin-End区域编写 tokenize(content) 函数&#xff0c;实现文本标记化的功能&#xff0c;具体参数与要求如下&#xff1a; 方法参数 content 为待标记化的文本&#xff1b; 文本标记的实现&#xff1a;使用正则表达式提取全小写化后的…...

Oracle修改用户密码

文章目录 Oracle修改用户密码Oracle用户锁定常见的两种状态Oracle用户锁定和解锁 Oracle修改用户密码 使用sys或system使用sysdba权限登录&#xff0c;然后执行以下命令修改密码&#xff1a; alter user 用户名 identified by 密码;密码过期导致的锁定&#xff0c;也通过修改…...

​LeetCode解法汇总1276. 不浪费原料的汉堡制作方案

目录链接&#xff1a; 力扣编程题-解法汇总_分享记录-CSDN博客 GitHub同步刷题项目&#xff1a; https://github.com/September26/java-algorithms 原题链接&#xff1a;力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台 描述&#xff1a; 圣诞活动预…...

Vue解决跨域问错误:has been blocked by CORS policy 后端跨域配置

解决跨域问题后端跨域配置代码&#xff1a; /*** 作者 hua* 描述 跨域配置*/ Configuration public class WebConfiguration implements WebMvcConfigurer {/*** 跨域配置对象* return CorsConfiguration对象*/private CorsConfiguration corsConfig() {CorsConfiguration cor…...

【谷歌云】注册谷歌云 创建Compute Engine

文章目录 一、Google Cloud注册1.1 账号信息1.2 付款信息验证1.3 验证成功 二、Compute Engine创建2.1 启动Compute Engine API2.2 创建实例2.3 新建虚拟机实例2.4 等待实例创建完成2.5 查看虚拟机配置信息2.6 创建防火墙规则2.7 SSH远程连接虚拟机 三、参考链接 一、Google Cl…...

面试数据库八股文五问五答第四期

面试数据库八股文五问五答第四期 作者&#xff1a;程序员小白条&#xff0c;个人博客 相信看了本文后&#xff0c;对你的面试是有一定帮助的&#xff01; ⭐点赞⭐收藏⭐不迷路&#xff01;⭐ 1&#xff09;什么情况下 mysql 会索引失效&#xff1f; 不使用索引列进行查询&a…...

2023 年中国金融级分布式数据库市场报告:TiDB 位列领导者梯队,创新能力与增长指数表现突出

近日&#xff0c;沙利文联合头豹研究院发布了中国数据库系列报告之《2023 年中国金融级分布式数据库市场报告》。 报告认为&#xff0c;金融行业对于分布式数据库信任度与认可度正在逐步提高&#xff0c;中国金融级分布式数据库市场正处于成熟落地的高增长阶段&#xff0c;行业…...

基于ExoPlayer的缓存方案实现

音视频APP 的一个必备功能就是在播放的时候会持续缓存完整个音频,同时进度条会更新缓存进度。但是目前Google推出的播放器ExoPlayer本身并没有提供什么方便的接口去实现这个功能,因此大多数的开发者可能会使用AndroidVideoCache 开源库来实现缓存。 AndroidVideoCache 的原理…...

前缀和算法 -- 寻找数组的中心坐标

个人主页&#xff1a;Lei宝啊 愿所有美好如期而遇 本题链接 力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台 输入描述 给定一个数组&#xff0c;接口为int pivotIndex(vector<int>& nums) 输出描述 我们以示例1为例画图解释&#xf…...

ubuntu搭建nfs服务centos挂载访问

在Ubuntu上设置NFS服务器 在Ubuntu上&#xff0c;你可以使用apt包管理器来安装NFS服务器。打开终端并运行&#xff1a; sudo apt update sudo apt install nfs-kernel-server创建共享目录 创建一个目录用于共享&#xff0c;例如/shared&#xff1a; sudo mkdir /shared sud…...

LeetCode - 394. 字符串解码

题目 394. 字符串解码 - 力扣&#xff08;LeetCode&#xff09; 思路 使用两个栈&#xff1a;一个存储重复次数&#xff0c;一个存储字符串 遍历输入字符串&#xff1a; 数字处理&#xff1a;遇到数字时&#xff0c;累积计算重复次数左括号处理&#xff1a;保存当前状态&a…...

Cloudflare 从 Nginx 到 Pingora:性能、效率与安全的全面升级

在互联网的快速发展中&#xff0c;高性能、高效率和高安全性的网络服务成为了各大互联网基础设施提供商的核心追求。Cloudflare 作为全球领先的互联网安全和基础设施公司&#xff0c;近期做出了一个重大技术决策&#xff1a;弃用长期使用的 Nginx&#xff0c;转而采用其内部开发…...

Neo4j 集群管理:原理、技术与最佳实践深度解析

Neo4j 的集群技术是其企业级高可用性、可扩展性和容错能力的核心。通过深入分析官方文档,本文将系统阐述其集群管理的核心原理、关键技术、实用技巧和行业最佳实践。 Neo4j 的 Causal Clustering 架构提供了一个强大而灵活的基石,用于构建高可用、可扩展且一致的图数据库服务…...

【Oracle】分区表

个人主页&#xff1a;Guiat 归属专栏&#xff1a;Oracle 文章目录 1. 分区表基础概述1.1 分区表的概念与优势1.2 分区类型概览1.3 分区表的工作原理 2. 范围分区 (RANGE Partitioning)2.1 基础范围分区2.1.1 按日期范围分区2.1.2 按数值范围分区 2.2 间隔分区 (INTERVAL Partit…...

iOS性能调优实战:借助克魔(KeyMob)与常用工具深度洞察App瓶颈

在日常iOS开发过程中&#xff0c;性能问题往往是最令人头疼的一类Bug。尤其是在App上线前的压测阶段或是处理用户反馈的高发期&#xff0c;开发者往往需要面对卡顿、崩溃、能耗异常、日志混乱等一系列问题。这些问题表面上看似偶发&#xff0c;但背后往往隐藏着系统资源调度不当…...

【分享】推荐一些办公小工具

1、PDF 在线转换 https://smallpdf.com/cn/pdf-tools 推荐理由&#xff1a;大部分的转换软件需要收费&#xff0c;要么功能不齐全&#xff0c;而开会员又用不了几次浪费钱&#xff0c;借用别人的又不安全。 这个网站它不需要登录或下载安装。而且提供的免费功能就能满足日常…...

RabbitMQ入门4.1.0版本(基于java、SpringBoot操作)

RabbitMQ 一、RabbitMQ概述 RabbitMQ RabbitMQ最初由LShift和CohesiveFT于2007年开发&#xff0c;后来由Pivotal Software Inc.&#xff08;现为VMware子公司&#xff09;接管。RabbitMQ 是一个开源的消息代理和队列服务器&#xff0c;用 Erlang 语言编写。广泛应用于各种分布…...

c++第七天 继承与派生2

这一篇文章主要内容是 派生类构造函数与析构函数 在派生类中重写基类成员 以及多继承 第一部分&#xff1a;派生类构造函数与析构函数 当创建一个派生类对象时&#xff0c;基类成员是如何初始化的&#xff1f; 1.当派生类对象创建的时候&#xff0c;基类成员的初始化顺序 …...

提升移动端网页调试效率:WebDebugX 与常见工具组合实践

在日常移动端开发中&#xff0c;网页调试始终是一个高频但又极具挑战的环节。尤其在面对 iOS 与 Android 的混合技术栈、各种设备差异化行为时&#xff0c;开发者迫切需要一套高效、可靠且跨平台的调试方案。过去&#xff0c;我们或多或少使用过 Chrome DevTools、Remote Debug…...