Spring Framework 学习笔记1:基础

news2024/9/26 1:25:26

Spring Framework 学习笔记1:基础

1.简介

1.1.生态和发展史

关于 Spring 的生态和发展史,可以观看这个视频。

1.2.系统架构

关于 Spring 的系统架构,可以观看这个视频。

2.Ioc

Spring 的核心概念是 Ioc (Inversion Of Control),它的目的是降低代码的耦合度,让对象不再由用户创建,而是由 Ioc 容器(Ioc Container)来创建和管理。

这里用一个简单示例说明 Spring 如何通过 Ioc 思想来对对象创建进行解耦。

这个项目结构很简单:

├─src
│  ├─main
│  │  ├─java
│  │  │  └─cn
│  │  │      └─icexmoon
│  │  │          └─springdemo
│  │  │              │  Application.java
│  │  │              │
│  │  │              ├─dao
│  │  │              │  │  UserDao.java
│  │  │              │  │
│  │  │              │  └─impl
│  │  │              │          UserDaoImpl.java
│  │  │              │
│  │  │              └─service
│  │  │                  │  UserService.java
│  │  │                  │
│  │  │                  └─impl
│  │  │                          UserServiceImpl.java
│  │  │
│  │  └─resources
│  └─test
│      └─java

项目中的各种对象之间的依赖都是直接用new创建的:

public class UserServiceImpl implements UserService {
    private UserDao userDao = new UserDaoImpl();
    @Override
    public void save(){
        System.out.println("UserServiceImpl.save() is called.");
        userDao.save();
    }
}

入口类也是简单的new了一个 Service 并执行具体方法:

public class Application {
    public static void main(String[] args) {
        UserService userService = new UserServiceImpl();
        userService.save();
    }
}

虽然这里实现类都有对应的接口,我们也都使用接口进行调用,但具体实现类的创建是用new,这种耦合是无法避免的。假设我们要用另一个 UserDao 的实现来替换当前实现:

public class UserDaoImpl2 implements UserDao {
    @Override
    public void save() {
        System.out.println("UserDaoImpl2.save() is called.");
    }
}

就必须修改 UserService 的实现类中相应的 new 语句:

public class UserServiceImpl implements UserService {
    private UserDao userDao = new UserDaoImpl2();
    // ...
}

下面我们看 Spring 是如何做的。

2.1.依赖

首先需要添加 Spring Framework 的依赖:

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

2.2.Spring Bean Definition

由 Ioc 容器创建和管理的对象被称作 Spring Bean,我们需要“告诉” Spring 框架需要创建哪些 Spring Bean 以及如何创建。具体来说需要用一个 XML 作为 Spring Bean 的定义文件(Spring Bean Definition)。

通过 Idea 在 Resource 目录下创建一个 application.xml 作为 Spring Bean 的定义文件。

添加 spring-context 依赖后,Idea 的创建 XML Configuration File 菜单中会出现一个子菜单 Spring Config,该菜单可以添加一个 Spring 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">
        
</beans>

按需要添加 Spring 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 id="userService" class="cn.icexmoon.springdemo.service.impl.UserServiceImpl"/>
    <bean id="userDao" class="cn.icexmoon.springdemo.dao.impl.UserDaoImpl"/>
</beans>

bean 节点的id属性指定 Bean 名称,class属性指定 Bean 类型。

2.3.Ioc 容器

接下来要创建 Ioc 容器,并用 Ioc 容器加载 Bean 定义,然后通过容器来获取对象。

public class Application {
    public static void main(String[] args) {
        //创建 IOC 容器
        ApplicationContext ctx = new ClassPathXmlApplicationContext("application.xml");
        UserService userService = (UserService) ctx.getBean("userService");
        userService.save();
    }
}

2.4.依赖注入

现在入口类中实现了 Ioc,但 UserServiceImpl 中依然用 new 的方式创建依赖对象:

public class UserServiceImpl implements UserService {
    private UserDao userDao = new UserDaoImpl2();
	// ...
}

如果一个 Spring Bean 依赖的对象是另一个 Spring Bean,我们可以通过 Spring Bean 定义告诉 Spring 它们之间的依赖关系,并由 Spring 自动完成相应的依赖创建,这种方式叫做依赖注入(DI,Dependency Injection)。

