Spring底层入门(十)

news2024/11/26 15:23:09

1、内嵌tomcat

        boot框架是默认内嵌tomcat的,不需要手动安装和配置外部的 Servlet 容器。

        简单的介绍一下tomcat服务器的构成:

  • Catalina: Catalina 是 Tomcat 的核心组件,负责处理 HTTP 请求、响应以及管理 Servlet 生命周期。它包括一个 Web 容器和一个 Servlet 引擎,用于处理 Servlet 和 JSP 页面。
  • Connector: 连接器是 Tomcat 与外部客户端之间通信的桥梁,负责处理传入的 HTTP 请求,并将其传递给 Catalina 处理。Tomcat 提供了多种类型的连接器,包括 HTTP 连接器(用于处理 HTTP 请求)、AJP 连接器(用于与 Apache HTTP Server 连接)等。
  • Realm: Realm 是 Tomcat 的安全认证和授权机制,用于验证用户身份并控制用户对受保护资源的访问权限。Tomcat 支持多种类型的 Realm,如基于内存的 Realm、基于数据库的 Realm 等。
  • Valves: 阀门是 Tomcat 的拦截器组件,用于在请求处理过程中执行特定的操作,如访问日志记录、安全验证、压缩等。Tomcat 提供了多种类型的阀门,可以通过配置文件进行灵活配置。
  • Host: Host 是 Tomcat 的虚拟主机,用于在同一物理服务器上托管多个域名或应用程序。每个 Host 都有一个唯一的名称和基础目录,可以配置不同的域名和应用程序。
  • Engine: Engine 是 Tomcat 的引擎,用于管理多个虚拟主机(Host)。它负责调度请求到相应的虚拟主机,并协调虚拟主机之间的资源共享和管理。
  • Context: Context 是 Tomcat 的上下文容器,用于管理和配置单个 Web 应用程序的运行环境。每个 Web 应用程序都有一个对应的 Context,包括其配置信息、Servlet 映射、Session 管理等。

        我们来模拟一下tomcat的执行过程:

        其中第四步是将下面自定义的servlet程序放入servletContext上下文中,并且手动指定映射路径(相当于Controller层加入@RequestMapping及派生注解指定路径)

public class A36 {
    public static void main(String[] args) throws IOException, LifecycleException {
        //1.创建tomcat对象
        Tomcat tomcat = new Tomcat();
        tomcat.setBaseDir("tomcat");

        //准备docBase,存放项目文件
        File docBase = Files.createTempDirectory("boot.").toFile();
        docBase.deleteOnExit();

        //3.创建tomcat项目 context
        Context context = tomcat.addContext("", docBase.getAbsolutePath());

     
        //4.编程添加servlet
        context.addServletContainerInitializer(new ServletContainerInitializer() {
            @Override
            public void onStartup(Set<Class<?>> set, ServletContext servletContext) throws ServletException {
                servletContext.addServlet("test",new MyServlet()).addMapping("/test");
                }
            }
        }, Collections.emptySet());

        //5.启动tomcat
        tomcat.start();


        //6.设置协议,创建连接器
        Connector connector = new Connector(new Http11Nio2Protocol());
        connector.setPort(8080);
        tomcat.setConnector(connector);
    }
}

        编写一个自定义的servlet程序:

public class MyServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.getWriter().print("abc");
    }
}

        而内嵌的tomcat如何与Spring进行整合?

        其关键点在于Config配置类中的 public DispatcherServletRegistrationBean registrationBean(DispatcherServlet dispatcherServlet) 方法。这个方法用于注册DispatcherServlet springmvc入口并创建一个 DispatcherServletRegistrationBean Bean,将传入的 DispatcherServlet 注册到 Spring 应用程序中。

       Config配置类又是在refresh容器前注册的:

 public static WebApplicationContext webApplicationContext(){
        AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
        context.register(Config.class);
        context.refresh();
        return context;
    }

       我们只需要在编程添加servlet这一步,获取容器中所有的bean,并且通过.onStartup() 方法完成注册:

     //得到applicationContext
        WebApplicationContext applicationContext = webApplicationContext();

    //4.编程添加servlet
        context.addServletContainerInitializer(new ServletContainerInitializer() {
            @Override
            public void onStartup(Set<Class<?>> set, ServletContext servletContext) throws ServletException {
                servletContext.addServlet("test",new MyServlet()).addMapping("/test");


                //得到ServletRegistrationBean中每一个注册的bean
                for (ServletRegistrationBean value : applicationContext.getBeansOfType(ServletRegistrationBean.class).values()) {
                    value.onStartup(servletContext);
                }
            }
        }, Collections.emptySet());

        .onStartup() 方法底层调用的依旧是 servletContext.addServlet()  方法

