当前位置: 首页 > news >正文

Spring学习笔记(一)

一 、Spring概述

(一)Spring是什么

        Spring是一个分层的Java SE/EE full-stack(一站式)轻量级开源框架,以 IoC(Inverse Of Control:反转控制)和 AOP(Aspect Oriented Programming:面向切面编程)为内核。

在java三层架构当中分别提供了相应技术:

表现层(web层) :SpringMVC 框架

业务层(service层) :Bean管理(IOC容器)

持久层(dao层) :JdbcTemplate模板对象以及提供了ORM模块整合其他优秀的持久层技术

(二)Spring框架发展历程

1997 年, IBM提出了EJB 的思想
1998 年,SUN制定开发标准规范 EJB1.0
1999 年,EJB1.1 发布
2001 年,EJB2.0 发布
2003 年,EJB2.1 发布
2006 年,EJB3.0 发布

Rod Johnson ( Spring 之父)
Expert One-to-One J2EE Design and Development(2002)
阐述了 J2EE 使用EJB 开发设计的优点及解决方案
Expert One-to-One J2EE Development without EJB(2004)
阐述了 J2EE 开发不使用 EJB的解决方式(Spring 雏形)

2017 年 9 月份发布了 Spring 的最新版本 Spring5.0 通用版(GA)

(三)Spring框架的优势

方便解耦,简化开发 :Spring 就是一个大工厂,可以管理所有对象的创建和依赖关系维护,交给Spring管理

AOP 编程的支持 :可以方便的实现对程序进行权限拦截,日志记录,运行的监控

声明式事务的支持 :通过配置方式完成对事务的管理,无需手动编程

方便程序的测试 :对Junit支持,可以通过注解方便的对Spring程序进行测试

整合外部优秀技术 : Spring内部提供了对各种优秀框架(Hibernate,Mybatis,Quartz等)的直接支持

javaEE技术的封装 :Spring对javaEE开发当中复杂难用的API(JavaEmail, RMI等)进行封装,降低了这些API的使用难度

(四)SpringFrameWork系统结构

二、 程序的耦合和解耦合

(一)什么是程序的耦合

        程序的耦合是程序之间的关联性,也就是多个类的联系是否紧密,多个对象之间的关系是否密切。

生活中的案例:

        你的房子里面有窗子,那么房子和窗子就有了关联

        耦合度是松还是紧就看你的关联是强还是弱,也就是修改的代价,比如你窗子是扣死在墙里的那么你修改窗子就必须修改墙 这就比较紧密了,反应在程序上就是耦合度高,不利于程序的扩展和维护。

        但是如果你窗子是按照某种规格的 可以自由拆装的,那么修改的代价就小,耦合度也就低了,反应在程序上就是耦合度低,利于程序的扩展和维护。

我们写程序的目标就是 高内聚 低耦合!

这样修改起来 就不会有太多的联系,不用改一个地方其他的都要修改。

(二)解决程序耦合的思路

1.编译不依赖,运行时才依赖

当我们讲解jdbc时,是通过反射来注册驱动的,代码如下:

Class.forName("com.mysql.jdbc.Driver");//使用的驱动类是指定了一个字符串

         此时的好处是,我们的类中在编译阶段不再需要具体的驱动类,就算删除mysql的驱动jar包,依然可以通过编译。在运行阶段才会依赖驱动包。实际开发当中,我们应该做到编译不依赖,运行时才依赖。

         上述代码产生的新问题,mysql驱动类的全限定类名作为一个字符串java类中是写死的,一旦发生改变,还需要修改源码。

         使用配置文件结合反射就可以解决上述问题。

2.使用工厂模式解耦合

        在实际开发中我们可以把三层的对象都使用配置文件配置起来,当启动服务器应用加载的时候,让一个类中的 方法通过读取配置文件,把这些对象创建出来并存起来。在接下来的使用的时候,直接拿过来用就好了。

        那么,这个读取配置文件,创建和获取三层对象的类就是工厂。

三、SpringIOC机制详解

(一)IOC概述及作用

1.IOC的简介及设计思想

        SpringIOC:IOC 是 Inversion of Control 的缩写,多数书籍翻译成“控制反转”,还有些书籍翻译成为“控制反向”或者“控制倒置”。

        1996 年,Michael Mattson 在一篇有关探讨面向对象框架的文章中,首先提出了 IOC 这个概念。简单来说就是把复杂系统分解成相互合作的对象,这些对象类通过封装以后,内部实现对外部是透明的,从而降低了解决问题的复杂度,而且可以灵活地被重用和扩展。

        IOC 理论提出的观点大体是这样的:借助于“第三方”实现具有依赖关系的对象之间的解耦 如下图

        大家看到了吧,由于引进了中间位置的“第三方”,也就是IOC容器,使得A、B、C、D这4个对象没有了耦合关系,齿轮之间的传动全部依靠“第三方”了, 全部对象的控制权全部上缴给“第三方”IOC容器,所以,IOC容器成了整个系统的关键核心,它起到了一种类似“粘合剂”的作用,把系统中的所有对象粘合 在一起发挥作用,如果没有这个“粘合剂”,对象与对象之间会彼此失去联系,这就是有人把IOC容器比喻成“粘合剂”的由来。
我们再来做个试验:把上图中间的IOC容器拿掉,然后再来看看这套系统:

        我们现在看到的画面,就是我们要实现整个系统所需要完成的全部内容。这时候,A、B、C、D这4个对象之间已经没有了耦合关系,彼此毫无联系,这样的话, 当你在实现A的时候,根本无须再去考虑B、C和D了,对象之间的依赖关系已经降低到了最低程度。所以,如果真能实现IOC容器,对于系统开发而言,这将是 一件多么美好的事情,参与开发的每一成员只要实现自己的类就可以了,跟别人没有任何关系!

2.IOC作用

        IOC本质上就是一个大工程,大容器。主要作用就是创建和管理对象的依赖关系,削减计算机程序的耦合(解除我们代码中的依赖关系),提高程序的可扩展性和可维护性。

