更多文章点击--spring源码分析系列

主要分析内容:

一、InstantiationAwareBeanPostProcessor简述与demo示例

二、InstantiationAwareBeanPostProcessor与BeanPostProcessor对比

三、InstantiationAwareBeanPostProcessor源码分析:注册时机和触发点

(源码基于spring 5.1.3.RELEASE分析)

一、InstantiationAwareBeanPostProcessor简述与demo示例

InstantiationAwareBeanPostProcessor继承自BeanPostProcessor 是spring非常重要的拓展接口,代表这bean的一段生命周期: 实例化(Instantiation)

 public interface InstantiationAwareBeanPostProcessor extends BeanPostProcessor {

     @Nullable
default Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
return null;
} default boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
return true;
} @Nullable
default PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName)
throws BeansException { return null;
} @Deprecated
@Nullable
default PropertyValues postProcessPropertyValues(
PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException { return pvs;
} }

由于InstantiationAwareBeanPostProcessor继承自BeanPostProcessor, 其他接口可以参考spring源码分析系列 (2) spring拓展接口BeanPostProcessor,这里针对多出接口说明一下:

1、postProcessBeforeInstantiation调用时机为bean实例化(Instantiation)之前 如果返回了bean实例, 则会替代原来正常通过target bean生成的bean的流程. 典型的例如aop返回proxy对象. 此时bean的执行流程将会缩短, 只会执行

BeanPostProcessor#postProcessAfterInitialization接口完成初始化。

2、postProcessAfterInstantiation调用时机为bean实例化(Instantiation)之后和任何初始化(Initialization)之前。

3、postProcessProperties调用时机为postProcessAfterInstantiation执行之后并返回true, 返回的PropertyValues将作用于给定bean属性赋值. spring 5.1之后出现以替换@Deprecated标注的postProcessPropertyValues

4、postProcessPropertyValues已经被标注@Deprecated,后续将会被postProcessProperties取代。

示例demo:

 public class InstantiationAwareBeanPostProcessorTest {
private ApplicationContext applicationContext ; @Before
public void beforeApplicationContext(){
/**
* ApplicationContext 自动注册 BeanPostProcessor、InstantiationAwareBeanPostProcessor、BeanFactoryPostProcessor
* 不需要手动注册
* */
applicationContext = new ClassPathXmlApplicationContext("ioc-InstantiationAwareBeanPostProcessor.xml") ;
} @Test
public void test(){
Bean bean = applicationContext.getBean("bean", Bean.class) ;
System.out.println(bean);
} @After
public void after(){
((ClassPathXmlApplicationContext)applicationContext).close();
}
}

InstantiationAwareBeanPostProcessorTest.java

 public class LogicInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor {

     @Override
public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
System.out.print("beanName:"+beanName+"执行..postProcessAfterInstantiation\n"); // 会影响postProcessProperties 是否执行,返回false不执行
return true;
} @Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
System.out.print("beanName:"+beanName+"执行..postProcessBeforeInstantiation\n");
if(beanClass == Bean.class){ //利用 其 生成动态代理
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(beanClass);
enhancer.setCallback(new BeanMethodInterceptor());
Bean bean = (Bean)enhancer.create();
System.out.print("返回动态代理\n");
return bean ;
}
return null ;
} @Override
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {
System.out.print("beanName:"+beanName+"执行..postProcessProperties\n");
return pvs;
} //************************************** BeanPostProcessor ********************************************** @Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.print("beanName:"+beanName+"执行..postProcessAfterInitialization\n");
return bean;
} @Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.print("beanName:"+beanName+"执行..postProcessBeforeInitialization\n");
return bean;
}
}

LogicInstantiationAwareBeanPostProcessor.java

 public class BeanMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("目标方法前:" + method+"\n");
Object object = methodProxy.invokeSuper(o, objects);
System.out.println("目标方法后:" + method+"\n");
return object;
}
}

BeanMethodInterceptor.java

 <?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:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"> <bean id="bean" class="com.nancy.ioc.Bean">
<property name="name" value="zhouxiaoxing"/>
</bean> <bean id="logicInstantiationAwareBeanPostProcessor" class="com.nancy.ioc.InstantiationAwareBeanPostProcessor.LogicInstantiationAwareBeanPostProcessor"/> </beans>