05f64f8c7e9d4baa8e204abf025c94d1.png

        boot在整合tomcat时,首先是创建了Spring容器,然后在调用onfresh()方法时会将tomcat创建出来,并且执行到添加servlet。

776932b42de54f5b9e78e6548e0c64b0.png

        在finishRefresh(); 方法会启动tomcat服务器并且设置协议,创建连接器。

c26ab70e0333416b89254e9ab6b52253.png

2、自动装配原理

        现在有如下的场景:在某个包下创建了两个bean,并且将其注册到了两个配置类中:

public class Bean1 {

}
public class Bean2 {
}
@Configuration
public class Config1 {

    @Bean
    public Bean1 bean1(){
        return new Bean1("第三方");
    }
}
@Configuration
public class Config2 {

    @Bean
    public Bean2 bean2(){
        return new Bean2();
    }
}

        假设这两个bean都是与数据库连接有关的组件,具有通用性。那么难道是每次在别的地方用到的时候,都去临时注册吗?答案肯定是否定的,就和方法封装一样,我们可以在其他运用到的地方进行导入:

        @Import 注解的作用就是导入其他配置类或组件类,如果在  @Import 注解中将Config1和Config2 的class 写死,这样不太好:

@Configuration
@Import(MyInportSelector.class)
public class MyConfig {

    @Bean
    public Bean1 bean1(){
        return new Bean1("本项目");
    }
}

        我们可以将Config1和Config2注册在自定义的MyInportSelector类中统一管理:

        在Spring中,某个自定义ImportSelector类下需要统一装配的组件,不是写死在自定义ImportSelector类中的,而是放在META-INF下的spring.factories中统一进行管理。

d80c3e064804447ca7f8ac1f4e6f37ce.png

873831d6c3b748b985a634e3a3766323.png

/**
 * 读取配置文件中的引用
 */
public class MyInportSelector implements ImportSelector {

    /**
     * 在方法中会读取所有jar包 META-INF下的spring.factories 做自动装配
     * @param importingClassMetadata
     * @return
     */
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {

        //需要自动装配的类不是写死在代码中的,而是放在配置文件中的
        //从配置文件中读取信息 META-INF下的spring.factories
        //com.itbaima.a37.MyInportSelector=\
        //com.itbaima.a37_1.Config1,\
        //com.itbaima.a37_1.Config2
        List<String> strings = SpringFactoriesLoader.loadFactoryNames(MyInportSelector.class, null);
        return strings.toArray(new String[0]);
    }
}

        完成EnableAutoConfiguration的自动装配:

/**
 * 读取配置文件中的引用
 */
public class MyInportSelector implements ImportSelector {

    /**
     * 在方法中会读取所有jar包 META-INF下的spring.factories 做自动装配
     * @param importingClassMetadata
     * @return
     */
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {

        for (String name : SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguration.class, null)) {
            System.out.println(name);
        }

    }
}

5adc049ce42349478c63d6c99eab63c2.png

         那如果在本项目注册的bean和其他外部引用的bean同名的问题呢?

         生效的是本项目中的。因为bean加载的时机不同,第三方的bean先加载,后加载的bean会覆盖先加载的同名的bean。然而在boot中默认是false不允许覆盖。解决方法:

        自定义的InportSelector实现DeferredImportSelector接口,可以推迟第三方bean的加载。并且需要在注册第三方bean时加上 @ConditionalOnMissingBean 注解,表明当容器中没有该名称的bean时才需要加载。(因为此时本项目中的bean已先于第三方的bean加载)

@Configuration
public class Config1 {

    @Bean
    @ConditionalOnMissingBean//当容器中缺少某个bean时才会添加
    public Bean1 bean1(){
        return new Bean1("第三方");
    }
}

