1. 背景

Spring框架本身非常庞大,源码阅读可以从Spring IOC容器的实现开始一点点了解。然而即便是IOC容器,代码仍然是非常多,短时间内全部精读完并不现实

本文分析比较浅,而完整的IOC创建bean实际上是非常复杂的。本文对于BeanDefinition的加载解析,bean实例化的反射调用细节不作介绍,仅以较为粗略的角度来大体感受IOC容器创建bean的过程。

本文涉及的Spring源码版本为4.3.5.RELEASE。

2. 想要了解什么

下面就抛出几个想要了解的问题,也是本文介绍所要围绕的关键点。

  • BeanFactory和ApplicationContext的区别
  • IOC容器创建Bean的大致过程
  • Bean的循环依赖是如何解决的
  • 那些Aware究竟是什么

3. 从源码中找出问题的答案

3.1 BeanFactory & ApplicationContext

3.1.1 BeanFactory体系

org.springframework.beans.factory.BeanFactory是Spring的Bean容器的一个非常基本的接口,位于spring-beans模块。它包括了各种getBean方法,如通过名称、类型、参数等,试图从Bean容器中返回一个Bean实例。还包括诸如containsBean, isSingleton, isPrototype等方法判断Bean容器中是否存在某个Bean或是判断Bean是否为单例/原型等等。



可以看到BeanFactory向下主要有三条继承路线

  • ListableBeanFactory

    在BeanFactory基础上,支持对Bean容器中Bean的枚举。
  • HierarchicalBeanFactory -> ConfigurableBeanFactory

    HierarchicalBeanFactory有个getParentBeanFactory方法,使得Bean容器具有亲缘关系。而ConfigurableBeanFactory则是对BeanFactory的一系列配置功能提供了支持。
  • AutowireCapableBeanFactory

    提供了一系列用于自动装配Bean的方法,用户代码比较少用到,更多是作为Spring内部使用。

3.1.2 ApplicationContext体系

org.springframework.context.ApplicationContext是Spring上下文的底层接口,位于spring-context模块。它可以视作是Spring IOC容器的一种高级形式,也是我们用Spring企业开发时必然会用到的接口,它含有许多面向框架的特性,也对应用环境作了适配。

从上面的图中,我们可以看到ApplicationContext作为BeanFactory的子接口,与BeanFactory之间也是通过HierarchicalBeanFactory与ListableBeanFactory桥接的。

ApplicationContext接口,继承了MessageSource, ResourceLoader, ApplicationEventPublisher接口,以BeanFactory为主线添加了许多高级容器特性。

3.2 Spring创建Bean的大致过程

搞清楚整个Spring IOC容器创建Bean的过程,对于阅读源码的效率会有很大的提升。

下面梳理一下整个过程:

  1. 实例化BeanFactoryPostProcessor实现类
  2. 调用BeanFactoryPostProcessor#postProcessBeanFactory
  3. 实例化BeanPostProcessor实现类
  4. 调用InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation
  5. 实例化Bean
  6. 调用InstantiationAwareBeanProcessor#postProcessAfterInstantiation
  7. 调用InstantiationAwareBeanPostProcessor#postProcessPropertyValues
  8. 为Bean注入属性
  9. 调用BeanNameAware#setBeanName
  10. 调用BeanClassLoaderAware#setBeanClassLoader
  11. 调用BeanFactoryAware#setBeanFactory
  12. 调用BeanPostProcessor#postProcessBeforeInitialization
  13. 调用InitializingBean#afterPropertiesSet
  14. 调用Bean的init-method
  15. 调用BeanPostProcessor#postProcessAfterInitialization

3.3 IOC容器依赖注入

完整来说,IOC容器的初始化过程中做了在容器中建立BeanDefinition的数据映射。之后所有的依赖的注入都依托于已经存在的BeanDefinition,限于篇幅,此处略去对BeanDefinition的建立作介绍。直接从上下文的getBean开始看起。

