本文节选自《Spring 5核心原理》

Spring IoC容器还有一些高级特性,如使用lazy-init属性对Bean预初始化、使用FactoryBean产生或者修饰Bean对象的生成、IoC容器在初始化Bean过程中使用BeanPostProcessor后置处理器对Bean声明周期事件进行管理等。

1 关于延时加载

我们已经知道,IoC容器的初始化过程就是对Bean定义资源的定位、载入和注册,此时容器对Bean的依赖注入并没有发生,依赖注入是在应用程序第一次向容器索取Bean时通过getBean()方法完成的。

当Bean定义资源的< bean>元素中配置了lazy-init=false属性时,容器将会在初始化时对所配置的Bean进行预实例化,Bean的依赖注入在容器初始化时就已经完成。这样,当应用程序第一次向容器索取被管理的Bean时,就不用再初始化和对Bean进行依赖注入了,而是直接从容器中获取已经完成依赖注入的Bean,提高了应用程序第一次向容器获取Bean的性能。

1.1. refresh()方法

IoC容器读入已经定位的Bean定义资源是从refresh()方法开始的,我们从AbstractApplicationContext类的refresh()方法入手分析,回顾一下源码:

@Override
public void refresh() throws BeansException, IllegalStateException {
...
//子类的refreshBeanFactory()方法启动
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
...
}

在refresh()方法中ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();启动了Bean定义资源的载入、注册过程。finishBeanFactoryInitialization()方法是对注册后的Bean定义中的预实例化(lazy-init=false,Spring默认进行预实例化,即为true)的Bean进行处理的地方。

1.2. 使用finishBeanFactoryInitialization()处理预实例化的Bean

当Bean定义资源被载入IoC容器之后,容器将Bean定义资源解析为容器内部的数据结构BeanDefinition,并注册到容器中,AbstractApplicationContext类中的finishBeanFactoryInitialization()方法对配置了预实例化属性的Bean进行预初始化,源码如下:

//对配置了lazy-init属性的Bean进行预实例化处理
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
//这是Spring 3新加的代码,为容器指定一个转换服务(ConversionService)
//在对某些Bean属性进行转换时使用
if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
beanFactory.setConversionService(
beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
} if (!beanFactory.hasEmbeddedValueResolver()) {
beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders (strVal));
} String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
for (String weaverAwareName : weaverAwareNames) {
getBean(weaverAwareName);
} //为了使类型匹配,停止使用临时的类加载器
beanFactory.setTempClassLoader(null); //缓存容器中所有注册的BeanDefinition元数据,以防被修改
beanFactory.freezeConfiguration(); //对配置了lazy-init属性的单例模式的Bean进行预实例化处理
beanFactory.preInstantiateSingletons();
}

其中ConfigurableListableBeanFactory是一个接口,preInstantiateSingletons()方法由其子类DefaultListableBeanFactory提供。

1.3. 对配置了lazy-init属性的单例模式的Bean的预实例化

对配置了lazy-init属性的单例模式的Bean的预实例化相关源码如下:

public void preInstantiateSingletons() throws BeansException {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Pre-instantiating singletons in " + this);
} List<String> beanNames = new ArrayList<>(this.beanDefinitionNames); for (String beanName : beanNames) {
//获取指定名称的Bean定义
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
//Bean不是抽象的,是单例模式的,且lazy-init属性配置为false
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
//如果指定名称的Bean是创建容器的Bean
if (isFactoryBean(beanName)) {
//FACTORY_BEAN_PREFIX="&",当Bean名称前面加"&"符号
//时,获取的是容器对象本身,而不是容器产生的Bean
//调用getBean方法,触发Bean实例化和依赖注入
final FactoryBean<?> factory = (FactoryBean<?>) getBean(FACTORY_BEAN_PREFIX + beanName);
//标识是否需要预实例化
boolean isEagerInit;
if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
//一个匿名内部类
isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>) () ->
((SmartFactoryBean<?>) factory).isEagerInit(),
getAccessControlContext());
}
else {
isEagerInit = (factory instanceof SmartFactoryBean &&
((SmartFactoryBean<?>) factory).isEagerInit());
}
if (isEagerInit) {
//调用getBean()方法,触发Bean实例化和依赖注入
getBean(beanName);
}
}
else {
getBean(beanName);
}
}
}