3、AopAutoConfiguration

        AopAutoConfiguration是用于负责配置和启用 AspectJ 面向切面编程(AOP)功能。

        我们模拟一下它的自动装配:

@Configuration
@Import(MyImportSelector.class)
public class Config {
}
public class MyImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{AopAutoConfiguration.class.getName()};
    }
}
public class A38 {
    public static void main(String[] args) {
        GenericApplicationContext context = new GenericApplicationContext();
        //注册各种后处理器
        AnnotationConfigUtils.registerAnnotationConfigProcessors(context.getDefaultListableBeanFactory());
        context.registerBean(Config.class);
        context.refresh();

        for (String name : context.getBeanDefinitionNames()) {
            System.out.println(name);
        }
    }
}

        下面这四条便是AopAutoConfiguration通过自动装配得到的BeanDefinitionName

org.springframework.boot.autoconfigure.aop.AopAutoConfiguration$AspectJAutoProxyingConfiguration$CglibAutoProxyConfiguration
org.springframework.aop.config.internalAutoProxyCreator
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration$AspectJAutoProxyingConfiguration
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration

        那么AopAutoConfiguration是如何选择装配哪些bean的呢?我们点进AopAutoConfiguration的源码看一下:

        @ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true) 是一个针对properties配置文件内容的判断,此处判断的含义是,如果配置文件中没有spring.aop前缀的键,或者有并且它的值为true时,会进入这个类。显然目前的条件是成立的,我们自定义的配置类中没有spring.aop前缀的键。

57fdcbc85f184fc2b0ae3d423fdfa1ba.png

        在静态内部类AspectJAutoProxyingConfiguration上也有一个注解:@ConditionalOnClass(Advice.class) 作用是判断是否存在一个名为Advice的类,在boot中是存在的,所以会进入AspectJAutoProxyingConfiguration类:

b5d556b6fc174171bb072319f91f9c4a.png         

        在JdkDynamicAutoProxyConfiguration和CglibAutoProxyConfiguration静态内部类上,分别有两个注解:

  • @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false") 判断properties配置文件中是否有spring.aop前缀的键,并且值要为false。
  • @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true", matchIfMissing = true) 判断properties配置文件中是否有spring.aop前缀的键,并且值要为ture,或者不存在。

        而两个静态内部类上标注的@EnableAspectJAutoProxy 注解,实际上也是加上了@Import

注解的自动装配配置类:

b82884968f194075a7c2ec6f013ad56f.png

        自定义的自动装配了类实现了ImportBeanDefinitionRegistrar接口,用于用编程的方式确定装配的内容:

9bc7855e12394133b86e2afceddb3902.png

        在AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry); 方法中,实际上是注册了AnnotationAwareAspectJAutoProxyCreator:

        复习一下(第三篇):AnnotationAwareAspectJAutoProxyCreator是用于自动创建代理以实现切面功能的Spring后处理器,将高级的Aspect切面分解并转换成低级的Advice切面,并且根据设置去选择JDK或CGLIB代理方式(proxyTargetClass属性),在AnnotationAwareAspectJAutoProxyCreator中,有两个重要的方法:

  • protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName):用于查找符合条件的切面通知器(Advisors)。
  • protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey):内部调用 .findEligibleAdvisors得到存放符合条件切面通知器的集合,如果集合不为空,就创建代理。

9b22ed63447246e5a68715d6ed3482f7.png

         根据条件,应该会自动装配CglibAutoProxyConfiguration:

d8a7e78b796245439d9b3089f7f6ca4a.png

        可从容器中获取AnnotationAwareAspectJAutoProxyCreator,isProxyTargetClass的取值是true,代表无论是否实现了接口,走的都是CGLIB代理方式。

AnnotationAwareAspectJAutoProxyCreator proxyCreator = context.getBean(AnnotationAwareAspectJAutoProxyCreator.class);
System.out.println(proxyCreator.isProxyTargetClass());

4、DataSource

        在DataSourceAutoConfiguration中,主要有两部分:

  • EmbeddedDatabaseConfiguration:Spring Framework 中用于配置内嵌式数据库(Embedded Database)的自动配置类之一
  • PooledDataSourceConfiguration:Spring Framework 中用于配置连接池数据源(Pooled DataSource)的自动配置类之一。