在这个示例中,现在userServiceuserDao都已经被定义为 Spring Bean,所以可以:

<bean id="userService" class="cn.icexmoon.springdemo.service.impl.UserServiceImpl">
    <property name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" class="cn.icexmoon.springdemo.dao.impl.UserDaoImpl"/>

property标签说明了userService Bean 中的userDao属性对应的另一个 Bean 是userDao

如果你用的是 Idea,应该可以注意到此时propertyname 属性是标红的,因为 Spring 需要使用 set 方法实现依赖注入,所以我们还要为其添加一个 set 方法:

public class UserServiceImpl implements UserService {
    @Setter
    private UserDao userDao;
    // ...
}

当然,现在不需要使用new了。

这里我使用 Lombok 添加 set 方法,需要添加相关的 Lombok 依赖,这里不再赘述。

现在已经将对象创建进行了解耦,如果我们要使用UserDaoImpl2作为实现而非UserDaoImpl,只需要修改 Bean 定义即可,不需要修改代码:

<bean id="userDao" class="cn.icexmoon.springdemo.dao.impl.UserDaoImpl2"/>

3.Spring Bean

3.1.生命周期

Spring Bean 有生命周期,我们可以利用一些生命周期回调在 Bean 的特定阶段执行代码。

在 UserDao 中定义两个方法,分别代表在 Bean 创建后和 Bean 销毁前需要执行的回调方法:

public class UserDaoImpl2 implements UserDao{
	// ...
    public void afterConstruct(){
        System.out.println("UserDaoImpl2 has constructed.");
    }

    public void beforeDestroyed(){
        System.out.println("UserDaoImpl2 will be destroyed.");
    }
}

要让这两个方法生效,还必须在 Bean 定义中告诉 Spring 这两个方法是生命周期回调方法:

<bean id="userDao"
      class="cn.icexmoon.springdemo.dao.impl.UserDaoImpl2"
      init-method="afterConstruct"
      destroy-method="beforeDestroyed"/>

如果实际运行程序,你会发现beforeDestroyed方法并不会被执行。

这是因为主程序执行完毕后,Java 虚拟机会直接进行垃圾回收,并不会通知 Ioc 容器,Ioc 容器自然也不会调用相应 Bean 的“销毁前回调方法”。

解决这个问题有两种方式,第一种是主动关闭 Ioc 容器:

public class Application {
    public static void main(String[] args) {
        //创建 IOC 容器
        ConfigurableApplicationContext ctx = new ClassPathXmlApplicationContext("application.xml");
        UserService userService = (UserService) ctx.getBean("userService");
        userService.save();
        // 关闭 IOC 容器
        ctx.close();
    }
}

需要注意的是,通常使用的 Ioc 接口ApplicationContext并没有close方法,所以这里必须使用一个上层接口ConfigurableApplicationContext作为引用。

第二种方式是将 Ioc 容器注册到 Java 虚拟机,这样 Java 虚拟机在程序执行完进行垃圾回收时就会通知 Ioc 容器,Ioc 容器自然就可以完成包括 Bean 生命周期回调之类的清理工作:

public class Application {
    public static void main(String[] args) {
        // 创建 IOC 容器
        ConfigurableApplicationContext ctx = new ClassPathXmlApplicationContext("application.xml");
        // 注册关闭钩子
        ctx.registerShutdownHook();
        UserService userService = (UserService) ctx.getBean("userService");
        userService.save();
    }
}

除了在 Bean 定义中指定生命周期方法外,还可以实现生命周期回调的相关接口:

public class UserDaoImpl2 implements UserDao, InitializingBean, DisposableBean {
	// ...
    @Override
    public void destroy() throws Exception {
        System.out.println("UserDaoImpl2 will be destroyed.");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("UserDaoImpl2 has constructed.");
    }
}

这样做就不再需要 XML 中定义相关回调方法:

<bean id="userDao"
      class="cn.icexmoon.springdemo.dao.impl.UserDaoImpl2"/>

3.2.作用域

Bean 有作用域,默认是单例(Singleton)。也就是说用同一个名称(或类型)从 Ioc 容器中获取到的会是同一个 Bean 实例:

