BeanFactory 使用前的准备

上一篇文章 https://www.cnblogs.com/redwinter/p/16165878.html 介绍了自定义标签的使用,完成了AbstractApplicationContext#refresh 第二个方法 的介绍,本文将继续介绍Spring源码的重要方法AbstractApplicationContext#refresh方法的第三个方法:prepareBeanFactory,准备BeanFactory

源码如下:

protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
// Tell the internal bean factory to use the context's class loader etc.
// 设置类加载器
beanFactory.setBeanClassLoader(getClassLoader());
// 设置Spel 表达式解析器,用于属性填充时对值进行表达式解析
beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
// 扩展点,添加一个属性编辑器的注册器,也可以使用 CustomEditorConfigurer 进行设置
// 后面在进行属性填充的时候会调用这个属性编辑器进行属性的解析
beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment())); // Configure the bean factory with context callbacks.
// 扩展点,添加一个BeanPostProcessor 这里添加这个进行处理,使用前置处理器执行下面忽略的六个Aware接口
beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
// 由于上面设置了这六个接口,因此需要忽略掉,不让Spring使用自动装配进行Bean的装配,而是使用BeanPostProcessor
// 的后置处理器的前置方法进行调用,因为如果不忽略,那么自定义的Bean中就会使用Setter注入进行装配,
// spring 这样做是为了统一的进行处理在Bean增强的时候
beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
beanFactory.ignoreDependencyInterface(ApplicationContextAware.class); // BeanFactory interface not registered as resolvable type in a plain factory.
// MessageSource registered (and found for autowiring) as a bean.
beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
beanFactory.registerResolvableDependency(ResourceLoader.class, this);
beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
beanFactory.registerResolvableDependency(ApplicationContext.class, this); // Register early post-processor for detecting inner beans as ApplicationListeners.
// 添加一个事件监听器的装饰器
beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this)); // Detect a LoadTimeWeaver and prepare for weaving, if found.
// aop织入 编译器织入、运行期织入、类加载织入
if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
// Set a temporary ClassLoader for type matching.
beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
} // Register default environment beans.
// 注册环境信息
if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
}
if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
}
if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
}
}

这个方法中主要做了以下事情:

  • 设置BeanFactory的类加载器。
  • 设置Bean的SPEL表达式的解析器,其作用是对值进行表达式的解析,比如在属性填充时,针对值是Properties或者String类型的时候就会使用el表达式进行解析。
  • 设置属性编辑器的注册器,作用是对属性进行解析,比如在属性填充时,针对字符串String类型的时候进行类型转换,就可以自定义属性编辑器针对性的进行解析操作。
  • 添加一些内置的BeanPostProcessor用于后面对象初始化时调用。
  • 设置环境信息,系统属性,系统环境变量等。

这个方法预留了一些扩展点,比如可以添加自定义的属性编辑器,添加自定义的BeanPostProcessor等。

定制Bean的属性解析器

我们知道在Bean的初始化时是分为两步,一步是属性填充,一步是初始化,在属性填充的时候,Spring会针对属性进行解析,如果属性值对应的类型和传入的值类型不一致,就会进行值的自定义解析,前提是你自定义了属性解析器,否则就会报错:报值的类型转换失败

接下来我们自定义一个属性解析器,比如我现在有个类CustomUser,其中有个属性类型是Address,还有个属性类型是Date,但是我在定义Bean的时候我把address属性设置为xxx_xxx_xxx,表示xxx省xxx市xxx区(县),date属性设置为yyyy-MM-dd HH:mm:ss格式的日期,我要让Spring帮我解析出正确的值出来,话不多说,上代码。

编写CustomUser类以及Address类

  • Address类
/**
* @author <a href="https://www.cnblogs.com/redwinter/">redwinter</a>
* @since 1.0
**/
public class Address { private String province;
private String city;
private String town; public String getProvince() {
return province;
} public void setProvince(String province) {
this.province = province;
} public String getCity() {
return city;
} public void setCity(String city) {
this.city = city;
} public String getTown() {
return town;
} public void setTown(String town) {
this.town = town;
} // 为了验证重写toString方法
@Override
public String toString() {
return "Address{" +
"province='" + province + '\'' +
", city='" + city + '\'' +
", town='" + town + '\'' +
'}';
}
}
  • CustomUser类
/**
* @author <a href="https://www.cnblogs.com/redwinter/">redwinter</a>
* @since 1.0
**/
public class CustomUser {
private String name;
private Address address;
private Date date; public Date getDate() {
return date;
} public void setDate(Date date) {
this.date = date;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public Address getAddress() {
return address;
} public void setAddress(Address address) {
this.address = address;
} @Override
public String toString() {
return "CustomUser{" +
"name='" + name + '\'' +
", address=" + address +
", date=" + date +
'}';
}
}

编写Address解析器和注册器