四、 SpringIOC入门案例

1.构建maven工程,添加Spring依赖 

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.jn</groupId><artifactId>SpringFrameWorkProject01</artifactId><version>1.0-SNAPSHOT</version><properties><maven.compiler.source>17</maven.compiler.source><maven.compiler.target>17</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding></properties><dependencies><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.13.2</version><scope>test</scope></dependency><dependency><!--导入spring的context坐标,context依赖core、beans、expression、apo--><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.3.18</version></dependency></dependencies></project>

2.创建持久层接口和实现类

 UserDao

package com.jn.dao;public interface UserDao {public void save();
}

 UserDaoImpl

package com.jn.dao.Impl;import com.jn.dao.UserDao;public class UserDaoImpl implements UserDao {@Overridepublic void save() {System.out.println("UserDao saving...");}
}

3.创建业务层接口和实现类 

 UserService

package com.jn.service;public interface UserService {public void saveService();
}

UserServiceImpl

package com.jn.service.Impl;import com.jn.service.UserService;public class UserServiceImpl implements UserService {@Overridepublic void saveService() {System.out.println("service saving...");}
}

4.resources创建ApplicationContext.xml

5.配置service和dao

<?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"><!--配置UserDaoImpl 对象--><bean id="userDao" class="com.jn.dao.Impl.UserDaoImpl"></bean><!--配置UserServiceImpl 对象--><bean id="userService" class="com.jn.service.Impl.UserServiceImpl"></bean>
</beans>

6.测试IOC 配置

package com.jn;import com.jn.dao.UserDao;
import com.jn.service.UserService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class IOCTest {//测试IOC容器是否配置成功@Testpublic void test(){//1、获取Spring IOC对象ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");//2、从容器中根据id获得UserDaoImpl对象,执行UserDao的save方法UserDao userDao = (UserDao) context.getBean("userDao");userDao.save();//2、从容器中根据id获得UserServiceImpl对象,执行UserService的saveService方法UserService userService = (UserService) context.getBean("userService");userService.saveService();}
}

运行结果:

项目整体结构图展示 

五、Spring基于XML的IOC细节

1.IOC配置文件详解

bean标签 

        用于配置对象交给Spring 来创建。默认情况下他会调用类中无参数的构造器,如果没有无参数构造器则不能成功创建

基本属性

        id : Bean实例对象在Spring容器当中的唯一标识

        class: Bean 的全限定类名

2 SpringIOC机制源码解析

2.1 IOC容器解析

IOC思想基于IOC容器完成,IOC容器底层就是对象工厂,Spring 中工厂的类结构图如下:

BeanFactory

        IOC容器的基本实现,是Spring内部使用的接口,不提供开发人员使用,加载配置文件时,不 会创建对象,在获得(使用)对象时才采取创建对象。

HierarchicalBeanFactory

        这个工厂接口非常简单,实现了Bean工厂的分层。 工厂接口也是继承自BeanFacotory,也是一个二级接口,相对于父接口,它只扩展了一个重要的功能——工厂分层

AutowireCapableBeanFactory

        该接口有自动装配能力,需要注意的是,ApplicationContext接口并没有实现此接口,因为应用代码很少用到此功能,如果确实需要的话,可以调用ApplicationContext的getAutowireCapableBeanFactory方法,来获取此接口的实例。

ListableBeanFactory

        获取bean时,Spring 鼓励使用这个接口定义的api,如查看Bean的个数、获取某一类型Bean的配置名、查看容器中是否包括某一Bean等方法。

ApplicationContext

        BeanFactory接口的子接口,提供更多强大的功能,一般由开发人员使用.接口提供了bean基础性操作同时,扩展了国际化等功能。ApplicationContext接口在加载配置文件时候就会配置文件当中的对象进行创建,存放在IOC容器当中

AnnotationConfigApplicationContext        

         当使用注解配置容器对象时,需要使用此类来创建 spring 容器。它用来读取注解。

ClassPathXmlApplicationContext

         它是从类的根路径下加载配置文件 推荐使用这种

FileSystemXmlApplicationContext

         它是从磁盘路径上加载配置文件,配置文件可以在磁盘的任意位置。

2.2 IOC容器底层bean初始化过程

BeanFactoryPostProcessor

        作用:定义了在bean工厂对象创建后,bean对象创建前执行的动作,用于对工厂进行创建后业务处理

         运行时机: 操作用于对工厂进行处理,仅运行一次

BeanPostProcessor

         作用: 定义了所有bean初始化前后进行的统一动作,用于对bean进行创建前业务处理与创建后业务处理

         运行时机:当前操作伴随着每个bean的创建过程,每次创建bean均运行该操作

InitializingBean

         作用:定义了每个bean的初始化前进行的动作,属于非统一性动作,用于对bean进行创建前业务处理。类似于init-method。

        运行时机:当前操作伴随着任意一个bean的创建过程,保障其个性化业务处理

六、手动实现自己的IOC容器

1.分析IOC 实现思路


