文章目录

  • InstantiationAwareBeanPostProcessor介绍
    • InstantiationAwareBeanPostProcessor执行流程
  • InstantiationAwareBeanPostProcessor使用
    • Book
    • Pen
    • BookProxyInstantiationAwareBeanPostProcessor
    • main
    • 全量代码
    • 执行结果
  • 源码解析
  • 总结

InstantiationAwareBeanPostProcessor介绍

InstantiationAwareBeanPostProcessor接口扩展了BeanPostProcessor子接口,提供了Bean被实例化之前、Bean实例化之后、Bean属性装配前更细粒度控制Bean创建流程的处理。

由于InstantiationAwareBeanPostProcessor扩展了BeanPostProcessor接口,所以该篇文章部分内容会与BeanPostProcessor接口的流程重合;尽管如此,也建议在看该篇文章前,先阅读关于BeanPostProcessor接口的详细介绍与源码解析:Spring之Bean后处理器——BeanPostProcessor的使用与源码解析

InstantiationAwareBeanPostProcessor接口比BeanPostProcessor新增了如下方法:

方法 描述
Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) Bean实例化之前,调用此方法;该方法传入目标Bean类型与BeanName;该方法可以返回一个该Bean类型的对象,或对该Bean的一个代理对象;当该方法返回了实例化对象后,后续的所有Bean实例化与初始化的动作将不再进行。只会调用后续的BeanPostProcessor#postProcessAfterInitialization()方法。
boolean postProcessAfterInstantiation(Object bean, String beanName) Bean实例化之后,调用此方法;该方法传入还没有装配属性的Bean对象以及BeanName。 如果该方法返回true,则将跳过后续的属性装配动作,一般应该返回true
PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) Bean实例化之后,装配属性之前,调用此方法;该方法传入在配置期间所配的PropertyValues以及BeanName。 该方法返回的PropertyValues将最终装配到Bean对象中。

InstantiationAwareBeanPostProcessor执行流程

InstantiationAwareBeanPostProcessor流程如下:

  • 执行Bean实例化之前,执行postProcessBeforeInstantiation();
  • 执行Bean实例化之后执行postProcessAfterInstantiation();
  • 执行Bean属性装配前执行postProcessProperties();
  • Bean属性装配后执行postProcessBeforeInitialization();
  • Bean的Init相关初始化完毕后执行postProcessAfterInitialization();

下图能比较好的反应出过程:

InstantiationAwareBeanPostProcessor使用

接下来写个例子。全量代码会在最后贴出来,直接CV运行即可。

Book

Book类,它实现了InitializingBean接口,其afterPropertiesSet()方法仅输出一行信息用来记录执行流程;还有一个customInit()方法使其加入到Springinit-method的执行过程;该类还有一个show()方法,用来在后面的BookProxyInstantiationAwareBeanPostProcessor处理器中对原始的Book对象生成一个代理类,该代理类将对show()方法进行增强处理,用来让咱们的BeanPostProcessor能实际干点事儿。
但是由于BookProxyInstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation()返回了代理对象,所以后续Book后续的初始化都不会在进行,所以其afterPropertiesSet()customInit()方法也不会再调用。

public class Book implements InitializingBean {private String name;private String author;public String getName() {return name;}public void setName(String name) {this.name = name;}public String getAuthor() {return author;}public void setAuthor(String author) {this.author = author;}public void show() {System.out.printf("book#show():%s\n", this);}public void customInit() {System.out.printf("%-85s:%s{%s}%n", "------Book#customInit", this.getClass().getSimpleName(), this);}@Overridepublic String toString() {return "Book{" +"name='" + name + '\'' +", author='" + author + '\'' +'}';}@Overridepublic void afterPropertiesSet() throws Exception {System.out.printf("%-85s:%s{%s}%n", "------Book#afterPropertiesSet", this.getClass().getSimpleName(), this);}
}

Pen

Pen类与Book类一样,它实现了InitializingBean接口,其afterPropertiesSet()方法仅输出一行信息用来记录执行流程;还有一个customInit()方法使其加入到Springinit-method的执行过程;