通过对lazy-init处理源码的分析可以看出,如果设置了lazy-init属性,则容器在完成Bean定义的注册之后,会通过getBean()方法触发指定Bean的初始化和依赖注入。如前所述,这样当应用程序第一次向容器索取所需的Bean时,容器不再需要对Bean进行初始化和依赖注入,可直接从已经完成实例化和依赖注入的Bean中取一个现成的Bean,提高了第一次获取Bean的性能。

2 关于FactoryBean和BeanFactory

Spring中,有两个很容易混淆的类:BeanFactory和FactoryBean。

BeanFactory:Bean工厂,是一个工厂(Factory),Spring IoC容器的最高层接口就是BeanFactory,它的作用是管理Bean,即实例化、定位、配置应用程序中的对象及建立这些对象之间的依赖。

FactoryBean:工厂Bean,是一个Bean,作用是产生其他Bean实例。这种Bean没有什么特别的要求,仅需要提供一个工厂方法,该方法用来返回其他Bean实例。在通常情况下,Bean无须自己实现工厂模式,Spring容器担任工厂的角色;在少数情况下,容器中的Bean本身就是工厂,其作用是产生其他Bean实例。

当用户使用容器时,可以使用转义字符“&”来得到FactoryBean本身,以区别通过FactoryBean产生的实例对象和FactoryBean对象本身。在BeanFactory中通过如下代码定义了该转义字符:

String FACTORY_BEAN_PREFIX = "&";

如果myJndiObject是一个FactoryBean,则使用&myJndiObject得到的是myJndiObject对象,而不是myJndiObject产生的对象。

2.1. FactoryBean源码

//工厂Bean,用于产生其他对象
public interface FactoryBean<T> { //获取容器管理的对象实例
@Nullable
T getObject() throws Exception; //获取Bean工厂创建的对象的类型
@Nullable
Class<?> getObjectType(); //Bean工厂创建的对象是否是单例模式的,如果是,
//则整个容器中只有一个实例对象,每次请求都返回同一个实例对象
default boolean isSingleton() {
return true;
} }

2.2. AbstractBeanFactory的getBean()方法

在分析Spring IoC容器实例化Bean并进行依赖注入的源码时,提到在getBean()方法触发容器实例化Bean时会调用AbstractBeanFactory的doGetBean()方法,其重要源码如下:


protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
...
BeanFactory parentBeanFactory = getParentBeanFactory();
//当前容器的父容器存在,且当前容器中不存在指定名称的Bean
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
//解析指定Bean名称的原始名称
String nameToLookup = originalBeanName(name);
if (parentBeanFactory instanceof AbstractBeanFactory) {
return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
nameToLookup, requiredType, args, typeCheckOnly);
}
else if (args != null) {
//委派父容器根据指定名称和显式的参数查找
return (T) parentBeanFactory.getBean(nameToLookup, args);
}
else {
//委派父容器根据指定名称和类型查找
return parentBeanFactory.getBean(nameToLookup, requiredType);
}
}
...
return (T) bean;
} //获取给定Bean的实例对象,主要完成FactoryBean的相关处理
protected Object getObjectForBeanInstance(
Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) { //容器已经得到了Bean实例对象,这个实例对象可能是一个普通的Bean,
//也可能是一个工厂Bean,如果是一个工厂Bean,则使用它创建一个Bean实例对象,
//如果调用本身就想获得一个容器的引用,则返回这个工厂Bean实例对象
//如果指定的名称是容器的解引用(dereference,即对象本身而非内存地址)
//且Bean实例也不是创建Bean实例对象的工厂Bean
if (BeanFactoryUtils.isFactoryDereference(name) && !(beanInstance instanceof FactoryBean)) {
throw new BeanIsNotAFactoryException(transformedBeanName(name), beanInstance.getClass());
} //如果Bean实例不是工厂Bean,或者指定名称是容器的解引用
//调用者获取对容器的引用时,直接返回当前的Bean实例
if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {
return beanInstance;
} //处理指定名称不是容器的解引用,或者根据名称获取的Bean实例对象是一个工厂Bean
//使用工厂Bean创建一个Bean的实例对象
Object object = null;
if (mbd == null) {
//从Bean工厂缓存中获取指定名称的Bean实例对象
object = getCachedObjectForFactoryBean(beanName);
}
//让Bean工厂生产指定名称的Bean实例对象
if (object == null) {
FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
//如果从Bean工厂生产的Bean是单例模式的,则缓存
if (mbd == null && containsBeanDefinition(beanName)) {
//从容器中获取指定名称的Bean定义,如果继承了基类,则合并基类的相关属性
mbd = getMergedLocalBeanDefinition(beanName);
}
//如果从容器得到了Bean定义信息,并且Bean定义信息不是虚构的,
//则让工厂Bean生产Bean实例对象
boolean synthetic = (mbd != null && mbd.isSynthetic());
//调用FactoryBeanRegistrySupport类的getObjectFromFactoryBean()方法
//实现工厂Bean生产Bean实例对象的过程
object = getObjectFromFactoryBean(factory, beanName, !synthetic);
}
return object;
}

