SpringBoot源码

SpringBoot核心前置内容

1.Spring注解编程的发展过程

image.png

1.1 Spring 1.x

2004年3月24日,Spring1.0 正式发布,提供了IoC,AOP及XML配置的方式。

在Spring1.x版本中提供的是纯XML配置的方式,也就是在该版本中必须要提供xml的配置文件,在该文件中通过 <bean> 标签来配置需要被IoC容器管理的Bean。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">


    <bean class="com.bobo.demo01.UserService" />
</beans>

调试代码

public static void main(String[] args) {
    ApplicationContext ac = new FileSystemXmlApplicationContext("classpath:applicationContext01.xml");
    System.out.println("ac.getBean(UserService.class) = " + ac.getBean(UserService.class));
}

输出结果

image.png

在Spring1.2版本的时候提供了@Transaction (org.springframework.transaction.annotation )注解。简化了事务的操作.

image.png

1.2 Spring 2.x

在2006年10月3日 Spring2.0问世了,在2.x版本中,比较重要的特点是增加了很多注解

Spring 2.5之前

在2.5版本之前新增的有 @Required @Repository @Aspect,同时也扩展了XML的配置能力,提供了第三方的扩展标签,比如 <dubbo>

@Required

如果你在某个java类的某个set方法上使用了该注释,那么该set方法对应的属性在xml配置文件中必须被设置,否则就会报错!!!

public class UserService {


    private String userName;

    public String getUserName() {
        return userName;
    }

    @Required
    public void setUserName(String userName) {
        this.userName = userName;
    }
}

如果在xml文件中不设置对应的属性就会给出错误的提示。

image.png

设置好属性后就没有了错误提示了

image.png

源码中可以看到 @Required从2.0开始提供

image.png

@Repository

@Repository 对应数据访问层Bean.这个注解在Spring2.0版本就提供的有哦,大家可能没有想到。

image.png

@Aspect

@Aspect是AOP相关的一个注解,用来标识配置类。

Spring2.5 之后

在2007年11月19日,Spring更新到了2.5版本,新增了很多常用注解,大大的简化配置操作。

注解说明
@Autowired依赖注入
@Qualifier配置@Autowired注解使用
@Component声明组件
@Service声明业务层组件
@Controller声明控制层组件
@RequestMapping声明请求对应的处理方法

在这些注解的作用下,可以不用在xml文件中去注册没有bean,这时只需要指定扫码路径,然后在对应的Bean头部添加相关的注解即可,这大大的简化了配置及维护工作。案例如下:

在配置文件中只需要配置扫码路径即可:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="com.bobo" />

</beans>

持久层代码:

@Repository
public class UserDao {

    public void query(){
        System.out.println("dao query ..." );
    }
}

业务逻辑层代码

@Service
public class UserService {

    @Autowired
    private UserDao dao;

    public void query(){
        dao.query();
    }
}

控制层代码:

@Controller
public class UserController {

    @Autowired
    private UserService service;

    public void query(){
        service.query();
    }
}

测试代码

public class Demo02Main {
    public static void main(String[] args) {
        ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext02.xml");
        UserController acBean = ac.getBean(UserController.class);
        acBean.query();
    }
}

虽然在Spring的2.5版本提供了很多的注解,也大大的简化了开发,但是任然没有摆脱XML配置驱动。

1.3 Spring 3.x

在2009年12月16日发布了Spring3.0版本,这是一个注解编程发展的里程碑版本,在该版本中全面拥抱Java5。提供了 @Configuration注解,目的就是去xml化。同时通过 @ImportResource来实现Java配置类和XML配置的混合使用来实现平稳过渡。

/**
 * @Configuration 标注的Java类 相当于 application.xml 配置文件
 */
@Configuration
public class JavaConfig {

    /**
     * @Bean 注解 标注的方法就相当于 <bean></bean> 标签
              也是 Spring3.0 提供的注解
     * @return
     */
    @Bean
    public UserService userService(){
        return new UserService();
    }
}

在Spring3.1 版之前配置扫描路径还只能在 XML 配置文件中通过 component-scan 标签来实现,在3.1之前还不能够完全实现去XML配置,在3.1 版本到来的时候,提供了一个 @ComponentScan注解,该注解的作用是替换掉 component-scan标签,是注解编程很大的进步,也是Spring实现无配置话的坚实基础。

@ComponentScan