public class Application {
    public static void main(String[] args) {
        // ...
        UserService userService = (UserService) ctx.getBean("userService");
        UserService userService2 = (UserService) ctx.getBean("userService");
        String equalResult = userService == userService2 ? "是同一个对象" : "不是同一个对象";
        System.out.println(equalResult);
        userService.save();
    }
}

可以在 Bean 定义中改变这一点:

<bean id="userService"
      class="cn.icexmoon.springdemo.service.impl.UserServiceImpl"
      scope="prototype">
    <property name="userDao" ref="userDao"/>
</bean>

现在 userService Bean 的作用域是原型(Prototype),即每次获取都会产生一个新的实例。

如果是 Web 开发,还会有其他作用域,比如request等。

3.3.实例化

3.3.1.构造器

一般情况下,Spring Bean 是通过无参构造器实现的实例创建:

public class UserServiceImpl implements UserService {
	// ...
    public UserServiceImpl() {
        System.out.println("UserServiceImpl's constructor is called.");
    }
}

运行程序后可以看到这个构造器被 Ioc 容器调用并产生输出。

当然,一般并不需要我们显式创建无参构造器,一个类没有任何构造器时会有一个默认的无参构造器。

3.3.2.静态工厂

有时候,对于一些需要复杂初始化逻辑的对象,我们会使用工厂模式进行创建。在 Spring 中,同样可以用工厂模式创建 Bean 实例。

假设有一个用于创建 UserDao 实例的静态工厂:

public class UserDaoFactory {
    public static UserDao createUserDao(){
        System.out.println("UserDaoFactory.createUserDao() is called.");
        return new UserDaoImpl2();
    }
}

在 Bean 定义中我们不再使用具体的类型创建 userDao,而是改为使用静态工厂:

<bean id="userDao"
      class="cn.icexmoon.springdemo.dao.factory.UserDaoFactory"
      factory-method="createUserDao"/>

这里的class是工厂类,factory-method是具体创建 Bean 实例的方法。

3.3.3.实例工厂

并非所有的工厂模式都是静态工厂,有一些会使用实例工厂。即工厂类本身有一些状态属性,需要先创建工厂类的实例,再用工厂实例创建目标对象。

假设有这样一个工厂类:

public class UserDaoFactory2 {
    public UserDao createUserDao(){
        System.out.println("UserDaoFactory2.createUserDao() is called.");
        return new UserDaoImpl2();
    }
}

此时就需要将工厂实例也定义为 Spring Bean,然后用这个工厂实例完成目标 Bean 实例的创建:

<bean id="userDao"
    factory-bean="userDaoFactory2"
    factory-method="createUserDao"/>
<bean id="userDaoFactory2" class="cn.icexmoon.springdemo.dao.factory.UserDaoFactory2"/>

3.4.依赖注入

3.4.1.Setter 注入

之前在 Ioc 中介绍过通过 set 方法完成依赖注入。这种方式也叫做 Setter 注入,除了可以用 Setter 注入其它的 Bean 实例外,还可以注入基本类型或者 String 类型的数据:

@Setter
public class UserDaoImpl implements UserDao {
    private String name;
    private int age;

    @Override
    public void save() {
        System.out.println("UserDaoImpl.save() is called.");
        System.out.printf("Name is %s and age is %d%n", name, age);
    }
}

Bean 定义:

<bean id="userDao"
      class="cn.icexmoon.springdemo.dao.impl.UserDaoImpl">
    <property name="age" value="11"/>
    <property name="name" value="Tom"/>
</bean>

3.4.2.构造器注入

除了通过 Setter 进行注入,还可以通过构造器进行注入。

比如,用构造器注入其它 Bean:

public class UserServiceImpl implements UserService {
    private UserDao userDao;

    public UserServiceImpl(UserDao userDao) {
        this.userDao = userDao;
    }
	// ...
}

这里不再需要 Set 方法。

Bean 定义:

<bean id="userService"
      class="cn.icexmoon.springdemo.service.impl.UserServiceImpl">
    <constructor-arg name="userDao" ref="userDao"/>
</bean>

同样可以通过构造器注入简单类型:

public class UserDaoImpl implements UserDao {
    private String name;
    private int age;