public class Pen implements InitializingBean {private String name;public String getName() {return name;}public void setName(String name) {this.name = name;}@Overridepublic String toString() {return "Pen{" +"name='" + name + '\'' +'}';}public void customInit() {System.out.printf("%-85s:%s{%s}%n", "------Pen#customInit", this.getClass().getSimpleName(), this);}@Overridepublic void afterPropertiesSet() throws Exception {System.out.printf("%-85s:%s{%s}%n", "------Pen#afterPropertiesSet", this.getClass().getSimpleName(), this);}
}

BookProxyInstantiationAwareBeanPostProcessor

BookProxyInstantiationAwareBeanPostProcessor实现了InstantiationAwareBeanPostProcessor接口;在其postProcessBeforeInstantiation()方法中对Book类型进行判断,如果是Book类型则使用CGLIB生成其代理,用来增强Book#show()方法,在其前后各打印一句话。其余方法都仅输出一条信息,用来描述其执行过程。

public class BookProxyInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor {@Overridepublic Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {System.out.printf("%-85s:%s%n", "------BookProxyInstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation", beanClass.getSimpleName());//判断传入的beanClass是否是Book类型if (Book.class.equals(beanClass)) {System.out.printf("\033[1;33m%-85s:对[%s]的\033[1;31mshow()\033[1;33m方法进行增强 \033[0m%n", "------BookProxyInstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation", beanClass.getSimpleName());//创建Book类的代理Enhancer enhancer = new Enhancer();enhancer.setSuperclass(Book.class);enhancer.setCallback((MethodInterceptor) (obj, method, args, methodProxy) -> {//如果不是show()方法则直接调用返回if (!method.equals(Book.class.getMethod("show"))) {return methodProxy.invokeSuper(obj, args);}//对show()做增强处理System.out.printf("\033[1;33m%-70s\033[0m%n", "Book#show()执行前");Object result = methodProxy.invokeSuper(obj, args);System.out.printf("\033[1;33m%-70s\033[0m%n", "Book#show()执行后");return result;});Book book = (Book) enhancer.create();book.setName("《非暴力沟通》");book.setAuthor("马歇尔·卢森堡");return book;}return null;}@Overridepublic boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {System.out.printf("%-85s:%s{%s}%n", "------BookProxyInstantiationAwareBeanPostProcessor#postProcessAfterInstantiation", bean.getClass().getSimpleName(), bean);return true;}@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {System.out.printf("%-85s:%s{%s}%n", "------BookProxyInstantiationAwareBeanPostProcessor#postProcessBeforeInitialization", bean.getClass().getSimpleName(), bean);return bean;}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {System.out.printf("%-85s:%s{%s}%n", "------BookProxyInstantiationAwareBeanPostProcessor#postProcessAfterInitialization", bean.getClass().getSimpleName(), bean);return bean;}@Overridepublic PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {System.out.printf("%-85s:%s{%s}%n", "------BookProxyInstantiationAwareBeanPostProcessor#postProcessProperties", bean.getClass().getSimpleName(), bean);pvs.forEach((name) -> {System.out.printf("%85s:%s {%s=%s}%n", "pvs", bean.getClass().getSimpleName(), name.getName(), name.getValue());});return pvs;}
}

main

定义一个Book类型与Pen类型的GenericBeanDefinition,将BookPencustomInit()添加到BeanDefinition中,并设置PenMutablePropertyValues用于初始化后的属性装配。
同时将BookProxyInstantiationAwareBeanPostProcessor注册到容器中。

public static void main(String[] args) {GenericApplicationContext context = new GenericApplicationContext();//注册Book的BeanDefinitionGenericBeanDefinition bookBeanDefinition = new GenericBeanDefinition();bookBeanDefinition.setBeanClass(Book.class);bookBeanDefinition.setScope(BeanDefinition.SCOPE_SINGLETON);//指定为单例模式bookBeanDefinition.setInitMethodName("customInit");//设置初始化方法context.registerBeanDefinition("book", bookBeanDefinition);//注册Pen的BeanDefinitionGenericBeanDefinition penBeanDefinition = new GenericBeanDefinition();penBeanDefinition.setBeanClass(Pen.class);penBeanDefinition.setScope(BeanDefinition.SCOPE_SINGLETON);//指定为单例模式penBeanDefinition.setInitMethodName("customInit");//设置初始化方法//设置pen属性penBeanDefinition.setPropertyValues(new MutablePropertyValues().addPropertyValue(new PropertyValue("name", "英雄钢笔")));context.registerBeanDefinition("pen", penBeanDefinition);//注册BeanDefinition//注册BookProxyInstantiationAwareBeanPostProcessor后处理器context.registerBean(BookProxyInstantiationAwareBeanPostProcessor.class);//刷新容器context.refresh();//获取Book的bean对象,调用show()方法context.getBean("book", Book.class).show();
}

全量代码

package com.baiyang.beanpostprocessor.instantiationaware;import org.springframework.beans.BeansException;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.PropertyValue;
import org.springframework.beans.PropertyValues;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.context.support.GenericApplicationContext;import java.util.concurrent.atomic.AtomicInteger;public class BookProxyInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor {public static AtomicInteger COUNT = new AtomicInteger();@Overridepublic Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {System.out.printf("%-85s:%s%n", COUNT.addAndGet(1) + "------BookProxyInstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation", beanClass.getSimpleName());//判断传入的beanClass是否是Book类型if (Book.class.equals(beanClass)) {System.out.printf("\033[1;33m%-85s:对[%s]的\033[1;31mshow()\033[1;33m方法进行增强 \033[0m%n", COUNT.addAndGet(1) + "------BookProxyInstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation", beanClass.getSimpleName());//创建Book类的代理Enhancer enhancer = new Enhancer();enhancer.setSuperclass(Book.class);enhancer.setCallback((MethodInterceptor) (obj, method, args, methodProxy) -> {//如果不是show()方法则直接调用返回if (!method.equals(Book.class.getMethod("show"))) {return methodProxy.invokeSuper(obj, args);}//对show()做增强处理System.out.printf("\033[1;33m%-70s\033[0m%n", "Book#show()执行前");Object result = methodProxy.invokeSuper(obj, args);System.out.printf("\033[1;33m%-70s\033[0m%n", "Book#show()执行后");return result;});Book book = (Book) enhancer.create();book.setName("《非暴力沟通》");book.setAuthor("马歇尔·卢森堡");return book;}return null;}@Overridepublic boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {System.out.printf("%-85s:%s{%s}%n", COUNT.addAndGet(1) + "------BookProxyInstantiationAwareBeanPostProcessor#postProcessAfterInstantiation", bean.getClass().getSimpleName(), bean);return true;}@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {System.out.printf("%-85s:%s{%s}%n", COUNT.addAndGet(1) + "------BookProxyInstantiationAwareBeanPostProcessor#postProcessBeforeInitialization", bean.getClass().getSimpleName(), bean);return bean;}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {System.out.printf("%-85s:%s{%s}%n", COUNT.addAndGet(1) + "------BookProxyInstantiationAwareBeanPostProcessor#postProcessAfterInitialization", bean.getClass().getSimpleName(), bean);return bean;}@Overridepublic PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {System.out.printf("%-85s:%s{%s}%n", COUNT.addAndGet(1) + "------BookProxyInstantiationAwareBeanPostProcessor#postProcessProperties", bean.getClass().getSimpleName(), bean);pvs.forEach((name) -> {System.out.printf("%85s:%s {%s=%s}%n", "pvs", bean.getClass().getSimpleName(), name.getName(), name.getValue());});return pvs;}public static void main(String[] args) {GenericApplicationContext context = new GenericApplicationContext();//注册Book的BeanDefinitionGenericBeanDefinition bookBeanDefinition = new GenericBeanDefinition();bookBeanDefinition.setBeanClass(Book.class);bookBeanDefinition.setScope(BeanDefinition.SCOPE_SINGLETON);//指定为单例模式bookBeanDefinition.setInitMethodName("customInit");//设置初始化方法context.registerBeanDefinition("book", bookBeanDefinition);//注册Pen的BeanDefinitionGenericBeanDefinition penBeanDefinition = new GenericBeanDefinition();penBeanDefinition.setBeanClass(Pen.class);penBeanDefinition.setScope(BeanDefinition.SCOPE_SINGLETON);//指定为单例模式penBeanDefinition.setInitMethodName("customInit");//设置初始化方法//设置pen属性penBeanDefinition.setPropertyValues(new MutablePropertyValues().addPropertyValue(new PropertyValue("name", "英雄钢笔")));context.registerBeanDefinition("pen", penBeanDefinition);//注册BeanDefinition//注册BookProxyInstantiationAwareBeanPostProcessor后处理器context.registerBean(BookProxyInstantiationAwareBeanPostProcessor.class);//刷新容器context.refresh();//获取Book的bean对象,调用show()方法context.getBean("book", Book.class).show();}static class Book implements InitializingBean {private String name;private String author;public String getName() {return name;}public void setName(String name) {this.name = name;}public String getAuthor() {return author;}public void setAuthor(String author) {this.author = author;}public void show() {System.out.printf("book#show():%s\n", this);}public void customInit() {System.out.printf("%-85s:%s{%s}%n", COUNT.addAndGet(1) + "------Book#customInit", this.getClass().getSimpleName(), this);}@Overridepublic String toString() {return "Book{" +"name='" + name + '\'' +", author='" + author + '\'' +'}';}@Overridepublic void afterPropertiesSet() throws Exception {System.out.printf("%-85s:%s{%s}%n", COUNT.addAndGet(1) + "------Book#afterPropertiesSet", this.getClass().getSimpleName(), this);}}static class Pen implements InitializingBean {private String name;public String getName() {return name;}public void setName(String name) {this.name = name;}@Overridepublic String toString() {return "Pen{" +"name='" + name + '\'' +'}';}public void customInit() {System.out.printf("%-85s:%s{%s}%n", COUNT.addAndGet(1) + "------Pen#customInit", this.getClass().getSimpleName(), this);}@Overridepublic void afterPropertiesSet() throws Exception {System.out.printf("%-85s:%s{%s}%n", COUNT.addAndGet(1) + "------Pen#afterPropertiesSet", this.getClass().getSimpleName(), this);}}
}

执行结果


从执行结果即可反应出InstantiationAwareBeanPostProcessor的执行流程:

1~3行的结果是加载Book的输出,由于BookProxyInstantiationAwareBeanPostProcessor在执行postProcessBeforeInstantiation方法之后返回了Book的代理对象,所以后续对Book的所有初始化将不再继续,调用了BeanDefinition#postProcessAfterInitialization之后就直接返回注册了。
4~10行的输出结果可以看出,Pen类的加载,由于没有任何InstantiationAwareBeanPostProcessor对其做任何处理,所以走完了所有的常规Bean加载流程。
最后三行输出执行了从容器中获取的book对象的show()方法,可以看出我们通过BookProxyInstantiationAwareBeanPostProcessor成功的改变了原本注入到容器中的Book对象,并成功对其show()方法进行了增强处理。

源码解析

spring版本:5.2.9

该节源码解析仅仅只讲关键的与InstantiationAwareBeanPostProcessor相关的位置。如果想要知其与InstantiationAwareBeanPostProcessor前后的细节,可以针对本节的所讲述的位置自行Debug进入详细阅读。

首先是Spring容器的经典入口:refresh()

然后调用AbstractApplicationContext所持有的实际BeanFactory(DefaultListableBeanFactory)句柄的preInstantiateSingletons()做Bean容器的初始化工作。


org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons:
bean的创建流程:getBean()->doGetBean()->createBean()->doCreateBean()
我们直接定位到createBean()


从上面的流程可以看出,当执行了resolveBeforeInstantiation(beanName,mbdToUse)方法后,有返回bean对象,则直接return出去了。
如果没有返回bean对象,则会执行doCreateBean()进行真正的常规创建流程。

在本文章的例子中,初始化book对象的流程中BookProxyInstantiationAwareBeanPostProcessorpostProcessBeforeInstantiation()返回了book的代理对象,所以在上面的流程中,直接返回出去了,没有执行doCreateBean()方法。

接下进入resolveBeforeInstantiation(beanName,mbdToUse)

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsBeforeInstantiation

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsAfterInitialization

createBean()将对象返回出去,一路回到getSingleton():
org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, org.springframework.beans.factory.ObjectFactory<?>)

可以看到此时,已经将一个经过了InstantiationAwareBeanPostProcessor处理的Bean对象注册到了BeanFactory中(在本例子里面是我们的BookProxyInstantiationAwareBeanPostProcessor创建的Book对象的代理类)。


经过以上流程可以看出,当InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation()如果返回了对象的话,那么Bean的创建流程将会直接结束返回该对象。


接下来讲InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation()不返回对象的Bean创建流程,在该例子里面是Pen的创建流程:

上面的流程中,由于执行了BookProxyInstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation()没有返回对象,所以会执行后面的doCreateBean()来创建对象。

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean

在上面的流程中,先通过反射创建了对象,然后调用populateBean()进行属性装配,属性装配后,执行Bean的初始化流程,包含了BeanPostProcessor的前后置处理、init-method相关处理。

我们知道,在InstantiationAwareBeanPostProcessor中目前还只执行了postProcessBeforeInstantiation(),还有剩下postProcessAfterInstantiation()postProcessProperties()postProcessBeforeInitialization()postProcessAfterInitialization()没有执行。

我们先看populateBean()中的逻辑:
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#populateBean

从上面的流程可以看出,populateBean()的流程是先调用InstantiationAwareBeanPostProcessor#postProcessAfterInstantiation(),然后调用其InstantiationAwareBeanPostProcessor#postProcessPropertyValues(),最后进行属性的装配工作。
此时和我们前面描述的执行流程保持一致。


接下来是postProcessBeforeInitialization()postProcessAfterInitialization()的执行还没有做。
返回到上层

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean(java.lang.String, java.lang.Object, org.springframework.beans.factory.support.RootBeanDefinition):

从上面的initializeBean()的流程可以看到我们首先调用了BeanPostProcessor#postProcessBeforeInitialization(),然后执行了invokeInitMethods()来处理bean的实现的InitializingBean接口的afterPropertiesSet()与自定义init-method;最后执行BeanPostProcessor#postProcessAfterInitialization();

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsBeforeInitialization


org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#invokeInitMethods

从上面的invokeInitMethods()可以看到包含了InitializingBean接口的处理与init-method的方法处理流程;
init-method方法最终会通过反射执行。

最后就是BeanPostProcessor#postProcessAfterInitialization()方法的执行:

至此,所有的InstantiationAwareBeanPostProcessor接口涉及到的流程都执行完毕,与前面讲述的流程一样,最后就是返回到getSingleton()将最后最终的对象注册到容器中。

总结

以上通过围绕InstantiationAwareBeanPostProcessor创建一个对指定Bean对象的代理对象,对其特定方法进行代理增强的例子,来介绍了InstantiationAwareBeanPostProcessor的应用,以及描述了InstantiationAwareBeanPostProcessor的"短路"会控制到后续常规Bean的创建流程。
同时,通过对源码的浅析来对InstantiationAwareBeanPostProcessor的执行前后流程进行了讲解。

Spring之Bean后处理器——InstantiationAwareBeanPostProcessor的使用与源码解析相关推荐