在AbstractApplicationContext抽象类中有一个getBeanFactory方法用于返回一个ConfigurableListableBeanFactory,所有BeanFactory接口的方法实际上都委托给子类内部的ConfigurableListableBeanFactory实现。

我们以AnnotationConfigApplicationContext,它在被构造时,内部的beanFactory实际上是由父类GenericApplicationContext来初始化一个DefaultListableBeanFactory的。

因此我们看某个bean是如何被加载的可以从DefaultListableBeanFactory的getBean方法看起,对于DefaultListableBeanFactory而言那些getBean方法实际上在AbstractBeanFactory这一层就都已经实现了,并且都委托给了AbstractBeanFactory#doGetBean。下面就来看一下doGetBean方法。

protected <T> T doGetBean( final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
throws BeansException { final String beanName = transformedBeanName(name);
Object bean; /*
* 尝试从缓存中拿取一个bean实例。
* Spring会在Bean还没完全初始化完毕的前,通过一个ObjectFactory提前暴露出bean实例,这样为解决循环依赖提供了遍历。
*/
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
if (logger.isDebugEnabled()) {
if (isSingletonCurrentlyInCreation(beanName)) {
logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +
"' that is not fully initialized yet - a consequence of a circular reference");
}
else {
logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
}
}
// 对FactoryBean的情况进行特殊处理。
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
} else {
// 如果正在创建的bean为原型并且已经正在创建,这种循环依赖是无法解决的,要抛出异常。
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
} // 如果该beanFactory中不包含要创建bean的beanDefinition,则尝试从父beanFactory中寻找。
BeanFactory parentBeanFactory = getParentBeanFactory();
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
String nameToLookup = originalBeanName(name);
if (args != null) {
return (T) parentBeanFactory.getBean(nameToLookup, args);
}
else {
return parentBeanFactory.getBean(nameToLookup, requiredType);
}
} if (!typeCheckOnly) {
markBeanAsCreated(beanName);
} try {
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
checkMergedBeanDefinition(mbd, beanName, args); // 有些bean是有depends-on/@DependsOn的,需要先初始化这些依赖。
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
for (String dep : dependsOn) {
if (isDependent(beanName, dep)) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
}
registerDependentBean(dep, beanName);
getBean(dep);
}
} // 创建单例bean。
if (mbd.isSingleton()) {
/*
* 调用父类DefaultSingletonBeanRegistry的getSingleton,具体创建bean的工作实际上仍然是
* 回调参数中传递的ObjectFactory#getObject方法,而createBean实际上是子类AbstractAutowireCapableBeanFactory实现的。
*/
sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
destroySingleton(beanName);
throw ex;
}
}
});
// 对FactoryBean的情况进行特殊处理。
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
// 创建原型bean。
else if (mbd.isPrototype()) {
Object prototypeInstance = null;
try {
// 前置处理,维护prototypesCurrentlyInCreation,加入当前bean记录。
beforePrototypeCreation(beanName);
// 委托给子类AbstractAutowireCapableBeanFactory来完成具体的创建bean工作。
prototypeInstance = createBean(beanName, mbd, args);
}
finally {
// 后置处理,维护prototypesCurrentlyInCreation信息,删除当前bean记录。
afterPrototypeCreation(beanName);
}
// 对FactoryBean的情况进行特殊处理。
bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
} else {
String scopeName = mbd.getScope();
final Scope scope = this.scopes.get(scopeName);
if (scope == null) {
throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
}
try {
Object scopedInstance = scope.get(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
beforePrototypeCreation(beanName);
try {
return createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
}
});
bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
}
catch (IllegalStateException ex) {
throw new BeanCreationException(beanName,
"Scope '" + scopeName + "' is not active for the current thread; consider " +
"defining a scoped proxy for this bean if you intend to refer to it from a singleton",
ex);
}
}
}
catch (BeansException ex) {
cleanupAfterBeanCreationFailure(beanName);
throw ex;
}
} // 到这里一个bean就已经创建完了,最后一步检查类型,如果不匹配会尝试转换。
if (requiredType != null && bean != null && !requiredType.isAssignableFrom(bean.getClass())) {
try {
return getTypeConverter().convertIfNecessary(bean, requiredType);
}
catch (TypeMismatchException ex) {
if (logger.isDebugEnabled()) {
logger.debug("Failed to convert bean '" + name + "' to required type '" +
ClassUtils.getQualifiedName(requiredType) + "'", ex);
}
throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
}
}
return (T) bean;
}