在上面获取给定Bean的实例对象的getObjectForBeanInstance()方法中,会调用FactoryBean- RegistrySupport类的getObjectFromFactoryBean()方法,该方法实现了Bean工厂生产Bean实例对象。

2.3. AbstractBeanFactory生产Bean实例对象

AbstractBeanFactory类中生产Bean实例对象的主要源码如下:

//Bean工厂生产Bean实例对象
protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) {
//Bean工厂是单例模式,并且Bean工厂缓存中存在指定名称的Bean实例对象
if (factory.isSingleton() && containsSingleton(beanName)) {
//多线程同步,以防止数据不一致
synchronized (getSingletonMutex()) {
//直接从Bean工厂的缓存中获取指定名称的Bean实例对象
Object object = this.factoryBeanObjectCache.get(beanName);
//如果Bean工厂缓存中没有指定名称的实例对象,则生产该实例对象
if (object == null) {
//调用Bean工厂的获取对象的方法生产指定Bean的实例对象
object = doGetObjectFromFactoryBean(factory, beanName);
Object alreadyThere = this.factoryBeanObjectCache.get(beanName);
if (alreadyThere != null) {
object = alreadyThere;
}
else {
if (shouldPostProcess) {
try {
object = postProcessObjectFromFactoryBean(object, beanName);
}
catch (Throwable ex) {
throw new BeanCreationException(beanName,
"Post-processing of FactoryBean's singleton object failed", ex);
}
}
//将生产的实例对象添加到Bean工厂的缓存中
this.factoryBeanObjectCache.put(beanName, object);
}
}
return object;
}
}
//调用Bean工厂的获取对象的方法生产指定Bean的实例对象
else {
Object object = doGetObjectFromFactoryBean(factory, beanName);
if (shouldPostProcess) {
try {
object = postProcessObjectFromFactoryBean(object, beanName);
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "Post-processing of FactoryBean's object failed", ex);
}
}
return object;
}
} //调用Bean工厂的方法生产指定Bean的实例对象
private Object doGetObjectFromFactoryBean(final FactoryBean<?> factory, final String beanName)
throws BeanCreationException { Object object;
try {
if (System.getSecurityManager() != null) {
AccessControlContext acc = getAccessControlContext();
try {
//实现PrivilegedExceptionAction接口的匿名内部类
object = AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () ->
factory.getObject(), acc);
}
catch (PrivilegedActionException pae) {
throw pae.getException();
}
}
else {
//调用BeanFactory接口实现类的创建对象方法
object = factory.getObject();
}
}
catch (FactoryBeanNotInitializedException ex) {
throw new BeanCurrentlyInCreationException(beanName, ex.toString());
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "FactoryBean threw exception on object creation", ex);
} //创建出来的实例对象为null,或者因为单例对象正在创建而返回null
if (object == null) {
if (isSingletonCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(
beanName, "FactoryBean which is currently in creation returned null from getObject");
}
object = new NullBean();
}
return object;
}

从上面的源码分析中可以看出,BeanFactory接口调用其实现类的获取对象的方法来实现创建Bean实例对象的功能。

2.4. FactoryBean实现类的获取对象的方法

FactoryBean接口的实现类非常多,比如Proxy、RMI、JNDI、ServletContextFactoryBean等。FactoryBean接口为Spring容器提供了一个很好的封装机制,具体的获取对象的方法由不同的实现类根据不同的实现策略来提供,我们分析一下最简单的AnnotationTestFactoryBean类的源码:

public class AnnotationTestBeanFactory implements FactoryBean<FactoryCreatedAnnotationTestBean> {
private final FactoryCreatedAnnotationTestBean instance = new FactoryCreatedAnnotationTestBean();
public AnnotationTestBeanFactory() {
this.instance.setName("FACTORY");
}
@Override
public FactoryCreatedAnnotationTestBean getObject() throws Exception {
return this.instance;
}
//AnnotationTestBeanFactory产生Bean实例对象的实现
@Override
public Class<? extends IJmxTestBean> getObjectType() {
return FactoryCreatedAnnotationTestBean.class;
}
@Override
public boolean isSingleton() {
return true;
}
}