流程解析: 
        根据路径、资源名称等方式,将xml文件、注解类加载到容器中
        通过BeanDefinitionReader将对象解析成BeanDefinition实例
        创建BeanFactory工厂(注册前后需要添加bean前置、后置处理器)
        通过BeanFactory工厂将对象实例化、对象初始化(初始化前后执行前置、后置处理器

2.IOC原理实现-环境搭建:构建maven工程,引入依赖   

    <dependencies><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.13.2</version><scope>test</scope></dependency><dependency><!--导入spring的context坐标,context依赖core、beans、expression、apo--><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.3.18</version></dependency></dependencies>

3.设计接口类和配置文件

 UserDao

package com.jn.dao;public interface UserDao {public void save();
}

UserDaoImpl 

package com.jn.dao.impl;import com.jn.dao.UserDao;public class UserDaoImpl implements UserDao {@Overridepublic void save() {System.out.println("UserDao Saving...");}
}

 配置UserDaoImpl

<?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"><!--配置UserDaoImpl--><bean id="userDao" class="com.jn.dao.impl.UserDaoImpl"></bean>
</beans>

4.解析配置文件

        <dependency><groupId>dom4j</groupId><artifactId>dom4j</artifactId><version>1.6.1</version></dependency>

5.创建自己的工厂类

package com.jn.factory;import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class MyBeanFactory {//创建一个map集合,模拟IOC容器private static Map<String, Object> map = new HashMap<>();//使用dom4j解析xml文件static {try{//1、获得一个解析器SAXReader saxReader = new SAXReader();//2、读取外部的配置文件String path = "src/main/resources/ApplicationContext.xml";//3、读取了整个文档对象Document document = saxReader.read(path);//4、获得根节点Element rootElement = document.getRootElement();//5、获得根节点下的所有bean标签对应的节点List<Element> bean =rootElement.elements("bean");for (Element element : bean) {String id = element.attributeValue("id");String clazz = element.attributeValue("class");//6、通过反射创建对象Class clz = Class.forName(clazz);Object object =clz.newInstance();//7、将创建好的对象,放入map集合中map.put(id,object);}}catch (Exception e){e.printStackTrace();}}//根据id从容器里面获得对象public static Object getBean(String id){Object object = map.get(id);return object;}
}

6..编写测试文件,展示测试结果

package com.jn;import com.jn.dao.UserDao;
import com.jn.factory.MyBeanFactory;
import org.junit.Test;public class IOCTest {@Testpublic void test(){//1、创建工厂对象MyBeanFactory factory = new MyBeanFactory();//从容器中根据id获得对象UserDao userDao = (UserDao) factory.getBean("userDao");System.out.println(userDao);userDao.save();}
}

七、bean的实例化

(一)、bean实例化介绍

(二)、bean实例化方式

1、构造方法的方式 

        它会根据默认无参构造函数来创建类对象。如果 bean 中没有默认无参构造函数,将会创建失败。

1.1创建User类
package com.jn.pojo;public class User {//构造方法实现Bean的实例化public User(){System.out.println("Constructor Function Bean");}
}
1.2管理user 类型对象
    <!--构造函数方法实现Bean的实例化配置--><bean id="user" class="com.jn.pojo.User"></bean>
1.3测试
    //测试构造方法实现Bean的实例化@Testpublic void testConstructorBean(){ApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");User user = (User) context.getBean("user");System.out.println(user);}

2、静态工厂方式

2.1 创建静态工厂ItemFactory
package com.jn.factory;import com.jn.pojo.User;public class ItemFactory {//静态方式实现Bean的实例化public static User createUser() {System.out.println("Static method running creat bean");return new User();}
}
2.2 管理User类型对象
    <!--静态工厂方式实现Bean的实例化--><bean id="staticBean" class="com.jn.factory.ItemFactory" ></bean>
2.3 测试
    //测试静态方法方法实现Bean的实例化@Testpublic void testStaticBean(){ApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");ItemFactory itemFactory = (ItemFactory) context.getBean("staticBean");itemFactory.createUser();}

3、实例工厂方式

3.1 创建实例工厂NewItemFactory
package com.jn.factory;import com.jn.pojo.User;public class NewItemFactory {//实例化工厂实现Bean的实例化public User createUser() {System.out.println("Dynamic method running creat bean");return new User();}
}
3.2 管理NewItemFactory类型对象
    <!--实例化工厂方式实现Bean的实例化--><bean id="nweItemFactory" class="com.jn.factory.NewItemFactory" ></bean><bean id="user" factory-bean="nweItemFactory" factory-method="createUser"></bean>
3.3 测试
    //测试实例化工厂方法方法实现Bean的实例化@Testpublic void testDynamicBean(){ApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");NewItemFactory itemFactory = (NewItemFactory) context.getBean("nweItemFactory");itemFactory.createUser();}

4、FactoryBean实例化Bean 

4.1创建UserDao接口
package com.jn.dao;public interface UserDao {public void save();
}
 4.2定义UserDaoImpl类
package com.jn.dao.impl;import com.jn.dao.UserDao;public class UserDaoImpl implements UserDao {@Overridepublic void save() {System.out.println("UserDao Saving...");}
}
4.3管理UserDao接口对象
    <!--使用FactoryBean实例化Bean--><bean id="userDaoFactoryBean" class="com.jn.factory.UserDaoFactoryBean"></bean>
 4.4测试
    //使用FactoryBean实例化Bean的测试@Testpublic void testFactoryBean(){ApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");UserDao userDao1 = (UserDao) context.getBean("userDaoFactoryBean");UserDao userDao2 = (UserDao) context.getBean("userDaoFactoryBean");//测试是否是单例模式创建——UserFactoryBean里面设置为了非单例System.out.println(userDao1);System.out.println(userDao2);userDao1.save();userDao2.save();}

实例化Bean项目总体结构 

 八、bean作用域

(一)、bean作用域介绍

        所谓Bean的作用域其实就是指Spring给我们创建出的对象的存活范围,在配置文件中通过bean的scope属性指定

        scope:指对象的作用范围,取值如下:

取值范围  说明
singleton    默认值,单例的
prototype    多例的
request      WEB 项目中,Spring 创建一个 Bean 的对象,将对象存入到   request 域中
session      WEB 项目中,Spring 创建一个 Bean 的对象,将对象存入到 session 域中
global session  WEB 项目中,应用在 Portlet 环境,如果没有 Portlet 环境那么globalSession 相当于 session

           

(二)、bean作用域的解析

1.scope值为singleton

         Bean的实例化个数:1个

         Bean的实例化时机:当Spring核心文件被加载时,实例化配置的Bean实例

2.scope值为prototype

         Bean的实例化个数:多个

         Bean的实例化时机:当调用getBean()方法时实例化Bean

3.Scope为其他值

         scope指定为其他值,需要在特定的环境下使用, 只需要作为一个了解知识,面试能够回答出来即可

九、Bean的生命周期

(一)、Bean的生命周期概括

        在Spring框架中,Bean的生命周期是指从创建Bean实例开始,到最终被销毁的整个过程。这个过程由Spring的IoC容器进行管理,涉及到多个阶段和扩展点,允许开发者在Bean的生命周期的不同阶段进行干预和自定义操作。 

(二)、Bean生命周期的演示 

1.UserDaoImpl中添加函数 

        使用内置对象的方式实现周期控制 

package com.jn.dao.Impl;import com.jn.dao.UserDao;public class UserDaoImpl implements UserDao {@Overridepublic void save() {System.out.println("UserDao saving...");}public void init(){System.out.println("init...");}public void destory(){System.out.println("destory...");}
}

2.修改UserServiceImpl

        使用接口的方式实现周期的控制 

package com.jn.service.Impl;import com.jn.dao.UserDao;
import com.jn.service.UserService;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;public class UserServiceImpl implements UserService , InitializingBean, DisposableBean {private UserDao userDao;public void setUserDao(UserDao userDao) {this.userDao = userDao;}@Overridepublic void saveService() {System.out.println("service saving...");}public void destroy() throws Exception {System.out.println("Service Destory");}public void afterPropertiesSet() throws Exception {System.out.println("Service Init");}
}

3. 配置对象

<!--测试Bean的生命周期实现的配置--><!--配置UserDaoImpl 对象init-method:设置bean初始化生命周期回调函数destroy-method:设置bean销毁生命周期回调函数,仅适用于单例对象--><bean id="userDao" class="com.jn.dao.Impl.UserDaoImpl" init-method="init" destroy-method="destory"></bean><!--配置UserServiceImpl 对象--><bean id="userService" class="com.jn.service.Impl.UserServiceImpl"><property name="userDao" ref="userDao"/></bean>

4.测试 

    //测试Bean的生命周期@Testpublic void testEeanLife(){ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");UserDao userDao = (UserDao) context.getBean("userDao");userDao.save();context.close();}

5.测试结果分析

        首先,当 Spring 容器启动时,会根据配置创建 `userDao` Bean,并通过指定的 `init-method` 调用 `UserDaoImpl` 中的 `init` 方法,打印出“init...”进行初始化。

        接着创建 `userService` Bean,在这个过程中,通过配置文件将 `userDao` 注入到 `userService` 中,触发 `setUserDao` 方法打印“set...”。

        同时,由于 `userService` 实现了 `InitializingBean` 接口,在属性设置完成后,容器会调用其 `afterPropertiesSet` 方法,打印“Service Init”进行进一步初始化。

         在测试方法中,获取 `userDao` 对象并调用其 `save` 方法,执行 `UserDaoImpl` 中的对应方法,打印“UserDao saving...”。 当测试方法执行 `context.close()` 关闭 Spring 容器时,对于实现了 `DisposableBean` 接口的 `userService` Bean,会调用其 `destroy` 方法,打印“Service Destory”进行销毁操作。

        同时,根据配置文件中指定的 `destroy-method`,也会调用 `UserDaoImpl` 的 `destory` 方法,打印“destory...”完成 `userDao` 的销毁。

十、Spring依赖注入

(一)、依赖注入的介绍

        依赖注入(Dependency Injection):它是 Spring 框架核心 IOC 的具体实现。在编写程序时,通过控制反转,把对象的创建交给了 Spring,但是代码中不可能出现没有依赖的情况。

        IOC 解耦只是降低他们的依赖关系,但不会消除。例如:业务层仍会调用持久层的方法。

那这种业务层和持久层的依赖关系,在使用 Spring 之后,就让 Spring 来维护了。

        简单的说,就是坐等框架把持久层对象传入业务层,而不用我们自己去获取

(二)、构造函数注入

1.构建Account类

        顾名思义,就是使用类中的构造函数,给成员变量赋值。注意,赋值的操作不是我们自己做的,而是通过配置的方式,让 Spring 框架来为我们注入。具体代码如下

package com.jn.pojo;import java.io.Serializable;
import java.util.Date;public class Account  implements Serializable {private String name;private Integer age;private Date birthday;public Account() {}public Account(String name, Integer age, Date birthday) {this.name = name;this.age = age;this.birthday = birthday;}@Overridepublic String toString() {return "Account{" +"name='" + name + '\'' +", age=" + age +", birthday=" + birthday +'}';}
}
2.配置Account类​​​​​​​
    <!--构造函数注入的配置
要求:类中需要提供一个对应参数列表的构造器函数涉及的标签:constructor-arg:属性:name: 执行参数在构造器中的名称value:它能赋的值是基本数据类型和 String 类型ref:它能赋的值是其他 bean 类型,也就是说,必须得是在配置文件中配置过的 bean--><bean id="time" class="java.util.Date"></bean><bean id="account" class="com.jn.pojo.Account"><constructor-arg name="name" value="王思梦"></constructor-arg><constructor-arg name="age" value="18"></constructor-arg><constructor-arg name="birthday" ref="time"></constructor-arg></bean>
3.测试
    //构造函数方法注入测试@Testpublic void testConstructInsertFunction(){ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");//根据Id得到对象AccountAccount account = (Account) context.getBean("account");System.out.println(account);}

(三)、setter注入

2.1 Account类添加setter

        顾名思义,就是在类中提供需要注入成员的 set 方法。具体代码如下:

package com.jn.pojo;import java.io.Serializable;
import java.util.Date;public class Account  implements Serializable {private String name;private Integer age;private Date birthday;public Account() {}public Account(String name, Integer age, Date birthday) {this.name = name;this.age = age;this.birthday = birthday;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}public Date getBirthday() {return birthday;}public void setBirthday(Date birthday) {this.birthday = birthday;}@Overridepublic String toString() {return "Account{" +"name='" + name + '\'' +", age=" + age +", birthday=" + birthday +'}';}
}
2.2 配置Account类
<!--
使用setter方法的方式给属性赋值
涉及的标签:
property属性:name:找的是类中set方法后面的部分ref: 给属性赋值是其他bean类型的value:给属性赋值是基本数据类型和 string 类型的实际开发当中, 此种方式用的比较多,推荐使用
--><bean id="noeTime" class="java.util.Date"></bean><bean id="setterFunction" class="com.jn.pojo.Account"><property name="name" value="铁头"></property><property name="age" value="18"></property><property name="birthday" ref="noeTime"></property></bean>
2.3 测试
    //setter注入方法的测试@Testpublic void testSettetInsertFunction(){//得到ClassPathXmlApplicationContext对象ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");//通过ClassPathXmlApplicationContext对象得到Account的实体化对象Account account = (Account) context.getBean("setterFunction");System.out.println("name:"+account.getName()+"\tage:"+account.getAge()+"\tbirthday:"+account.getBirthday());}

(四)、注入集合数据

3.1 修改Account类

        顾名思义,就是给类中的集合成员传值,它用的也是set方法注入的方式,只不过变量的数据类型都是集合。

        这里注入数组,List,Set,Map,Properties。具体代码如下:

 

 /*输入集合的Spring依赖测试*/private String[] myStrs;private List<String> myList;private Set<String> mySet;private Map<String,String> myMap;private Properties myProps;public String[] getMyStrs() {return myStrs;}public void setMyStrs(String[] myStrs) {this.myStrs = myStrs;}public List<String> getMyList() {return myList;}public void setMyList(List<String> myList) {this.myList = myList;}public Set<String> getMySet() {return mySet;}public void setMySet(Set<String> mySet) {this.mySet = mySet;}public Map<String, String> getMyMap() {return myMap;}public void setMyMap(Map<String, String> myMap) {this.myMap = myMap;}public Properties getMyProps() {return myProps;}public void setMyProps(Properties myProps) {this.myProps = myProps;}
3.2 配置Account类
<!-- 注入集合类型的数据测试--><bean id="listAccount" class="com.jn.pojo.Account"><!--注入数组数据--><property name="myStrs"><array><value>王思梦</value><value>铁头</value><value>铁头铁头,下雨不愁,人家有伞,我有铁头</value></array></property><!--注入list数据--><property name="myList"><list><value>紫霞</value><value>白晶晶</value><value>至尊宝</value></list></property><!--注入map数据--><property name="myMap"><map><entry key="name" value="萧炎"></entry><entry key="age"><value>26</value></entry></map></property><!--注入set数据--><property name="mySet"><list><value>小医仙</value><value>古熏儿</value><value>美杜莎</value></list></property><!--注入properties集合--><property name="myProps"><props><prop key="gender">男</prop><prop key="kongfu">佛怒火连</prop></props></property></bean>
3.3 测试
    //测试注入集合的方式添加Spring依赖@Testpublic void testListInsertFunction(){//获得ClassPathXmlApplicationContext对象ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");//通过ClassPathXmlApplicationContext对象得到Account的实体化对象Account account = (Account) context.getBean("listAccount");System.out.println("array" + Arrays.toString(account.getMyStrs()));System.out.println("list" + account.getMyList());System.out.println("set" + account.getMySet());System.out.println("map" + account.getMyMap());System.out.println("props" + account.getMyProps());}

十一、Spring配置文件模块化

(一)、 Spring模块化的介绍

        我们现在的配置都集中配在了一个ApplicationContext.xml文件中,当开发人员过多时, 如果所有bean都配置到同一个配置文件中,会使这个文件巨大,而且也不方便维护。 针对这个问题,Spring提供了多配置文件的方式,也就是所谓的配置文件模块化。

(二)、Spring模块化的配置

1、Spring模块化配置方式一

        并列的多个配置文件 直接编写多个配置文件,比如说beans1.xml,beans2.xml......, 然后在创建ApplicationContext的时候,直接传入多个配置文件。

ApplicationContext context = new ClassPathXmlApplicationContext("Account.xml","UserDao.xml","...");

1.1新建Account.xml文件 
<?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="listAccount" class="com.jn.pojo.Account"><!--注入数组数据--><property name="myStrs"><array><value>王思梦</value><value>铁头</value><value>铁头铁头,下雨不愁,人家有伞,我有铁头</value></array></property><!--注入list数据--><property name="myList"><list><value>紫霞</value><value>白晶晶</value><value>至尊宝</value></list></property><!--注入map数据--><property name="myMap"><map><entry key="name" value="萧炎"></entry><entry key="age"><value>26</value></entry></map></property><!--注入set数据--><property name="mySet"><list><value>小医仙</value><value>古熏儿</value><value>美杜莎</value></list></property><!--注入properties集合--><property name="myProps"><props><prop key="gender">男</prop><prop key="kongfu">佛怒火连</prop></props></property></bean>
</beans>
1.2新建UserDao.xml文件 
<?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"><!--配置UserDaoImpl--><bean id="userDao" class="com.jn.dao.impl.UserDaoImpl"></bean>
</beans>
1.3测试 
//测试程序的模块化@Testpublic void testModularization(){ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("Account.xml","UserDao.xml");//通过context对象得到UserDao实体对象UserDao userDao = (UserDao) context.getBean("userDao");//通过context对象得到Account的实体化对象Account account = (Account) context.getBean("listAccount");userDao.save();System.out.println("array" + Arrays.toString(account.getMyStrs()));System.out.println("list" + account.getMyList());System.out.println("set" + account.getMySet());System.out.println("map" + account.getMyMap());System.out.println("props" + account.getMyProps());}

2、Spring模块化配置方式二

主从配置文件 先配置一个主配置文件,然后在里面导入其它的配置文件。

        <import resource="Account.xml" />
        <import resource="UserDao.xml" />

注意:

同一个xml文件中不能出现相同名称的bean,如果出现会报错

多个xml文件如果出现相同名称的bean,不会报错,但是后加载的会覆盖前加载的bean,所以企业开发中尽 量保证bean的名称是唯一的。

2.1 引入到主配置文件
<import resource="Account.xml"></import>
<import resource="UserDao.xml"></import>

2.2测试 
    //测试程序的模块化@Testpublic void testModularization(){ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");//通过context对象得到UserDao实体对象UserDao userDao = (UserDao) context.getBean("userDao");//通过context对象得到Account的实体化对象Account account = (Account) context.getBean("listAccount");userDao.save();System.out.println("array" + Arrays.toString(account.getMyStrs()));System.out.println("list" + account.getMyList());System.out.println("set" + account.getMySet());System.out.println("map" + account.getMyMap());System.out.println("props" + account.getMyProps());}

项目整体目录结构 

十二、模板设计模式解析

(一)、模板设计模式介绍

        模板方法(Template Method)模式的定义如下:定义一个操作中的算法骨架,而将算法的一些步骤延迟到子类中,使得子类可以不改变该算法结构的情况下重定义该算法的某些特定步骤。它是一种类行为型模式

模板方法模式的静态结构图如下:

(二)、模板设计模式的应用场景

        在多个子类中拥有相同的方法,而且逻辑相同时,可以将这些方法抽出来放到一个模板抽象类中程序主框架相同,细节不同的情况下,也可以使用模板方法

举例说明:

        a:使用过Servlet的人都清楚,除了要在web.xml做相应的配置外,还需继承一个叫HttpServlet的抽象类。HttpService类提供了一个service()方法,这个方法调用七个do方法中的一个或几个,完成对客户端调用的响应。这些do方法需要由HttpServlet的具体子类提供,因此这是典型的模板方法模式。

        b:持久层对数据库的操作,Spring提供了JdbcTemplate模板对象, 完成CRUD操作。

(三)、出国留学手续设计程序

1.需求

        分析:出国留学手续一般经过以下流程:索取学校资料,提出入学申请,办理因私出国护照、出境卡和公证,申请签证,体检、订机票、准备行装,抵达目标学校等,其中有些业务对各个学校是一样的,但有些业务因学校不同而不同,所以比较适合用模板方法模式来实现。

        在本实例中,我们先定义一个出国留学的抽象类 StudyAbroad,里面包含了一个模板方法 TemplateMethod(),该方法中包含了办理出国留学手续流程中的各个基本方法,其中有些方法的处理由于各国都一样,所以在抽象类中就可以实现,但有些方法的处理各国是不同的,必须在其具体子类(如美国留学类 StudyInAmerica)中实现。如果再增加一个国家,只要增加一个子类就可以了。

2.程序代码实现

 2.1父类的实现
package com.jn.template;public abstract  class StudyAbroad {//定义抽象方法,索取学校资料public abstract void LookingForSchool();//定义抽象方法,定义入学申请public abstract void ApplyForEnrol();//定义入学申请方法:public void ApplyForPassport() {System.out.println("三.办理因私出国护照、出境卡和公证:");System.out.println("  1)持录取通知书、本人户口簿或身份证向户口所在地公安机关申请办理因私出国护照和出境卡。");System.out.println("  2)办理出生公证书,学历、学位和成绩公证,经历证书,亲属关系公证,经济担保公证。");}//定义申请签证方法public void ApplyForVisa() {System.out.println("四.申请签证:");System.out.println("  1)准备申请国外境签证所需的各种资料,包括个人学历、成绩单、工作经历的证明;个人及家庭收入、资金和财产证明;家庭成员的关系证明等;");System.out.println("  2)向拟留学国家驻华使(领)馆申请入境签证。申请时需按要求填写有关表格,递交必需的证明材料,缴纳签证。有的国家(比如美国、英国、加拿大等)在申请签证时会要求申请人前往使(领)馆进行面试。");}//体检、订机票、准备行装 方法public void ReadyGoAbroad() {System.out.println("五.体检、订机票、准备行装:");System.out.println("  1)进行身体检查、免疫检查和接种传染病疫苗;");System.out.println("  2)确定机票时间、航班和转机地点。");}//定义抵达抽象方法public abstract void Arriving();//模板方法: 定义算法结构public void templateMethod(){LookingForSchool();ApplyForEnrol();ApplyForPassport();ApplyForVisa();ReadyGoAbroad();Arriving();}
}
2.2子类的定义

        定义具体的子类,美国留学 

package com.jn.template;public class StudyInAmerica extends StudyAbroad{//索取资料的具体实现@Overridepublic void LookingForSchool() {System.out.println("一.索取学校以下资料:");System.out.println("  1)对留学意向国家的政治、经济、文化背景和教育体制、学术水平进行较为全面的了解;");System.out.println("  2)全面了解和掌握国外学校的情况,包括历史、学费、学制、专业、师资配备、教学设施、学术地位、学生人数等;");System.out.println("  3)了解该学校的住宿、交通、医疗保险情况如何;");System.out.println("  4)该学校在中国是否有授权代理招生的留学中介公司?");System.out.println("  5)掌握留学签证情况;");System.out.println("  6)该国政府是否允许留学生合法打工?");System.out.println("  8)毕业之后可否移民?");System.out.println("  9)文凭是否受到我国认可?");}//入学申请的具体实现@Overridepublic void ApplyForEnrol() {System.out.println("二.入学申请:");System.out.println("  1)填写报名表;");System.out.println("  2)将报名表、个人学历证明、最近的学习成绩单、推荐信、个人简历、托福或雅思语言考试成绩单等资料寄往所申请的学校;");System.out.println("  3)为了给签证办理留有充裕的时间,建议越早申请越好,一般提前1年就比较从容。");}//抵达的具体实现@Overridepublic void Arriving() {System.out.println("六.抵达目标学校:");System.out.println("  1)安排住宿;");System.out.println("  2)了解校园及周边环境。");}}
2.3测试 
package com.jn.template;public class StudyAbroadProcess {public static void main(String[] args) {StudyAbroad tm = new StudyInAmerica();tm.templateMethod();}}

项目整体结构目录 

相关文章:

Spring学习笔记(一)

一 、Spring概述 (一)Spring是什么 Spring是一个分层的Java SE/EE full-stack&#xff08;一站式&#xff09;轻量级开源框架&#xff0c;以 IoC&#xff08;Inverse Of Control&#xff1a;反转控制&#xff09;和 AOP&#xff08;Aspect Oriented Programming&#xff1a;面…...

Java编程基础

Java是一种广泛使用的编程语言&#xff0c;以其跨平台兼容性、面向对象的特性和健壮的安全性而闻名。本篇文章将带你了解Java编程的基础知识。 Java简介 Java是由Sun Microsystems&#xff08;现在是Oracle Corporation的一部分&#xff09;在1995年发布的。它是一种静态类型…...

C++【string类,模拟实现string类】

&#x1f31f;个人主页&#xff1a;落叶 &#x1f31f;当前专栏: C专栏 目录 为什么学习string类 C语言中的字符串 标准库中的string类 auto和范围for auto关键字 迭代器 范围for string类的常用接口说明和使用 1. string类对象的常见构造 2.string类对象的容量操作 3…...

Jupyter lab 打开时默认使用 Notebook 而不是浏览器

Jupyter lab 打开时默认使用 Notebook 而不是浏览器 正文 正文 今天遇到了一个特别有意思的事情&#xff0c;这里我们以 Windows \textrm{Windows} Windows 系统举例。 我们知道通常我们需要使用如下代码在 Terminal \textrm{Terminal} Terminal 中打开 Jupyter lab \textr…...

【linux】ubunda repo是什么

Ubuntu repo&#xff08;repository&#xff0c;简称repo&#xff09;是一个软件仓库&#xff0c;它是存储和分发软件包的服务器或一组服务器。通俗地说&#xff0c;Ubuntu repo就像一个巨大的在线软件商店&#xff0c;用户可以从中下载和安装各种软件。 主要特点 软件集合&a…...

【MySQL】深层理解索引及特性(重点)--下(12)

索引&#xff08;重点&#xff09; 1. 索引的作用2. 索引操作2.1 主键索引2.1.1 主键索引的特点2.1.2 创建主键索引 2.2 唯一键索引2.2.1 唯一键索引的特点2.2.2 唯一索引的创建 2.3 普通索引2.3.1 普通索引的特点2.3.2 普通索引的创建 2.4 全文索引2.4.1 全文索引的作用2.4.2 …...

无人机声学侦测算法详解!

一、算法原理 无人机在飞行过程中&#xff0c;其电机工作、旋翼震动以及气流扰动等都会产生一定程度的噪声。这些噪声具有独特的声学特征&#xff0c;如频率范围、时域和频域特性等&#xff0c;可以用于无人机的检测与识别。声学侦测算法利用这些特征&#xff0c;通过一系列步…...

git 提交仓库

创建 git 仓库&#xff1a; mkdir pySoundImage cd pySoundImage git init touch README.md git add README.md git commit -m “first commit” git remote add origin https://gitee.com/hunan-co-changsha-branch/pytest.git git push -u origin master 已有仓库&#xff…...

基于大语言模型(LLM)自主Agent 智能体综述

近年来,LLM(Large Language Model)取得了显著成功,并显示出了达到人类智能的巨大潜力。基于这种能力,使用LLM作为中央控制器来构建自助Agent,以获得类人决策能力。 Autonomous agents 又被称为智能体、Agent。指能够通过感知周围环境、进行规划以及执行动作来完成既定任务。…...

使用命令行管理 Windows 环境变量

1. 使用命令提示符 (CMD) 1.1. 设置环境变量 添加或修改临时环境变量&#xff08;当前会话有效&#xff09; set MY_VARvalue添加或修改用户环境变量 setx MY_VAR "value"添加或修改系统环境变量&#xff08;需要管理员权限&#xff09;&#xff1a; setx /M MY…...

AUTODL配置百度网盘数据传输

AUTODL使用 1.配置百度网盘开放平台 2.接入并创建应用 3.创建应用 4.添加授权...

LeetCode46. 全排列(2024秋季每日一题 57)

给定一个不含重复数字的数组 nums &#xff0c;返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。 示例 1&#xff1a; 输入&#xff1a;nums [1,2,3] 输出&#xff1a;[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]] 示例 2&#xff1a; 输入&#xff1a;nums …...

SpringBoot新闻稿件管理系统:架构与实现

3系统分析 3.1可行性分析 通过对本新闻稿件管理系统实行的目的初步调查和分析&#xff0c;提出可行性方案并对其一一进行论证。我们在这里主要从技术可行性、经济可行性、操作可行性等方面进行分析。 3.1.1技术可行性 本新闻稿件管理系统采用SSM框架&#xff0c;JAVA作为开发语…...

MinIO讲解和java应用案例示范

一、MinIO 基本概念 1.1 什么是 MinIO&#xff1f; MinIO 是一个高性能的对象存储服务器&#xff0c;专为云原生应用设计。它支持 Amazon S3 API&#xff0c;因此可以与现有的 S3 客户端和工具集成。MinIO 主要用于存储非结构化数据&#xff0c;如图片、视频、备份文件和日志…...

区块链技术与应用 【全国职业院校技能大赛国赛题目解析】第1套 区块链系统设计与运维部分

模块一:区块链产品方案设计及系统运维(35分) 选手完成本模块的任务后,将任务中设计结果、运行代码、运行结果等截图粘贴至客户端桌面【区块链技术应用赛\重命名为工位号\模块一提交结果.docx】中对应的任务序号下。 前述: 我们收到答案后,将针对比赛的答案和样题进行解…...

yaml文件编写

Kubernetes 支持YAML和JSON格式管理资源 JSON 格式:主要用于 api 接口之间消息的传递 YAML 格式;用于配置和管理,YAML是一种简洁的非标记性语言,内容格式人性化容易读懂 一&#xff0c;yaml语法格式 1.1 基本语法规则 使用空格进行缩进&#xff08;不使用制表符&#xff0…...

TOEIC 词汇专题:娱乐休闲篇

TOEIC 词汇专题&#xff1a;娱乐休闲篇 在娱乐和休闲活动中&#xff0c;我们会接触到许多特定的词汇。这些词汇涉及到活动入场、观众互动、评论等各个方面&#xff0c;帮助你在相关场景中更加自如。 1. 入场和观众 一些常用词汇帮助你轻松应对观众与入场管理相关的场景&#…...

驱动TFT-1.44寸屏(ST7735)显示器

目录 一、驱动芯片介绍 二、驱动方式 三、主函数main运行 四、完整代码下载 TFT1.44寸屏&#xff0c;搭配ST7735驱动芯片&#xff0c;是一种专为小型电子设备设计的彩色液晶显示解决方案。该屏幕采用薄膜晶体管&#xff08;TFT&#xff09;技术&#xff0c;能够实现高亮度、…...

鸿蒙HarmonyOS NEXT一多适配技术方案

鸿蒙一多是什么 HarmonyOS 系统面向多终端提供了“一次开发&#xff0c;多端部署”&#xff08;后文中简称为“一多”&#xff09;的能力&#xff0c;让开发者可以基于一种设计&#xff0c;高效构建多端可运行的应用。 一套代码工程&#xff0c;一次开发上架&#xff0c;多端按…...

golang 中map使用的一些坑

golang 中map使用的一些坑 1、使用map[string]interface{}&#xff0c;类型断言[]int失败 接收下游的数据是用json转为map[string]any go a : "{\"a\":\"1\",\"b\":[123]}" var marshal map[string]any json.Unmarshal([]byte(a), &…...

cordova 离线打包Android -Linux

背景 已有 cordova 运行环境的docker镜像&#xff1b; 需要在离线环境下执行 cordova 从创建项目到构建安装包一系列命令&#xff0c;最终生成 apk 文件。 方案 先在有网环境&#xff08;最好与离线环境的OS一致&#xff09;走一遍 cordova 创建打包工程、添加插件、添加平…...

【python】OpenCV—findContours(4.3)

文章目录 1、功能描述2、代码实现3、完整代码4、结果展示5、涉及到的库函数5.1、cv2.Canny5.2 cv2.boxPoints 6、参考 1、功能描述 找出图片中的轮廓&#xff0c;拟合轮廓外接椭圆和外接矩阵 2、代码实现 导入必要的库&#xff0c;固定好随机种子 import cv2 as cv import …...

前端通过nginx部署一个本地服务的方法

前端通过nginx部署一个本地服务的方法&#xff1a; 1.下载ngnix nginx 下载完成后解压缩后运行nginx.exe文件 2.打包你的前端项目文件 yarn build 把生成的dist文件复制出来&#xff0c;替换到nginx的html文件下 3.配置conf目录的nginx.conf文件 主要配置server监听 ser…...

Linux:防火墙和selinux对服务的影响

1-1selinux 1-1 SELinux是对程序、文件等权限设置依据的一个内核模块。由于启动网络服务的也是程序&#xff0c;因此刚好也 是能够控制网络服务能否访问系统资源的一道关卡。 1-2 SELinux是通过MAC的方式来控制管理进程&#xff0c;它控制的主体是进程&#xff0c;而目标则是…...

从 vue 源码看问题 — vue 如何进行异步更新?

前言 在上一篇 如何理解 vue 响应式&#xff1f; 中&#xff0c;了解到响应式其实是通过 Observer 类中调用 defineReactive() 即 Object.defineProperty() 方法为每个目标对象的 key&#xff08;key 对应的 value 为非数组的&#xff09; 设置 getter 和 setter 实现拦截&…...

【go从零单排】go中的基本数据类型和变量

Don’t worry , just coding! 内耗与overthinking只会削弱你的精力&#xff0c;虚度你的光阴&#xff0c;每天迈出一小步&#xff0c;回头时发现已经走了很远。 基本类型 go中的string、int、folat都可以用连接boolen可以用逻辑表达式计算 package mainimport "fmt&quo…...

标签之文字排版,图片,链接,音视频(HTML) 基础版

目录 标签之文字排版,图片,链接,音视频知识点: 练习题一: 效果: 练习题二: 效果: 标签之文字排版,图片,链接,音视频知识点: 超文本:链接 标记:标签<> 双标签 单标签 <br>//换行 <hr>//水平线 向后tab 向前shifttab html注释<!----> css /**/ …...

基于SpringBoot+Gpt个人健康管家管理系统【提供源码+答辩PPT+参考文档+项目部署】

作者简介&#xff1a;✌CSDN新星计划导师、Java领域优质创作者、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和学生毕业项目实战,高校老师/讲师/同行前辈交流。✌ 主要内容&#xff1a;&#x1f31f;Java项目、Python项目、前端项目、PHP、ASP.NET、人工智能…...

十四届蓝桥杯STEMA考试Python真题试卷第二套第一题

来源&#xff1a;十四届蓝桥杯STEMA考试Python真题试卷第二套编程第一题 题目描述&#xff1a; 给定一个字符串&#xff0c;输出字符串中最后一个字符。 输入描述&#xff1a; 输入一个字符串 输出描述&#xff1a; 输出字符串中最后一个字符 样例输入&#xff1a; hgf 样…...

【Windows修改Docker Desktop(WSL2)内存分配大小】

记录一下遇到使用Docker Desktop占用内存居高不下的问题 自从使用了Docker Desktop&#xff0c;电脑基本每天都需要重启&#xff0c;内存完全不够用&#xff0c;从16g扩展到24&#xff0c;然后到40G&#xff0c;还是不够用&#xff1b;打开Docker Desktop 运行时间一长&#x…...