上面针对AbstractBeanFactory#doGetBean方法进行了源码分析,从中我们可以看出它主要会干这几件事情:

  1. 转换beanName。
  2. 尝试从缓存的单例中拿实例。
  3. 如果要创建的bean是原型模式,且已经在尝试创建,这种循环依赖是无法解决的。
  4. 当前beanFactory不包含要创建的bean的beanDefinition,会尝试从parentBeanFactory中获取。
  5. 如果当前bean有依赖(xml的话就是有depends-on,注解的话有@DependsOn),则需要先完成那些bean的创建初始化。
  6. 针对scope分类讨论创建。我们比较关心的就是单例,其次是原型。
  7. 类型检查,并且尝试转换。

我们一般比较关心的就是单例bean和原型bean的创建。

在获取单例bean时doGetBean方法会调用父类DefaultSingletonBeanRegistry#getSingleton。可以把DefaultSingletonBeanRegistry当作一个“单例bean桶”,因为它确实就是一个用来存放单例bean的桶。但是这个桶本身不关心bean到底该怎么创建,所以对于桶里还没有的bean,它将创建bean的职责通过回调ObjectFactory#getObject来完成,而AbstractBeanFactory中传递给getSingleton方法的ObjectFactory#getObject的具体实现是调用createBean,这个方法是真正创建并初始化bean的方法,由子类AbstractAutowireCapableBeanFactory完成。

对于获取原型bean则简单多了,不用关心放到桶里缓存的事情,直接调用createBean创建就是了。

所以我们接下来通过AbstractAutowireCapableBeanFactory来看一下一个Bean具体是如何创建并初始化的。

protected Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) throws BeanCreationException {
if (logger.isDebugEnabled()) {
logger.debug("Creating instance of bean '" + beanName + "'");
}
RootBeanDefinition mbdToUse = mbd; Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
mbdToUse = new RootBeanDefinition(mbd);
mbdToUse.setBeanClass(resolvedClass);
} try {
mbdToUse.prepareMethodOverrides();
}
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(),
beanName, "Validation of method overrides failed", ex);
} try {
/*
* 在对象被实例化前,这里有一个短路逻辑,会调用InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation。
* 如果存在某个InstantiationAwareBeanPostProcessor的调用结果不为null,则形成了短路,接下来调用BeanPostProcessor#postProcessAfterInitialization。
*
* 实际上,一般Spring里默认就LazyInitTargetSourceCreator和QuickTargetSourceCreator可能会使得这里的短路生效。
* 大部分情况AOP还是在bean被正常实例化后通过调用postProcessAfterInitialization实现的。
*/
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);
} // 创建bean的主要方法。
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
if (logger.isDebugEnabled()) {
logger.debug("Finished creating instance of bean '" + beanName + "'");
}
return beanInstance;
}