Proxy、RMI、JNDI等其他实现类都根据相应的策略提供方法,这里不做一一分析,这已经不是Spring的核心功能,感兴趣的“小伙伴”可以自行深入研究。

3 再述autowiring

Spring IoC容器提供了两种管理Bean依赖关系的方式:

(1)显式管理:通过BeanDefinition的属性值和构造方法实现Bean依赖关系管理。

(2)autowiring:Spring IoC容器有依赖自动装配功能,不需要对Bean属性的依赖关系做显式的声明,只需要配置好autowiring属性,IoC容器会自动使用反射查找属性的类型和名称,然后基于属性的类型或者名称来自动匹配容器中的Bean,从而自动完成依赖注入。

容器对Bean的自动装配发生在容器对Bean依赖注入的过程中。在对Spring IoC容器的依赖注入源码进行分析时,我们已经知道容器对Bean实例对象的依赖属性注入发生在AbstractAutoWireCapableBeanFactory类的populateBean()方法中,下面通过程序流程分析autowiring的实现原理。

3.1. AbstractAutoWireCapableBeanFactory对Bean实例对象进行属性依赖注入

应用程序第一次通过getBean()方法(配置了lazy-init预实例化属性的除外)向IoC容器索取Bean时,容器创建Bean实例对象,并且对Bean实例对象进行属性依赖注入,AbstractAutoWire- CapableBeanFactory的populateBean()方法就实现了属性依赖注入的功能,其主要源码如下:

//将Bean属性设置到生成的实例对象上
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {

//获取容器在解析Bean定义时为BeanDefinition设置的属性值
PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null); //处理依赖注入,首先处理autowiring自动装配的依赖注入
if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME ||
mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
MutablePropertyValues newPvs = new MutablePropertyValues(pvs); //根据Bean名称进行autowiring自动装配处理
if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME) {
autowireByName(beanName, mbd, bw, newPvs);
} //根据Bean类型进行autowiring自动装配处理
if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
autowireByType(beanName, mbd, bw, newPvs);
} pvs = newPvs;
} //对非autowiring的属性进行依赖注入处理
...
}

3.2. Spring IoC容器根据Bean名称或者类型进行autowiring自动属性依赖注入

Spring IoC容器根据Bean名称或者类型进行autowiring自动属性依赖注入的重要代码如下:

//根据类型对属性进行自动依赖注入
protected void autowireByType(
String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) { //获取用户定义的类型转换器
TypeConverter converter = getCustomTypeConverter();
if (converter == null) {
converter = bw;
} //存放解析的要注入的属性
Set<String> autowiredBeanNames = new LinkedHashSet<>(4);
//对Bean对象中非简单属性(不是简单继承的对象,如8种原始类型、字符、URL等都是简单属性)进行处理
String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
for (String propertyName : propertyNames) {
try {
//获取指定属性名称的属性描述器
PropertyDescriptor pd = bw.getPropertyDescriptor(propertyName);
//不对Object类型的属性进行autowiring自动依赖注入
if (Object.class != pd.getPropertyType()) {
//获取属性的赋值方法
MethodParameter MethodParam = BeanUtils.getWriteMethodParameter(pd);
//检查指定类型是否可以被转换为目标对象的类型
boolean eager = !PriorityOrdered.class.isInstance(bw.getWrappedInstance());
//创建一个要被注入的依赖描述
DependencyDescriptor desc = new AutowireByTypeDependencyDescriptor(MethodParam, eager);
//根据容器的Bean定义解析依赖关系,返回所有要被注入的Bean对象
Object autowiredArgument = resolveDependency(desc, beanName, autowiredBeanNames, converter);
if (autowiredArgument != null) {
//将属性赋值为所引用的对象
pvs.add(propertyName, autowiredArgument);
}
for (String autowiredBeanName : autowiredBeanNames) {
//为指定名称属性注册依赖Bean名称,进行属性的依赖注入
registerDependentBean(autowiredBeanName, beanName);
if (logger.isDebugEnabled()) {
logger.debug("Autowiring by type from bean name '" + beanName + "' via property '"
+ propertyName + "' to bean named '" + autowiredBeanName + "'");
}
}
//释放已自动注入的属性
autowiredBeanNames.clear();
}
}
catch (BeansException ex) {
throw new UnsatisfiedDependencyException(mbd.getResourceDescription(), beanName, propertyName, ex);
}
}
}