  1. Spring 注解面面通 之 @CrossOrigin 处理请求源码解析

      @CrossOrigin源码解析主要分为两个阶段:   ① @CrossOrigin注释的方法扫描注册.   ② 请求匹配@CrossOrigin注释的方法.   本文针对第②阶段从源码角度进行解 ...

  2. Spring的Autowired自动装配(XML版本+Annotation版本+源码+解析)

    http://moshowgame.iteye.com/blog/1607718 @Autowired自动装配 上面的例子我们用的都是手动装配的,如果DAO-Service一多那就很麻烦了,那么我们需 ...

  3. Spring 注解面面通 之 @CrossOrigin 注册处理方法源码解析

      参照<Spring 注解面面通 之 @RequestMapping 注册处理方法源码解析>,其讲解了@RequestMapping注释的处理方法注册过程,而@CrossOrigin是基 ...

  4. 品Spring:详细解说bean后处理器

    一个小小的里程碑 首先感谢能看到本文的朋友,感谢你的一路陪伴. 如果每篇都认真看的话,会发现本系列以bean定义作为切入点,先是详细解说了什么是bean定义,接着又强调了bean定义为什么如此重要. ...

  5. Spring MVC源码解析——HandlerMapping(处理器映射器)

    Sping MVC 源码解析--HandlerMapping处理器映射器 1. 什么是HandlerMapping 2. HandlerMapping 2.1 HandlerMapping初始化 2. ...