ioc-InstantiationAwareBeanPostProcessor.xml

 public class Bean {
public Bean(){ }
public Bean(String name){
System.out.println("构造函数被调用啦");
this.name = name ;
} private String name ; public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} @Override
public String toString() {
return "Bean{" +
"name='" + name + '\'' +
'}';
}
}

Bean.java

运行结果如下:  由于postProcessBeforeInstantiation通过cglib生成代理,  所以直接执行BeanPostProcessor#postProcessAfterInitialization接口完成初始化。bean生命周期缩短

beanName:bean执行..postProcessBeforeInstantiation
返回动态代理
beanName:bean执行..postProcessAfterInitialization
目标方法前:public java.lang.String com.nancy.ioc.Bean.toString() 目标方法后:public java.lang.String com.nancy.ioc.Bean.toString() Bean{name='null'}

修改LogicInstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation如下 :

     @Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
System.out.print("beanName:"+beanName+"执行..postProcessBeforeInstantiation\n");
return null ;
} @Override
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {
System.out.print("beanName:"+beanName+"执行..postProcessProperties\n"); if(bean instanceof Bean){
//修改bean中name 的属性值
PropertyValue value = pvs.getPropertyValue("name");
System.out.print("修改之前 name 的value是:"+value.getValue()+"\n");
value.setConvertedValue("我修改啦");
return pvs;
}
return pvs;
}

运行结果如下:  由于postProcessBeforeInstantiation返回null 并 postProcessAfterInstantiation返回true 所以执行会postProcessProperties。此时bean生命周期正常process

beanName:bean执行..postProcessBeforeInstantiation
beanName:bean执行..postProcessAfterInstantiation
beanName:bean执行..postProcessProperties
修改之前 name 的value是:TypedStringValue: value [zhouxiaoxing], target type [null]
beanName:bean执行..postProcessBeforeInitialization
beanName:bean执行..postProcessAfterInitialization
Bean{name='我修改啦'}

详细demo示例可以参考: https://gitee.com/zhouxiaoxing91/learning-src/tree/master

二、InstantiationAwareBeanPostProcessor与BeanPostProcessor对比

1、BeanPostProcessor 执行时机为bean初始化(Initialization)阶段,日常可以拓展该接口对bean初始化进行定制化处理。

2、InstantiationAwareBeanPostProcessor 执行时机bean实例化(Instantiation)阶段,典型用于替换bean默认创建方式,例如aop通过拓展接口生成代理对应,主要用于基础框架层面。如果日常业务中需要拓展该,spring推荐使用适配器类InstantiationAwareBeanPostProcessorAdapter。

3、所有bean创建都会进行回调。

三、InstantiationAwareBeanPostProcessor源码分析:注册时机和触发点

1、由于InstantiationAwareBeanPostProcessor实质也是BeanPostProcessor接口,register时机是一致的,可参考:spring源码分析系列 (2) spring拓展接口BeanPostProcessor 。

2、这里着重分析接口触发的时机,跟BeanPostProcessor一样触发入口从AbstractAutowireCapableBeanFactory#createBean开始 :

   /**
* Central method of this class: creates a bean instance,
* populates the bean instance, applies post-processors, etc.
* @see #doCreateBean
*/
@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException { // 省略...... try {
/**
* postProcessorsBeforeInstantiation 触发入口
*/
// Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
if (bean != null) {
return bean;
}
}
catch (Throwable ex) {
throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,
"BeanPostProcessor before instantiation of bean failed", ex);
} try {
/**
* postProcessAfterInstantiation、postProcessProperties 触发入口
*/
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
if (logger.isTraceEnabled()) {
logger.trace("Finished creating instance of bean '" + beanName + "'");
}
return beanInstance;
}
catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
// A previously detected exception with proper bean creation context already,
// or illegal singleton state to be communicated up to DefaultSingletonBeanRegistry.
throw ex;
}
catch (Throwable ex) {
throw new BeanCreationException(
mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex);
}
}