@ComponentScan的作用是指定扫码路径,用来替代在XML中的 <component-scan>标签,默认的扫码路径是当前注解标注的类所在的包及其子包。

定义UserService

@Service
public class UserService {
}

创建对于的Java配置类

@Configuration
@ComponentScan
public class JavaConfig {

    public static void main(String[] args) {
        ApplicationContext ac = new AnnotationConfigApplicationContext(JavaConfig.class);
        System.out.println("ac.getBean(UserService.class) = " + ac.getBean(UserService.class));
    }
}

输出的结果

image.png

当然也可以指定特定的扫描路径

@Configuration
// 指定特定的扫描路径
@ComponentScan(value = {"com.bobo.demo04"})
public class JavaConfig {

    public static void main(String[] args) {
        ApplicationContext ac = new AnnotationConfigApplicationContext(JavaConfig.class);
        System.out.println("ac.getBean(UserService.class) = " + ac.getBean(UserService.class));
    }
}
@Import

@Import注解只能用在类上,作用是快速的将实例导入到Spring的IoC容器中,将实例导入到IoC容器中的方式有很多种,比如 @Bean注解,@Import注解可以用于导入第三方包。具体的使用方式有三种。
用在类上,也能将类导入spring容器

静态导入

静态导入的方式是直接将需要导入到IoC容器中的对象类型直接添加进去即可。

image.png

这种方式的好处是简单,直接,但是缺点是如果要导入的比较多,则不太方便,而且也不灵活。
在这里插入图片描述

ImportSelector

@Import注解中也可以添加一个实现了 ImportSelector接口的类型,这时不会将该类型导入IOC容器中,而是会调用 ImportSelector接口中定义的 selectImports方法,将该方法的返回的字符串数组的类型添加到容器中。

定义两个业务类

public class Cache {
}
public class Logger {
}

定义ImportSelector接口的实现,方法返回的是需要添加到ICC容器中的对象对应的类型的全类路径的字符串数组,可以根据不同的业务需求而导入不同的类型,会更加的灵活些

public class MyImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{Logger.class.getName(),Cache.class.getName()};
    }
}

导入测试案例

@Configuration
@Import(MyImportSelector.class)
public class JavaConfig {
    public static void main(String[] args) {
        ApplicationContext ac = new AnnotationConfigApplicationContext(JavaConfig.class);
        for (String beanDefinitionName : ac.getBeanDefinitionNames()) {
            System.out.println("beanDefinitionName = " + beanDefinitionName);
        }
    }
}

输出结果:

image.png

ImportBeanDefinitionRegistrar

除了上面所介绍的ImportSelector方式灵活导入以外还提供了 ImportBeanDefinitionRegistrar 接口,也可以实现,相比 ImportSelector 接口的方式,ImportBeanDefinitionRegistrar 的方式是直接在定义的方法中提供了 BeanDefinitionRegistry ,自己在方法中实现注册

public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        // 将需要注册的对象封装为 RootBeanDefinition 对象
        RootBeanDefinition cache = new RootBeanDefinition(Cache.class);
        registry.registerBeanDefinition("cache",cache);

        RootBeanDefinition logger = new RootBeanDefinition(Logger.class);
        registry.registerBeanDefinition("logger",logger);
    }
}

测试代码

@Configuration
@Import(MyImportBeanDefinitionRegistrar.class)
public class JavaConfig {
    public static void main(String[] args) {
        ApplicationContext ac = new AnnotationConfigApplicationContext(JavaConfig.class);
        for (String beanDefinitionName : ac.getBeanDefinitionNames()) {
            System.out.println("beanDefinitionName = " + beanDefinitionName);
        }
    }
}

输出结果

image.png

@EnableXXX

@Enable模块驱动,其实是在系统中先开发好各个功能独立的模块,比如 Web MVC 模块, AspectJ代理模块,Caching模块等。

image.png

案例说明,先定义好功能模块

/**
 * 定义一个Java配置类
 */
@Configuration
public class HelloWorldConfiguration {

    @Bean
    public String helloWorld(){
        return "Hello World";
    }
}

然后定义@Enable注解