166ef2c692e1446b847f88775eceb2c7.png

         EmbeddedDatabaseConfiguration很少用到,我们重点看PooledDataSourceConfiguration:

        DataSourceConfiguration的每个静态内部类上都加入了@ConditionalOnClass 条件判断注解。

6df6315917904568af86530b53e82bf5.png

        Boot中默认是有HikariDataSource源的:

7f92b9659e2643f29b036ec20a3dfe13.png

        所以会将HikariDataSource注册成bean并设置连接信息:

ad839d01606a493e991f2decac48bd6b.png

5、MybatisAutoConfiguration

        在MybatisAutoConfiguration类上,有如下的注解:

  • @ConditionalOnClass({SqlSessionFactory.class, SqlSessionFactoryBean.class}) :表明必须要存在SqlSessionFactory和SqlSessionFactoryBean两个类。因为mybatis与boot整合需要数据源连接信息。
  • @ConditionalOnSingleCandidate(DataSource.class) :表明数据源必须是唯一的,不能存在多份不同的数据源
  • @EnableConfigurationProperties({MybatisProperties.class}) : 表明将来会创建一个MybatisProperties对象,用于将环境中的键值信息与对象绑定(要求配置中的键名必须前缀mybatis):
  • 9766eaec806c48a2b3335188c42d9604.png
  • @AutoConfigureAfter({ DataSourceAutoConfiguration.class, MybatisLanguageDriverAutoConfiguration.class }) : 表明该配置类是在DataSourceAutoConfiguration、MybatisLanguageDriverAutoConfiguration加载完成后再进行初始化。

        上述任何一个条件不满足都不会进入MybatisAutoConfiguration:

c1fcb9aa8cc243e2b05686d91110a72f.png

         public SqlSessionFactory sqlSessionFactory(DataSource dataSource) 方法用于创建SqlSessionFactory的bean,注意加上了@ConditionalOnMissingBean 代表容器中没有其他第三方的SqlSessionFactory的bean时,才会初始化MybatisAutoConfiguration中的SqlSessionFactory。

168db19eb2e04a8683e6b99b79905b95.png

         public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) 方法用于创建SqlSession模板:

ccbc0e8c817a456d842634a27a725733.png

         SqlSessionTemplate是线程安全的,在同一个线程中,不同方法可以共用一个实例。

f2a69f7a877f42ed99920f855aa4a05d.png

        最后还有一个MapperScannerRegistrarNotFoundConfiguration内部类:        

        它只有在没有自定义的MapperFactoryBean和MapperScannerConfigurer时才会生效:

f273003f6ef74a11b7c6aeeb9066e113.png

        然后通过@Import(AutoConfiguredMapperScannerRegistrar.class) 注解导入了AutoConfiguredMapperScannerRegistrar,作用是扫描和引导类同包下的被@Mapper注解控制的mapper/dao接口,将这些定义为bean

60694da114bf424286524da590e294ca.png

6、DataSourceTransactionManagerAutoConfiguration

        DataSourceTransactionManagerAutoConfiguration是用于自动配置数据源事务管理器(DataSource Transaction Manager)的类之一。

  • 在类上标注了@ConditionalOnClass({ JdbcTemplate.class, TransactionManager.class }) 注解,要求必须要有事务管理器和JDBC模板方法。
  •  @EnableConfigurationProperties(DataSourceProperties.class) 会将配置文件中以spring.datasource为前缀的key绑定到DataSourceProperties对象中(设置数据库连接信息,username,password...)

7b2fbe2fe7fd42fcb8842013e1d75964.png

         并且导入了EnableConfigurationPropertiesRegistrar自动装配类。

95e321848e7d4dfab83f2902a7e4b919.png

46d65e60492243519abe52cfe35087b6.png

        同样JdbcTransactionManagerConfiguration静态内部类限定容器中只能有一份数据源:

  • DataSourceTransactionManager:创建处理与JDBC 数据源相关的事务的Bean。

3315c773fd924330b9efc2d056e3e017.png


        下面介绍一些与整合Spring MVC相关的配置类:

7、TransactionAutoConfiguration

      TransactionAutoConfiguration 是用于自动配置事务管理的类之一。

  • @ConditionalOnClass(PlatformTransactionManager.class) 表明容器中必须要有PlatformTransactionManager类,简单来说,它定义了事务管理器的核心功能。      
  • @AutoConfigureAfter({ JtaAutoConfiguration.class, HibernateJpaAutoConfiguration.class,DataSourceTransactionManagerAutoConfiguration.class, Neo4jDataAutoConfiguration.class }) 表明TransactionAutoConfiguration是在@AutoConfigureAfter 注解value中的类初始化成后再加载。
  • @EnableConfigurationProperties(TransactionProperties.class) 可以将配置文件中以spring.transaction为前缀的key绑定到TransactionProperties对象上。

54b83ecc5b484bf197ca79c0b3358c6b.png

        并且导入了EnableConfigurationPropertiesRegistrar自动装配类

6d5d6f74a4334de1a87e609b3ef9ee97.png

        TransactionAutoConfiguration类中,提供了声明式和响应式事务的支持:

f0d136f414eb489ca66042300d2dee90.png

        以及创建事务管理器:

5637d02b618445388a205187fcdfa0fe.png

        还有配置声明式事务的管理:

3d3b1fa841b54215a3ea36e3d6daf60b.png

8、ServletWebServerFactoryAutoConfiguration

        是 Spring Boot 中负责配置 Servlet 容器工厂的自动配置类之一:

  • @ConditionalOnClass(ServletRequest.class):ServletRequest类存在,条件成立。(ServletRequest定义了客户端向服务器发送的 HTTP 请求的主要属性和操作。它是一个核心组件,用于在服务器端处理 HTTP 请求,HttpServletRequest就是它的子类)
  • @ConditionalOnWebApplication(type = Type.SERVLET) :应用程序是一个 Servlet Web 应用程序时,条件成立
  • @EnableConfigurationProperties(ServerProperties.class):用于启用特定类型的配置属性绑定到ServerProperties对象中。

53aaaf055e8d42d79e1d251e49e81020.png

7040325d9091450eb7c51f9ca626596b.png

        这个类中主要注册了两个Bean:

  • ServletWebServerFactoryCustomizer:这个方法的主要作用是根据传入的参数定制 Servlet Web 服务器工厂的行为,包括配置服务器属性、注册 Web 监听器以及处理 Cookie 的 SameSite 属性。
  • TomcatServletWebServerFactoryCustomizer:这个方法的主要作用是根据传入的参数定制 Tomcat Servlet Web 服务器的行为,包括配置服务器属性、Session 会话管理、安全性等方面。

9、DispatcherServletAutoConfiguration

        用于配置和启用 DispatcherServlet。DispatcherServlet 是 Spring MVC 中的中央调度器,用于处理传入的 HTTP 请求,并将它们分发到相应的处理程序(Controller)进行处理:

  •  @ConditionalOnWebApplication(type = Type.SERVLET) :应用程序是一个 Servlet Web 应用程序时,条件成立
  • @ConditionalOnClass(DispatcherServlet.class) :容器中必须包含DispatcherServlet类,条件成立
  • @AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class :在ServletWebServerFactoryAutoConfiguration类装配完成后( Servlet 容器工厂创建完成),装配本类。

ce93698a6237464eac27aad081d49530.png

        在这个类中,较为重要的是DispatcherServletConfiguration和DispatcherServletRegistrationConfiguration两个静态内部类:

  • DispatcherServletConfiguration:主要是将DispatcherServlet注册成bean,以及一个解析客户端发送的包含文件上传的 HTTP 请求,并将上传的文件转换成可操作的对象的bean
  • DispatcherServletRegistrationConfiguration:主要作用是将DispatcherServletRegistrationBean注册成bean,并且设置tomcat容器启动时即进行DispatcherServlet初始化

10、WebMvcAutoConfiguration

        主要用于配置和初始化 Spring MVC 的各种组件:

        RequestMappingHandlerAdapter:

fe42384c6ada45199338bac05c9f4e45.png        RequestMappingHandlerMapping:

a858622822034404812d05f708ca076b.png

         ExceptionHandlerExceptionResolver:

8646a4fb56584a38bb6e0663ac4ee050.png