2.1、跟进AbstractAutowireCapableBeanFactory#resolveBeforeInstantiation, 分析postProcessorsBeforeInstantiation执行时机 :

   /**
* Apply before-instantiation post-processors, resolving whether there is a
* before-instantiation shortcut for the specified bean.
* @param beanName the name of the bean
* @param mbd the bean definition for the bean
* @return the shortcut-determined bean instance, or {@code null} if none
*/
@Nullable
protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
Object bean = null;
if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {
// Make sure bean class is actually resolved at this point.
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
Class<?> targetType = determineTargetType(beanName, mbd);
if (targetType != null) {
/**
* 回调beanPostProcessorsBeforeInstantiation实例化,如果返回bean非null则直接执行
* beanPostProcessorsAfterInitialization进行实例初始化
*/
bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
if (bean != null) {
bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
}
}
}
mbd.beforeInstantiationResolved = (bean != null);
}
return bean;
} /**
* Apply InstantiationAwareBeanPostProcessors to the specified bean definition
* (by class and name), invoking their {@code postProcessBeforeInstantiation} methods.
* <p>Any returned object will be used as the bean instead of actually instantiating
* the target bean. A {@code null} return value from the post-processor will
* result in the target bean being instantiated.
* @param beanClass the class of the bean to be instantiated
* @param beanName the name of the bean
* @return the bean object to use instead of a default instance of the target bean, or {@code null}
* @see InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation
*/
@Nullable
protected Object applyBeanPostProcessorsBeforeInstantiation(Class<?> beanClass, String beanName) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
/**
* 只要其中一个postProcessBeforeInstantiation返回实例bean即结束回调,
* 这个bean将会直接返回给bean容器管理
*/
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
Object result = ibp.postProcessBeforeInstantiation(beanClass, beanName);
if (result != null) {
return result;
}
}
}
return null;
} @Override
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
throws BeansException { Object result = existingBean;
for (BeanPostProcessor processor : getBeanPostProcessors()) {
Object current = processor.postProcessAfterInitialization(result, beanName);
if (current == null) {
return result;
}
result = current;
}
return result;
}

2、跟进AbstractAutowireCapableBeanFactory#doCreateBean, 分析postProcessAfterInstantiation、postProcessProperties 执行时机 :

  /**
* Actually create the specified bean. Pre-creation processing has already happened
* at this point, e.g. checking {@code postProcessBeforeInstantiation} callbacks.
* <p>Differentiates between default bean instantiation, use of a
* factory method, and autowiring a constructor.
* @param beanName the name of the bean
* @param mbd the merged bean definition for the bean
* @param args explicit arguments to use for constructor or factory method invocation
* @return a new instance of the bean
* @throws BeanCreationException if the bean could not be created
* @see #instantiateBean
* @see #instantiateUsingFactoryMethod
* @see #autowireConstructor
*/
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException { // 省略...... // Initialize the bean instance.
Object exposedObject = bean;
try {
/** 依据bean definition 完成bean属性赋值 */
populateBean(beanName, mbd, instanceWrapper);
/** 执行bean初始化 */
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
catch (Throwable ex) {
if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
throw (BeanCreationException) ex;
}
else {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
}
} // 省略...... return exposedObject;
}
跟进AbstractAutowireCapableBeanFactory#populateBean
 /**
* Populate the bean instance in the given BeanWrapper with the property values
* from the bean definition.
* @param beanName the name of the bean
* @param mbd the bean definition for the bean
* @param bw the BeanWrapper with bean instance
*/
@SuppressWarnings("deprecation") // for postProcessPropertyValues
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
if (bw == null) {
if (mbd.hasPropertyValues()) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance");
}
else {
// Skip property population phase for null instance.
return;
}
} // Give any InstantiationAwareBeanPostProcessors the opportunity to modify the
// state of the bean before properties are set. This can be used, for example,
// to support styles of field injection.
boolean continueWithPropertyPopulation = true; /**
* 满足两个要求:
* 1、BeanDefinition为应用程序bean,而非基础框架bean信息。
* 2、注册过InstantiationAwareBeanPostProcessor类型接口,上文有提到这个标志位。
* 3、注册了多个接口时,只要其中一个postProcessAfterInstantiation返回false,即停止后续执行。
*/
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
continueWithPropertyPopulation = false;
break;
}
}
}
} /**
* 判定是否执行以下流程,受到postProcessAfterInstantiation返回结果影响
*/
if (!continueWithPropertyPopulation) {
return;
} PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null); /**
* ioc依赖注入
*/
if (mbd.getResolvedAutowireMode() == AUTOWIRE_BY_NAME || mbd.getResolvedAutowireMode() == AUTOWIRE_BY_TYPE) {
MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
// Add property values based on autowire by name if applicable.
if (mbd.getResolvedAutowireMode() == AUTOWIRE_BY_NAME) {
autowireByName(beanName, mbd, bw, newPvs);
}
// Add property values based on autowire by type if applicable.
if (mbd.getResolvedAutowireMode() == AUTOWIRE_BY_TYPE) {
autowireByType(beanName, mbd, bw, newPvs);
}
pvs = newPvs;
} /**
* InstantiationAwareBeanPostProcessor标志位 和 依赖注入检查标志位
*/
boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
boolean needsDepCheck = (mbd.getDependencyCheck() != AbstractBeanDefinition.DEPENDENCY_CHECK_NONE); PropertyDescriptor[] filteredPds = null;
if (hasInstAwareBpps) {
if (pvs == null) {
pvs = mbd.getPropertyValues();
} /**
* 1、优先回调postProcessProperties. spring-5.1之后新增回调接口
* 2、再回调postProcessPropertyValues,一旦返回null即结束. spring-5.1之前逻辑
*/
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
if (pvsToUse == null) {
if (filteredPds == null) {
filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
}
pvsToUse = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
if (pvsToUse == null) {
return;
}
}
pvs = pvsToUse;
}
}
} /**
* 依赖注入校验
*/
if (needsDepCheck) {
if (filteredPds == null) {
filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
}
checkDependencies(beanName, mbd, filteredPds, pvs);
} /**
* 此时会将上述处理后的PropertyValues应用于bean属性
*/
if (pvs != null) {
applyPropertyValues(beanName, mbd, bw, pvs);
}
}
 