通过上面的源码分析可以看出,通过属性名进行自动依赖注入相比通过属性类型进行自动依赖注入要稍微简单一些。但是真正实现属性注入的是DefaultSingletonBeanRegistry类的registerDependentBean()方法。

3.3. DefaultSingletonBeanRegistry的registerDependentBean()方法实现属性依赖注入

DefaultSingletonBeanRegistry的registerDependentBean()方法实现属性依赖注入的重要代码如下:

//为指定的Bean注入依赖的Bean
public void registerDependentBean(String beanName, String dependentBeanName) {
//处理Bean名称,将别名转换为规范的Bean名称
String canonicalName = canonicalName(beanName);
Set<String> dependentBeans = this.dependentBeanMap.get(canonicalName);
if (dependentBeans != null && dependentBeans.contains(dependentBeanName)) {
return;
} //多线程同步,保证容器内数据的一致性
//在容器中通过“Bean名称→全部依赖Bean名称集合”查找指定名称Bean的依赖Bean
synchronized (this.dependentBeanMap) {
//获取指定名称Bean的所有依赖Bean名称
dependentBeans = this.dependentBeanMap.get(canonicalName);
if (dependentBeans == null) {
//为Bean设置依赖Bean信息
dependentBeans = new LinkedHashSet<>(8);
this.dependentBeanMap.put(canonicalName, dependentBeans);
}
//在向容器中通过“Bean名称→全部依赖Bean名称集合”添加Bean的依赖信息
//即,将Bean所依赖的Bean添加到容器的集合中
dependentBeans.add(dependentBeanName);
}
//在容器中通过“Bean名称→指定名称Bean的依赖Bean集合”查找指定名称Bean的依赖Bean
synchronized (this.dependenciesForBeanMap) {
Set<String> dependenciesForBean = this.dependenciesForBeanMap.get(dependentBeanName);
if (dependenciesForBean == null) {
dependenciesForBean = new LinkedHashSet<>(8);
this.dependenciesForBeanMap.put(dependentBeanName, dependenciesForBean);
}
//在容器中通过“Bean名称→指定Bean的依赖Bean名称集合”添加Bean的依赖信息
//即,将Bean所依赖的Bean添加到容器的集合中
dependenciesForBean.add(canonicalName);
}
}

可以看出,autowiring的实现过程如下:

(1)对Bean的属性调用getBean()方法,完成依赖Bean的初始化和依赖注入。

(2)将依赖Bean的属性引用设置到被依赖的Bean属性上。

(3)将依赖Bean的名称和被依赖Bean的名称存储在IoC容器的集合中。

Spring IoC容器的autowiring自动属性依赖注入是一个很方便的特性,可以简化开发配置,但是凡事都有两面性,自动属性依赖注入也有不足:首先,Bean的依赖关系在配置文件中无法很清楚地看出来,会给维护造成一定的困难;其次,由于自动属性依赖注入是Spring容器自动执行的,容器是不会智能判断的,如果配置不当,将会带来无法预料的后果。所以在使用自动属性依赖注入时需要综合考虑。

本文为“Tom弹架构”原创,转载请注明出处。技术在于分享,我分享我快乐!

如果本文对您有帮助,欢迎关注和点赞;如果您有任何建议也可留言评论或私信,您的支持是我坚持创作的动力。

原创不易,坚持很酷,都看到这里了,小伙伴记得点赞、收藏、在看,一键三连加关注!如果你觉得内容太干,可以分享转发给朋友滋润滋润!