    public UserDaoImpl(String name, int age) {
        this.name = name;
        this.age = age;
    }
	// ...
}

Bean 定义:

<bean id="userDao"
      class="cn.icexmoon.springdemo.dao.impl.UserDaoImpl">
    <constructor-arg name="age" value="11"/>
    <constructor-arg name="name" value="tom"/>
</bean>

上面用构造器参数名称来“定位”参数进行注入的方式是最常见的,但这样意味着构造器的形参名称不能改变。

所以构造器注入存在一些变种写法,比如指定参数位置:

<bean id="userDao"
      class="cn.icexmoon.springdemo.dao.impl.UserDaoImpl">
    <constructor-arg index="0" value="tom"/>
    <constructor-arg index="1" value="11"/>
</bean>

这样做的缺陷是构造器形参的位置不能改变。

还比如通过参数类型进行匹配:

<bean id="userDao"
      class="cn.icexmoon.springdemo.dao.impl.UserDaoImpl">
    <constructor-arg type="java.lang.String" value="tom"/>
    <constructor-arg type="int" value="11"/>
</bean>

这样做的缺陷是当形参中存在多个形参类型相同的情况,就无法完成匹配。

3.4.3.自动装配

使用自动装配可以省略手动注入的步骤:

<bean id="userService"
      class="cn.icexmoon.springdemo.service.impl.UserServiceImpl">
    <constructor-arg name="userDao" ref="userDao"/>
</bean>

将其改为自动装配:

<bean id="userService"
      class="cn.icexmoon.springdemo.service.impl.UserServiceImpl"
      autowire="byType">
</bean>

这样 Spring 就可以根据userService中依赖的类型来自动查找符合条件的 Bean 并进行注入。

现在运行会出现一个空指针异常,因为自动装配是通过 Setter 注入实现的,所以需要为相应的属性添加 Setter:

public class UserServiceImpl implements UserService {
    @Setter
    private UserDao userDao;
    // ...
}

除了按照类型自动装配,还可以按照名称自动装配:

<bean id="userService"
      class="cn.icexmoon.springdemo.service.impl.UserServiceImpl"
      autowire="byName">
</bean>

自动装配不能针对简单类型,只能针对其他的 Spring Bean,如果指定了 Setter 注入或构造器注入,自动装配会失效。

3.4.4.集合注入

如果属性依赖是集合,同样可以通过 Bean 定义完成注入:

@Setter
public class CollectionServiceImpl implements CollectionService {
    private Object[] array;
    private List<Object> list;
    private Set<Object> set;
    private Map<String, Object> map;
    private Properties properties;

    @Override
    public void print() {
        System.out.println("array:" + Arrays.toString(array));
        System.out.println("list:" + list);
        System.out.println("set:" + set);
        System.out.println("map:" + map);
        System.out.println("properties:" + properties);
    }
}

public interface CollectionService {
    void print();
}

public class Application {
    public static void main(String[] args) {
        // ...
        CollectionService collectionService = ctx.getBean(CollectionService.class);
        collectionService.print();
    }
}

Bean 定义:

<bean class="cn.icexmoon.springdemo.service.impl.CollectionServiceImpl">
    <property name="array">
        <array>
            <value>1</value>
            <value>2</value>
            <value>3</value>
        </array>
    </property>
    <property name="list">
        <list>
            <value>Tom</value>
            <value>LiLei</value>
            <value>Jack</value>
        </list>
    </property>
    <property name="set">
        <set>
            <value>1</value>
            <value>2</value>
            <value>3</value>
        </set>
    </property>
    <property name="map">
        <map>
            <entry key="country" value="china"/>
            <entry key="province" value="sichuan"/>
            <entry key="city" value="chengdu"/>
        </map>
    </property>
    <property name="properties">
        <props>
            <prop key="country">china</prop>
            <prop key="province">sichuan</prop>
            <prop key="city">chengdu</prop>
        </props>
    </property>
</bean>

除了注入字面量,也可以注入对其他 Bean 的引用。

比如说现在有多个 Person 类型的 Bean:

@Setter
@ToString
public class Person {
    private String name;
    private Integer age;
}
<bean id="tom" class="cn.icexmoon.springdemo.entity.Person">
    <property name="name" value="Tom"/>
    <property name="age" value="11"/>