/**
 * 定义@Enable注解
 * 在该注解中通过 @Import 注解导入自定义的模块,使之生效。
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(HelloWorldConfiguration.class)
public @interface EnableHelloWorld {
}

测试代码

@Configuration
// 加载 自定义 模块
@EnableHelloWorld
public class JavaMian {

    public static void main(String[] args) {
        ApplicationContext ac = new AnnotationConfigApplicationContext(JavaMian.class);
        String helloWorld = ac.getBean("helloWorld", String.class);
        System.out.println("helloWorld = " + helloWorld);
    }
}

效果

image.png

1.4 Spring 4.x

2013年11月1 日更新的Spring 4.0 ,完全支持Java8.这是一个注解完善的时代,提供的核心注解是@Conditional条件注解。@Conditional 注解的作用是按照一定的条件进行判断,满足条件就给容器注册Bean实例。

@Conditional的定义为:

// 该注解可以在 类和方法中使用
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {

    /**
     * 注解中添加的类型必须是 实现了 Condition 接口的类型
     */
    Class<? extends Condition>[] value();

}

Condition是个接口,需要实现matches方法,返回true则注入bean,false则不注入。

案例讲解:

/**
 * 定义一个 Condition 接口的是实现
 */
public class MyCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
		@verride
		public boolean matches(ConditionContext context,AnnotatedTypeMetadata metadata) {
		// 根据我们特定的业务需求来决定是否注入对应的对象
        try {
            boolean flage = context.getRegistry().containsBeanDefinition("userService");
            if (flage) {
                Class.forName("com.bobo.test.test666");
                return flage;
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return false;// 默认返回false
    }
}

创建Java配置类

@Configuration
public class JavaConfig {

    @Bean
    // 条件注解,添加的类型必须是 实现了 Condition 接口的类型
    // MyCondition的 matches 方法返回true 则注入,返回false 则不注入
    @Conditional(MyCondition.class)
    public StudentService studentService(){
        return new StudentService();
    }

    public static void main(String[] args) {
        ApplicationContext ac = new AnnotationConfigApplicationContext(JavaConfig.class);
        for (String beanDefinitionName : ac.getBeanDefinitionNames()) {
            System.out.println("beanDefinitionName = " + beanDefinitionName);
        }
    }
}

测试:

image.png

但是将 matchs方法的返回结果设置为 true 则效果不同

image.png

所以@Conditional的作用就是提供了对象导入IoC容器的条件机制,这也是SpringBoot中的自动装配的核心关键。当然在4.x还提供一些其他的注解支持,比如 @EventListener,作为ApplicationListener接口编程的第二选择,@AliasFor解除注解派生的时候冲突限制。@CrossOrigin作为浏览器跨域资源的解决方案。

1.5 Spring 5.x

2017年9月28日,Spring来到了5.0版本。5.0同时也是SpringBoot2.0的底层。注解驱动的性能提升方面不是很明显。在Spring Boot应用场景中,大量使用@ComponentScan扫描,导致Spring模式的注解解析时间耗时增大,因此,5.0时代引入**@Indexed**,为Spring模式注解添加索引

当在项目中使用了 @Indexed之后,编译打包的时候会在项目中自动生成 META-INT/spring.components文件。当Spring应用上下文执行 ComponentScan扫描时,META-INT/spring.components将会被 CandidateComponentsIndexLoader 读取并加载,转换为 CandidateComponentsIndex对象,这样的话 @ComponentScan不在扫描指定的package,而是读取 CandidateComponentsIndex对象,从而达到提升性能的目的。

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context-indexer</artifactId>
</dependency>

使用@Indexed注解

image.png

image.png
核心条件注解@Conditional控制对象是否注入到容器中,@Import 注解中导入的类型
ImportSelector 接口,不会将该类型注入到容器中而是会将selectImports的返回的类型的全类路径的字符串的数据注入到容器中动态注入(类似于factoryBean)
ImportBeanDefinitionRegistrar 的方式是直接在定义的方法中提供了 BeanDefinitionRegistry ,自己在方法中实现注册。(自己注册了bean定义,接口类似于factoryBean)
@Indexed ,编译打包的时候会在项目中自动生成 META-INT/spring.components文件。当Spring应用上下文执行 ComponentScan扫描时,META-INT/spring.components将会被 CandidateComponentsIndexLoader 读取并加载,从而达到提升spring性能的目的。

2. 什么是SPI

为什么要讲SPI呢?因为在SpringBoot的自动装配中其实有使用到SPI机制,所以掌握了这部分对于SpringBoot的学习还是很有帮助的。

SPI ,全称为 Service Provider Interface,是一种服务发现机制。它通过在ClassPath路径下的META-INF/services文件夹查找文件,自动加载文件里所定义的类。这一机制为很多框架扩展提供了可能,比如在Dubbo、JDBC中都使用到了SPI机制。先通过一个很简单的例子来看下它是怎么用的。

案例介绍

先定义接口项目

在这里插入图片描述

然后创建一个扩展的实现,先导入上面接口项目的依赖

    <dependencies>
        <dependency>
            <groupId>com.bobo</groupId>
            <artifactId>JavaSPIBase</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>

然后创建接口的实现

/**
 * SPI:MySQL对于 baseURL 的一种实现
 */
public class MySqlConnection implements BaseData {
    @Override
    public void baseURL() {
        System.out.println("mysql 的扩展实现....");
    }
}

然后在resources目录下创建 META-INF/services 目录,然后在目录中创建一个文件,名称必须是定义的接口的全类路径名称。然后在文件中写上接口的实现类的全类路径名称
在这里插入图片描述

在这里插入图片描述

同样的再创建一个案例

在这里插入图片描述

然后在测试的项目中测试

    public static void main(String[] args) {
        ServiceLoader<BaseData> providers = ServiceLoader.load(BaseData.class);
        Iterator<BaseData> iterator = providers.iterator();
        while(iterator.hasNext()){
            BaseData next = iterator.next();
            next.baseURL();
        }
    }

根据不同的导入,执行的逻辑会有不同

image.png

image.png

源码查看

ServiceLoader

首先来看下ServiceLoader的类结构

   // 配置文件的路径
    private static final String PREFIX = "META-INF/services/";

    // 加载的服务  类或者接口
    private final Class<S> service;

    // 类加载器
    private final ClassLoader loader;

    // 访问权限的上下文对象
    private final AccessControlContext acc;

    // 保存已经加载的服务类
    private LinkedHashMap<String,S> providers = new LinkedHashMap<>();

    // 内部类,真正加载服务类
    private LazyIterator lookupIterator;

load

load方法创建了一些属性,重要的是实例化了内部类,LazyIterator。

public final class ServiceLoader<S> implements Iterable<S>
    private ServiceLoader(Class<S> svc, ClassLoader cl) {
        //要加载的接口
        service = Objects.requireNonNull(svc, "Service interface cannot be null");
        //类加载器
        loader = (cl == null) ? ClassLoader.getSystemClassLoader() : cl;
        //访问控制器
        acc = (System.getSecurityManager() != null) ? AccessController.getContext() : null;
         reload();
        
    }
    public void reload() {
        //先清空
        providers.clear();
        //实例化内部类 
        LazyIterator lookupIterator = new LazyIterator(service, loader);
    }
}