  • 解析器(编辑器)
/**
* @author <a href="https://www.cnblogs.com/redwinter/">redwinter</a>
* @since 1.0
**/
public class AddressPropertyEditor extends PropertyEditorSupport { @Override
public void setAsText(String text) throws IllegalArgumentException {
/**
* 自定义属性编辑器,将属性解析成自定义对象,比如传入的是一个字符串,可以解析成另外一个对象
*/
String[] arr = text.split("_");
Address address = new Address();
address.setProvince(arr[0]);
address.setCity(arr[1]);
address.setTown(arr[2]);
// 设置值
setValue(address);
}
}
  • 注册器
/**
* @author <a href="https://www.cnblogs.com/redwinter/">redwinter</a>
* @since 1.0
**/
public class AddressPropertyEditorRegistrar implements PropertyEditorRegistrar {
@Override
public void registerCustomEditors(PropertyEditorRegistry registry) {
registry.registerCustomEditor(Address.class,new AddressPropertyEditor());
}
}

编写Date解析器和注册器

  • 解析器(编辑器)
/**
* @author <a href="https://www.cnblogs.com/redwinter/">redwinter</a>
* @since 1.0
**/
public class DatePropertyEditor extends PropertyEditorSupport { private final DateFormat dateFormat; public DatePropertyEditor(DateFormat dateFormat) {
this.dateFormat = dateFormat;
} @Override
public void setAsText(String text) throws IllegalArgumentException {
if (!StringUtils.hasText(text)) {
System.out.println("日期类型的属性不能为空!");
return;
}
try {
Date date = dateFormat.parse(text);
setValue(date);
} catch (ParseException e) {
e.printStackTrace();
}
}
}
  • 注册器
/**
* @author <a href="https://www.cnblogs.com/redwinter/">redwinter</a>
* @since 1.0
**/
public class DatePropertyEditorRegistrar implements PropertyEditorRegistrar {
@Override
public void registerCustomEditors(PropertyEditorRegistry registry) {
registry.registerCustomEditor(Date.class,new DatePropertyEditor(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")));
}
}

配置注册器到Spring容器中

配置注册器有两种方式,一种是直接在定制BeanFactory的方法中添加注册器,一种是在Spring配置文件中添加

我在Spring容器中先配置CustomUser的信息以及Date日期类型的注册器:

<?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:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:redwinter="http://www.redwinter.com/schema/redwinter"
xsi:schemaLocation="http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd
http://www.redwinter.com/schema/redwinter http://www.redwinter.com/schema/redwinter.xsd
"> <!--自定义标签-->
<redwinter:dl id ="redwinter" email="abc@qq.com" password="123456" username="redwinter-name"/>
<redwinter:dl id ="redwinter123456" email="123456-abc@qq.com" password="123456" username="redwinter-name"/>
<!--自定义属性编辑器的解析器-->
<bean class="com.redwinter.test.CustomUser">
<property name="name" value="冬玲记忆"/>
<property name="address" value="四川省_成都市_郫都区"/>
<property name="date" value="2022-04-19 19:50:20"/>
</bean>
<!--配置自定义的编辑器注册器-->
<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
<property name="propertyEditorRegistrars">
<list>
<bean class="com.redwinter.test.DatePropertyEditorRegistrar"/>
</list>
</property>
</bean> </beans>

另外还有个Address的注册器我配置在定制BeanFactory的方法中:

@Override
protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
// 扩展点 设置不去处理循环依赖或者beanDefinition覆盖
super.setAllowBeanDefinitionOverriding(true);
super.setAllowCircularReferences(true);
super.customizeBeanFactory(beanFactory);
// 添加一个自定义的属性编辑器的注册器
beanFactory.addPropertyEditorRegistrar(new AddressPropertyEditorRegistrar());
}

好了,配置完成,运行试试:

public class BeanCreate {