总结一下:

  • InstantiationAwareBeanPostProcessor的触发入口从AbstractAutowireCapableBeanFactory#createBean开始。
  • bean实例化之前会检测是否存在该类型的接口,并触发前置postProcessBeforeInstantiation。注册多个实例时会依次执行回调,任何一个返回非null则直接执行BeanPostProcessor#postProcessAfterInitialization完成初始化。返回的bean直接返回容器,生命周期缩短。
  • 后置postProcessAfterInstantiation会在实例化之后,依赖注入和初始化方法之前。注册多个接口只要其中一个返回false,即停止后续执行。 返回结果会影响后续执行流程,通过此定制化bean属性注入等操作。
  • 优先回调postProcessProperties,spring-5.1之后新增回调接口 用以替代标注过时的postProcessPropertyValues方法。
  • InstantiationAwareBeanPostProcessor设计主要给基础性框架使用,日常应用spring推荐使用适配器类InstantiationAwareBeanPostProcessorAdapter。

 

spring源码分析系列 (3) spring拓展接口InstantiationAwareBeanPostProcessor的更多相关文章

  1. spring源码分析系列 (5) spring BeanFactoryPostProcessor拓展类PropertyPlaceholderConfigurer、PropertySourcesPlaceholderConfigurer解析

    更多文章点击--spring源码分析系列 主要分析内容: 1.拓展类简述: 拓展类使用demo和自定义替换符号 2.继承图UML解析和源码分析 (源码基于spring 5.1.3.RELEASE分析) ...

  2. spring源码分析系列 (1) spring拓展接口BeanFactoryPostProcessor、BeanDefinitionRegistryPostProcessor

    更多文章点击--spring源码分析系列 主要分析内容: 一.BeanFactoryPostProcessor.BeanDefinitionRegistryPostProcessor简述与demo示例 ...

  3. spring源码分析系列 (2) spring拓展接口BeanPostProcessor

    Spring更多分析--spring源码分析系列 主要分析内容: 一.BeanPostProcessor简述与demo示例 二.BeanPostProcessor源码分析:注册时机和触发点 (源码基于 ...

  4. 【Spring源码分析系列】ApplicationContext 相关接口架构分析

    [原创文章,转载请注明出处][本文地址]http://www.cnblogs.com/zffenger/p/5813470.html 在使用Spring的时候,我们经常需要先得到一个Applicati ...

  5. spring源码分析系列

    spring源码分析系列 (1) spring拓展接口BeanFactoryPostProcessor.BeanDefinitionRegistryPostProcessor spring源码分析系列 ...

  6. spring源码分析系列 (8) FactoryBean工厂类机制

    更多文章点击--spring源码分析系列 1.FactoryBean设计目的以及使用 2.FactoryBean工厂类机制运行机制分析 1.FactoryBean设计目的以及使用 FactoryBea ...

  7. spring源码分析系列 (15) 设计模式解析

    spring是目前使用最为广泛的Java框架之一.虽然spring最为核心是IOC和AOP,其中代码实现中很多设计模式得以应用,代码看起来简洁流畅,在日常的软件设计中很值得借鉴.以下是对一些设计模式的 ...

  8. 【Spring源码分析系列】启动component-scan类扫描加载过程

    原文地址:http://blog.csdn.net/xieyuooo/article/details/9089441/ 在spring 3.0以上大家都一般会配置一个Servelet,如下所示: &l ...

  9. spring源码分析系列3:BeanFactory核心容器的研究

    目录 @(spring源码分析系列3:核心容器的研究) 在讲容器之前,再明确一下知识点. BeanDefinition是Bean在容器的描述.BeanDefinition与Bean不是一个东西. Be ...

随机推荐

  1. ajax使用异步问题

    使用$.ajax(...)中 async:默认为true,表示异步,具体描述,请查看别的文档 var formData = new FormData($("#dataForm")[ ...

  2. visual studio 2017 installer 安装包的安装必备组件设置

    visual studio installer 2017  安装包的安装必备组件设置,默认设置只有net frmwork 4.6.1,如下图 这个时候如果打包安装,那么打出来的包一定需要先安装4.6. ...

  3. laravel job 与 event 的区别

    job 是异步执行.适合耗时长的任务.例如,批量发送邮件,短信. event 是在 request 的生命周期内执行.适合耗时短的操作.例如,更改数据字段状态. 但是, event 的好处是,可以复用 ...

  4. 性能测试八:jmeter进阶之beanshell

    * BeanShell是一种完全符合Java语法规范的脚本语言,并且又拥有自己的一些语法和方法; * BeanShell是一种松散类型的脚本语言(这点和JS类似); * BeanShell是用Java ...

  5. Jmeter NonGUI模式

    一般情况下我们都是在NonGUI模式下运行jmeter.这样做有两个好处 节省系统资源,能够产生更大的负载 可以通过命令行参数对测试场景进行更精细的配置 示例 创建luzhi.jmx脚本 jmeter ...

  6. POJ 2503 单词映射(map)

    Sample Input dog ogdaycat atcaypig igpayfroot ootfrayloops oopslay atcayittenkayoopslaySample Output ...

  7. canvas简单下雨特效

    前面做了两个简单的效果,这次就来个下雨的效果 思路简单的说一下 随机在屏幕中的位置画雨滴,moveTo(x,y) 雨滴的长度就是lineTo(x,y+len) 每次重新绘制页面,就能达到下雨的效果了 ...

  8. noip2016 天天爱跑步

    没看过正解..应该是些乱七八糟想不出来的东西 解法1: 首先,必须要做的是将每条路径拆成2个直的路径 那么对于那条从深度大的到深度小的路径 dep[x]-dep[y]应该等于观察时间 那么就可以在这些 ...

  9. Python全局解释器锁

    超过十年以上,没有比解释器全局锁(GIL)让Python新手和专家更有挫折感或者更有好奇心.    Python的底层 要理解GIL的含义,我们需要从Python的基础讲起.像C++这样的语言是编译型 ...

  10. fmod()函数 (对浮点数取模)

    头文件:#include <math.h> fmod() 用来对浮点数进行取模(求余),其原型为:    double fmod (double x); 设返回值为 ret,那么 x = ...