查找实现类和创建实现类的过程,都在LazyIterator完成。当调用iterator.hasNext和iterator.next方法的时候,实际上调用的都是LazyIterator的相应方法。

private class LazyIterator implements Iterator<S>{
    Class<S> service;
    ClassLoader loader;
    Enumeration<URL> configs = null;
    Iterator<String> pending = null;
    String nextName = null; 
    private boolean hasNextService() {
        //第二次调用的时候,已经解析完成了,直接返回
        if (nextName != null) {
            return true;
        }
        if (configs == null) {
            //META-INF/services/ 加上接口的全限定类名,就是文件服务类的文件
            //META-INF/services/com.viewscenes.netsupervisor.spi.SPIService
            String fullName = PREFIX + service.getName();
            //将文件路径转成URL对象
            configs = loader.getResources(fullName);
        }
        while ((pending == null) || !pending.hasNext()) {
            //解析URL文件对象,读取内容,最后返回
            pending = parse(service, configs.nextElement());
        }
        //拿到第一个实现类的类名
        nextName = pending.next();
        return true;
    }
}

创建实例对象,当然,调用next方法的时候,实际调用到的是,lookupIterator.nextService。它通过反射的方式,创建实现类的实例并返回。

private class LazyIterator implements Iterator<S>{
    private S nextService() {
        //全限定类名
        String cn = nextName;
        nextName = null;
        //创建类的Class对象
        Class<?> c = Class.forName(cn, false, loader);
        //通过newInstance实例化
        S p = service.cast(c.newInstance());
        //放入集合,返回实例
        providers.put(cn, p);
        return p; 
    }
}

