Spring 相关问题

文章目录

  • Spring
    • 1 Spring 框架中用到的设计模式
    • 2 Spring 事务、隔离级别
    • 3 单例 Bean 是线程安全的吗
  • Spring IOC
    • 1 Spring 容器:BeanFactory & ApplicationContext
    • 2 依赖注入的两种方式
    • 3 Bean 的生命周期
    • 4 依赖注入的四个注解
    • 5 如何解决循环依赖
  • Spring AOP
    • 1 基本概念
    • 2 通知的执行顺序
    • 3 Spring AOP 和 AspectJ 有什么区别(动态代理与静态代理)
    • 4 动态代理:JDK / CGLIB
  • Spring MVC
    • 1 作用
    • 2 Spring MVC 执行流程
  • Spring Boot
    • 1 @SpringBootApplication 做了哪些事
    • 2 Spring Boot 自动装配原理
  • Spring Cloud
    • 1 CAP

Spring

1 Spring 框架中用到的设计模式

参考:Spring 中的设计模式

  1. 工厂模式
    Spring 通过 BeanFactoryApplicationContext 创建 Bean 对象
  2. 单例模式
    Bean 默认都是单例的
  3. 代理模式
    AOP 功能的实现基于代理模式
  4. 模板模式
    xxxTemplate(例如 RestTemplate 等)使用了模板模式
  5. 适配器模式
    Spring AOP 的增强或通知(Advice,例如 @ControllerAdvice)使用到了适配器模式;Spring MVC 中用到了适配器模式适配 Controller

2 Spring 事务、隔离级别

  • 一般使用 @Transactional 注解方式实现,只能应用到 public 方法上,否则不生效
  • 事务传播:解决业务层方法之间互相调用的事务问题
传播行为解释
PROPAGATION_REQUIRED@Transactional注解默认使用。如果当前存在事务,则加入该事务(变成了同一事务,只要一个方法回滚,整个事务均回滚);如果当前没有事务,则创建一个新的事务
PROPAGATION_REQUIRES_NEW创建一个新的事务,如果当前存在事务,则把当前事务挂起。也就是说不管外部方法是否开启事务,会新开启自己的事务,且开启的事务相互独立,互不干扰
PROPAGATION_NESTED如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则等价于 PROPAGATION_REQUIRED
PROPAGATION_MANDATORY如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常
  • 隔离级别:类似于 MySQL
隔离级别解释
ISOLATION_DEFAULT默认的隔离级别,MySQL 默认采用的 REPEATABLE_READ 隔离级别,Oracle 默认采用的 READ_COMMITTED 隔离级别
ISOLATION_READ_UNCOMMITTED允许读取尚未提交的数据变更,可能会导致 脏读、幻读或不可重复读
ISOLATION_READ_COMMITTED允许读取并发事务已提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生
ISOLATION_REPEATABLE_READ对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生
ISOLATION_SERIALIZABLE所有事务依次执行,这样事务之间就不可能产生干扰,可以防止脏读、不可重复读以及幻读
  • @Transactional(rollbackFor = MyException.class) 注解
    如果类或者方法加了这个注解,那么这个类里面的方法抛出异常,就会回滚,数据库里面的数据也会回滚

    @Transactional 注解中如果不配置 rollbackFor 属性,那么事务只会在遇到 RuntimeException 的时候才会回滚,加上 rollbackFor=Exception.class ,可以让事务在遇到自定义的非运行时异常时也回滚

3 单例 Bean 是线程安全的吗

  • 不是,Spring 框架并没有对单例 Bean 进行多线程的封装处理
  • 大部分时候 Bean 是无状态的(比如 dao ,无状态即不会保存数据),所以大部分 Bean 是线程安全的
  • 如果 Bean 有状态的话,需要开发者保证线程安全,最简单的就是改变 Bean 的作用域,把 singleton 变更为 prototype ,这样请求 Bean 相当于 new Bean(),就可以保证线程安全了

Spring IOC