	@Test
public void classPathXml() {
// ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring-test.xml");
ClassPathXmlApplicationContext context = new MyClassPathXmlApplicationContext("classpath:spring-test.xml"); Redwinter redwinter = (Redwinter) context.getBean("redwinter");
System.out.println(redwinter.getEmail()); Redwinter redwinter123456 = (Redwinter) context.getBean("redwinter123456");
System.out.println(redwinter123456.getEmail()); CustomUser bean = context.getBean(CustomUser.class);
System.out.println(bean);
}
}

输出日志:

abc@qq.com
123456-abc@qq.com
CustomUser{name='冬玲记忆', address=Address{province='四川省', city='成都市', town='郫都区'}, date=Tue Apr 19 19:50:20 CST 2022}

说明配置没有问题,输出了想要的结果。

这里有个疑问为什么你知道在编写注册器和解析器的时候需要实现这些类呢?

其实原因很简单,根据源码我们知道Spring默认添加了一个ResourceEditorRegistrar注册器,进去ResourceEditorRegistrar类中发现,他实现了PropertyEditorRegistrar接口,然后重写了registerCustomEditors方法,在这个方法中他添加了很多编辑器:

@Override
public void registerCustomEditors(PropertyEditorRegistry registry) {
ResourceEditor baseEditor = new ResourceEditor(this.resourceLoader, this.propertyResolver);
doRegisterEditor(registry, Resource.class, baseEditor);
doRegisterEditor(registry, ContextResource.class, baseEditor);
doRegisterEditor(registry, InputStream.class, new InputStreamEditor(baseEditor));
doRegisterEditor(registry, InputSource.class, new InputSourceEditor(baseEditor));
doRegisterEditor(registry, File.class, new FileEditor(baseEditor));
doRegisterEditor(registry, Path.class, new PathEditor(baseEditor));
doRegisterEditor(registry, Reader.class, new ReaderEditor(baseEditor));
doRegisterEditor(registry, URL.class, new URLEditor(baseEditor)); ClassLoader classLoader = this.resourceLoader.getClassLoader();
doRegisterEditor(registry, URI.class, new URIEditor(classLoader));
doRegisterEditor(registry, Class.class, new ClassEditor(classLoader));
doRegisterEditor(registry, Class[].class, new ClassArrayEditor(classLoader)); if (this.resourceLoader instanceof ResourcePatternResolver) {
doRegisterEditor(registry, Resource[].class,
new ResourceArrayPropertyEditor((ResourcePatternResolver) this.resourceLoader, this.propertyResolver));
}
}

继续点doRegisterEditor方法发现最终是将这些编辑器加入到了PropertyEditorRegistry接口的默认实现类PropertyEditorRegistrySupport类的customEditors属性中,而且你还在这个类中发现,有很多的编辑器是默认加载进去的:

private void createDefaultEditors() {
this.defaultEditors = new HashMap<>(64); // Simple editors, without parameterization capabilities.
// The JDK does not contain a default editor for any of these target types.
this.defaultEditors.put(Charset.class, new CharsetEditor());
this.defaultEditors.put(Class.class, new ClassEditor());
this.defaultEditors.put(Class[].class, new ClassArrayEditor());
this.defaultEditors.put(Currency.class, new CurrencyEditor());
this.defaultEditors.put(File.class, new FileEditor());
this.defaultEditors.put(InputStream.class, new InputStreamEditor());
this.defaultEditors.put(InputSource.class, new InputSourceEditor());
this.defaultEditors.put(Locale.class, new LocaleEditor());
this.defaultEditors.put(Path.class, new PathEditor());
this.defaultEditors.put(Pattern.class, new PatternEditor());
this.defaultEditors.put(Properties.class, new PropertiesEditor());
this.defaultEditors.put(Reader.class, new ReaderEditor());
this.defaultEditors.put(Resource[].class, new ResourceArrayPropertyEditor());
this.defaultEditors.put(TimeZone.class, new TimeZoneEditor());
this.defaultEditors.put(URI.class, new URIEditor());
this.defaultEditors.put(URL.class, new URLEditor());
this.defaultEditors.put(UUID.class, new UUIDEditor());
this.defaultEditors.put(ZoneId.class, new ZoneIdEditor()); // Default instances of collection editors.
// Can be overridden by registering custom instances of those as custom editors.
this.defaultEditors.put(Collection.class, new CustomCollectionEditor(Collection.class));
this.defaultEditors.put(Set.class, new CustomCollectionEditor(Set.class));
this.defaultEditors.put(SortedSet.class, new CustomCollectionEditor(SortedSet.class));
this.defaultEditors.put(List.class, new CustomCollectionEditor(List.class));
this.defaultEditors.put(SortedMap.class, new CustomMapEditor(SortedMap.class)); // Default editors for primitive arrays.
this.defaultEditors.put(byte[].class, new ByteArrayPropertyEditor());
this.defaultEditors.put(char[].class, new CharArrayPropertyEditor()); // The JDK does not contain a default editor for char!
this.defaultEditors.put(char.class, new CharacterEditor(false));
this.defaultEditors.put(Character.class, new CharacterEditor(true)); // Spring's CustomBooleanEditor accepts more flag values than the JDK's default editor.
this.defaultEditors.put(boolean.class, new CustomBooleanEditor(false));
this.defaultEditors.put(Boolean.class, new CustomBooleanEditor(true)); // The JDK does not contain default editors for number wrapper types!
// Override JDK primitive number editors with our own CustomNumberEditor.
this.defaultEditors.put(byte.class, new CustomNumberEditor(Byte.class, false));
this.defaultEditors.put(Byte.class, new CustomNumberEditor(Byte.class, true));
this.defaultEditors.put(short.class, new CustomNumberEditor(Short.class, false));
this.defaultEditors.put(Short.class, new CustomNumberEditor(Short.class, true));
this.defaultEditors.put(int.class, new CustomNumberEditor(Integer.class, false));
this.defaultEditors.put(Integer.class, new CustomNumberEditor(Integer.class, true));
this.defaultEditors.put(long.class, new CustomNumberEditor(Long.class, false));
this.defaultEditors.put(Long.class, new CustomNumberEditor(Long.class, true));
this.defaultEditors.put(float.class, new CustomNumberEditor(Float.class, false));
this.defaultEditors.put(Float.class, new CustomNumberEditor(Float.class, true));
this.defaultEditors.put(double.class, new CustomNumberEditor(Double.class, false));
this.defaultEditors.put(Double.class, new CustomNumberEditor(Double.class, true));
this.defaultEditors.put(BigDecimal.class, new CustomNumberEditor(BigDecimal.class, true));
this.defaultEditors.put(BigInteger.class, new CustomNumberEditor(BigInteger.class, true)); // Only register config value editors if explicitly requested.
if (this.configValueEditorsActive) {
StringArrayPropertyEditor sae = new StringArrayPropertyEditor();
this.defaultEditors.put(String[].class, sae);
this.defaultEditors.put(short[].class, sae);
this.defaultEditors.put(int[].class, sae);
this.defaultEditors.put(long[].class, sae);
}
}

所以为什么我们在定义Bean属性的时候这些默认的属性会自动帮你转换出来,就是这个原因。那么注册器的编写我们也可以直接实现PropertyEditorRegistrar这个接口,然后重写registerCustomEditors方法把自定义的编辑器加入即可。

那编辑器怎么实现呢?

编辑器的话自然也就很简单了,随便点击一个编辑器看下他是怎么实现的,你就可以实现出来了,最终发现这些编辑器都是继承了PropertyEditorSupport这个类,而PropertyEditorSupport这个类实现了PropertyEditor这个接口,那这么方法实现哪个呢?

不着急看源码:

	private Object doConvertTextValue(@Nullable Object oldValue, String newTextValue, PropertyEditor editor) {
try {
editor.setValue(oldValue);
}
catch (Exception ex) {
if (logger.isDebugEnabled()) {
logger.debug("PropertyEditor [" + editor.getClass().getName() + "] does not support setValue call", ex);
}
// Swallow and proceed.
}
// 调动转换方法,这里就会调用到自定义的属性编辑器中,执行自定义的逻辑转换
editor.setAsText(newTextValue);
return editor.getValue();
}

源码明确写了使用编辑器调用setAsText方法进行新值的转换,然后再去获取getValue得到值,那么说明只需要重写setAsText方法并且将转换的值调用setValue方法即可。

所以我们直接继承PropertyEditorSupport类,然后重写setAsText方法即可实现属性值的解析和转换。

接下来就是分析AbstractApplicationContext#refresh方法的第四个方法和第五个方法,第四个方法postProcessBeanFactory是一个空方法,留给子类实现,第五个方法invokeBeanFactoryPostProcessors 是执行BeanFactoryPostProcessor,这篇就到这里,下一篇文章继续。

Spring 源码(5)BeanFactory使用的准备及自定义属性值解析器的更多相关文章

  1. Spring源码分析——BeanFactory体系之抽象类、类分析(二)

    上一篇分析了BeanFactory体系的2个类,SimpleAliasRegistry和DefaultSingletonBeanRegistry——Spring源码分析——BeanFactory体系之 ...

  2. Spring源码分析——BeanFactory体系之抽象类、类分析(一)

    上一篇介绍了BeanFactory体系的所有接口——Spring源码分析——BeanFactory体系之接口详细分析,本篇就接着介绍BeanFactory体系的抽象类和接口. 一.BeanFactor ...

  3. Spring源码阅读-BeanFactory体系结构分析

    BeanFactory是Spring中非常重要的一个类,搞懂了它,你就知道了bean的初始化和摧毁过程,对于深入理解IOC有很大的帮助. BeanFactory体系结构 首先看一下使用IDEA生成的继 ...