看到这儿,已经很清楚了。获取到类的实例,自然就可以对它为所欲为了!

SpringBoot自动装配原理分析

SpringBoot初始化核心流程源码

SpringBoot中的监听机制详解

SpringBoot中的属性文件加载原理

SpringBoot中的Tomcat容器加载

SpringBoot中的Acuator监控

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

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

相关文章

夏泽网注册码

夏泽网注册码申请法:1.打开注册码申请页&#xff0c;http://nianjian.xiaze.com/getcode.php 上面会显示你的注册码链接 (是个红色的链接,不同的时间不同的人这个链接不一样)。 2.将注册码链接以超链接的方式发布在各大网站、论坛、博客&#xff08;支持各大论坛、百度空间、 网…

117.龙芯2k1000-pmon(16)- linux下升级pmon

pmon的升级总是有些不方便&#xff0c;至少是要借助串口和串口工具 如果现场不方便连接串口&#xff0c;是不是可以使用网线升级pmon呢&#xff1f; 答案当然是可行的。 环境&#xff1a;2k1000linux3.10麒麟的文件系统 如今我已经把这个工具开发出来了。 GitHub - zhaozhi…

如何开通 GitHub Sponsors

Github Sponsors 是什么&#xff1f; 简单来说&#xff0c;GitHub Sponsors 是一个赞助服务&#xff0c;它允许个人和组织直接向开源贡献者和项目提供财务赞助&#xff0c;以便于有更多的时间和资源来专注于自己的开源工作。 这对于开源贡献者来说是非常棒&#x1f389;的&…

Swift 入门学习:集合(Collection)类型趣谈-下

概览 集合的概念在任何编程语言中都占有重要的位置&#xff0c;正所谓&#xff1a;“古来聚散地&#xff0c;宿昔长荆棘&#xff1b;游人聚散中&#xff0c;一片湖光里”。把那一片片、一瓣瓣、一粒粒“可耐”的小精灵全部收拢、吸纳的井然有序、条条有理&#xff0c;怎能不让…

008-slot插槽

slot插槽 1、插槽 slot 的简单使用2、插槽分类2.1 默认插槽2.2 具名插槽2.3 作用域插槽 插槽就是子组件中的提供给父组件使用的一个占位符&#xff0c;用<slot></slot> 表示&#xff0c;父组件可以在这个占位符中填充任何模板代码&#xff0c;如 HTML、组件等&…

【Docker】容器的生态系统

Docker提供了一整套技术支持&#xff0c;包括核心技术、平台技术、支持技术。 核心技术 容器核心技术是指能让Container&#xff08;容器&#xff09;在host&#xff08;集群、主机&#xff09;上运行起来的那些技术。 1&#xff09;容器规范&#xff1a;OCI&#xff08;runt…

round四舍五入在python2与python3版本间区别

round()方法返回数值的小数点四舍五入到n个数字。 语法 以下是round()方法的语法&#xff1a; round( x ,n) 参数 x --这是一个数值&#xff0c;表示需要格式化的数值 n --这也是一个数值,表示小数点后保留多少位 返回值 该方法返回 数值x 的小数点四舍五入到n个数字 …

计算机设计大赛 疲劳驾驶检测系统 python

文章目录 0 前言1 课题背景2 Dlib人脸识别2.1 简介2.2 Dlib优点2.3 相关代码2.4 人脸数据库2.5 人脸录入加识别效果 3 疲劳检测算法3.1 眼睛检测算法3.2 打哈欠检测算法3.3 点头检测算法 4 PyQt54.1 简介4.2相关界面代码 5 最后 0 前言 &#x1f525; 优质竞赛项目系列&#x…

图|dfs bfs|最小生成树|最短路|一篇搞定图的所有知识点

文章目录 图前言项目代码仓库图的基本概念图的表示方法邻接矩阵邻接表图的一些相关概念 图的遍历bfsdfs如果给的图不是连通图&#xff1f; 最小生成树Kruskal算法Prim算法 最短路径单源最短路径--Dijkstra算法单源最短路径--Bellman-Ford算法多源最短路径--Floyd-Warshall算法 …

数据治理实践——YY 直播业务指标治理实践