从上面可以看到AbstractAutowireCapableBeanFactory#createBean是创建bean的主要入口方法,但仍然不是最主要在“干活”的方法。继续向下看doCreateBean方法。

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) throws BeanCreationException {

    BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
// 尝试从factoryBean缓存中获取。
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
// 创建bean实例。
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null);
Class<?> beanType = (instanceWrapper != null ? instanceWrapper.getWrappedClass() : null); synchronized (mbd.postProcessingLock) {
if (!mbd.postProcessed) {
try {
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
}
catch (Throwable ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Post-processing of merged bean definition failed", ex);
}
mbd.postProcessed = true;
}
} /*
* Spring为了解决单例bean的循环引用问题,会在bean还没有完全初始化完毕前通过添加singletonFactory
* 使得其它bean可以拿到某个bean的实例引用。
*/
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
if (logger.isDebugEnabled()) {
logger.debug("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
addSingletonFactory(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
return getEarlyBeanReference(beanName, mbd, bean);
}
});
} // 接下去初始化bean。
Object exposedObject = bean;
try {
// 填充bean中的属性。
populateBean(beanName, mbd, instanceWrapper);
if (exposedObject != null) {
/*
* 调用初始化方法,比如:
* 1. 各种aware回调
* 2. 调用BeanPostProcessor#postProcessBeforeInitialization
* 3. 调用InitializingBean#afterPropertiesSet, xml中的init-method
* 4. 调用BeanPostProcessor#postProcessAfterInitialization
*/
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);
}
} if (earlySingletonExposure) {
Object earlySingletonReference = getSingleton(beanName, false);
/*
* 上面的getSingleton第二个参数为false表示不会主动触发early reference的创建。
* 所以此处earlySingletonReference只有在bean创建过程中发现有别的bean与当前bean有循环依赖才不为空。
*/
if (earlySingletonReference != null) {
/*
* 如果当前bean调用initializeBean没有增强原始bean实例,则取earlySingletonReference。
*
* 举例:
* BeanA与BeanB互相依赖。Srping先创建BeanA,再创建BeanB。
* BeanA通过addSingletonFactory暴露了获取BeanA引用的途径。
*
* 在populateBean的时候需要注入BeanB,而BeanB又需要注入BeanA,
* 则在获取BeanA时会调用原先BeanA暴露的ObjectFactory,继而使得earlySingletonObjects中加入了BeanA引用。
*
* 回到BeanA的创建过程,走到此步时,发现initializeBean没有增强原始bean实例,
* 则需要取其它循环依赖bean拿BeanA时在registry留下的结果(原始bean经过SmartInstantiationAwareBeanPostProcessor#getEarlyBeanReference回调)。
*/
if (exposedObject == bean) {
exposedObject = earlySingletonReference;
}
else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
// 获取当前bean依赖的其它bean。
String[] dependentBeans = getDependentBeans(beanName);
// 过滤筛选出真正依赖的bean。
Set<String> actualDependentBeans = new LinkedHashSet<String>(dependentBeans.length);
for (String dependentBean : dependentBeans) {
if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
actualDependentBeans.add(dependentBean);
}
}
/*
* 举例:
* BeanA与BeanB互相依赖。Srping先创建BeanA,再创建BeanB。
* BeanA的创建走到这里时会抛出异常。
*
* 原因是上面的exposedObject != bean说明initializeBean方法的调用增强了原始的BeanA。
* 而BeanB中注入的BeanA很可能是原始beanA(可能会有SmartInstantiationAwareBeanPostProcessor#getEarlyBeanReference回调,
* 也就是BeanB中注入的BeanA不是此处BeanA的最终版exposedObject。
*/
if (!actualDependentBeans.isEmpty()) {
throw new BeanCurrentlyInCreationException(beanName,
"Bean with name '" + beanName + "' has been injected into other beans [" +
StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
"] in its raw version as part of a circular reference, but has eventually been " +
"wrapped. This means that said other beans do not use the final version of the " +
"bean. This is often the result of over-eager type matching - consider using " +
"'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
}
}
}
} try {
registerDisposableBeanIfNecessary(beanName, bean, mbd);
}
catch (BeanDefinitionValidationException ex) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
} return exposedObject;
}

3.4 Bean的循环依赖是如何解决的

