困惑一:层与层之间紧密耦合在了一起,接口与具体实现紧密耦合在了一起
解决思路:程序代码中不要手动new对象,第三方根据要求为程序提供需要的Bean对象
困惑二:通用的事务功能耦合在业务代码中,通用的日志功能耦合在业务代码中
解决思路:程序代码中不要手动new对象,第三方根据要求为程序提供需要的Bean对象的代理对象,代理对象内部动态结合业务和通用功能
实际开发中,对象之间的耦合关系,就类似手表内部的齿轮,每个齿轮都紧密啮合在一起,一旦某个齿轮发生故障,那么整个系统也意味着崩溃。尽可能让对象之间的关系保持松耦合状态是我们期望的。
IoC思想: Inversion of Control,翻译为“控制反转”或“反转控制”,强调的是原来在程序中创建Bean的权利反转给第三方。
例如:原来在程序中手动的去 new UserServiceImpl(),手动的去new UserDaoImpl(),而根据IoC思想的指导,寻求一个第三方去创建UserServiceImpl对象和UserDaoImpl对象。这样程序与具体对象就失去的直接联系。
谁去充当第三方角色呢?
BeanFactory怎么知道产生哪些Bean实例呢?
例如,用IoC思想去反转UserServiceImpl的创建权,由原来程序中创建反转给通过BeanFactory去创建
上面使用BeanFactory的方式已经实现的"控制反转",将Bean的创建权交给了BeanFactory,如果我们想将UserDao的创建权也反转给BeanFactory,与此同时UserService内部还需要用到UserDao实例对象,那应该怎样操作呢?
该方式是否存在一些问题?
UserService存在于BeanFactory中,UserDao也存在于BeanFactory中,可以在BeanFactory内部进行结合
将UserDao在BeanFactory内部设置给UserService的过程叫做 注入
,而UserService需要依赖UserDao的注入才能正常工作,这个过程叫做 依赖注入
面试题:IoC 和 DI 的关系?
IoC:Inversion of Control,控制反转,将Bean的创建权由原来程序反转给第三方
DI:Dependency Injection,依赖注入,某个Bean的完整创建依赖于其他Bean(或普通参数)的注入
第一种观点:IoC强调的是Bean创建权的反转,而DI强调的是Bean的依赖关系,认为不是一回事
第二种观点:IoC强调的是Bean创建权的反转,而DI强调的是通过注入的方式反转Bean的创建权,认为DI是IoC的其中一种实现方式
IoC和DI思想主要是解决前面我们的困惑一,困惑二还没有解决
困惑二的解决方案是,借助于IoC思想,将Bean的创建权反转给BeanFactory,而BeanFactory生产的Bean是目标Bean的代理对象,这样就可以在代理对象中对目标对象方法进行相应的增强。
AOP,Aspect Oriented Programming,面向切面编程,是对面向对象编程OOP的升华。OOP是纵向对一个事物的抽象,一个对象包括静态的属性信息,包括动态的方法信息等。而AOP是横向的对不同事物的抽象,属性与属性、方法与方法、对象与对象都可以组成一个切面,而用这种思维去设计编程的方式叫做面向切面编程
三种思想总结
IoC控制反转,是将程序创建Bean的权利反转给第三方;
DI依赖注入,某个完整Bean需要依赖于其他Bean(或属性)的注入;
AOP面向切面编程,用横向抽取方法(属性、对象等)思想,组装成一个功能性切面。
上面提出了一些思想来解决遇到的问题,而这些思想的实现就需要通过编码去落地,往往我们把具备一定业务领域解决方案的"工具"称为框架。
框架的基本特点:
框架(Framework),是基于基础技术之上,从众多业务中抽取出的通用解决方案;
框架是一个半成品,使用框架规定的语法开发可以提高开发效率,可以用简单的代码就能完成复杂的基础业务;
框架内部使用大量的设计模式、算法、底层代码操作技术,如反射、内省、xml解析、注解解析等;
框架一般都具备扩展性;
有了框架,我们可以将精力尽可能的投入在纯业务开发上而不用去费心技术实现以及一些辅助业务。
Java中常用的框架:
不同语言,不同领域都有属于自己的框架,使用框架开发是作为程序员的最基础的底线。Java语言中的框架,可以分为基础框架和服务框架:
基础框架:完成基本业务操作的框架,如MyBatis、Spring、SpringMVC、Struts2、Hibernate等
服务框架:特定领域的框架,一般还可以对外提供服务框架,如MQ、ES、Nacos等
架构师(高级程序员):把思想落地变为实现的人,例如上面的设计和BeanFactory的编写,即框架的设计和实现者。
程序员:使用框架完成业务的人,其中UserServiceImpl、beans.xml、测试类都是我们编写的。
spring是一个开源的轻量级Java开发应用框架,可以简化企业级应用开发。Spring解决了开发者在JavaEE开发中遇到的许多常见的问题,提供了功能强大IOC、AOP及Web MVC等功能。是当前企业中Java开发几乎不能缺少的框架之一。Spring的生态及其完善,不管是Spring哪个领域的解决方案都是依附于在Spring Framework基础框架的。
Spring的官网:www.spring.io
根据下图,分析一下Spring的BeanFactory的开发步骤:
1)导入Spring的jar包或Maven坐标;
<!--Spring核心-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.7</version>
</dependency>
2)定义UserService接口及其UserServiceImpl实现类;
public interface UserService {
}
public class UserServiceImpl implements UserService {
}
3)创建beans.xml配置文件,将UserServiceImpl的信息配置到该xml中;
<bean id="userService" class="com.test.service.impl.UserServiceImpl"></bean>
4)编写测试代码,创建BeanFactory,加载配置文件,获取UserService实例对象。
//创建BeanFactory
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
//创建读取器
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
//加载配置文件
reader.loadBeanDefinitions("beans.xml");
//获取Bean实例对象
UserDao userService = (UserService) beanFactory.getBean("userService");
上面使用BeanFactory完成了IoC思想的实现,下面去实现以下DI依赖注入:
1)定义UserDao接口及其UserDaoImpl实现类;
public interface UserDao {
}
public class UserDaoImpl implements UserDao {
}
2)修改UserServiceImpl代码,添加一个setUserDao(UserDao userDao)用于接收注入的对象;
public class UserServiceImpl implements UserService {
public void setUserDao(UserDao userDao) {
System.out.println(userDao);
}
}
3)修改beans.xml配置文件,在UserDaoImpl的中嵌入配置注入;
<bean id="userService" class="com.test.service.impl.UserServiceImpl">
<property name="userDao" ref="userDao"/>
</bean>
4)修改测试代码,获得UserService时,setUserService方法执行了注入操作。
//创建BeanFactory
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
//创建读取器
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
//加载配置文件
reader.loadBeanDefinitions("beans.xml");
//获取Bean实例对象
UserDao userService = (UserService) beanFactory.getBean("userService");
ApplicationContext 称为Spring容器,内部封装了BeanFactory,比BeanFactory功能更丰富更强大,使用ApplicationContext 进行开发时,xml配置文件的名称习惯写成applicationContext.xml
//创建ApplicationContext,加载配置文件,实例化容器
ApplicationContext applicationContext = new ClassPathxmlApplicationContext(“applicationContext.xml");
//根据beanName获得容器中的Bean实例
UserService userService = (UserService) applicationContext.getBean("userService");
System.out.println(userService);
1)BeanFactory是Spring的早期接口,称为Spring的Bean工厂,ApplicationContext是后期更高级接口,称之为Spring 容器;
2)ApplicationContext在BeanFactory基础上对功能进行了扩展,例如:监听功能、国际化功能等。BeanFactory的API更偏向底层,ApplicationContext的API大多数是对这些底层API的封装;
3)Bean创建的主要逻辑和功能都被封装在BeanFactory中,ApplicationContext不仅继承了BeanFactory,而且ApplicationContext内部还维护着BeanFactory的引用,所以,ApplicationContext与BeanFactory既有继承关系,又有融合关系。
4)Bean的初始化时机不同,原始BeanFactory是在首次调用getBean时才进行Bean的创建,而ApplicationContext则是配置文件加载,容器一创建就将Bean都实例化并初始化好。
ApplicationContext除了继承了BeanFactory外,还继承了ApplicationEventPublisher(事件发布器)、ResouresPatternResolver(资源解析器)、MessageSource(消息资源)等。但是ApplicationContext的核心功能还是BeanFactory。
applicationContext内部维护着beanFactory的引用,在学习过程中会查看beanFactory内部维护的属性,断点查看如下图示内容的
验证BeanFactory和ApplicationContext对Bean的初始化时机,在UserDaoImpl的无参构造内打印一句话,验证构造方法的执行时机
public class UserDaoImpl implements UserDao {
public UserDaoImpl() {
System.out.println("UserDaoImpl创建了...");
}
}
断点观察,BeanFactory方式时,当调用getBean方法时才会把需要的Bean实例创建,即延迟加载;而ApplicationContext是加载配置文件,容器创建时就将所有的Bean实例都创建好了,存储到一个单例池中,当调用getBean时直接从单例池中获取Bean实例返回
BeanFactory是核心接口,项目运行过程中肯定有具体实现参与,这个具体实现就是DefaultListableBeanFactory,而ApplicationContext内部维护的Beanfactory的实现类也是它
只在Spring基础环境下,即只导入spring-context坐标时,此时ApplicationContext的继承体系
只在Spring基础环境下,常用的三个ApplicationContext作用如下:
实现类 | 功能描述 |
---|---|
ClassPathXmlApplicationContext | 加载类路径下的xml配置的ApplicationContext |
FileSystemXmlApplicationContext | 加载磁盘路径下的xml配置的ApplicationContext |
AnnotationConfigApplicationContext | 加载注解配置类的ApplicationContext |
如果Spring基础环境中加入了其他组件解决方案,如web层解决方案,即导入spring-web坐标,此时ApplicationContext的继承体系
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.3.7</version>
</dependency>
在Spring的web环境下,常用的两个ApplicationContext作用如下:
实现类 | 功能描述 |
---|---|
XmlWebApplicationContext | web环境下,加载类路径下的xml配置的ApplicationContext |
AnnotationConfigWebApplicationContext | web环境下,加载磁盘路径下的xml配置的ApplicationContext |
Spring开发中主要是对Bean的配置,Bean的常用配置一览如下:
Xml配置方式 | 功能描述 |
---|---|
<bean id=“” class=“”> | Bean的id和全限定名配置 |
<bean name=“”> | 通过name设置Bean的别名,通过别名也能直接获取到Bean实例 |
<bean scope=“”> | Bean的作用范围,BeanFactory作为容器时取值singleton和prototype |
<bean lazy-init=“”> | Bean的实例化时机,是否延迟加载。BeanFactory作为容器时无效 |
<bean init-method=“”> | Bean实例化后自动执行的初始化方法,method指定方法名 |
<bean destroy-method=“”> | Bean实例销毁前的方法,method指定方法名 |
<bean autowire=“byType”> | 设置自动注入模式,常用的有按照类型byType,按照名字byName |
<bean factory-bean=“” factory-method=“”/> | 指定哪个工厂Bean的哪个方法完成Bean的创建 |
例如:配置UserDaoImpl由Spring容器负责管理
<bean id="userDao" class="com.test.dao.impl.UserDaoImpl"/>
此时存储到Spring容器(singleObjects单例池)中的Bean的beanName是userDao,值是UserDaoImpl对象,可以根据beanName获取Bean实例
applicationContext.getBean("userDao");
如果不配置id,则Spring会把当前Bean实例的全限定名作为beanName
applicationContext.getBean("com.test.dao.impl.UserDaoImpl");
可以为当前Bean指定多个别名,根据别名也可以获得Bean对象
<bean id="userDao" name="aaa,bbb" class="com.test.dao.impl.UserDaoImpl"/>
此时多个名称都可以获得UserDaoImpl实例对象
applicationContext.getBean("userDao");
applicationContext.getBean("aaa");
applicationContext.getBean("bbb");
默认情况下,单纯的Spring环境Bean的作用范围有两个:Singleton和Prototype
当scope设置为singleton时,获得两次对象打印结果是一样的
Spring 的单例模式指的是在 Spring 容器中管理的 Bean 默认为单例模式,即每个 Bean 在 Spring 容器中只会存在一个实例。对于 Spring 的单例模式来说,在多线程的情况下是线程安全的。这是因为 Spring 的单例 Bean 是在容器启动时被创建,并在整个应用的生命周期中只有一个实例,因此不会存在多个线程同时访问创建多个实例的情况。
然而,需要注意的是,单例模式并不代表单例 Bean 内部所有的状态数据都是线程安全的。如果单例 Bean 内部包含了可变状态,且被多个线程同时修改,那么仍然需要开发者自己来确保线程安全,可以通过加锁或使用线程安全的数据结构来保证。
<bean id="userDao" class="com.test.dao.impl.UserDaoImpl" scope="singleton"/>
Object userDao = applicationContext.getBean("userDao");
Object userDao2 = applicationContext.getBean("userDao");
System.out.println(userDao); //com.test.dao.impl.UserDaoImpl@631330c
System.out.println(userDao2); //com.test.dao.impl.UserDaoImpl@631330c
通过断点调试,观察可以发现单例池中存在 userDao 实例
当scope设置为prototype时,获得两次对象打印结果是不一样的
<bean id="userDao" class="com.test.dao.impl.UserDaoImpl" scope="prototype"/>
Object userDao = applicationContext.getBean("userDao");
Object userDao2 = applicationContext.getBean("userDao");
System.out.println(userDao); //com.test.dao.impl.UserDaoImpl@4d50efb8
System.out.println(userDao2); //com.test.dao.impl.UserDaoImpl@7e2d773b
通过断点调试,观察可以发现单例池中不存在 userDao 实例,但是 userDao的信息已经被存储到beanDefinitionMap中了
当lazy-init设置为true时为延迟加载,也就是当Spring容器创建的时候,不会立即创建Bean实例,等待用到时在创建Bean实例并存储到单例池中去,后续在使用该Bean直接从单例池获取即可,本质上该Bean还是单例的
<bean id="userDao" class="com.test.dao.impl.UserDaoImpl" lazy-init="true"/>
Bean在被实例化后,可以执行指定的初始化方法完成一些初始化的操作,Bean在销毁之前也可以执行指定的销毁方法完成一些操作
<bean id="userDao" class="com.test.dao.impl.UserDaoImpl" init-method="init" destroy-method="destroy"/>
public class UserDaoImpl implements UserDao {
public UserDaoImpl() {
System.out.println("UserDaoImpl创建了...");
}
public void init(){
System.out.println("初始化方法...");
}
public void destroy(){
System.out.println("销毁方法...");
}
}
public class ApplicationContextTest {
public static void main(String[] args) throws Exception {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = applicationContext.getBean(UserService.class);
//执行销毁方法
applicationContext.close();
}
}
扩展:除此之外,我们还可以通过实现 InitializingBean 接口,完成一些Bean的初始化操作,如下:
public class UserDaoImpl implements UserDao, InitializingBean {
public UserDaoImpl() {
System.out.println("UserDaoImpl创建了...");
}
public void init(){
System.out.println("初始化方法...");
}
public void destroy(){
System.out.println("销毁方法...");
}
//执行时机早于init-method配置的方法
public void afterPropertiesSet() throws Exception {
System.out.println("InitializingBean...");
}
}
Spring的实例化方式主要如下两种:
构造方式实例化:底层通过构造方法对Bean进行实例化
工厂方式实例化:底层通过调用自定义的工厂方法对Bean进行实例化
构造方式实例化Bean又分为无参构造方法实例化
和有参构造方法实例化
,Spring中配置的<bean>几乎都是无参构造方式,此处不在赘述。下面讲解有参构造方法实例化Bean
//有参构造方法
public UserDaoImpl(String name){
}
有参构造在实例化Bean时,需要参数的注入,通过<constructor-arg>标签,嵌入在<bean>标签内部提供构造参数,如下:
<bean id="userDao" class="com.test.dao.impl.UserDaoImpl">
<constructor-arg name="name" value="haohao"/>
</bean>
工厂方式实例化Bean,又分为如下三种:
静态工厂方法实例化Bean,其实就是定义一个工厂类,提供一个静态方法用于生产Bean实例,在将该工厂类及其静态方法配置给Spring即可
//工厂类
public class UserDaoFactoryBean {
//非静态工厂方法
public static UserDao getUserDao(String name){
//可以在此编写一些其他逻辑代码
return new UserDaoImpl();
}
}
<bean id="userDao" class="com.test.factory.UserDaoFactoryBean" factorymethod="getUserDao">
<constructor-arg name="name" value="haohao"/>
</bean>
<constructor-arg>标签不仅仅是为构造方法传递参数,只要是为了实例化对象而传递的参数都可以通过<constructor-arg>标签完成,例如上面通过静态工厂方法实例化Bean所传递的参数也是要通过<constructor-arg>进行传递的
测试代码,直接通过ApplicationContext获得userDao即可
ApplicationContext applicationContext =new ClassPathxmlApplicationContext("applicationContext.xml");
Object userDao = applicationContext.getBean("userDao");
System.out.println(userDao);
断点调试,UserDaoImpl实例对象会存在于单例池中
实例工厂方法,也就是非静态工厂方法产生Bean实例,与静态工厂方式比较,该方式需要先有工厂对象,在用工厂对象去调用非静态方法,所以在进行配置时,要先配置工Bean,在配置目标Bean
//工厂类
public class UserDaoFactoryBean2 {
//非静态工厂方法
public UserDao getUserDao(String name){
//可以在此编写一些其他逻辑代码
return new UserDaoImpl();
}
}
<!-- 配置实例工厂Bean -->
<bean id="userDaoFactoryBean2" class="com.test.factory.UserDaoFactoryBean2"/>
<!-- 配置实例工厂Bean的哪个方法作为工厂方法 -->
<bean id="userDao" factory-bean="userDaoFactoryBean2" factory-method="getUserDao">
<constructor-arg name="name" value="haohao"/>
</bean>
测试代码同上,直接通过ApplicationContext获得userDao即可,不在赘述
通过断点观察单例池singletonObjects,发现单例池中既有工厂Bean实例,也有目标Bean实例,且都是在Spring容器创建时,就完成了Bean的实例化
上面不管是静态工厂方式还是非静态工厂方式,都是自定义的工厂方法,Spring提供了FactoryBean的接口规范,FactoryBean接口定义如下:
public interface FactoryBean<T> {
String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType";
T getObject() throws Exception; //获得实例对象方法
Class<?> getObjectType(); //获得实例对象类型方法
default boolean isSingleton() {
return true;
}
}
定义工厂实现FactoryBean
public class UserDaoFactoryBean3 implements FactoryBean<UserDao> {
public UserDao getObject() throws Exception {
return new UserDaoImpl();
}
public Class<?> getObjectType() {
return UserDao.class;
}
}
配置FactoryBean交由Spring管理即可
<bean id="userDao" class="com.test.factory.UserDaoFactoryBean3"/>
通过Spring容器根据beanName可以正常获得UserDaoImpl
ApplicationContext applicationContext =new ClassPathxmlApplicationContext("applicationContext.xml");
Object userDao = applicationContext.getBean("userDao");
System.out.println(userDao);
通过断点观察发现Spring容器创建时,FactoryBean被实例化了,并存储到了单例池singletonObjects中,但是getObject() 方法尚未被执行,UserDaoImpl也没被实例化,当首次用到UserDaoImpl时,才调用getObject() ,此工厂方式产生的Bean实例不会存储到单例池singletonObjects中,会存储到 factoryBeanObjectCache 缓存池中,并且后期每次使用到userDao都从该缓存池中返回的是同一个userDao实例。
Bean的依赖注入有两种方式:
注入方式 | 配置方式 |
---|---|
通过Bean的set方法注入 | <property name=“userDao” ref=“userDao”/><property name=“userDao” value=“haohao”/> |
通过构造Bean的方法进行注入 | <constructor-arg name=“name” ref=“userDao”/><constructor-arg name=“name” value=“haohao”/> |
其中,ref 是 reference 的缩写形式,翻译为:涉及,参考的意思,用于引用其他Bean的id。value 用于注入普通属性值。
依赖注入的数据类型有如下三种:
普通数据类型,例如:String、int、boolean等,通过value属性指定。
引用数据类型,例如:UserDaoImpl、DataSource等,通过ref属性指定。
集合数据类型,例如:List、Map、Properties等。
注入 List 集合 – 普通数据
void setStrList(List<String> strList){
strList.forEach(str->{
System.out.println(str);
});
}
<property name="strList">
<list>
<value>haohao</value>
<value>miaomiao</value>
</list>
</property>
注入 List 集合 – 引用数据
public void setObjList(List<UserDao> objList){
objList.forEach(obj->{
System.out.println(obj);
});
}
<property name="objList">
<list>
<bean class="com.test.dao.impl.UserDaoImpl"></bean>
<bean class="com.test.dao.impl.UserDaoImpl"></bean>
<bean class="com.test.dao.impl.UserDaoImpl"></bean>
</list>
</property>
也可以直接引用容器中存在的Bean
<!--配置UserDao-->
<bean id="userDao" class="com.test.dao.impl.UserDaoImpl"/>
<bean id="userDao2" class=
文章浏览阅读1.1w次,点赞7次,收藏34次。vue-grid-layout的使用、实例、遇到的问题和解决方案_vue-grid-layout
文章浏览阅读218次。然后连接一个数据源,就会在下面自动产生一个添加附件的组件。把这个控件复制粘贴到页面里,就可以单独使用来上传了。插入一个“编辑”窗体。_powerapps点击按钮上传附件
文章浏览阅读264次。(1) Abstraction (抽象)(2) Polymorphism (多态)(3) Inheritance (继承)(4) Encapsulation (封装)_"object(cnofd[\"ofdrender\"])十条"
文章浏览阅读133次。删除node_modules,重新npm install看是否成功。在 package.json 文件中的 scripts 中加入。修改你的第三方库的bug等。然后目录会多出一个目录文件。_修改 node_modules
文章浏览阅读883次。【代码】【】kali--password:su的 Authentication failure问题,&sudo passwd root输入密码时Sorry, try again._password: su: authentication failure
文章浏览阅读1w次,点赞13次,收藏97次。整理5个优秀的微信小程序开源项目。收集了微信小程序开发过程中会使用到的资料、问题以及第三方组件库。_微信小程序开源模板
文章浏览阅读128次。Centos7最简搭建NFS服务器_centos7 搭建nfs server
文章浏览阅读1.2k次,点赞2次,收藏3次。前言mybatis在持久层框架中还是比较火的,一般项目都是基于ssm。虽然mybatis可以直接在xml中通过SQL语句操作数据库,很是灵活。但正其操作都要通过SQL语句进行,就必须写大量的xml文件,很是麻烦。mybatis-plus就很好的解决了这个问题。..._mybaitis-plus ruledataobjectattributemapper' and 'com.picc.rule.management.d
文章浏览阅读325次。EECE 1080C / Programming for ECESummer 2022Laboratory 4: Global Functions PracticePlagiarism will not be tolerated:Topics covered:function creation and call statements (emphasis on global functions)Objective:To practice program development b_eece1080c
文章浏览阅读53次。被同机房早就1年前就学过的东西我现在才学,wtcl。设要求的数为\(x\)。设当前处理到第\(k\)个同余式,设\(M = LCM ^ {k - 1} _ {i - 1}\) ,前\(k - 1\)个的通解就是\(x + i * M\)。那么其实第\(k\)个来说,其实就是求一个\(y\)使得\(x + y * M ≡ a_k(mod b_k)\)转化一下就是\(y * M ...
文章浏览阅读1.3k次。首先,问题是如何出现的?晚上复查代码,发现一个activity没有调用自己的ondestroy方法我表示非常的费解,于是我检查了下代码。发现再finish代码之后接了如下代码finish();System.exit(0);//这就是罪魁祸首为什么这样写会出现问题System.exit(0);////看一下函数的原型public static void exit (int code)//Added ..._android 手动杀死app,activity不执行ondestroy
文章浏览阅读894次。Q: SylixOS 版权是什么形式, 是否分为<开发版税>和<运行时版税>.A: SylixOS 是开源并免费的操作系统, 支持 BSD/GPL 协议(GPL 版本暂未确定). 没有任何的运行时版税. 您可以用她来做任何 您喜欢做的项目. 也可以修改 SylixOS 的源代码, 不需要支付任何费用. 当然笔者希望您可以将使用 SylixOS 开发的项目 (不需要开源)或对 SylixOS 源码的修改及时告知笔者.需要指出: SylixOS 本身仅是笔者用来提升自己水平而开发的_select函数 导致堆栈溢出 sylixos