目录 一、问题背景 1.1 问题场景 1.2 问题小结 二、治理方案 2.1 治理目标 2.2 团队协同&#xff0c;共建规范 2.3 指标管理的定位 2.4 指标管理的目标及思路 2.5 指标管理&#xff0c;规范内容落地 2.6 数仓设计-关联指标维度 2.7 数据报表开发-配置口径说明 2.8 …

MongoDB在Linux环境下的安装与配置

目录 1. 准备工作 2. 安装MongoDB 2.1 传输MongoDB安装包 2.2 解压安装包 2.3 创建MongoDB安装目录 2.4 创建数据目录和日志目录 3. 启动MongoDB服务 3.1 启动MongoDB 3.2 连接MongoDB 3.3 退出MongoDB 1. 准备工作 在安装MongoDB之前&#xff0c;请确保您已具备以下…

Solidity Uniswap V2 价格预言机

预言机是连接区块链与链下服务的桥梁&#xff0c;这样就可以从智能合约中查询现实世界的数据。Chainlink 是最大的oracle网络之一&#xff0c;创建于 2017 年&#xff0c;如今已成为许多 DeFi 应用的重要组成部分。https://github.com/XuHugo/solidityproject Uniswap 虽然是链…

【leetcode热题】寻找旋转排序数组中的最小值 II

难度&#xff1a; 困难通过率&#xff1a; 38.7%题目链接&#xff1a;. - 力扣&#xff08;LeetCode&#xff09; 题目描述 假设按照升序排序的数组在预先未知的某个点上进行了旋转。 ( 例如&#xff0c;数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。 请找出其中最小的…

【react框架】跟我一起速读Next.js官方入门教学课程文档

文章目录 前言目录结构样式方案正常引入样式文件Tailwind方案CSS Modules方案clsx方案 文字和图片优化文字图片 Pages和Layout的机制PagesLayout 通过Link组件改变路由并且拆分打包提供Hooks通过Vercel创建数据未完待续... 前言 对于那些对Next.js一无所知的前端伙伴来说&…

鸿蒙开发(二)-项目结构

鸿蒙开发(二)-项目结构 上篇文章我们讲了如何配置鸿蒙开发的基础环境&#xff0c;以及创建了第一个鸿蒙程序。 这篇我们讲述了鸿蒙应用的项目目录结构。 如图所示&#xff1a;我们切换项目project可以看到。 另一种则是Ohos模式: AppScope->app.json5 应用的全局配置 {&q…

华为新发布磁电存储“王炸”,到底是什么?

最近&#xff0c;在巴塞罗那举行的2024年世界移动通信大会&#xff08;MWC24&#xff09;上&#xff0c;华为数据存储产品线总裁周彼得博士介绍了这款即将面世的产品。他向听众表示&#xff0c;与磁带存储相比&#xff0c;该设备可以降低20%的总连接成本&#xff0c;而与硬盘相…

DHCP中继实验(华为)

思科设备参考&#xff1a; 一&#xff0c;技术简介 DHCP中继&#xff0c;可以实现在不同子网和物理网段之间处理和转发DHCP信息的功能。如果DHCP客户机与DHCP服务器在同一个物理网段&#xff0c;则客户机可以正确地获得动态分配的IP地址。如果不在同一个物理网段&#xff0c;…

Web渗透测试流程

什么是渗透测试 渗透测试 (penetration test),是通过模拟恶意黑客的攻击方法&#xff0c;来评估计算机网络系统安全的一种评估方法。这个过程包括对系统的任何弱点、技术缺陷或漏洞的主动分析&#xff0c;这个分析是从一个攻击者可能存在的位置来进行的&#xff0c;并且从这个…

机器学习第29周周报 Beyond Dropout

文章目录 week29 Beyond Dropout摘要Abstract一、泛化理论二、文献阅读1. 题目2. abstract3. 网络架构3.1 特征图失真3.2 失真优化 4. 文献解读4.1 Introduction4.2 创新点4.3 实验过程4.3.1 全连接层实验4.3.2 卷积网络上的实验 4.4 结论 小结参考文献 week29 Beyond Dropout …

国产硅片膜厚检测仪

硅片膜厚检测仪是半导体行业中一种至关重要的设备&#xff0c;用于精确测量硅片上薄膜的厚度。在半导体制造工艺中&#xff0c;薄膜厚度的控制对于保证器件性能和可靠性具有决定性的作用。因此&#xff0c;硅片膜厚检测仪的研发和应用对于推动半导体技术的发展具有重要意义。 一…