【微服务】spring 条件注解从使用到源码分析详解
目录
一、前言
二、spring 条件注解概述
2.1 条件注解@Conditional介绍
2.2 @Conditional扩展注解
2.2.1 @Conditional扩展注解汇总
三、spring 条件注解案例演示
3.1 @ConditionalOnBean
3.2 @ConditionalOnMissingBean
3.2.1 使用在类上
3.2.2 使用场景补充
3.3 @ConditionalOnClass
3.4 @ConditionalOnExpression
3.5 @ConditionalOnProperty
四、条件注解源码剖析
4.1 @ConditionalOnMissingClass 源码剖析
4.1.1 debug过程分析
4.1.2 源码分析过程小结
五、条件注解的应用场景
5.1 条件注解使用案例
六、写在文末
一、前言
spring框架之所以得到普遍应用,一方面是框架优良的封装性,能够帮助开发人员节省很多底层开发工作,从而提升效率,另一方面框架自身提供了很多扩展点,开发者通过这些扩展点可以对自身业务进行很好的拓展,比如自定义starter,从而与spring框架无缝集成。
随着springboot逐渐成为微服务开发的标配,得到很多开发者的喜爱,springboot相比spring来说,增加了很多对开发友好的功能、配置等,尤其是全注解开发是springboot框架的一大特色,由于注解相当的多,接下来以spring框架中一个容易被大家忽略的扩展点,即条件注解为例进行说明。
二、spring 条件注解概述
2.1 条件注解@Conditional介绍
spring中核心条件注解是@Conditional,它用于判断Bean是否满足特定的条件,如果满足(或不满足)条件,则将标注了该注解的bean注册到IOC容器,否则不注册。因此, @Conditional这个注解的核心就是用来控制 Bean 的创建的。
SpringBoot自动配置功能里面就大量的使用了条件注解,还有很多与springboot集成的第三方组件也有使用。
@Conditional注解和Condition接口搭配一起使用,通过对应Condition接口来告知是否满足匹配条件,注解源码如下:
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {/*** 所有用于匹配的Condition接口(实现该接口的类),只有这些类都返回true才认为是满足条件*/Class<? extends Condition>[] value();
}
@Conditional注解可以添加在@Configuration、@Component、@Service等修饰的类上,用于控制对应的Bean是否需要创建,如果添加在@Bean修饰的方法上,则用于控制方法对应的Bean是否需要创建
2.2 @Conditional扩展注解
事实上,在实际开发或看到的框架级源码中,直接使用@Conditional并不多,更多的是使用像@ConditionalOnBean,@ConditionalOnMissingBean,或@ConditionalOnClass等这样的注解,我们也称之为扩展注解,以@ConditionalOnBean为例,不妨看一下该注解源码,可以看到,该注解添加了对@Conditional的引用,侧面说明了属于@Conditional注解的扩展这一说法。
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional({OnBeanCondition.class})
public @interface ConditionalOnBean {Class<?>[] value() default {};String[] type() default {};Class<? extends Annotation>[] annotation() default {};String[] name() default {};SearchStrategy search() default SearchStrategy.ALL;Class<?>[] parameterizedContainer() default {};
}
2.2.1 @Conditional扩展注解汇总
下面列举的是@Conditional常用的扩展注解
注解名称 | 使用参考 | 生效位置 | 备注 |
---|---|---|---|
@ConditionalOnBean | @ConditionalOnBean(value=AA.class) | bean | IOC容器中存在括号中的实例时生效 |
@ConditionalOnMissingBean | @ConditionalOnMissingBean(name = "bean名称") | bean | IOC容器中不存在括号中的实例时生效 |
@ConditionalOnClass | @ConditionalOnClass(AA.class) | class | 类加载器中存在对应的类生效 |
@ConditionalOnMissingClass | @ConditionalOnMissingClass(AA.class) | class | 类加载器中不存在对应的类生效 |
@ConditionalOnExpression | @ConditionalOnExpression(“’表达式值’”) | class 或 bean | 判断SpEL 表达式成立生效 |
@ConditionalOnProperty | @ConditionalOnProperty(prefix = “spring.redis”, name = “port”, havingValue = “3306”, matchIfMissing = true) | class 或 bean | 应用环境属性满足条件生效 |
@ConditionalOnResource | @ConditionalOnResource(resources= “common.properties”) | class 或 bean | 应用环境存在指定的资源文件生效 |
三、spring 条件注解案例演示
上面总结了@Conditional条件注解以及相关的扩展注解,为了加深对这些注解的理解,接下来针对各个注解的使用通过代码进行演示,以形成更深刻的印象。
3.1 @ConditionalOnBean
@ConditionalOnBean对应的Condition处理类是OnBeanCondition,即在IOC容器中存在指定的Bean时当前的bean则被创建,源码如下,该注解可用在方法或类上。
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional({OnBeanCondition.class})
public @interface ConditionalOnBean {Class<?>[] value() default {};String[] type() default {};Class<? extends Annotation>[] annotation() default {};String[] name() default {};SearchStrategy search() default SearchStrategy.ALL;Class<?>[] parameterizedContainer() default {};
}
创建如下配置类,在当前类中,在depart这个bean的方法上通过ConditionalOnBean注解来控制,即如果IOC容器中存在了role的bean实例,则depart这个bean才会被创建出来;
@ComponentScan("com.congge")
@Configuration
public class ScanConfig {@Beanpublic Role role(){return new Role("0002","测试角色");}/*** 也可以写为:@ConditionalOnBean(name = "role") , name的值确保是正确的* @return*/@Bean@ConditionalOnBean(Role.class)public Depart depart(){return new Depart("0001","架构部");}}
按照上述代码,role这个bean实例会被创建,所以depart的bean也会被创建出来,使用下面的代码进行测试;
public static void main(String[] args) {AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ScanConfig.class);//角色存在Role role = (Role)applicationContext.getBean("role");System.out.println(role.getRoleName());//部门是否存在Depart depart = (Depart)applicationContext.getBean("depart");System.out.println(depart.getDeptName());}
运行上面的代码,可以看到如下效果,说明depart的bean实例也创建了;
如果去掉role的bean实例创建,改造后代码如下
@ComponentScan("com.congge")
@Configuration
public class ScanConfig {@Bean@ConditionalOnBean(Role.class)public Depart depart(){return new Depart("0001","架构部");}}
改造测试代码
public static void main(String[] args) {AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ScanConfig.class);//角色存在//Role role = (Role)applicationContext.getBean("role");//System.out.println("role :" + role);//System.out.println(role.getRoleName());//部门是否存在Depart depart = (Depart)applicationContext.getBean("depart");System.out.println("depart :" + depart);//System.out.println(depart.getDeptName());}
再次运行时直接报错了,即depart这个bean不存在,反向说明@ConditionalOnBean生效了
3.2 @ConditionalOnMissingBean
@ConditionalOnMissingBean对应的Condition实现类是OnBeanCondition,即IOC容器中不存在指定的Bean则创建当前的bean(或类中的bean),对应源码如下:
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional({OnBeanCondition.class})
public @interface ConditionalOnMissingBean {Class<?>[] value() default {};String[] type() default {};Class<?>[] ignored() default {};String[] ignoredType() default {};Class<? extends Annotation>[] annotation() default {};String[] name() default {};SearchStrategy search() default SearchStrategy.ALL;Class<?>[] parameterizedContainer() default {};
}
紧接上述案例,在创建user的bean的方法上面使用ConditionalOnMissingBean注解,代码如下:
@ComponentScan("com.congge")
@Configuration
public class ScanConfig {@Beanpublic Role role(){return new Role("0002","测试角色");}@Bean@ConditionalOnMissingBean(name = "depart")public User user(){return new User("0003","jerry");}}
按照代码猜想,user方法使用当前注解,表示当IOC容器中不存在depart这个bean的时候,user这个bean会被创建出来,使用下面的测试代码
public static void main(String[] args) {AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ScanConfig.class);//角色存在Role role = (Role)applicationContext.getBean("role");System.out.println(role.getRoleName());//用户是否存在User user = (User)applicationContext.getBean("user");System.out.println(user.getUserName());}
运行这段代码,看到如下效果,说明注解生效了
3.2.1 使用在类上
以上演示的是两个注解使用在方法上的效果,从源码上可以发现,也支持在类上面使用,如果作用在类上面时表示整个类中的bean生效;
我们在类上添加这个注解,如果IOC容器中并没有userBean这个实例的情况下,可以创建role和depart的bean实例
@ComponentScan("com.congge")
@Configuration
@ConditionalOnMissingBean(name = "userBean")
public class ScanConfig {@Beanpublic Role role(){return new Role("0002","测试角色");}/*** 也可以写为:@ConditionalOnBean(name = "role")* @return*/@Beanpublic Depart depart(){return new Depart("0001","架构部");}}
运行如下的测试代码
public static void main(String[] args) {AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ScanConfig.class);//用户是否存在//User user = (User)applicationContext.getBean("userBean");//System.out.println(user.getUserName());//角色存在Role role = (Role)applicationContext.getBean("role");System.out.println(role.getRoleName());//角色存在Depart depart = (Depart)applicationContext.getBean("depart");System.out.println(depart.getDeptName());}
通过控制台输出日志也印证了上面的猜想,可以正确获取role或depart对应的bean
3.2.2 使用场景补充
对于该注解的使用场景,做如下补充:
- 项目中,只允许某个bean注入一次的时候使用,即保证在ioc中这个bean的实例只有一个1;
- 一般来说,对于自定义配置类,可以加@ConditionalOnMissingBean注解,以避免多个配置同时注入的风险;
加了这个注解后,当你注册两个相同类型的bean时,
会抛出异常
,它会保证同一个类型的bean只有一个
如下,同时创建两个User的bean实例,user1和user2;
@Bean@ConditionalOnMissingBeanpublic User user1(){return new User("user1","james");}@Bean@ConditionalOnMissingBeanpublic User user2(){return new User("user2","zhaoyun");}
通过运行下面的测试代码发现,在这种情况下,最终只创建了user1这个bean
public static void main(String[] args) {AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ScanConfig.class);User user1 = (User)applicationContext.getBean("user1");System.out.println(user1.getUserName());User user2 = (User)applicationContext.getBean("user2");System.out.println(user2.getUserName());}
运行效果如下:
3.3 @ConditionalOnClass
@ConditionalOnClass对应的Condition处理类是OnClassCondition,即如果当前类路径下面有指定的类时则生效。对应的源码如下:
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional({OnClassCondition.class})
public @interface ConditionalOnClass {Class<?>[] value() default {};String[] name() default {};
}
在当前工程中存在User这样一个类的情况下,通过这个注解,可以预想role这个bean可以被创建出来
@ComponentScan("com.congge")
@Configuration
//@ConditionalOnMissingBean(name = "userBean")
public class ScanConfig {/*** 也可以写成: com.congge.entity.User* @return*/@Bean@ConditionalOnClass(User.class)public Role role(){return new Role("0002","测试角色");}}
运行测试代码,可以看到role对应的bean被创建出来了;
假如将User类删除或注释掉,使用下面代码,再次测试
/*** 也可以写成: com.congge.entity.User* @return*/@Bean//@ConditionalOnClass(User.class)@ConditionalOnClass(name ="com.congge.entity.User")public Role role(){return new Role("0002","测试角色");}
运行可以看到下面的效果
@ConditionalOnMissingClass 可以类比进行本地验证
3.4 @ConditionalOnExpression
@ConditionalOnExpression对应的Condition处理类是OnExpressionCondition,即只有当SpEL表达式满足条件的时候则生效,对应的源码如下
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@Documented
@Conditional({OnExpressionCondition.class})
public @interface ConditionalOnExpression {String value() default "true";
}
在本地配置文件 application.properties中增加如下配置:
server.port=8087#条件表达式需用到的配置
hxstrive.type=server
hxstrive.user.enable=true
hxstrive.order.size=10
在配置类上添加@ConditionalOnExpression注解,代码如下,当作用在类上面的时候,表示当表达式中的条件得到满足时,类中的bean将会被创建;
@Configuration
@ConditionalOnExpression("'${hxstrive.type}'.equals('server') && (${hxstrive.user.enable})")
public class ScanConfig {@Beanpublic Role role(){return new Role("0002","测试角色");}
}
关于SPEL表达式的写法,网上有非常详细的资料可供参考,这里就不再过多展示了
再在启动类中获取一下role的bean,如果能够获取到,说明表达式生效了
@SpringBootApplication
@EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class})
public class AnnoApp {public static void main(String[] args) {ConfigurableApplicationContext context = SpringApplication.run(AnnoApp.class, args);Role role = (Role) context.getBean("role");System.out.println(role.getRoleName());}
}
运行上述启动类,通过控制台可以发现,role这个bean的实例创建出来了
3.5 @ConditionalOnProperty
@ConditionalOnProperty对应的Condition实现类OnPropertyCondition,只有当对应的配置属性和给定条件的值相等的时候则生效,源码如下,
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@Documented
@Conditional({OnPropertyCondition.class})
public @interface ConditionalOnProperty {String[] value() default {};String prefix() default "";String[] name() default {};String havingValue() default "";boolean matchIfMissing() default false;
}
该注解通过其三个属性prefix,name以及havingValue来实现的,其中prefix表示配置文件里节点前缀,name用来从application.properties中读取某个属性值,havingValue表示目标值,具体来说:
- 如果该值为空,则返回false;
- 如果值不为空,则将该值与havingValue指定的值进行比较,如果一样则返回true,否则返回false;
- 返回值为false,则该configuration不生效;为true则生效;
在如下代码中,userBean的创建依赖于类上的注解@ConditionalOnProperty以及其中的属性值是否满足要求
@Configuration
@ConditionalOnProperty(prefix="spring.user",name = "username", havingValue = "jerry")
public class BeanConfig {@Bean("userBean")public User userBean(){return new User("0003","mike");}
}
在application.properties配置文件中存在如下配置
spring.user.username=jerry
基于上面的配置条件下,运行下面的启动类,可以看到可以获取到userBean的实例
public static void main(String[] args) {ConfigurableApplicationContext context = SpringApplication.run(AnnoApp.class, args);User user = (User) context.getBean("userBean");System.out.println(user.getUserName());}
四、条件注解源码剖析
以@ConditionalOnMissingClass为例,分析下该注解的源码执行过程,有如下代码,在ScanConfig这个类上使用了该注解,表示如果注解中的User类存在的情况下,role这个bean将会被创建;
@Configuration
@ComponentScan("com.congge")
@ConditionalOnMissingClass(value = "com.congge.entity.User")
public class ScanConfig {@Beanpublic Role role(){return new Role("0002","测试角色");}}
在当前的工程中,我们先将User类删除或注释掉,使用如下的代码进行测试,效果是role的bean被创建出来了,这也正好与@ConditionalOnMissingClass这个注解要实现的功能内涵相符;
4.1 @ConditionalOnMissingClass 源码剖析
springboot从启动到如何进行扫描,注解解析,bean解析以及后置处理器等等流程这里不继续展开,这是一个相对复杂的过程,我们直接从该注解源码入手来看,搞清楚其底层实现原理。
@ConditionalOnMissingClass 源码如下,该注解可以作用在类上,也可以作用在某个方法即bean的创建上;
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional({OnClassCondition.class})
public @interface ConditionalOnMissingClass {String[] value() default {};
}
实现条件选择的重要入口OnClassCondition.class,从这里点击进去,可以看到,该类继承了FilteringSpringBootCondition这个类,一般如果有继承关系,还需要从父类寻找线索;
从FilteringSpringBootCondition继续深入,看到了SpringBootCondition这个类,
继续深入到SpringBootCondition这个类中,才找到了最终的答案,即要完成条件的判断,最终需要调用matches这个方法,这个方法的接口定义就是在Condition接口中定义的;
接下去只需要把matches这个方法的来龙去脉搞清楚即可,关于这个方法,我们通过代码注释加以理解
//针对所有条件注解进行判断
public final boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {//条件注解是写在哪个类或方法上呢?通过这个方法进行获取String classOrMethodName = getClassOrMethodName(metadata);try {//拿到条件注解的判断结果ConditionOutcome outcome = this.getMatchOutcome(context, metadata);//记录当前条件判断的结果到日志中this.logOutcome(classOrMethodName, outcome);//将判断的结果记录到ConditionEvaluationReport中去this.recordEvaluation(context, classOrMethodName, outcome);//获取判断的结果return outcome.isMatch();} catch (NoClassDefFoundError var5) {throw new IllegalStateException("Could not evaluate condition on " + classOrMethodName + " due to " + var5.getMessage() + " not found. Make sure your own configuration does not rely on that class. This can also happen if you are @ComponentScanning a springframework package (e.g. if you put a @ComponentScan in the default package by mistake)", var5);} catch (RuntimeException var6) {throw new IllegalStateException("Error processing condition on " + this.getName(metadata), var6);}}
4.1.1 debug过程分析
从上面的matches方法入手,通过debug来到下面的流程
找到具体的条件注解对应的实现
进入到OnClassCondition类中找到getMatchOutcome方法,源码如下,可结合注释进行理解
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {ClassLoader classLoader = context.getClassLoader();ConditionMessage matchMessage = ConditionMessage.empty();//拿到ConditionalOnClass 注解中的value值,也就是需要判断这个类是否存在List<String> onClasses = this.getCandidates(metadata, ConditionalOnClass.class);List onMissingClasses;if (onClasses != null) {//过滤onClasses中不存在的类onMissingClasses = this.filter(onClasses, ClassNameFilter.MISSING, classLoader);//如果上一步过滤出来了不存在的类,说明不匹配if (!onMissingClasses.isEmpty()) {return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnClass.class, new Object[0]).didNotFind("required class", "required classes").items(Style.QUOTE, onMissingClasses));}//否则就是匹配到了matchMessage = matchMessage.andCondition(ConditionalOnClass.class, new Object[0]).found("required class", "required classes").items(Style.QUOTE, this.filter(onClasses, ClassNameFilter.PRESENT, classLoader));}//拿到ConditionalOnMissingClass 注解中的value值,也就是需要判断这个类是否存在onMissingClasses = this.getCandidates(metadata, ConditionalOnMissingClass.class);if (onMissingClasses != null) {//进一步检查这个ConditionalOnMissingClass中的class类是否存在List<String> present = this.filter(onMissingClasses, ClassNameFilter.PRESENT, classLoader);//如果过滤出来有不符合条件的,说明不匹配if (!present.isEmpty()) {return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnMissingClass.class, new Object[0]).found("unwanted class", "unwanted classes").items(Style.QUOTE, present));}matchMessage = matchMessage.andCondition(ConditionalOnMissingClass.class, new Object[0]).didNotFind("unwanted class", "unwanted classes").items(Style.QUOTE, this.filter(onMissingClasses, ClassNameFilter.MISSING, classLoader));}return ConditionOutcome.match(matchMessage);}
通过断点调试,会进入到解析ConditionalOnMissingClass注解的方法,然后解析到了User这个类
继续跟进,最终会调用到 ConditionOutcome.match 这个方法,由于上述的判断通过了,这里直接返回了使用ConditionOutcome封装的对象,其中match为true;
返回到调用的地方,当上述的判断通过之后,SpringBootCondition中的matches方法返回true
到这里,相信对spring注册bean过程稍有了解的同学不难猜到,接下来将会执行对ScanConfig类的解析,并对该类中需要创建bean的方法进行创建的过程,这也不难理解,在端点的调用链路上即可窥探一二
从上面的端点执行链路不难看出,在执行doRegisterBean的过程中,调用了一个shouldSkip的方法,就是在这个方法中完成了对上述ConditionalOnMissingClass注解的解析过程,如果matches方法返回true之后,我们继续前进一步,接下去就是一个注册bean的过程;
解析并注册bean,即对ScanConfig类解析和创建BeanDefinition等一些列步骤;
4.1.2 源码分析过程小结
上述以@ConditionalOnMissingClass注解为例分析了源码执行的过程,其他的几个注解可以类似的进行研究,总结来说,条件注解的解析发生在bean注册之前,作为是否创建bean的判断条件,只要记住这一点就可以了。
五、条件注解的应用场景
在springboot框架中,关于条件注解有非常多的应用,有心的同学可以在很多与springboot框架集成的外部组件的源码中看到,比如ConditionalOnMissingClass注解用于日志整合中,作为兜底类,比如日志配置中,如果某个项目中没有特殊的配置,那么就用兜底的配置,否则就用兜底的配置。这就是没有指定的实现,就会有默认的配置实现。
5.1 条件注解使用案例
下面以@ConditionalOnProperty这个注解为例配合一个定时任务调度场景说明,我们的需求是:
通过一个外部配置文件来控制定时任务是否执行;
能够实现上述需求的方式有很多,这里选择使用@ConditionalOnProperty条件表达式注解来实现,具体来说,在执行定时任务的类上面(或方法)添加该注解,通过注解中引入配置参数的值来控制即可。代码如下:
@Component
@EnableScheduling
public class UserTask {//每5秒钟执行一次@Scheduled(cron="0/5 * * * * ?")@ConditionalOnProperty(prefix="user.task",name = "enable", havingValue = "true")public void cron(){System.out.println("执行定时任务");}}
第一次,在配置参数为false的情况下,启动程序,等待5秒之后并没有执行
将参数开启为true,再次启动程序,等待5秒之后,,定时任务就执行了
六、写在文末
spring条件注解是spring框架注解体系中非常重要的组成成员,也是spring为开发者提供的扩展点之一,合理使用条件注解,可以给应用开发带来意想不到的效果,希望对看到本文的你提供一个思路,本文到此技术,感谢观看。
相关文章:
【微服务】spring 条件注解从使用到源码分析详解
目录 一、前言 二、spring 条件注解概述 2.1 条件注解Conditional介绍 2.2 Conditional扩展注解 2.2.1 Conditional扩展注解汇总 三、spring 条件注解案例演示 3.1 ConditionalOnBean 3.2 ConditionalOnMissingBean 3.2.1 使用在类上 3.2.2 使用场景补充 3.3 Condit…...
客户案例:高性能、大规模、高可靠的AIGC承载网络
客户是一家AIGC领域的公司,他们通过构建一套完整的内容生产系统,革新内容创作过程,让用户以更低成本完成内容创作。 客户网络需求汇总 RoCE的计算网络RoCE存储网络1.不少于600端口200G以太网接入端口,未来可扩容至至少1280端口1.…...
Flutter性能揭秘之RepaintBoundary
作者:xuyisheng Flutter会在屏幕上绘制Widget。如果一个Widget的内容需要更新,那就只能重绘了。尽管如此,Flutter同样会重新绘制一些Widget,而这些Widget的内容仍有部分未被改变。这可能会影响应用程序的执行性能,有时…...
29.Netty源码之服务端启动:创建EventLoopSelector流程
highlight: arduino-light 源码篇:从 Linux 出发深入剖析服务端启动流程 通过前几章课程的学习,我们已经对 Netty 的技术思想和基本原理有了初步的认识,从今天这节课开始我们将正式进入 Netty 核心源码学习的课程。希望能够通过源码解析的方式…...
Kotllin实现ArrayList的基本功能
前言 上次面试时,手写ArrayList竟然翻车,忘了里面的扩容与缩容的条件,再次实现一次,加深印象 源码讲了什么 实现了List列表和RandomAccess随机访问接口List具有增删改查功能,RandomAccess支持下标访问内部是一个扩容…...
C++的初步介绍,以及C++与C的区别
C和C的区别 C又称C plus plus,且C语言是对C语言的扩充,几乎支持所有的C语言语法;C语言:面向过程的语言(注重问题的解决方法和算法)C:面向对象的语言 (求解的方法)面向对…...
JDK 核心jar之 rt.jar
一、JDK目录展示 二、rt.jar 简介 2.1.JAR释义 在软件领域,JAR文件(Java归档,英语:Java Archive)是一种软件包文件格式,通常用于聚合大量的Java类文件、相关的元数据和资源(文本、图片等&…...
el-form表单验证:只在点击保存时校验(包含select、checkbox、radio)
1、input类型 input类型 在el-input里加入:validate-event"false" <el-form-item label"活动名称" prop"name"><el-input v-model"ruleForm.name" :validate-event"false"></el-input> </el-form-i…...
Golang基本语法(上)
1. 变量与常量 Golang 中的标识符与关键字 标识符 Go语言中标识符由字母数字和_(下划线)组成,并且只能以字母和_开头。 举几个例子:abc, _, _123, a123。 关键字 关键字和保留字都不建议用作变量名: Go语言中有25个关键字。 此…...
jenkins使用
安装插件 maven publish over ssh publish over ssh 会将打包后的jar包,通过ssh推送到指定的服务器上,,在jenkins中设置,推送后脚本,实现自动部署jar包,, 装了这个插件之后,可以在项…...
多线程基础篇(包教包会)
文章目录 一、第一个多线程程序1.Jconsole观察线程2.线程休眠-sleep 二、创建线程三、Thread类及常见方法1. Thread 的常见构造方法2. Thread 的几个常见属性3. 启动线程 - start4. 中断线程5. 等待一个线程 四、线程状态五、线程安全问题(synchronized)(重点&#…...
Android/Java中,各种数据类型之间的互相转换,给出各种实例,附上中文注释
目录 1.字符串(String)转整数(int): 2.整数(int)转字符串(String): 3.字符串(String)转浮点数(float)&…...
机器学习知识点总结:什么是EM(最大期望值算法)
什么是EM(最大期望值算法) 在现实生活中,苹果百分百是苹果,梨百分白是梨。 生活中还有很多事物是概率分布,比如有多少人结了婚,又有多少人有工作, 如果我们想要调查人群中吸大麻者的比例呢?敏感问题很难得…...
漏洞挖掘和安全审计的技巧与策略
文章目录 漏洞挖掘:发现隐藏的弱点1. 源代码审计:2. 黑盒测试:3. 静态分析工具: 安全审计:系统的全面评估1. 渗透测试:2. 代码审计:3. 安全策略审查: 代码示例:SQL注入漏…...
[SpringBoot3]Web服务
五、Web服务 基于浏览器的B/S结构应用十分流行。SpringBoot非常适合Web应用开发,可以使用嵌入式Tomcat、Jetty、Undertow或Netty创建一个自包含的HTTP服务器。一个SpringBoot的Web应用能够自己独立运行,不依赖需要安装的Tomcat、Jetty等。SpringBoot可以…...
构建系统自动化-autoreconf
autoreconf简介 autoreconf是一个GNU Autotools工具集中的一个命令,用于自动重新生成构建系统的配置脚本和相关文件。 Autotools是一组用于自动化构建系统的工具,包括Autoconf、Automake和Libtool。它们通常用于跨平台的软件项目,以便在不同…...
Mysql之InnoDB和MyISAM的区别
InnoDB和MyISAM是MySQL数据库中两种常见的存储引擎,它们在功能和性能方面有一些明显的区别。下面是它们之间的详细解释和说明: 底层数据 存数据的时候,MyISAM是数据和索引分开存储,分为MYD和MYI 而InnoDB是数据即索引࿰…...
Unity 之 Transform.Translate 实现局部坐标系中进行平移操作的方法
文章目录 Translate 默认使用局部坐标也可以转换成世界坐标 Translate 默认使用局部坐标 在Unity中,Transform.Translate是用于在游戏对象的局部坐标系中进行平移操作的方法。这意味着它将游戏对象沿着其自身的轴进行移动,而不是世界坐标轴。这在实现物…...
PostgreSQL Error: sorry, too many clients already
Error PG的默认最大连接数是100. 如果超过100就会报错sorry, too many clients already Find show max_connections; SELECT COUNT(*) from pg_stat_activity; SELECT * FROM pg_stat_activity;Solution 提高最大连接数 ALTER SYSTEM SET max_connections 然后重启pg查看…...
Vue2(路由)
目录 一,路由原理(hash)二,路由安装和使用(vue2)三,路由跳转四,路由的传参和取值五,嵌套路由六,路由守卫最后 一,路由原理(hash&#…...
中介者模式-协调多个对象之间的交互
在深圳租房市场,有着许多的“二房东”,房主委托他们将房子租出去,而租客想要租房的话,也是和“二房东”沟通,租房期间有任何问题,找二房东解决。对于房主来说,委托给“二房东”可太省事了&#…...
Python框架【自定义过滤器、自定义数据替换过滤器 、自定义时间过滤器、选择结构、选择练习、循环结构、循环练习、导入宏方式 】(三)
👏作者简介:大家好,我是爱敲代码的小王,CSDN博客博主,Python小白 📕系列专栏:python入门到实战、Python爬虫开发、Python办公自动化、Python数据分析、Python前后端开发 📧如果文章知识点有错误…...
红黑树遍历与Redis存储
引言 在计算机科学领域,红黑树(Red-Black Tree)是一种自平衡的二叉查找树,它能在O(log n)的时间复杂度内完成插入、删除和查找操作。由于其高效性和可预测性的性能,红黑树在许多领域都得到广泛应用。本文将重点介绍红…...
前端处理图片文件的方法
在项目开发过程中,有一个需求,需要前端对上传的图片进行处理,以字符串的形式传给后端,实现效果如下: 1.上传图片的组件 在该项目中,使用了element plus组件库 <el-uploadv-model:file-list"fileL…...
「Java」《深入解析Java多线程编程利器:CompletableFuture》
《深入解析Java多线程编程利器:CompletableFuture》 一、 引言1. 对多线程编程的需求和挑战的介绍2. 介绍CompletableFuture的作用和优势 二. CompletableFuture简介1. CompletableFuture是Java中提供的一个强大的多线程编程工具2. 与传统的Thread和Runnable相比的优…...
Docker容器与虚拟化技术:容器运行时说明与比较
目录 一、理论 1.容器运行时 2.容器运行时接口 3.容器运行时层级 4.容器运行时比较 5.强隔离容器 二、问题 1.K8S为何难以实现真正的多租户 三、总结 一、理论 1.容器运行时 (1)概念 Container Runtime 是运行于 k8s 集群每个节点中ÿ…...
vue导出文件流获取附件名称并下载(在response.headers里解析filename导出)
导出文件流下载,拦截器统一处理配置 需求以往实现的方法(各自的业务层写方法)现在实现的方法(axios里拦截器统一配置处理)把文章链接复制粘贴给后端,让大佬自己赏阅。 需求 之前实现的导出都是各自的业务层…...
山东省图书馆典藏《乡村振兴战略下传统村落文化旅游设计》鲁图中大许少辉博士八一新书
山东省图书馆《乡村振兴战略下传统村落文化旅游设计》鲁图中大许少辉博士八一新书...
2023-08-19力扣每日一题-水题/位运算解法
链接: 2235. 两整数相加 题意: ab 解: ab 补一个位运算写法,进位是(a&b)<<1,不进位的计算结果为a^b 实际代码: #include<iostream> using namespace std; int sum(int num1, int n…...
Hadoop学习:深入解析MapReduce的大数据魔力之数据压缩(四)
Hadoop学习:深入解析MapReduce的大数据魔力之数据压缩(四) 4.1 概述1)压缩的好处和坏处2)压缩原则 4.2 MR 支持的压缩编码4.3 压缩方式选择4.3.1 Gzip 压缩4.3.2 Bzip2 压缩4.3.3 Lzo 压缩4.3.4 Snappy 压缩4.3.5 压缩…...
胶州为企业做网站的公司/重庆森林台词
2019独角兽企业重金招聘Python工程师标准>>> src:http://nanhaochen.blog.51cto.com/228629/47081 又碰到servlet 输出中文乱码的问题,恼火。研究了一下,有了新的发现和认识。 原始代码: java 代码 protected void doGet(HttpServ…...
一些可以做翻译的网站/seo外包网络公司
源码获取:博客首页 "资源" 里下载! 项目介绍 CRM人事管理系统,主要功能有: 用户管理:用户查询、添加用户、编辑、删除; 职位管理:职位查询、添加职位、删除; 部门管理&am…...
做网站的职责/托管竞价推广公司
在jsp中,中文乱码常会让人心乱如麻。对于中文处理的常见对策,在网上经常可见的主要是下面2种:<% pagecontentType"text/html;charsetgb2312" %>或者:<%String Hi"你好";byt…...
wordpress文章缩进/西安百度竞价开户
2.Spring Data Solr 入门2.1 Spring Data Solr 简介虽然支持任何编程语言的能力具有很大的市场价值,你可能感兴趣的问题是:我如何将Solr 的应用集成到 Spring 中?可以,Spring Data Solr 就是为了方便 Solr 的开发所研制的一个框架…...
建网站靠什么盈利/泉州百度首页优化
https://yqfile.alicdn.com/9c0c0036557ef60fefa27576a01e0e9b4e5efe6f.png" >前言 在企业邮件应用市场中,Exchange Server 2010 SP1/SP2 占据着重要位置。新版本Exchange Server提供了更高的安全性、稳定性、易用性,并且提供了更丰富的功能&…...
管理系统介绍/津seo快速排名
原文:Asp.Net Web API 2第九课——自承载Web API前言 阅读本文之前,您也可以到Asp.Net Web API 2 系列导航进行查看 http://www.cnblogs.com/aehyok/p/3446289.html Asp.Net Web API可以需要IIS。你可以在你自己的主机上来承载一个Web API。 本教程来展示在控制台应…...