11、ErrorMvcAutoConfiguration

        主要作用是在应用程序启动时,自动配置一些默认的错误处理策略和错误页面。

        此前提到的BasicErrorController(处理基本的错误页面和错误信息),ErrorPageRegistrar(转发到自定义的错误页面),在本类中都有体现:

0e5554ebc5d5496eb05c4fc118c60f15.png

 

 

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

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

相关文章

机器人学【一、刚体运动】

机器人学 文章目录 机器人学1. 刚体运动1.1 刚体变换刚体刚体变换 1.2 三维空间中的旋转运动群求质点坐标的相对变换旋转矩阵的合成法则用线性算子来计算叉积叉积的右手法则叉积用于计算线速度旋转的指数坐标Rodrigues公式计算旋转矩阵的例子四元数 1.3 三维空间中的刚体运动齐…

人人都是开发者?Baidu Comate智能代码助手改变你传统的编程之路

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏: 《C干货基地》《粉丝福利》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 文章目录 引入一、人人都是开发者二、Baidu Comate 智能编码助手2.1 Baidu Comate 是什么&#xff1f;2.2 Baidu Comate 支持那…

DDD面试题:DDD聚合和表的对应关系是什么 ?(来自蚂蚁面试)

尼恩说在前面&#xff1a; 在40岁老架构师 尼恩的读者交流群(50)中&#xff0c;最近有小伙伴拿到了一线互联网企业如字节、阿里、滴滴、极兔、有赞、希音、百度、网易、美团的面试资格&#xff0c;遇到很多很重要的面试题&#xff1a; DDD 的外部接口调用&#xff0c;应该放在…

idea护眼主题推荐

idea是一个非常好用的开发工具&#xff0c;程序员们天天都与它打交道&#xff0c;最近写代码眼睛的了干眼症&#xff0c;对屏幕特别敏感&#xff0c;所以关注了下主题相关的内容&#xff0c;jetbrains官方也有主题市场插件&#xff1a;https://plugins.jetbrains.com/search?t…

IDEA终端环境配置

Idea如何配置终端&#xff1b; 第一步&#xff1a;找到我的电脑&#xff0c;右击——属性——高级系统设置——环境变量 先配置path: 在后面加入&#xff1a;C:\Program Files (x86)\Java\jdk1.7.0_75\bin&#xff08;每个人放置jdk的位置不同。&#xff09; 新建classpath:…

【比邻智选】MR880A模组

&#x1f680;高性价比&#xff0c;5G/4G双模&#xff0c;稳定可靠 &#x1f310;功能丰富&#xff0c;5G特性一应俱全 &#x1f9e9;多封装兼容&#xff0c;适配性强&#xff0c;灵活升级智能设备

照片文件夹惊变白板?揭秘背后的原因及恢复秘籍

我们生活在一个数字化时代&#xff0c;照片已经成为记录生活的重要方式。然而&#xff0c;当你兴冲冲地打开保存珍贵记忆的文件夹时&#xff0c;却发现里面的照片全都变成了白板&#xff0c;这种心情无异于晴天霹雳。那么&#xff0c;这究竟是怎么回事呢&#xff1f; 照片文件夹…

基于springboot + vue 实现的简易博客系统

项目效果图 登陆页面 文章列表 发表文章 用户管理 栏目管理 数据统计 后端技术栈后端主要采用了&#xff1a; 1.SpringBoot 2.SpringSecurity 3.MyBatis 4.部分接口遵循Restful风格 5.MySQL 前端技术栈前端主要采用了&#xff1a; 1.Vue 2.axios 3.Elemen…

接口自动化框架篇:接口框架中的日志记录封装!

接口自动化框架中的日志记录是一个重要的环节&#xff0c;它能帮助我们追踪接口的执行情况、调试问题、分析测试结果等。通过规范的日志记录&#xff0c;我们可以更好地管理和维护接口自动化测试代码。 以下是一个从0到1的详细规范&#xff0c;来进行接口框架中的日志记录封装…

SalFAU-Net:显著性目标检测的显著性融合注意U-Net

