Spring中经典的7种设计模式源码分析

news/2024/7/27 21:30:22/文章来源:https://blog.csdn.net/weixin_44716935/article/details/136714217

一、工厂模式

  • Spring使用工厂模式来创建Bean对象,如BeanFactory、ApplicationContext等。
  • 工厂模式为bean的创建过程提供了一个框架,同时隔离了实例化细节,使得代码更加解耦。
  1. BeanFactory接口

BeanFactory接口仍然是Spring工厂模式的基础,它定义了获取Bean实例的基本方法。

public interface BeanFactory {Object getBean(String name) throws BeansException;// ...
}
  1. DefaultListableBeanFactory类

DefaultListableBeanFactory继承自AbstractAutowireCapableBeanFactory,实现了BeanDefinitionRegistry接口,用于存储和管理BeanDefinition。它是最常用的BeanFactory实现类。

public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactoryimplements ConfigurableListableBeanFactory, BeanDefinitionRegistry {// ...
}
  1. AbstractBeanFactory类

AbstractBeanFactory作为抽象基类,定义了getBean()方法的模板实现。

public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {@Overridepublic Object getBean(String name) throws BeansException {return doGetBean(name, null, null, false);}protected <T> T doGetBean(final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)throws BeansException {// 合并父BeanFactoryfinal String beanName = transformedBeanName(name);Object sharedInstance = getSingleton(beanName);// ...// 创建Bean实例RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);checkMergedBeanDefinition(mbd, beanName, args);Object instance = null;if (mbd.isSingleton()) {instance = resolveBeforeInstantiation(beanName, mbd);if (instance == null) {instance = createBean(beanName, mbd, args);}} else {instance = createBean(beanName, mbd, args);}// ...return instance;}
}
  1. 创建Bean实例

AbstractAutowireCapableBeanFactory提供了createBean()方法,用于创建Bean实例。该方法委托给了AbstractAutowireCapableBeanFactory的createBeanInstance()方法,来实现不同的实例化策略。

protected Object createBeanInstance(String beanName, RootBeanDefinition mbd, Object[] args) {// 决定使用哪种实例化策略Constructor<?> constructorToUse = null;// ...// 使用适当的实例化策略boolean resolved = false;boolean autowireNecessary = false;if (args == null) {synchronized (mbd.constructorArgumentLock) {// ...autowireNecessary = mbd.resolvedConstructorOrFactoryMethod != null;resolved = true;}}Object beanInstance = null;if (resolved) {// 使用工厂方法实例化beanInstance = getInstantiationStrategy().instantiate(mbd, null, this);}// ...return beanInstance;
}
  1. 实例化策略

Spring 5中提供了多种实例化策略,包括:

  • 简单实例化策略(SimpleInstantiationStrategy)
  • CGLIB实例化策略(CglibSubclassingInstantiationStrategy)
  • 通过工厂方法实例化(FactoryMethodInstantiationStrategy)
  • 通过静态工厂方法实例化

这些实例化策略均实现了InstantiationStrategy接口,并在AbstractAutowireCapableBeanFactory的createBeanInstance()方法中根据条件来选择合适的策略。

二、单例模式

  • Spring默认情况下将每个bean创建为单例模式,可以通过配置修改为原型模式。
  • 单例模式确保了在整个应用程序中只有一个bean实例,从而节省内存资源。
  1. 单例 Bean 缓存

DefaultSingletonBeanRegistry 使用 ConcurrentHashMap 来缓存单例 Bean 的实例:

/** Cache of singleton objects: bean name to bean instance */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
  1. getSingleton() 方法

在 AbstractBeanFactory 的 getBean() 方法中,首先会尝试从单例 Bean 缓存中获取 Bean 实例:

@Override
public Object getBean(String name) throws BeansException {return doGetBean(name, null, null, false);
}protected <T> T doGetBean(final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly) throws BeansException {// 获取"真实"的 beanName,考虑了 FactoryBeanfinal String beanName = transformedBeanName(name);Object sharedInstance = getSingleton(beanName);if (sharedInstance != null && args == null) {// ...}// ...
}

getSingleton() 方法定义在 DefaultSingletonBeanRegistry 中:

public Object getSingleton(String beanName) {return getSingleton(beanName, true);
}protected Object getSingleton(String beanName, boolean allowEarlyReference) {// 检查缓存中是否存在实例Object singletonObject = this.singletonObjects.get(beanName);if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {// 如果当前 Bean 正在创建中,则需要处理循环依赖singletonObject = this.earlySingletonObjects.get(beanName);if (singletonObject == null && allowEarlyReference) {// ...}}return singletonObject;
}
  1. addSingleton() 方法

如果缓存中不存在该 Bean 的实例,Spring 会调用相应的实例化策略创建 Bean 实例,并在创建完成后,将其缓存到单例缓存中:

protected void addSingleton(String beanName, Object singletonObject) {synchronized (this.singletonObjects) {this.singletonObjects.put(beanName, singletonObject);// 从 singletonFactories 中移除this.singletonFactories.remove(beanName);this.earlySingletonObjects.remove(beanName);this.registeredSingletons.add(beanName);}
}
  1. 单例作用域

在解析 Bean 定义时,Spring 会根据 scope 属性确定 Bean 的作用域,默认为 singleton,即单例模式。如果是单例作用域,Spring 会在获取 Bean 实例时从单例缓存中获取或创建一个实例并加入缓存。

public Object getBean(String name) throws BeansException {return doGetBean(name, null, null, false);
}protected <T> T doGetBean(final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly) {// ...// 创建或获取已经创建的实例if (mbd.isSingleton()) {sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {@Overridepublic Object getObject() throws BeansException {try {return createBean(beanName, mbd, args);} catch (BeansException ex) {// ...}}});// ...}// ...
}

在上面的代码中,如果 Bean 的作用域是单例,Spring 会调用 getSingleton() 方法尝试从单例缓存中获取 Bean 实例。如果缓存中不存在,则使用传入的 ObjectFactory 回调函数创建 Bean 实例,并将其加入单例缓存中。

  1. 循环依赖处理

Spring 还提供了处理单例 Bean 循环依赖的机制。在 getSingleton() 方法中,如果发现当前 Bean 正在创建中,会尝试从 earlySingletonObjects 缓存中获取提前曝光的 Bean 实例(ObjectFactory),从而解决循环依赖问题。

总结起来,Spring 5 中通过维护一个单例 Bean 缓存 (ConcurrentHashMap)、getSingleton() 方法从缓存中获取或创建 Bean 实例、以及 addSingleton() 方法将 Bean 实例加入缓存,实现了单例模式的管理。同时,Spring 还根据 Bean 定义的 scope 属性确定 Bean 的作用域,默认为单例模式。这种实现方式保证了在同一个 Spring IoC 容器中,单例 Bean 只会被实例化一次,提高了性能并节省了内存资源,

三、代理模式

  • Spring的AOP (Aspect Oriented Programming) 功能就是利用代理模式实现的。
  • 代理模式在不修改目标对象的情况下,通过代理对象来增强目标对象的功能。Spring AOP的实现主要依赖两个模块:Spring AOP和AspectJ
  1. AOP代理创建

在Spring AOP中,如果目标对象实现了接口,则默认使用JDK动态代理创建AOP代理。如果目标对象没有实现接口,则使用CGLIB创建代理对象。这种选择是由AopProxyFactory决定的:

public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {@Overridepublic AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {Class<?> targetClass = config.getTargetClass();if (targetClass == null) {throw new AopConfigException("TargetSource cannot determine target class: " +"Either an interface or a target is required for proxy creation.");}if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {return new JdkDynamicAopProxy(config);}return new ObjenesisCglibAopProxy(config);}else {return new JdkDynamicAopProxy(config);}}
}
  1. JDK动态代理

JDK动态代理通过实现InvocationHandler接口和使用Proxy类来创建代理对象,代码如下:

public class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {private final Class<?> proxiedInterface;public Object getProxy(ClassLoader classLoader) {// 使用Proxy创建代理对象return Proxy.newProxyInstance(classLoader, new Class<?>[] { this.proxiedInterface }, this);}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 调用拦截器链执行方法MethodInvocation invocation = new ReflectiveMethodInvocation(...);return invocation.proceed();}
}
  1. CGLIB代理

CGLIB代理通过继承目标对象的方式创建代理对象,代码如下:

public class ObjenesisCglibAopProxy extends CglibAopProxy {@Overridepublic Object getProxy(ClassLoader classLoader) {// 使用Enhancer创建代理对象Enhancer enhancer = new Enhancer();enhancer.setSuperclass(this.advised.getTargetClass());enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);enhancer.setStrategy(new ClassLoaderAwareUndeclaredThrowableStrategy(classLoader));Callback[] callbacks = getCallbacks(this.advised);Class<?>[] types = new Class<?>[callbacks.length];for (int x = 0; x < types.length; x++) {types[x] = callbacks[x].getClass();}// 设置过滤器enhancer.setCallbackFilter(new ProxyCallbackFilter(this.advised.getConfigurationOnlyCopy(), types));enhancer.setCallbackTypes(types);// 创建代理对象return createProtectedProxy(enhancer, callbacks, this.advised);}
}
  1. Advice链执行

无论是JDK动态代理还是CGLIB代理,在目标方法调用时都会执行相应的Advice链。Spring使用MethodInvocation类来执行Advice链:

public class ReflectiveMethodInvocation implements ProxyMethodInvocation {protected final Object[] arguments;public Object proceed() throws Throwable {// 获取适当的拦截器链List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);if (chain.isEmpty()) {// 直接执行目标方法retVal = invokeSingalTarget();}else {// 执行拦截器链retVal = new ReflectiveMethodInvocation(proxy, target, method, arguments, target.getClass(), chain).proceed();}return retVal;}
}

在执行Advice链的过程中,Spring会根据Advice的类型(前置、环绕、异常、最终返回)在适当的位置执行对应的Advice逻辑。

总的来说,Spring 5利用JDK动态代理和CGLIB两种方式创建AOP代理对象,并通过执行相应的Advice链来增强目标对象的功能。代理模式的使用使得Spring AOP能够在不修改目标对象的前提下,动态地为目标对象增加新的行为和职责。可以自定义Advice,从而灵活地扩展目标对象的功能。

四、模板方法模式

  • Spring中的 JDBCTemplate、HibernateTemplate等以模板方法模式为基础。
  • 模板方法模式定义了一个算法的骨架,并允许子类重写特定的步骤。
  1. JdbcTemplate

JdbcTemplate是Spring JDBC中的核心类,用于简化JDBC操作。它提供了一系列执行SQL语句的模板方法,如query()、update()等。这些模板方法都遵循相同的模式:

public <T> T query(String sql, ResultSetExtractor<T> rse) throws DataAccessException {// 创建语句PreparedStatement stmt = null;try {stmt = createPreparedStatement(sql);// 设置参数applyStatementSettings(stmt);// 执行查询ResultSet rs = null;try {rs = executeQuery(stmt);// 执行ResultSetExtractorreturn rse.extractData(rs);} finally {// ...}} finally {// ...}
}
  1. 模板方法骨架

在JdbcTemplate的模板方法中,有一些固定的步骤是不可变的,如创建语句、设置参数、执行查询等,这些步骤构成了模板方法的骨架。

  1. 钩子方法

在模板方法中,也会调用一些抽象方法或钩子方法,这些方法留给子类去实现。比如executeQuery()、applyStatementSettings()等。子类可以通过扩展JdbcTemplate并重写这些钩子方法来自定义相应的行为。

protected PreparedStatement createPreparedStatement(String sql, @Nullable List<Object> paramList) throws SQLException {PreparedStatement stmt = getPreparedStatementCreator().createPreparedStatement(sql, this.getRetrieveKeys(paramList));applyStatementSettings(stmt);return stmt;
}protected abstract PreparedStatementCreator getPreparedStatementCreator();protected void applyStatementSettings(PreparedStatement stmt) {// ...
}
  1. 回调接口

除了通过子类继承的方式来自定义行为,Spring还提供了一些回调接口,允许开发者在执行模板方法时注入自定义的逻辑。比如ResultSetExtractor、PreparedStatementSetter等。

public <T> T query(String sql, ResultSetExtractor<T> rse) throws DataAccessException {// ...return rse.extractData(rs);
}

总的来说,Spring 5中通过模板方法模式提供了一些可复用的模板类,如JdbcTemplate。这些模板类定义了一系列算法的骨架,并通过钩子方法和回调接口,允许开发者在不改变模板结构的情况下,自定义相应的行为。这种模式提高了代码的可重用性和可扩展性,同时也增强了代码的可维护性。Spring框架中还有许多其他模板类的实现,如RedisTemplate、RestTemplate等,都遵循了模板方法模式的设计思想。

五、观察者模式

  • Spring的事件机制就是基于观察者模式实现的。
  • 观察者模式定义了对象之间的一对多依赖关系,当一个对象改变状态时,它的所有依赖者都会得到通知。
    Spring 5中使用观察者模式来实现事件驱动机制,主要通过ApplicationEvent和ApplicationListener来实现。
  1. ApplicationEvent

ApplicationEvent是Spring中事件的基类,它继承自jdk的EventObject。所有的事件都需要继承ApplicationEvent。

public abstract class ApplicationEvent extends EventObject {private static final long serialVersionUID = 7099057708183571937L;private final long timestamp;public ApplicationEvent(Object source) {super(source);this.timestamp = System.currentTimeMillis();}// ...
}
  1. ApplicationListener

ApplicationListener是观察者接口,用于监听并处理ApplicationEvent事件。

@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {void onApplicationEvent(E event);
}
  1. ApplicationEventMulticaster

ApplicationEventMulticaster是观察者模式中的主题或发布者角色,它维护了一个ApplicationListener集合,用于发布事件并通知所有监听器。

public abstract class AbstractApplicationEventMulticaster implements ApplicationEventMulticaster {private final ListenerRetriever defaultRetriever = new DefListenerRetriever(false);private final ListenerRetriever retrieverWithSet = new DefListenerRetriever(true);// 添加监听器public void addApplicationListener(ApplicationListener<?> listener) {synchronized (this.retrieveListeners) {this.retrieveListeners.add(listener);this.singletonRetrievers.clear();}}// 发布事件并通知监听器public void multicastEvent(final ApplicationEvent event, ResolvableType eventType) {resolveListeners(event, retrieverCache.getListeners(eventType, this.defaultRetriever, this.applicationContext.getId()));}// 内部通知监听器处理事件protected void invokeListener(ApplicationListener listener, ApplicationEvent event) {listener.onApplicationEvent(event);}// ...
}
  1. 注册和发布事件

在Spring中,可以通过实现ApplicationListener接口并将其注册为Bean来监听事件。发布事件时,ApplicationEventMulticaster会遍历所有的监听器并调用其onApplicationEvent()方法。

// 定义事件
public class MyEvent extends ApplicationEvent {// ...
}// 定义监听器
@Component
public class MyListener implements ApplicationListener<MyEvent> {@Overridepublic void onApplicationEvent(MyEvent event) {// 处理事件}
}// 发布事件
@Component
public class EventPublisher {@Autowiredprivate ApplicationContext context;public void publish() {context.publishEvent(new MyEvent("Hello Event"));}
}

Spring 5中通过ApplicationEvent、ApplicationListener和ApplicationEventMulticaster实现了观察者模式。ApplicationEvent是事件对象,ApplicationListener是观察者接口,ApplicationEventMulticaster扮演发布者的角色,负责管理监听器并发布事件。开发者可以自定义事件和监听器,并通过ApplicationContext发布事件。这种实现方式使得Spring框架具有了良好的可扩展性和灵活性,能够很好地支持事件驱动编程。

六、策略模式

  • Spring的资源访问模块(Resource)中使用了策略模式。
  • 策略模式定义了一系列算法,并将每个算法封装成一个对象,从而使它们可以互相替换。
    Spring 5中使用了策略模式来实现一些功能,允许在运行时根据不同的条件选择不同的算法或策略。以Spring中的Resource接口为例,我们来分析一下策略模式在Spring 5中的实现。
  1. Resource接口

Resource接口定义了一组用于获取低级资源的操作方法。它是Spring资源访问策略的入口接口。

public interface Resource extends InputStreamSource {boolean exists();boolean isOpen();URL getURL() throws IOException;File getFile() throws IOException;// ...
}
  1. Resource实现类

Spring提供了多个Resource接口的具体实现类,每个实现类都对应一种资源访问策略。

  • ClassPathResource: 用于访问类路径下的资源
  • FileSystemResource: 用于访问文件系统中的资源
  • UrlResource: 用于访问URL资源
  • ByteArrayResource: 用于访问字节数组资源
  • InputStreamResource: 用于访问输入流资源
public class ClassPathResource extends AbstractResource {// ...
}public class FileSystemResource extends AbstractResource implements ResourceLoaderAware {// ...  
}public class UrlResource extends AbstractFileResolvingResource {// ...
}
  1. Resource加载器

Spring提供了ResourceLoader接口及其实现类DefaultResourceLoader,用于根据不同的前缀来选择合适的Resource实现类。

public interface ResourceLoader {Resource getResource(String location);
}public class DefaultResourceLoader implements ResourceLoader {@Overridepublic Resource getResource(String location) {if (location.startsWith("/")) {return getResourceByPath(location);} else if (location.startsWith("classpath:")) {return new ClassPathResource(location.substring("classpath:".length()), getClassLoader());} else {try {// Try URLURL url = new URL(location);return new UrlResource(url);} catch (MalformedURLException ex) {// No URL -> resolve as resource path.return getResourceByPath(location);}}}// ...
}
  1. 使用示例

在应用程序中,可以通过ResourceLoader或ApplicationContext来获取不同类型的Resource实例。

@Component
public class MyService {@Autowiredprivate ResourceLoader resourceLoader;public void loadResource(String location) {Resource resource = resourceLoader.getResource(location);// 使用Resource执行相关操作}
}

Spring 5通过Resource接口及其多个具体实现类,以及ResourceLoader加载器,实现了策略模式。不同的Resource实现类对应不同的资源访问策略,而ResourceLoader则根据资源位置的前缀来选择合适的Resource实现类。

七、装饰器模式

  • Spring中的 HttpServletRequestDecorator和 HttpServletResponseDecorator 使用了装饰器模式。
  • 装饰器模式可以在不修改原有对象结构的情况下,动态地给该对象增加新的行为和职责。

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

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

相关文章

吴恩达深度学习笔记:神经网络的编程基础2.9-2.14

目录 第一门课&#xff1a;神经网络和深度学习 (Neural Networks and Deep Learning)第二周&#xff1a;神经网络的编程基础 (Basics of Neural Network programming)2.9 逻辑回归中的梯度下降&#xff08;Logistic Regression Gradient Descent&#xff09; 第一门课&#xff…

数字生活的未来:探索Web3的全新世界

随着科技的飞速发展&#xff0c;我们正迈向一个数字化的未来。而在这个数字化的时代&#xff0c;Web3技术的崛起正引领着我们进入一个全新的世界。本文将深入探讨Web3技术的特点以及它给我们带来的全新体验。 1. 去中心化的特点 Web3的去中心化是其最显著的特点之一&#xff0…

基于微信小程序的校园跑腿小程序,附源码

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;…

智慧楼宇物联网建设实施方案(2)

建设方案 楼宇综合管理平台 智慧楼宇物联网应用综合管理系统是对整个物联网系统的集中监控和展示。其主要功能是对各应用子系统的关键监测数据进行数据格式解析并呈现。进而使管理者能够从整体上对整个物联网系统运行状态有个直观的了解。其不同于各专业子系统的管理软件,重…

蓝桥杯单片机快速开发笔记——定时器

一、基本原理&#xff1a; 定时器的作用&#xff1a; 定时器是一种用于产生精确时间延时的模块&#xff0c;可以在程序中用来进行时间控制、计时等操作。 定时器的工作原理&#xff1a; 51单片机的定时器是通过内部的计数器来实现的&#xff0c;计数器每隔一个固定的时间周期自…

Python之Web开发中级教程----创建Django项目

Python之Web开发中级教程----创建Django项目 使用虚拟环境&#xff1a; Workon py3_django3 1.创建Django项目 django-admin startproject name 例&#xff1a;git的本地仓库下新建studentmanager的项目 cd /home/go/work/gtest/ django-admin startproject bookmanager 新…

hadoop伪分布式环境搭建详解

&#xff08;操作系统是centos7&#xff09; 1.更改主机名&#xff0c;设置与ip 的映射关系 hostname //查看主机名 vim /etc/hostname //将里面的主机名更改为master vim /etc/hosts //将127.0.0.1后面的主机名更改为master&#xff0c;在后面加入一行IP地址与主机名之间的…

【CSP试题回顾】201709-3-JSON查询

CSP-201709-3-JSON查询 解题思路 1. 初始化数据结构 map<string, string> strContent: 存储字符串类型属性的内容。键是属性名&#xff08;可能包含通过点.连接的多级属性名&#xff09;&#xff0c;值是属性的字符串值。vector<string> keyVec: 存储当前正在处…

VsCode 使用密钥连接 Centos

在 centos 下生成密钥 ssh-keygen 执行上述命令后&#xff0c;一路回车&#xff0c;直到出现如下界面&#xff1a; 查看密钥生成情况 cd /root/.ssh ls 结果如下所示&#xff1a; 服务器上安装公钥 cd /root/.ssh cat id_rsa.pub >> authorized_keys ls >查看确…

CVE-2024-27199 JetBrains TeamCity 身份验证绕过漏洞2

漏洞简介 TeamCity Web 服务器中发现了第二个身份验证绕过漏洞。这种身份验证旁路允许在没有身份验证的情况下访问有限数量的经过身份验证的端点。未经身份验证的攻击者可以利用此漏洞修改服务器上有限数量的系统设置&#xff0c;并泄露服务器上有限数量的敏感信息。 项目官网…

LAMP网站部署(Discuz论坛网站部署)

目录 mysql命令 语法 选项 参数 实例 安装php 安装Mariadb 关掉防火墙和selinux 启动HTTP服务 初始化数据库 查看数据库是否创建成功 修改HTTP的配置文件 浏览器打开 将以下所有目录都加上权限 最后首页效果 mysql命令 是MySQL数据库服务器的客户端工具&#xff0c;它工作在命…

tomcat的webapp文件中发布web应用

一、Web服务器 1.什么是Web 概述&#xff1a; web(World Wide Web)即全球广域网&#xff0c;也称为万维网&#xff0c;它是一种基于超文本和HTTP的、全球性的、动态交百的、跨平台的分布式图形信息系统。是建立在internet上的一种网络服务&#xff0c;为浏览者在Intern…

【深度学习笔记】9_5 多尺度目标检测

注&#xff1a;本文为《动手学深度学习》开源内容&#xff0c;部分标注了个人理解&#xff0c;仅为个人学习记录&#xff0c;无抄袭搬运意图 9.5 多尺度目标检测 在9.4节&#xff08;锚框&#xff09;中&#xff0c;我们在实验中以输入图像的每个像素为中心生成多个锚框。这些…

物联网技术助力智慧城市转型升级:智能、高效、可持续

目录 一、物联网技术概述及其在智慧城市中的应用 二、物联网技术助力智慧城市转型升级的路径 1、提升城市基础设施智能化水平 2、推动公共服务智能化升级 3、促进城市治理现代化 三、物联网技术助力智慧城市转型升级的成效与展望 1、成效显著 2、展望未来 四、物联网技…

机试:蛇形矩阵

问题描述: 代码示例: //蛇形矩阵 #include <bits/stdc.h> using namespace std;int main(){int n;cout << "输入样例" << endl; cin >> n;int k 1; for(int i 0; i < n; i){if( i %2 0){//单数行for(int j 0; j < n; j){ cout &…

运维自动化之ansible工具

目录 前言 一、Ansible 工具概述 1、Ansible 功能 2、Ansible 特性 3、Ansible 优缺点 4、Ansible 架构 4.1 Ansible 组成 4.2 Ansible 命令执行来源 二、Ansible 安装和基础用法 1、Ansible 安装 1.1 yum源安装 1.2 使用python编译安装 1.3 Git方式安装 2、Ansib…

《小程序从入门到入坑》框架语法

前言 哈喽大家好&#xff0c;我是 SuperYing&#xff0c;我们继续小程序入门系列&#xff0c;本文将对小程序框架语法进行比较全面的介绍。在《小程序从入门到入坑》简介及工程创建中&#xff0c;我们提到小程序项目结构&#xff0c;主要包括 app.json&#xff0c;app.js&…

Airtest-Selenium升级兼容Selenium 4.0,给你全新体验!

一、前言 在上期更新推文中提到&#xff0c;我们Airtest-Selenium更新到了1.0.6版本&#xff0c;新增支持Selenium4.0的语法&#xff0c;那么我们来看一下Airtest-Selenium更新后有什么新的内容吧~ 二、selenium 4.0有什么新功能 selenium4.0最主要的还是定位元素方法的更新…

使用 opencv 识别答题卡,生成填涂答案

一般答题卡设计时都在试卷4个角预留4个一样大小的黑块 仅能识别选择题判断题之类的填涂答题的题目&#xff0c;不能识别填空题应用题等其它主观题 使用 opencv 识别试卷图片中所有黑块&#xff0c;再根据黑块大小获取四个角的位置&#xff0c;根据四个黑块位置校正图像 将图…

给电脑加硬件的办法 先找电脑支持的接口,再买相同接口的

需求&#xff1a;我硬盘太小&#xff0c;换或加一个大硬盘 结论&#xff1a;接口是NVMe PCIe 3.0 x4 1.找到硬盘型号 主硬盘 三星 MZALQ512HALU-000L2 (512 GB / 固态硬盘) 2.上官网查 或用bing查 非官方渠道信息&#xff0c;不确定。