Spring核心原理之 IoC容器中那些鲜为人知的细节(3)的更多相关文章

  1. Spring核心原理之IoC容器初体验(2)

    本文节选自<Spring 5核心原理> 1 IoC与DI基本概念 IoC(Inversion of Control,控制反转)就是把原来代码里需要实现的对象创建.依赖,反转给容器来帮忙实现 ...

  2. 30个类手写Spring核心原理之Ioc顶层架构设计(2)

    本文节选自<Spring 5核心原理> 1 Annotation(自定义配置)模块 Annotation的代码实现我们还是沿用Mini版本的,保持不变,复制过来便可. 1.1 @GPSer ...

  3. [原创]java WEB学习笔记101:Spring学习---Spring Bean配置:IOC容器中bean的声明周期,Bean 后置处理器

    本博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用 内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系. 本人互联网技术爱 ...

  4. Spring核心模块:IoC容器介绍

    1.IoC容器运用的是控制反转模式. 2.IoC容器负责管理对象之间的依赖关系,并完成对象的注入. 3.在IoC设计中,会将依赖关系注入到特定组件中,其中setter注入和构造器注入是主要的注入方式. ...

  5. Spring扩展:替换IOC容器中的Bean组件 -- @Replace注解

    1.背景:     工作中是否有这样的场景?一个软件系统会同时有多个不同版本部署,比如我现在做的IM系统,同时又作为公司的技术输出给其他银行,不同的银行有自己的业务实现(比如登陆验证.用户信息查询等) ...

  6. Spring源码分析-从@ComponentScan注解配置包扫描路径到IoC容器中的BeanDefinition,经历了什么(一)?

    阅前提醒 全文较长,建议沉下心来慢慢阅读,最好是打开Idea,点开Spring源码,跟着下文一步一步阅读,更加便于理解.由于笔者水平优先,编写时间仓促,文中难免会出现一些错误或者不准确的地方,恳请各位 ...

  7. 【spring源码学习】spring的IOC容器之自定义xml配置标签扩展namspaceHandler向IOC容器中注册bean

    [spring以及第三方jar的案例]在spring中的aop相关配置的标签,线程池相关配置的标签,都是基于该种方式实现的.包括dubbo的配置标签都是基于该方式实现的.[一]原理 ===>sp ...

  8. Spring重点—— IOC 容器中 Bean 的生命周期

    一.理解 Bean 的生命周期,对学习 Spring 的整个运行流程有极大的帮助. 二.在 IOC 容器中,Bean 的生命周期由 Spring IOC 容器进行管理. 三.在没有添加后置处理器的情况 ...

  9. Spring基础——在 IOC 容器中 Bean 之间的关系

    一.在 Spring IOC 容器中 Bean 之间存在继承和依赖关系. 需要注意的是,这个继承和依赖指的是 bean 的配置之间的关系,而不是指实际意义上类与类之间的继承与依赖,它们不是一个概念. ...

随机推荐

  1. 洛谷 P4099 - [HEOI2013]SAO(树形 dp)

    题面传送门 题意: 有一个有向图 \(G\),其基图是一棵树 求它拓扑序的个数 \(\bmod (10^9+7)\) \(n \in [1,1000]\) 如果你按照拓扑排序的方法来做,那恐怕你已经想 ...

  2. WC 2007 剪刀石头布

    WC 2007 剪刀石头布 看到这个三元环的问题很容易可以考虑到求不合法的三元环的数量的最小值. 什么情况不合法?既然不合法,当且仅当三元环中有一个人赢了另外两个人.所以我们考虑对于一个人而言,如果她 ...

  3. Codeforces 1528F - AmShZ Farm(转化+NTT+推式子+第二类斯特林数)

    Codeforces 题目传送门 & 洛谷题目传送门 神仙题,只不过感觉有点强行二合一(?). 首先考虑什么样的数组 \(a\) 符合条件,我们考虑一个贪心的思想,我们从前到后遍历,对于每一个 ...

  4. C++ and OO Num. Comp. Sci. Eng. - Part 3.

    2. Expressions and Statements 声明是将一个种类型的变量引入程序的语句. 作用域 作用域又一对花括号限定,在所有花括号之外的为全局作用域. 在作用域内声明的变量为局部变量. ...

  5. window修改dns本地文件

    文件地址: C:\Windows\System32\drivers\etc 先修改权限: 最后用记事本打开编辑保存即可

  6. 变量、内存区域、MDK文件(map、htm)

    变量分为:局部变量和全局变量 局部变量:函数体内部定义的变量,作用域为函数内部,static声明(静态局部变量)该变量则函数调用结束后不消失而保留值,分配的存储空间不释放. 全局变量:函数体外部定义的 ...

  7. 18. MYSQL 字符编码配置

    MYSQL 5.7版本的my.ini 在C盘隐藏文件夹下 C:\ProgramData\MySQL\MySQL Server 5.7 [client] default-character-set=ut ...

  8. HTTP请求 Java API

    1.导入依赖 <dependency> <groupId>commons-httpclient</groupId> <artifactId>common ...

  9. 【STM32】WS2812介绍、使用SPI+DMA发送数据

    这篇要使用到SPI+DMA,需要了解的话,可以参考我另两篇博客 时钟:https://www.cnblogs.com/PureHeart/p/11330967.html SPI+DMA通信:https ...

  10. Handler与多线程

    1.Handler介绍 在Android开发中,我们常会使用单独的线程来完成某些操作,比如用一个线程来完成从网络上下的图片,然后显示在一个ImageView上,在多线程操作时,Android中必须保证 ...