  6. Spring Bean的生命周期以及IOC源码解析

    IOC源码这一块太多只能讲个大概吧,建议还是去买本Spring IOC源码解析的书来看比较好,我也是自己看源代码以及视频整理的笔记 Bean的生命周期大概可以分为四个阶段,具体的等会再说,先看看IOC ...

  7. Spring源码解析-bean实例化

    Spring源码解析-bean实例化 ​ 本文介绍Spring创建 bean 过程中的第一个步骤:实例化 bean. 1. Bean实例化源码 ​ 虽然实例化Bean有多种方式(包括静态工厂和工厂实例 ...

  8. Spring源码解析(五)-Bean的实例化流程(上)

    在前面已经完成了对需要实例化bean的收集并封装成BeanDefinition,并且将BeanPostProcess等组件进行了提前实例化.接下来就到了容器启动的最后一步,也是最复杂的一步-实例化be ...

  9. Spring源码解析(七)-Bean属性间的循环依赖

    首先复习一下前面学习的Spring容器启动的大致流程,首先Spring会先扫描所有需要实例化的Bean,将这些Bean的信息封装成一个个BeanDefinition,然后注册到BeanDefiniti ...

  10. Spring 源码解析 - Bean创建过程 以及 解决循环依赖

    一.Spring Bean创建过程以及循环依赖 上篇文章对 Spring Bean资源的加载注册过程进行了源码梳理和解析,我们可以得到结论,资源文件中的 bean 定义信息,被组装成了 BeanDef ...

最新文章

  1. 企业微信发送企业红包java_发放企业红包
  2. 结构光双目视觉测距原理
  3. Objections vs. excuses
  4. TCP/IP中的传输层协议TCP、UDP
  5. JavaScript刷新页面,不重复提交
  6. bat 调用class文件_[Golang实现JVM第五篇]静态方法调用的实现
  7. MyBatis学习02
  8. 将点分十进制转换为ip地址表示
  9. 管道无损检测python_初用python-docx
  10. 1206_MISRA_C规范学习笔记_Rule 10.1 Rule 10.2
  11. windows计算机日志时间id6008,windows错误事件id6008修复方法
  12. 思科服务器dns配置文件,cisco设置dns方法
  13. AUTOSAR-基本概念
  14. matlab for循环与subs应用 求解
  15. 日拱一卒无有尽,功不唐捐终入海
  16. 12点转成0点(原因时间格式化为十二小时制导致)
  17. 12035徐波:PMBOK融会贯通:会议一览
  18. java 队列线程池_JAVA工作队列与线程池
  19. 手机端设置缩放的解决方法和遇到的UC浏览器的坑
  20. 怀旧服小号最多的服务器,怀旧服入坑攻略:10个快速练级技巧要领

热门文章

  1. 无线射频收发芯片SI24R1技术相关问题解答
  2. Python面向对象加强2.Python 中类的内置属性和内置方法(魔法函数)
  3. python编程一球从100米_Python求解一个球从100米高度自由落下的问题
  4. ajax批量上传数据,Ajax上传数据和上传文件(三种方式)
  5. 2020年大学生编程比赛---ACM、蓝桥杯、天梯赛
  6. nssa和stub_Stub区域和NSSA区域比较
  7. java使用代理请求https
  8. python爬数据实例_Python实例教程爬虫爬取NBA数据功能示例
  9. UWP开发入门(八)——聊天窗口和ItemTemplateSelector
  10. 将鼠标放在A标签上时浏览器左下角会显示跳转后界面地址,如何隐藏