</bean>
<bean id="liLei" class="cn.icexmoon.springdemo.entity.Person">
    <property name="name" value="LiLei"/>
    <property name="age" value="20"/>
</bean>
<bean id="jack" class="cn.icexmoon.springdemo.entity.Person">
    <property name="name" value="Jack"/>
    <property name="age" value="25"/>
</bean>

将其注入到 CollectionService 的一个集合属性中:

@Setter
public class CollectionServiceImpl implements CollectionService {
    // ...
    private List<Person> persons;

    @Override
    public void print() {
        // ...
        System.out.println("persons:" + persons);
    }
}
<bean class="cn.icexmoon.springdemo.service.impl.CollectionServiceImpl">
    <property name="persons">
        <list>
            <ref bean="jack"/>
            <ref bean="liLei"/>
            <ref bean="tom"/>
        </list>
    </property>
    <!-- ... -->
</bean>

5.案例:Spring 管理第三方数据源

步骤为先添加相应依赖,然后将数据源对象定义为 Bean,再通过依赖注入的方式传入数据库连接相关信息。

添加依赖:

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.16</version>
</dependency>

Bean 定义:

<bean class="com.alibaba.druid.pool.DruidDataSource">
    <property name="username" value="root"/>
    <property name="password" value="mysql"/>
    <property name="url" value="jdbc://localhost:3306/test"/>
    <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
</bean>

获取 Bean 实例:

DruidDataSource druidDataSource = ctx.getBean(DruidDataSource.class);
System.out.println(druidDataSource);
System.out.println("username:" + druidDataSource.getUsername());
System.out.println("password:"+druidDataSource.getPassword());
System.out.println("url:"+druidDataSource.getUrl());
System.out.println("driver:"+druidDataSource.getDriverClassName());

详细演示请观看这个视频。

6.从 properties 文件读取属性

上面的案例中,数据库连接信息直接被写在 Bean 定义文件中,这样做是不太好的,一般这些信息会使用单独的 properties 文件进行保存。

比如在 resource 目录下创建一个jdbc.properties

jdbc.username=root
jdbc.password=mysql
jdbc.url=jdbc://localhost:3306/test
jdbc.driver=com.mysql.jdbc.Driver

在 Bean 定义中读取 properties 文件,需要先引入一个 XML 命名空间context

<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">
    <!-- ... -->
</beans>

利用context:property-placeholder标签读取 properties 中的属性并导入占位符:

<context:property-placeholder location="jdbc.properties"/>

placeholder 是占位符的意思,即${}符号。

使用占位符进行注入:

<bean class="com.alibaba.druid.pool.DruidDataSource">
    <property name="username" value="${jdbc.username}"/>
    <property name="password" value="${jdbc.password}"/>
    <property name="url" value="${jdbc.url}"/>
    <property name="driverClassName" value="${jdbc.driver}"/>
</bean>

Spring 也会读取系统的环境变量,比如:

<property name="username" value="${username}"/>

虽然 properties 文件中没有定义,但实际上${username}的值是系统当前的用户名。

此时即使你在 properties 文件中定义了这个属性:

username=666

依然会是系统变量生效,也就是说系统环境变量优先级是高于 properties 中定义的属性的。

如果不希望在加载占位符属性时系统变量生效,可以:

<context:property-placeholder location="jdbc.properties" system-properties-mode="NEVER"/>

如果需要加载多个 properties 文件:

<context:property-placeholder location="jdbc.properties,other.properties" system-properties-mode="NEVER"/>

可以使用,或空格进行分隔。

此外,还可以使用通配符加载多个文件:

<context:property-placeholder location="*.properties" system-properties-mode="NEVER"/>

默认情况下location中的文件名实际上都指的是从当前类路径(Class Path)中加载的文件,不过最好还是显式指定:

<context:property-placeholder location="classpath:*.properties" system-properties-mode="NEVER"/>

但这样不会加载当前项目依赖的 jar 包中的配置文件,如果需要加载,可以:

<context:property-placeholder location="classpath*:*.properties" system-properties-mode="NEVER"/>

7.BeanFactory

BeanFactory 同样是一个表示 IoC 容器的接口,Spring 1.0 使用这个接口实现 IoC容器。