1 Spring 容器:BeanFactory & ApplicationContext

  • BeanFactoryApplicationContext 是 Spring 的两大核心接口,都可以当做 Spring 的容器, ApplicationContextBeanFactory 的子接口

  • BeanFactroy 采用的是 延迟加载 形式来注入 Bean 的,即只有在使用到某个 Bean 时(调用getBean()),才对该 Bean 进行实例化

    • 这样导致不能发现一些存在的 Spring 的配置问题:如果Bean的某一个属性没有注入,BeanFacotry 加载后,直至第一次使用调用 getBean 方法才会抛出异常
    • 是一种低级的容器,类似于一个 HashMap,Key 是 BeanName,Value 是 Bean 实例,通常只提供注册(put),获取(get)这两个功能
  • ApplicationContext 在容器启动时,一次性创建了所有的 Bean(把创建资源的过程放在服务器启动时)。这样在容器启动时就可以发现 Spring 中存在的配置错误,这样有利于检查所依赖属性是否注入。 ApplicationContext 启动后预载入所有的单实例Bean,通过预载入单实例 Bean,确保当需要的时候就不用等待

2 依赖注入的两种方式

  1. 使用 setter 方法
    - 容器通过调用无参构造器或无参static工厂方法实例化 Bean 之后,调用该 Bean 的 setter 方法
    - 用于实现可选依赖,适合设置少量属性

  2. 使用有参构造器
    - 构造器依赖注入通过容器触发一个类的构造器来实现的
    - 用于实现强制依赖,适合设置大量属性

3 Bean 的生命周期

参考链接

  • Bean 自身的四个基础步骤:

    1. 实例化 Instantiation,调用构造函数,执行构造函数的属性注入
    2. 属性赋值 Populate,执行 setter 的属性注入
    3. 初始化 Initialization,调用自定义的 Bean 初始化方法
    4. 销毁 Destruction,调用自定义的 Bean 销毁方法
  • 指定 Bean 的初始化方法和销毁方法,一般在配置类使用注解 @Bean(initMethod = "myInit",destroyMethod = "myDestory")

  • 另外,在四个基础步骤上,添加了一些 Bean级生命周期方法、容器级生命周期方法,实现了 AOP 的功能

    • Bean级生命周期方法:Bean 类直接实现接口的方法,这些接口包括 BeanNameAwareBeanFactoryAwareApplicationContextAwareInitializingBeanDisposableBean 等(只对当前 Bean 产生影响)
    • 容器级生命周期方法BeanPostProcessor(初始化前后) 和它的子类 InstantiationAwareBeanPostProcessor(实例化前后) 的一些方法(对多个 Bean 产生影响)

在这里插入图片描述

4 依赖注入的四个注解

注解作用范围
@Autowired按类型自动装配构造方法、成员变量、Setter 方法
@Qualifier按名称自动装配,需要和 @Autowired 搭配使用成员变量、Setter 方法
@Resource按名称或类型自动装配;未指定规则时,默认先按名称装配,找不到满足要求的 bean,再按类型装配成员变量、Setter 方法
@Value注入 int / float / String 等基本数据类型成员变量、Setter 方法

5 如何解决循环依赖

参考链接

  • Spring 能解决循环依赖的前置条件:
    1. 循环依赖的 Bean 必须是单例
    2. 发生的循环依赖 包含 Setter 注入发生在 Bean 属性赋值阶段),而不能解决 只有 构造器注入时(发生在 Bean 实例化阶段)发生的循环依赖,具体参考下图

在这里插入图片描述
(情况3可行4不可行的原因: Spring 在创建 Bean 的时候默认是按照自然排序来进行创建的,如果 A 使用构造器注入 B,则 A 无法被创建)

  • 三级缓存结构
    1. 一级缓存 —— 单例池 singletonObjects,存放已经创建完成的 Bean 对象
    2. 二级缓存 —— 早期曝光对象 earlySingletonObjects ,存放三级缓存工厂创建的 Bean 实例化后的对象,或代理后的对象
    3. 三级缓存 —— 早期曝光对象工厂 singletonFactories,如果 Bean 被 AOP 代理,通过对应的工厂获取到的是代理后的对象,否则获取到的是 实例化阶段(没有经过属性赋值和初始化) 的对象,获取后放入二级缓存

在给 B 注入的时候为什么要注入一个代理对象

  • 对 A 进行了 AOP 代理时,说明希望从容器中获取到的就是 A 代理后的对象而不是 A 本身,因此把A当作依赖进行注入时也要注入它的代理对象

三级缓存的作用

  • 延迟对实例化阶段生成的对象的代理,只有真正发生循环依赖的时候,才去提前生成代理对象;否则只会创建一个工厂并将其放入到三级缓存中,但是不会通过工厂真正创建对象