不是所有的循环依赖Spring都能够解决的。

  • 对于最简单的情况,bean为单例,且使用Autowired或者setter注入,Spring是可以解决这样的循环依赖的。通过上面的代码中我们可以看出,在一个Bean实例化后,会调用addSingletonFactory方法,在IOC容器中通过一个ObjectFactory暴露出可以获取还未完全初始化完毕的bean引用。若存在循环依赖,则依赖的bean可以在调用getBean时通过getSingleton方法获取到循环依赖的bean。

  • 但是Spring是不允许出现原型环的,举例来说,BeanA和BeanB循环依赖且scope都为prototype。因为prototype的bean,不会触发addSingletonFactory,即每次get这样的bean都会新创建一个。所以创建BeanA需要注入一个BeanB,而这个BeanB又需要注入一个新的BeanA,这样的循环依赖是没办法解决的。Spring会判断当前bean是否是prototype并且已经在创建中,然后抛出异常。

  • 对于构造器依赖,可以作一下讨论,下面讨论的bean的scope都为单例

    • 如果BeanA构造器中依赖BeanB,并且BeanA先创建,则无论BeanB以哪种形式依赖BeanA,都没办法解决这样的循环依赖。因为实例化BeanA需要先得到BeanB(此时还未提前暴露引用),BeanB依赖BeanA,但是拿不到BeanA提前暴露的引用,这就形成了无限循环。这种情况会在BeanB试图获取BeanA时在beforeSingletonCreation方法抛出异常。
    • 如果BeanA非构造器依赖BeanB,并且BeanA先创建,BeanB即使构造器依赖BeanA,也可以进行解决循环依赖。 因为这种情况BeanB可以拿到BeanA提前暴露的引用。

3.5 那些Aware究竟是什么

Spring中有很多XXXAware接口,从字面意思上很容易理解:就是bean能够“感知”XXX。通常这些接口的方法都是setXXX。在项目里做一个工具类实现ApplicationContextAware接口,里面可以塞一个ApplicationContext实例到静态域中,在代码中就可以很方便获取到Spring上下文进行一些操作。

那么Spring对于这些Aware接口是在哪一步调用的呢?答案其实在上面的源码分析中已经提到。在AbstractAutowireCapableBeanFactory#initializeBean方法中,Spring默认会对实现BeanNameAware, BeanClassLoaderAware, BeanFactoryAware进行回调,为它们注入beanName, classLoader, beanFactory等。

而对于更多的一些扩展,Spring基于那些processor实现了很强的可拓展性与可插拔性。比如我们非常熟悉的ApplicationContextAware接口实际上是通过ApplicationContextAwareProcessor来实际调用的,它继承了BeanPostProcessor,其中postProcessBeforeInitialization方法中会对EnvironmentAware, EmbeddedValueResolverAware, ApplicationContextAware等等一系列Aware接口的子类Bean进行回调,为其注入相关资源。

那么ApplicationContextAwareProcessor是什么时候出现在BeanPostProcessor集合中的呢?在AbstractApplicationContext#prepareBeanFactory方法中,Spring有如下代码:

beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));

也就是当Spring上下文在初始化prepareBeanFactory的时候就已经添加了ApplicationContextAwareProcessor。