现在经常使用的ApplicatonContext接口扩展自BeanFactory

image-20230821170738499

之前使用的ConfigurableApplicationContext接口扩展了Lifecycle接口,因此有close方法。

7.1.延迟加载 Bean

ApplicationContext 的实现类在加载 Bean 时默认为急切加载,比如:

public class CtxApplicaton {
    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("application.xml");
    }
}

如果要让某些 Bean 使用“延迟加载”,可以:

<bean id="userService"
      class="cn.icexmoon.springdemo.service.impl.UserServiceImpl"
      autowire="byName"
      lazy-init="true"/>

8.参考资料

  • 黑马程序员SSM框架教程

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

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

相关文章

grafana8.3创建告警规则

1. 部署grafana的配置文件修改 因为要采用发送邮件的方式通知告警内容所以&#xff0c;在部署grafana时要先配置好SMTP / Emailing的内容&#xff1a; [smtp]enabled true # 开启smtphost smtp.mxhichina.com:465 #设置邮箱服务器地址user testtest.com #设置邮箱用户pas…

Flink SQL你用了吗?

分析&回答 Flink 1.1.0&#xff1a;第一次引入 SQL 模块&#xff0c;并且提供 TableAPI&#xff0c;当然&#xff0c;这时候的功能还非常有限。Flink 1.3.0&#xff1a;在 Streaming SQL 上支持了 Retractions&#xff0c;显著提高了 Streaming SQL 的易用性&#xff0c;使…

排序之选择排序

文章目录 前言一、直接选择排序1、直接选择排序基本思想2、直接选择排序代码实现3、直接选择排序的效率 二、堆排序1、堆排序2、堆排序的效率 前言 选择排序的基本思想就是每一次从待排序的数据元素中选出最小(或最大)的一个元素&#xff0c;存放在序列的起始位置&#xff0c;…

汇编--int指令

中断信息可以来自CPU的内部和外部&#xff0c; 当CPU的内部有需要处理的事情发生的时候&#xff0c;将产生需要马上处理的中断信息&#xff0c;引发中断过程。在http://t.csdn.cn/jihpG&#xff0c;我们讲解了中断过程和两种内中断的处理。 这一章中&#xff0c; 我们讲解另一种…

freemarker学习+集成springboot+导出word

目录 一 FreeMarker简介 二 集成springboot&#xff0c;实现案例导出 三 常见面试题总结 一 FreeMarker简介 FreeMarker 是一款 模板引擎&#xff1a; 即一种基于模板和要改变的数据&#xff0c; 并用来生成输出文本(HTML网页&#xff0c;电子邮件&#xff0c;配置文件&…

2024河南光伏展|河南储能展|河南国际太阳能光伏储能展览会

2024第四届中国&#xff08;郑州&#xff09;太阳能光伏及储能产业展览会 时间&#xff1a;2024年2月26-28日 地点&#xff1a;郑州.中原国际博览中心 河南国际太阳能光伏及储能产业展览会是一个盛大的行业聚会&#xff0c;旨在展示、交流、合作和创新。这个展览会将会是一个…

MATLAB中residue函数用法

目录 语法 说明 示例 求解具有实根的部分分式展开式 展开具有复数根和同次分子及分母的分式 展开分子次数高于分母次数的分式 residue函数的功能是部分分式展开&#xff08;部分分式分解&#xff09;。 语法 [r,p,k] residue(b,a) [b,a] residue(r,p,k) 说明 [r,p…

echarts饼图点击区块事件

效果图&#xff1a; 代码&#xff1a; let option {color: pieColors,series: [{name: Access From,type: pie,radius: [36%, 56%],avoidLabelOverlap: false,label: {formatter: params > {// console.log(params)return {color${params.dataIndex}|${params.name}(${par…

Scala集合继承体系图

Scala集合简介 1&#xff09; Scala 的集合有三大类&#xff1a;序列 Seq、集Set、映射 Map&#xff0c;所有的集合都扩展自 Iterable特质。 2&#xff09; 对于几乎所有的集合类&#xff0c;Scala 都同时提供了可变和不可变的版本&#xff0c;分别位于以下两个包 不可变集合…