只用二级缓存能解决循环依赖吗

  • 如果使用二级缓存解决循环依赖,意味着所有 Bean 在实例化后就要完成 AOP 代理,这样违背了 Spring 设计的原则:在 Bean 生命周期的最后一步完成 AOP 代理,而不是在实例化后立刻进行 AOP 代理
  • 解决流程

    1. A、B 两个类发生循环引用时,在 A 实例化后,使用实例化后的对象创建一个对象工厂,并添加到三级缓存中
    2. A 实例化后进行属性注入时,会创建B,同时 B 又依赖了 A,此时 getBean(a) 会从缓存中获取:第一步,先获取到三级缓存中的工厂;第二步,调用对象工工厂的 getObject() 获取到对应的对象,将其注入 B 中
    3. B 创建完后,将 B 再注入到 A 中,此时 A 再完成后续操作,循环依赖解决
  • A 没有被 AOP 代理时

在这里插入图片描述

  • A 被 AOP 代理时
    在这里插入图片描述

Spring AOP

1 基本概念

AOP的作用:作为面向对象的一种补充,用于将那些与业务无关,但却对多个对象产生影响的公共行为和逻辑,抽取并封装为一个可重用的模块,这个模块被命名为“切面”(Aspect)。切面可以减少系统中的重复代码,降低模块间的耦合度,同时提高系统的可维护性。可用于权限认证、日志、事务处理等

  1. 切面(Aspect):通知 + 切入点
  2. 连接点(Join Point)可以被增强的方法,Java 只支持方法成为连接点
  3. 通知(Advice):切面的工作被称为通知
  4. 切入点(Pointcut)实际增强的方法,连接点的子集
  5. 目标对象(Target Object): 被切面所通知的对象,即被增强的对象
  6. 织入(Weaving):把切面应用到目标对象并创建新的代理对象的过程

2 通知的执行顺序

@Component(value = "enhanceClass")
@Aspect
public class EnhanceClass {@Before(value = "execution(* pojo.MyClass.add(..))")public void before() {System.out.println("前置通知");}@AfterReturning(value = "execution(* pojo.MyClass.add(..))")public void afterReturning() {System.out.println("后置通知,正常返回时才执行");}@After(value = "execution(* pojo.MyClass.add(..))")public void after() {System.out.println("最终通知,无论是否正常返回都执行");}@AfterThrowing(value = "execution(* pojo.MyClass.add(..))")public void afterThrowing() {System.out.println("异常通知");}@Around(value = "execution(* pojo.MyClass.add(..))")public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {System.out.println("环绕通知:前");proceedingJoinPoint.proceed();System.out.println("环绕通知:后");}
}- 无异常时:
1. around before advice
2. before advice
3. target method
4. around after advice
5. after advice
6. afterReturning advice- 有异常时:
1. around before advice
2. before advice
3. target method
4. around after advice
5. after advice
6. afterThrowing advice
7. 异常发生

3 Spring AOP 和 AspectJ 有什么区别(动态代理与静态代理)

  • Spring AOP 采用了动态代理,AspectJ 采用了静态代理
  • AspectJ 使用 静态代理AOP 框架会在编译阶段生成 AOP 代理类,因此也称为编译时增强,他会在编译阶段将切面织入到 Java 字节码中,运行的时候就是增强之后的 AOP 对象
  • Spring AOP 使用 动态代理,AOP 框架不会去修改字节码,而是每次运行时在内存中临时生成一个 AOP 对象,这个AOP 对象包含了目标对象的全部方法,并且在特定的切点做了增强处理,并回调原对象的方法
  • 静态代理与动态代理区别在于生成 AOP 代理对象的时机不同,AspectJ 的静态代理方式具有更好的性能,但需要特定的编译器进行处理,而 Spring AOP 则无需特定的编译器处理

4 动态代理:JDK / CGLIB