Spring IOC容器创建bean过程浅析的更多相关文章

  1. spring-framework-中文文档一:IoC容器、介绍Spring IoC容器和bean

    5. IoC容器 5.1介绍Spring IoC容器和bean 5.2容器概述 本章介绍Spring Framework实现控制反转(IoC)[1]原理.IoC也被称为依赖注入(DI).它是一个过程, ...

  2. 02.Spring Ioc 容器 - 创建

    基本概念 Spring IoC 容器负责 Bean 创建.以及其生命周期的管理等.想要使用 IoC容器的前提是创建该容器. 创建 Spring IoC 容器大致有两种: 在应用程序中创建. 在 WEB ...

  3. spring IOC容器实例化Bean的方式与RequestContextListener应用

    spring IOC容器实例化Bean的方式有: singleton 在spring IOC容器中仅存在一个Bean实例,Bean以单实例的方式存在. prototype 每次从容器中调用Bean时, ...

  4. Spring IoC容器的初始化过程

    Spring IoC容器的初始化包括 BeanDefinition的Resource定位.载入和注册 这三个基本的过程.IoC容器的初始化过程不包含Bean依赖注入的实现.Bean依赖的注入一般会发生 ...

  5. Spring IoC 容器和 bean 对象

    程序的耦合性: 耦合性(Coupling),又叫耦合度,是对模块间关联程度的度量.耦合的强弱取决于模块间接口的复杂性.调用模块的方式以及通过界面传送数据的多少.模块间的耦合度是指模块之间的依赖关系,包 ...

  6. spring IOC 容器中 Bean 的生命周期

    IOC 容器中 Bean 的生命周期: 1.通过构造器或工厂方法创建 Bean 实例 2.为 Bean 的属性设置值和对其他 Bean 的引用 3.调用 Bean 后置处理器接口(BeanPostPr ...

  7. Spring IOC容器中Bean的生命周期

    1.IOC容器中Bean的生命周期 构造器函数 设置属性 初始化函数(在Bean配置中 init-method) 使用Bean 结束时关闭容器(在Bean中配置destroy-method) 2.Be ...

  8. Spring IOC容器对bean的生命周期进行管理的过程

    1.通过构造器或者工厂方法创建bean的实例 2.为bean的属性设置值和对其他bean的引用 3.将bean的实例传递给bean的后置处理器BeanPostProcessor的postProcess ...

  9. Spring源码:Spring IoC容器加载过程(2)

    Spring源码版本:4.3.23.RELEASE 一.加载XML配置 通过XML配置创建Spring,创建入口是使用org.springframework.context.support.Class ...

随机推荐

  1. CAlayer一

    // // ViewController.m // Layer // // Created by City--Online on 15/4/9. // Copyright (c) 2015年 City ...

  2. [EWS]查找 文件夹

    摘要 有时在操作exchange的时候,需要查找用户exchange文件夹,比如用户新建了一些文件夹. 一个例子 这里以查找用户outlook邮箱中的历史对话文件夹为例. private const ...

  3. [C#]非阻塞监听键盘输入

    摘要 最近需要调研监控用户键盘输入的内容,然后收集数据进行用户行为分析.然后就用控制台程序弄了一个demo. 代码如下 class Program { static void Main(string[ ...

  4. 使用cglib实现数据库框架的级联查询

    写在前面的 这一章是之前写的<手把手教你写一个Java的orm框架> 的追加内容.因为之前写的数据库框架不支持级联查询这个操作,对于有关联关系的表用起来还是比较麻烦,于是就准备把这个功能给 ...

  5. class文件打包成jar

    电脑左下角“开始”——“运行”——输入cmd——cd+空格+clss文件所在文件夹的路径——jar+空格+-cf+空格+“jar包的名字”.jar+空格+*.class.好了...

  6. IDEA创建Struts2报错——web.xml

    这里记录一个问题,用IDEA创建Struts2时会出现的错误,cannot resolve class or package ‘filter’,出现在web.xml文件中,不修改这个,那么你配置好了T ...

  7. [转载] Spring框架——AOP前置、后置、环绕、异常通知

    通知类型: 步骤: 1. 定义接口 2. 编写对象(被代理对象=目标对象) 3. 编写通知(前置通知目标方法调用前调用) 4. 在beans.xml文件配置 4.1 配置 被代理对象=目标对象 4.2 ...

  8. instanceof与constructor的区别

    名词介绍 instanceof 的作用是判断实例对象是否为构造函数的实例,实际上判断的是实例对象的__proto__属性与构造函数的prototype属性是否指向同一引用: constructor 的 ...

  9. cf113D. Museum(期望 高斯消元)

    题意 题目链接 Sol 设\(f[i][j]\)表示Petya在\(i\),\(Vasya\)在\(j\)的概率,我们要求的是\(f[i][i]\) 直接列方程高斯消元即可,由于每个状态有两维,因此时 ...

  10. element-ui switch组件源码分析整理笔记(二)

    源码如下: <template> <div class="el-switch" :class="{ 'is-disabled': switchDisab ...