软件确认测试的依据有哪些

确认测试 随着我国软件市场的日益繁荣和软件产品的大量涌现,软件评测的重要性越来越显现出来了。 一、确认测试的作用 1、软件确认测试对保障软件产品质量,开拓国际软件市场,促进软件市场健康发展都将发挥重要作用。 2、确认测试的目的是要表明软件是可以工作的&#xff0c…

【Nacos】2023.9.1 最新Nacos源码启动教程 | 超详细 | 包含踩坑经历和解决办法

在官网下载Nacos源码压缩包&#xff0c;或者使用git克隆到本地&#xff0c;使用IDEA打开项目。 Nacos GitHub地址 修改项目的jdk版本为1.8&#xff0c;修改maven使用的jdk版本也是1.8 打开最外边的pom文件&#xff0c;注释掉代码规范检查的插件&#xff08;nacos源码有些impor…

机器学习——聚类算法一

机器学习——聚类算法一 文章目录 前言一、基于numpy实现聚类二、K-Means聚类2.1. 原理2.2. 代码实现2.3. 局限性 三、层次聚类3.1. 原理3.2. 代码实现 四、DBSCAN算法4.1. 原理4.2. 代码实现 五、区别与相同点1. 区别&#xff1a;2. 相同点&#xff1a; 总结 前言 在机器学习…

springboot+activiti5.0整合(工作流引擎)

概述 springboot整合activiti使用modeler进行流程创建&#xff0c;编辑、部署以及删除实例&#xff08;可运行&#xff09; 详细 1、现在来说一下流程&#xff0c;先建立spring boot项目&#xff0c;导入对应的jar包。 <dependencies><dependency><groupId&…

Java 循环语句解析:从小白到循环达人

如果你正在学习编程&#xff0c;那么循环语句是一个绕不开的重要话题。循环语句让我们能够重复执行一段代码&#xff0c;从而实现各种各样的功能。在本篇博客中&#xff0c;我们将围绕 Java 编程语言中的循环语句展开&#xff0c;从最基础的概念出发&#xff0c;一步步引领你从…

Qt日历控件示例-QCalendarWidget

基本说明 QCalendarWidget介绍&#xff1a; QCalendarWidget 是 Qt 框架中提供的一个日期选择控件,用户可以通过该控件快速选择需要的日期,并且支持显示当前月份的日历。 这里&#xff0c;我们继承了QCalendarWidget&#xff0c;做了一些简单封装和样式调整 1.使用的IDE&…

java_error_in_idea.hprof 文件

在用户目录下的java_error_in_idea.hprof文件(/Users/用户) 大约1.5个G,IDEA的错误日志,可以删除

shiny根据数据的长度设置多个色板

shiny根据数据的长度设置多个色板 library(shiny) library(colourpicker) ui <- fluidPage(# 添加一个选择颜色的下拉菜单uiOutput("color_dropdown") )server <- function(input, output) {# 数据长度data_length <- reactive({length(c("数据1"…

Linux(centos) 下 Mysql 环境安装

linux 下进行环境安装相对比较简单&#xff0c;可还是会遇到各种奇奇怪怪的问题&#xff0c;我们来梳理一波 安装 mysql 我们会用到下地址&#xff1a; Mysql 官方文档的地址&#xff0c;可以参考&#xff0c;不要全部使用 https://dev.mysql.com/doc/refman/8.0/en/linux-i…

文献阅读:Deep Learning based Semantic Communications: An Initial Investigation

目录 论文简介动机&#xff1a;为什么作者想要解决这个问题&#xff1f;贡献&#xff1a;作者在这篇论文中完成了什么工作(创新点)&#xff1f;规划&#xff1a;他们如何完成工作&#xff1f;理由&#xff1a;通过什么实验验证它们的工作结果自己的看法 论文简介 作者 Huiqiang…

day-06 多进程服务器端 -- 进程间通信

一.多进程服务器端 &#xff08;一&#xff09;进程概念及应用 利用之前学习到的内容&#xff0c;我们的服务器可以按照顺序处理多个客户端的服务请求。在客户端和服务时间增长的情况下&#xff0c;服务器就不足以满足需求了。 1.两种类型的服务器端 &#xff08;1&#xff…