SalFAU-Net:显著性目标检测的显著性融合注意U-Net 摘要IntroductionRelated Works SalFAU-Net: Saliency Fusion Attention U-Net for Salient Object Detection 摘要 显著目标检测&#xff08;SOD&#xff09;在计算机视觉中仍然是一个重要的任务&#xff0c;其应用范围从图像…

2024数维杯B题完整思路24页+配套代码1-4问+可视化结果图

后续参考论文也会进行一个更新 2024年数维杯数学建模B题主要关注生物质和煤共热解问题的研究 点击链接加入群聊【2024数维杯数学建模ABC题资料汇总】&#xff1a; 2024数维杯B题完整思路18页1-5问配套代码后续参考论文https://www.jdmm.cc/file/2710636 该段文字的第一个问题…

双层嵌线和线径的替代方案

电机只有三种嵌线方式 1.单层嵌线 2.双层嵌线 3.单双层嵌线 前面说的都是单层嵌线&#xff0c;下面介绍双层嵌线&#xff01; 双层嵌线一般线径都比较粗&#xff01; 线径只有几种规格的&#xff0c;大线径可用几根小线径替代&#xff01; 满足的原则&#xff1a;大线径A的…

OpenHarmony 实战开发——编译4.0 bete2及master分支,添加参数--no-prebuilt-sdk跳过编译sdk

简介 用户应用程序泛指运行在设备的操作系统之上&#xff0c;为用户提供特定服务的程序&#xff0c;简称“应用”。一个应用所对应的软件包文件&#xff0c;称为“应用程序包”。 OpenHarmony 提供了应用程序包开发、安装、查询、更新、卸载的管理机制&#xff0c;方便开发者…

项目管理在软件工程中的实践方法

软件工程是一个复杂的过程&#xff0c;涉及到需求分析、设计、编码、测试和维护等多个阶段。有效的项目管理对于确保软件项目成功至关重要。以下是结合附件内容&#xff0c;关于项目管理在软件工程中实践的一些方法。 1. 明确项目愿景和目标 在项目启动之初&#xff0c;项目经…

做业务有时候确实是需要一些运气

无论是考试还是做业务&#xff0c;那些考得好或者是业绩做得突出的人&#xff0c;都喜欢谦虚地说一句&#xff1a;只是运气好&#xff0c;就是运气好而已&#xff0c;也没什么..... 有人说&#xff1a;发财也要靠好运气&#xff0c;一个人运气好真的如此重要吗&#xff1f;那么…

查看微信小程序主包大小

前言 略 查看微信小程序主包大小 在微信开发者工具右上角找到“详情->基本信息” 查看微信小程序主包构成 通过微信开发者工具中的“代码依赖分析”工具查看

农业智能小型气象站功能有哪些?

TH-NQ10农业智能小型气象站是一种结合了智能化技术的小型气象监测设备&#xff0c;主要用于农业生产环境中。它配备了各种传感器&#xff0c;用于实时监测和记录气象参数&#xff0c;如温度、湿度、风速、降雨量等&#xff0c;以及土壤参数&#xff0c;如土壤墒情、土壤温度等。…

【OceanBase诊断调优】—— SQL 执行报错而不能计入 SQL_AUDIT 的情况

通常&#xff0c;执行成果的 SQL 都会计入 SQL_AUDIT 中&#xff0c;而执行报错的 SQL 则需要依据其执行报错的阶段来决定是否计入 SQL_AUDIT 中。 在 OceanBase 数据库中&#xff0c;SQL 请求的执行流程如图所示。 如果 SQL 在进入 Executor 阶段前发生报错&#xff0c;则该 …

YOLOV5更换转置卷积,助力涨点!

由于转置卷积是nn库自带的,所以我们直接找到models文件夹中的yolo.py文件中的 parse_model函数,再在如下图的地方添加转置卷积模块 # YOLOv5 🚀 by Ultralytics, AGPL-3.0 license """ YOLO-specific modules.Usage:$ python models/yolo.py --cfg yolov5s.…

【Android Studio】开启真机调试

1 打开手机的开发者模式 各种款式的手机进入开发者模式的情况不同&#xff0c;但大致是在 【关于手机】中多次点击系统版本即可进入。这里以小米8为例&#xff0c;记录下流程。 1.1 进入手机开发者模式 【设置】->【我的设备】->【全部参数】->【MIUI版本】连续点击3…