  4. Spring源码分析——BeanFactory体系之接口详细分析

    Spring的BeanFactory的继承体系堪称经典.这是众所周知的!作为Java程序员,不能错过! 前面的博文分析了Spring的Resource资源类Resouce.今天开始分析Spring的I ...

  5. Spring源码学习笔记之基于ClassPathXmlApplicationContext进行bean标签解析

    bean 标签在spring的配置文件中, 是非常重要的一个标签, 即便现在boot项目比较流行, 但是还是有必要理解bean标签的解析流程,有助于我们进行 基于注解配置, 也知道各个标签的作用,以及 ...

  6. Spring源码学习之BeanFactory体系结构

    一.BeanFactory BeanFactory是Spring IOC容器的鼻祖,是IOC容器的基础接口,所有的容器都是从它这里继承实现而来.可见其地位.BeanFactory提供了最基本的IOC容 ...

  7. Spring源码学习

    Spring源码学习--ClassPathXmlApplicationContext(一) spring源码学习--FileSystemXmlApplicationContext(二) spring源 ...

  8. spring源码-aop源码-5.1

    一.aop的源码部分还是有点复杂的,但是为了更好的理解,我这里会省去很多不必要的逻辑实现过程.主要方向还是更好的理解整体代码的实现过程. 二.说明重点:aop的过程主要过程有两点:第一点,发现正确和适 ...

  9. Spring源码系列 — Bean生命周期

    前言 上篇文章中介绍了Spring容器的扩展点,这个是在Bean的创建过程之前执行的逻辑.承接扩展点之后,就是Spring容器的另一个核心:Bean的生命周期过程.这个生命周期过程大致经历了一下的几个 ...

随机推荐

  1. SINAMICS S120的核心控制单元CU320使用教程,电机模块接线

    SINAMICS是西门子公司新一代的驱动产品,它正在逐步取代现有的MASTERDRIVES及SIMODRIVE系列的驱动系统.SINAMICS S120是集V/f控制.矢量控制和伺服控制于一体的多轴驱 ...

  2. Rust-Sqlx极简教程

    简介 sqlx 是 rust 中的一个数据库访问工具.具有以下特点: 异步:原生就支持异步,在并发性高的场合能够得到更好的支持 编译时检查查询:sqlx可以在 cargo build 的时候检查执行s ...

  3. 从0到1搭建k8s集群系列1:安装虚拟机及docker

    前言 本系列文章记录了本人学习k8s集群搭建的过程,从k8s基本组件的安装.到部署mysql服务到k8s集群.部署web项目到k8s集群以及安装可视化界面管理工具kuboard. 因为k8s的组件安装 ...

  4. Rabbitmq安装与部署

    一:安装依赖软件Erlang 安装包otp_src_22.3.tar.gz,下载到部署服务器tar -zxvf解压 mv otp_src_22.3 ./erlang变更文件夹名字 可能需要安装的依赖包 ...

  5. java中的四种引用类型

    为什么需要引用: Java的内存回收不需要程序员负责,JVM会在必要时启动Java GC完成垃圾回收. Java以便我们控制对象的生存周期,提供给了我们四种引用方式,引用强度从强到弱分别为:强引用.软 ...

  6. 机器学习之linear_model (线性回归算法模型)

    1.matplotlib 首先看一下这个静态图绘制模块 静态图形处理 数据分析三剑客 Numpy : 主要为了给pandas提供数据源 pandas : 更重要的数据结构 matplotlib : 静 ...

  7. poi整合springboot超简单入门例子

    1.导入依赖 2.application.properties只需要数据库连接信息就可以 3.目录结构 有个没用的service,请忽略 4.Controller,因为入门列子,所以简单的导出 导入读 ...

  8. gateway聚合swagger3统一管理api文档

    springboot微服务整合swagger3方法很简单,下文会演示.但是在分布式项目中如果每个微服务都需要单独的分开访问获取接口文档就不方便了,本文将详细讲解springcloud gateway网 ...

  9. 判断集合中存在String字符串 或 判断集合中不存在String字符串

    一.使用场景 用于集合中有多个相近的字符,无法使用包含判断 如: 这里如果我想判断以上集合中是否包含"信封件-DE"就会被"信封件-DE2"影响到 毕竟:&qu ...

  10. Netty之非阻塞处理

    Netty 是一个异步的.基于事件驱动的网络应用框架,用以快速开发高性能.高可靠性的网络 IO 程序. 一.异步模型 同步I/O : 需要进程去真正的去操作I/O: 异步I/O:内核在I/O操作完成后 ...