JDK动态代理

  • JDK 动态代理基于接口(被代理的只能是接口,如果方法不来自于接口,则无法增强方法;如果类没有实现接口,则完全无法创建代理),通过反射机制生成一个实现代理接口的类(创建的代理类型也只能是接口类型,而不能是具体的类)
  • JDK 动态代理根据对象实例建立代理实例,先创建被代理对象,才能创建代理对象
  • 基于接口实现的原因
    • JDK 动态代理生成的代理对象需要继承 Proxy 这个类,在 Java 中类只能是单继承关系,无法再继承一个代理类,所以只能基于接口代理
  • 新生成的代理对象的 Class 对象会继承 Proxy,且实现所有的入参 interfaces 中的接口,在实现的方法中实际是调用入参 InvocationHandlerinvoke(..) 方法
    • 代理类的定义和被代理对象没有关系,和 InvocationHandler 的实现也没有关系,而主要和接口数组有关:动态创建了每个接口的实现代码(即:将调用请求转发给 InvocationHandler
  • DEMO
    1. 接口与接口的实现
interface CanWalk {void walk();
}interface CanEat {void eat();
}class Human implements CanWalk, CanEat {@Overridepublic void walk() {System.out.println("walk with human feet");}@Overridepublic void eat() {System.out.println("eat with human mouth");}
}class Fish implements CanEat {@Overridepublic void eat() {System.out.println("eat with fish mouth");}
}
  1. InvocationHandler
    • 代理对象调用原生方法的时候,最终实际上调用到的是 invoke() 方法,然后 invoke() 方法调用了被代理对象的原生方法
    • invoke() 的首个参数 o 代表的是代理对象本身,不能传递给 method.invoke(),否则会死循环
class MyInvocationHandler implements InvocationHandler {private Object realObject;public MyInvocationHandler(Object realObject) {this.realObject = realObject;}/*** proxy :动态代理对象(Proxy)* method : 要调用的原生方法* args : 方法参数**/@Overridepublic Object invoke(Object o, Method method, Object[] objects) throws Throwable {System.out.println("JDK dynamic proxy: write to log");method.invoke(realObject, objects);  // realObject而非o!!return null;}
}
  1. 获取代理对象并执行增强方法
    • 得到的代理对象只能被强转为传入工厂方法的接口之一,而不能强转为某个类(如下,如果 proxyInstance 被强转为 Human 类型则出现运行时异常)
    • 得到的代理对象是 Proxy 的子类
    • 调用 Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) 方法创建一个代理对象,方法的三个入参分别是
      1. ClassLoader loader:用于加载代理对象的 Class 类加载器
      2. Class<?>[] interfaces:代理对象需要实现的接口
      3. InvocationHandler h:代理对象的处理器
    static void JDKDynamicProxyTest() {// 被代理的对象1Human human = new Human();// 获取代理对象,只能强转成传入的接口之一CanWalk humanProxy = (CanWalk) Proxy.newProxyInstance(Human.class.getClassLoader(),  // 指定类加载器Human.class.getInterfaces(),  // 指定接口,可以替换为 new Class[]{CanWalk.class, CanEat.class}new MyInvocationHandler(human)  // 自定义的 InvocationHandler);// 调用代理对象的方法,转发给InvocationHandler.invoke(...)humanProxy.walk();// 被代理的对象2Fish fish = new Fish();CanEat fishProxy = (CanEat) Proxy.newProxyInstance(Fish.class.getClassLoader(),new Class[]{CanEat.class},new MyInvocationHandler(fish));fishProxy.eat();}执行结果:
JDK dynamic proxy: write to log
walk with human feet
JDK dynamic proxy: write to log
eat with fish mouth任何其它类(即使没有实现上述两个接口),也能用这个动态代理类记录日志

CGLIB动态代理

  • JDK 动态代理的目标对象必须是一个接口,CGLIB 动态代理则是基于类代理
  • cglib 针对类建立代理实例,创建代理对象时,不必创建被代理对象
  • 新生成的代理对象的 Class 对象会继承被代理的类,重写父类(被代理的类)的所有非 finalpublic 方法,将其改为调用 Callback 的方法
  • 因为被代理类需要被继承,所以 final 修饰的类不能使用 cglib
class MyInterceptor implements MethodInterceptor {/*** @param o 代理类对象* @param method 调用的原生方法* @param objects 方法参数* @param methodProxy 代理方法* @throws Throwable*/@Overridepublic Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {System.out.println("cglib dynamic proxy: write to log");methodProxy.invokeSuper(o, objects);return null;}
}static void CglibDynamicProxyTest() {// 创建代理类Enhancer enhancer = new Enhancer();enhancer.setSuperclass(Human.class);  // 设置被代理的类(无需创建被代理的对象)enhancer.setCallback(new MyInterceptor());  // 方法增强Human humanProxy = (Human) enhancer.create();// 调用增强方法humanProxy.eat();humanProxy.walk();}

Spring MVC

1 作用

  • Spring MVC 是一种基于 Java 的实现 MVC(Model - View - Controller) 设计模式的,请求驱动类型的,轻量级 WEB 框架
  • Spring MVC 底层是 Servlet,对 Servlet 进行了深层次的封装

2 Spring MVC 执行流程

在这里插入图片描述

  1. 客户端(浏览器)发送请求,直接请求到 DispatcherServlet
  2. DispatcherServlet 根据请求信息调用 HandlerMapping,解析请求对应的 HandlerExecutionChain(包括 Handler 对象以及对应的拦截器)
  3. 解析到对应的 Handler(也就是 Controller)后,开始由 HandlerAdapter 适配器处理
  4. HandlerAdapter 会根据 Handler 来调用真正的处理器开处理请求,并处理相应的业务逻辑
  5. 处理器处理完业务后,会返回一个 ModelAndView 对象,Model 是返回的数据对象,View 是逻辑上的视图
  6. DispatcherServlet 根据返回的 ModelAndView,选择合适的 ViewResolver 根据逻辑 View 查找实际的 View
  7. DispaterServlet 进行视图渲染
  8. 把渲染结果返回给请求者(浏览器)

Spring Boot

1 @SpringBootApplication 做了哪些事

@SpringBootApplication = @Configuration + @EnableAutoConfiguration + @ComponentScan

  • @EnableAutoConfiguration:启用 SpringBoot 的自动配置机制
  • @ComponentScan: 扫描被 @Component (@Service,@Controller) 注解的 bean,注解默认会扫描该类所在的包下所有的类
  • @Configuration:允许在 Spring 上下文中注册额外的 bean 或导入其他配置类

2 Spring Boot 自动装配原理

自动装配可以简单理解为:通过注解或者一些简单的配置就能在 Spring Boot 的帮助下实现某块功能

  1. @SpringBootApplication -> @EnableAutoConfiguration -> AutoConfigurationImportSelector
  2. AutoConfigurationImportSelector 实现了 ImportSelector 接口中的 selectImports 方法,该方法主要用于获取所有符合条件的类的全限定类名(返回 String 数组,一系列的 “xxxAutoConfiguration”),这些类需要被加载到 IoC 容器中
private static final String[] NO_IMPORTS = new String[0];public String[] selectImports(AnnotationMetadata annotationMetadata) {// <1>.判断自动装配开关是否打开if (!this.isEnabled(annotationMetadata)) {return NO_IMPORTS;} else {//<2>.获取所有需要装配的beanAutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata);  // 下图详细说明return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());}
}

下图中的红色字体“自动配置类名”即为 xxxAutoConfiguration 类
在这里插入图片描述

在这里插入图片描述

  1. 所有 Spring Boot Starter 下的 META-INF/spring.factories 都会被读取到。XXXAutoConfiguration 类的作用就是按需加载组件

在这里插入图片描述

  1. 启动应用时,不同的 Stater 的 spring.factories 的配置很多,每次启动不会全部加载。只有自动配置类或其中的方法上的 @ConditionalOnXXX 中的所有条件都满足才会生效

@Configuration
// 检查相关的类是否存在,存在才会加载
@ConditionalOnClass({EnableAspectJAutoProxy.class, Aspect.class, Advice.class, AnnotatedElement.class})
@ConditionalOnProperty(prefix = "spring.aop", name = {"auto"}, havingValue = "true", matchIfMissing = true)
public class AopAutoConfiguration {// ...@Configuration@EnableAspectJAutoProxy(proxyTargetClass = true)@ConditionalOnProperty(prefix = "spring.aop", name = {"proxy-target-class"}, havingValue = "true", matchIfMissing = true)public static class CglibAutoProxyConfiguration {public CglibAutoProxyConfiguration() {}}// ...
}

Spring Cloud

1 CAP

在一个分布式系统中,Consistency(一致性)、 Availability(可用性)、Partition tolerance(分区容错性),三者不可同时获得

  • C:假设在 T 时刻写入了一个值,那么在 T 之后的读取一定要能读到这个最新的值(所有节点在同一时间的数据完全一致,越多节点,数据同步越耗时)
  • A:无论系统发生任何故障,都仍然能对外提供服务(服务一直可用,而且是正常响应时间)
  • P:网络分区容错性(高可用性),一个节点挂掉不影响其它的节点。由于当前的网络硬件肯定会出现延迟丢包等问题,所以分区容忍性是必须需要实现的,只能在一致性和可用性之间进行权衡

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

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

相关文章

ndr4108贴片晶振是多少频率_关于山羊挺身你知道多少?这些干货速来了解一下...

就是因为有些人愿意吃苦&#xff0c;有些人缺乏决心毅力&#xff0c;才会有成功和失败之分。身材不是一天不运动或一天乱吃所造成&#xff0c;瘦身健身的效果也不是一天的努力可以看出来的。如同学业、事业&#xff0c;成功都不是一条直线&#xff0c;而是一条上上下下的崎岖路…

BeetleX之Websocket服务使用

BeetleX.FastHttpApi不仅是一个Webapi服务组件&#xff0c;它同时也是一个Websocket服务组件。由于BeetleX.FastHttpApi的实现是直接支持Websocket Upgrade操作&#xff0c;所以当启动服务后是HTTP还是Websocket完全取决于请求方&#xff1b;其原理和aspcore一样&#xff0c;同…

typeorm_Nestjs 热更新 + typeorm 配置

Nestjs 开发环境热更新的方案Nestjs 的热更新是基于 Webpack HMR(Hot-Module Replacement) 方案警告请注意&#xff0c;webpack不会自动将您的资产(例如graphql文件)复制到dist文件夹。同样&#xff0c;webpack与glob静态路径(例如TypeOrmModule中的实体属性)不兼容。1 使用 CL…

缓存一致性和跨服务器查询的数据异构解决方案canal

当项目的请求量上去了之后&#xff0c;通常有两种做法来应对高并发&#xff0c;第一是尽最大可能的使用cache来对抗&#xff0c;第二是尽最大可能的分库分表对抗。。。说起来容易&#xff0c;做起来并不那么乐观&#xff0c;这一篇就来浅析下。一&#xff1a;如何保证缓存一致性…

synchronized 实现原理

参考链接 文章目录一 基本使用1 三个作用2 三种用法二 同步原理1 监视器 Monitor2 synchronized 用于同步代码块3 synchronized 用于同步方法3 Mark Word4 对象头的 Mark Word 和线程的 Lock Record三 锁的优化1 自旋锁2 锁消除3 锁粗化4 偏向锁5 轻量级锁、重量级锁以及三种锁…

开头th_是什么文件_Python文件读写最详细的讲解

本文来自公众号&#xff1a;可乐的数据分析之路今天这篇文章来详细讲解一下Python中的文件读写。1、文件读写的流程1)类比windows中手动操作txt文档&#xff0c;说明python中如何操作txt文件&#xff1f;① windows中手动操作txt文件的步骤找到word文档打开word文档查看(或操作…

把 Console 部署成 Windows 服务,四种方式总有一款适合你!

一&#xff1a;背景 1. 讲故事上周有一个项目交付&#xff0c;因为是医院级项目需要在客户的局域网独立部署。程序&#xff1a;netcore 2.0&#xff0c;操作系统&#xff1a;windows server 2012&#xff0c;坑爹的事情就来了, netcore sdk 一直装不上&#xff0c;网上找了资料…

AQS ReentrantLock 实现原理

参考链接 文章目录1 AQS (AbstractQuenedSynchronizer)2 Lock 接口与显式条件3 转账 Demo&#xff1a;解决死锁的两种方案4 ReentrantLock 非公平锁加锁流程5 ReentrantLock 和 synchronized 的异同6 ReentrantReadWriteLock1 AQS (AbstractQuenedSynchronizer) 基于 AQS 的同步…

TensorFlow 2学习和工业CV领域应用 心得分享

我是一名来自苏州的机器视觉开发者&#xff0c;从事传统的机器视觉算法开发有11年了&#xff0c;从2018年开始&#xff0c;因为一些复杂微弱的瑕疵检测项目遇到的传统算法瓶颈&#xff0c;开始接触到了深度学习&#xff0c;并选择了使用TensorFlow&#xff0c;期间也是不断摸索…

历史版本_新版本爆料第弹丨英雄练习新去处,荣耀历史秀出来!

《万物有灵》新版本即将到来新版本来临之前妲己宝宝给自己定下了2个小目标&#xff01;via.小五怎么不开心目标一扩展自己小得可怜的英雄勺成为一名拥有英雄海的补位大神目标二通过自己的实力获得N1个响当当的荣耀称号很多召唤师会有疑问&#xff1a;凭妲己宝宝的实力&#xff…

循环遍历多层json_面试官:JSON.stringify() 实现深拷贝有什么问题

为什么要进行深拷贝JS中的变量在内存中存储分为值类型和引用类型&#xff1a; 值类型&#xff1a; 1、占用空间固定&#xff0c;保存在栈中&#xff1b; 2、保存与复制的是值本身&#xff1b; 3、基本类型数据是值类型&#xff08;String,Number,undefined,Boolean,Null&#x…

.NET架构小技巧(6)——什么是好的架构

首先声明&#xff0c;可能本篇文章的含金量配不上这个标题&#xff0c;因为说起架构&#xff0c;可能大家都比较关注高大上的架构&#xff0c;比如分布式的&#xff0c;高并发的&#xff0c;低耦合的&#xff0c;易扩展的等等&#xff0c;本篇可能使你失望了&#xff0c;因为这…

电子工程系庆贺电贺信_创造下一代光电子集成电路

全球互联网正以每年24%的复合速度增长&#xff0c;到2021年将达到每年3.3 zb字节。高速光通信在这个不断连接的世界中是迫切需要的&#xff0c;为了跟上这种增长&#xff0c;光模块的制造的发展是迫切需要的。复旦大学电子工程系博士研究生刘晓研究了集成构成光模块的电子电路和…

禁用笔记本键盘_如何禁用/启用笔记本内置键盘?

有些小伙伴外接了USB键盘想屏蔽掉笔记本的内置键盘&#xff0c;绞尽脑汁都没有办法禁用&#xff0c;其实方法很简单只需要一个简单的命令即可。1、右键点击左下角开始图标(WinX)&#xff0c;选择Windows Powershell(管理员)。2、在打开的窗口中&#xff0c;输入cmd。3、然后输入…

IdentityServer4系列 | 资源密码凭证模式

一、前言从上一篇关于客户端凭证模式中&#xff0c;我们通过创建一个认证授权访问服务&#xff0c;定义一个API和要访问它的客户端&#xff0c;客户端通过IdentityServer上请求访问令牌&#xff0c;并使用它来控制访问API。其中&#xff0c;我们也注意到了在4.x版本中于之前3.x…

深入探究ASP.NET Core Startup的初始化

前言Startup类相信大家都比较熟悉,在我们使用ASP.NET Core开发过程中经常用到的类&#xff0c;我们通常使用它进行IOC服务注册&#xff0c;配置中间件信息等。虽然它不是必须的&#xff0c;但是将这些操作统一在Startup中做处理&#xff0c;会在实际开发中带来许多方便。当我们…

【源码】常用的人脸识别数据库以及上篇性别识别源码

上一篇《使用ML.NET模型生成器来完成图片性别识别》发布后&#xff0c;很多朋友希望得到源码&#xff0c;这里附上地址&#xff1a;https://github.com/xin-lai/GenderRecognition常用的人脸数据库对于部分朋友说&#xff0c;找不到训练的数据&#xff0c;这里也给出部分数据&a…

程序员过关斩将--真的可以用版本号的方式来保证MQ消费消息的幂等性?

灵魂拷问MQ消息的消费为什么有时候要求幂等性&#xff1f;你们都说可以用版本号来解决幂等性消费&#xff1f;什么才是消息幂等性消费的根本性问题&#xff1f;随着系统的复杂性不断增加&#xff0c;多数系统都会引入MQ来进行解耦&#xff0c;其实从引入MQ的初衷来说&#xff0…

spring的钩子_spring提供的钩子,你知道哪些

俗话说得好“工欲善其事必先利其器”&#xff0c;现如今springboot与springcloud已成为快速构建web应用的利器。作为一个爪洼工程师&#xff0c;知道如下的spring扩展点&#xff0c;可能会让你编写出扩展性、维护性更高的代码。spring提供的钩子&#xff0c;你知道哪些bean的生…

.Net 5性能改进

起因在.Net Core跳过4.0,避免和先.Net Framework 4.0同名,版本号变为5.0,同时也不在叫.Net Core改为.Net 5(统一的叫法),先看看官方对.Net版本规划.本文主要是根据https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-5/ 翻译而来.不完全翻译.顺序也有所调…