模拟实现 Spring Ioc 控制反转功能

使用 => 原理 => 源码 => 模拟实现

使用:了解

原理:熟悉

源码 And 模拟实现: 精通

  1. 对照 Spring 功能点
  2. Spring 功能点原理
  3. Spring 功能点源码
  4. 模拟实现功能点核心逻辑

开发环境:

 Spring: 4.3.12.RELEASE
JDK: 1.8

一、对照 Spring 功能点

Maven pom.xml 文件

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <groupId>org.galsang</groupId>
<artifactId>SpringDemo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging> <name>SpringDemo</name>
<url>http://maven.apache.org</url> <properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.encoding>UTF-8</maven.compiler.encoding>
<maven_compiler_plugin>3.5.1</maven_compiler_plugin>
<jdk.version>1.8</jdk.version>
<spring.version>4.3.12.RELEASE</spring.version> <junit.vsersion>4.12</junit.vsersion>
<lombok.version>1.16.18</lombok.version>
</properties> <dependencies>
<!-- junit start -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.vsersion}</version>
<scope>test</scope>
</dependency>
<!-- junit end --> <!-- lombok start -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</dependency>
<!-- lombok end --> <!-- spring start -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- spring end --> </dependencies> <build>
<finalName>SpringDemo</finalName>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven_compiler_plugin}</version>
<configuration>
<source>${jdk.version}</source>
<target>${jdk.version}</target>
<encoding>${maven.compiler.encoding}</encoding>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build> </project>

创建 Student 类 和 Test 类

Student 类


package org.galsang.bean; import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor; /**
* Description: 学生Bean对象
* <br /> Author: vimx86
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Student { /**
* 姓名
*/
private String name; /**
* 年龄
*/
private int age; }

Test 类


package org.galsang.bean; import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.java.Log; /**
* Description: 测试注入
* <br /> Author: vimx86
*/
@Log
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Test { private Student student; public void print() {
log.info("student: ==== " + student);
} }

applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean name="student" class="org.galsang.bean.Student">
<property name="name" value="万年中华好学生"></property>
<property name="age" value="10000"></property>
</bean> <bean name="test" class="org.galsang.bean.Test" scope="prototype">
<property name="student" ref="student"></property>
</bean> </beans>

App主程序


package org.galsang; import lombok.extern.java.Log;
import org.galsang.bean.Student;
import org.galsang.bean.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext; /**
* Description: 主程序
* <br /> Author: vimx86
*/
@Log
public class App { public static void main(String[] args) { // 1、Main方法入口
// 2、BeanFactory - ClassPathXmlApplicationContext
// 3、加载配置文件 classpath:applicationContext.xml
// 4、根据配置文件中相关标签的定义,解析 Bean,并创建 Bean
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:applicationContext.xml"); // 查看 用户定义的 Bean 对象名称
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (int i = 0; i < beanDefinitionNames.length; i++) {
log.info(i + " == " + beanDefinitionNames[i]);
} // 由容器负责管理的对象
Test test = applicationContext.getBean("test", Test.class);
test.print(); // new 的新对象,容器不负责管理。。。没有进行属性 student 的注入
new Test().print(); // 多例
Test testA = applicationContext.getBean("test", Test.class);
Test testB = applicationContext.getBean("test", Test.class);
log.info("testA == testB: === " + (testA == testB)); // false log.info("testA.getStudent() == testB.getStudent(): === " + (testA.getStudent() == testB.getStudent())); // true // 单例
Student studentA = applicationContext.getBean("student", Student.class);
Student studentB = applicationContext.getBean("student", Student.class);
log.info("studentA == studentB: === " + (studentA == studentB)); // true } }

运行主程序App


十一月 23, 2017 10:11:41 上午 org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@685f4c2e: startup date [Thu Nov 23 10:11:41 CST 2017]; root of context hierarchy
十一月 23, 2017 10:11:41 上午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from class path resource [applicationContext.xml]
十一月 23, 2017 10:11:42 上午 org.galsang.App main
信息: 0 == student
十一月 23, 2017 10:11:42 上午 org.galsang.App main
信息: 1 == test
十一月 23, 2017 10:11:42 上午 org.galsang.bean.Test print
信息: student: ==== Student(name=万年中华好学生, age=10000)
十一月 23, 2017 10:11:42 上午 org.galsang.bean.Test print
信息: student: ==== null
十一月 23, 2017 10:11:42 上午 org.galsang.App main
信息: testA == testB: === false
十一月 23, 2017 10:11:42 上午 org.galsang.App main
信息: testA.getStudent() == testB.getStudent(): === true
十一月 23, 2017 10:11:42 上午 org.galsang.App main
信息: studentA == studentB: === true

二、Spring 功能点原理

The Spring Container 实质上就是一个超级大工厂,在系统启动时,工厂通过读取配置元数据(Configuration Metadata)进行初始化相应的(Your Business Objects(POJOS))供使用(Fully configured system Ready for Use)。

Spring IOC 功能时序图


三、Spring 功能点源码

初始化工厂

ClassPathXmlApplicationContext


/**
* Create a new ClassPathXmlApplicationContext, loading the definitions
* from the given XML file and automatically refreshing the context.
* @param configLocation resource location
* @throws BeansException if context creation failed
*/
public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
this(new String[] {configLocation}, true, null);
} /**
* Create a new ClassPathXmlApplicationContext with the given parent,
* loading the definitions from the given XML files.
* @param configLocations array of resource locations
* @param refresh whether to automatically refresh the context,
* loading all bean definitions and creating all singletons.
* Alternatively, call refresh manually after further configuring the context.
* @param parent the parent context
* @throws BeansException if context creation failed
* @see #refresh()
*/
public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) throws BeansException {
super(parent);
setConfigLocations(configLocations);
if (refresh) {
refresh();
}
}

AbstractApplicationContext


@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh(); // Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory); try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory); // Initialize message source for this context.
initMessageSource(); // Initialize event multicaster for this context.
initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses.
onRefresh(); // Check for listener beans and register them.
registerListeners(); // Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event.
finishRefresh();
} catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
} // Destroy already created singletons to avoid dangling resources.
destroyBeans(); // Reset 'active' flag.
cancelRefresh(ex); // Propagate exception to caller.
throw ex;
} finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
} /**
* Prepare this context for refreshing, setting its startup date and
* active flag as well as performing any initialization of property sources.
*/
protected void prepareRefresh() {
this.startupDate = System.currentTimeMillis();
this.closed.set(false);
this.active.set(true); if (logger.isInfoEnabled()) {
logger.info("Refreshing " + this);
} // Initialize any placeholder property sources in the context environment
initPropertySources(); // Validate that all properties marked as required are resolvable
// see ConfigurablePropertyResolver#setRequiredProperties
getEnvironment().validateRequiredProperties(); // Allow for the collection of early ApplicationEvents,
// to be published once the multicaster is available...
this.earlyApplicationEvents = new LinkedHashSet<ApplicationEvent>();
}

DefaultListableBeanFactory

beanDefinitionMap 用于初始化保存bean的定义。


/** Map of bean definition objects, keyed by bean name */
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>(256);

四、模拟实现功能点核心逻辑

模拟实现 Spring IOC 核心逻辑时序图

Maven pom.xml


<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <groupId>org.galsang</groupId>
<artifactId>MySpringImplIoc</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging> <name>MySpringImplIoc</name>
<url>http://maven.apache.org</url> <properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.encoding>UTF-8</maven.compiler.encoding>
<maven_compiler_plugin>3.5.1</maven_compiler_plugin>
<jdk.version>1.8</jdk.version> <junit.vsersion>4.12</junit.vsersion>
<lombok.version>1.16.18</lombok.version>
<dom4j.version>1.6.1</dom4j.version> </properties> <dependencies>
<!-- junit start -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.vsersion}</version>
<scope>test</scope>
</dependency>
<!-- junit end --> <!-- lombok start -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</dependency>
<!-- lombok end --> <!-- dom4j start -->
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>${dom4j.version}</version>
</dependency>
<!-- dom4j end --> <dependency>
<groupId>jaxen</groupId>
<artifactId>jaxen</artifactId>
<version>1.2.0-atlassian-2</version>
</dependency> <dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.9.3</version>
</dependency> </dependencies> <build>
<finalName>MySpringImplIoc</finalName>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven_compiler_plugin}</version>
<configuration>
<source>${jdk.version}</source>
<target>${jdk.version}</target>
<encoding>${maven.compiler.encoding}</encoding>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build> </project>

创建 BeanDefinition 和 PropertyValue


package org.galsang.bean; import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor; import java.util.ArrayList;
import java.util.List; /**
* Description: Bean 定义
* <br /> Author: vimx86
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class BeanDefinition { /**
* Bean 的id
*/
private String id; /**
* Bean 的名称
*/
private String name; /**
* Bean 对应的类的全名
*/
private String className; /**
* Bean 的模式默认为单例
*/
private String scope = "singleton"; /**
* Bean 的属性标签
*/
private List<PropertyValue> properties = new ArrayList<>(); } package org.galsang.bean; import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor; /**
* Description: Bean 的属性标签的定义
* <br /> Author: vimx86
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class PropertyValue { /**
* 属性名称
*/
private String name; /**
* 属性值
*/
private String value; /**
* 引用值
*/
private String ref; }

创建 BeansException


package org.galsang.bean; /**
* Description:BeansException
* <br /> Author: vimx86
*/
public class BeansException extends RuntimeException {
public BeansException(String message) {
super(message);
}
}

创建 BeanFactory 接口定义


package org.galsang.bean; /**
* Description: Bean 工厂的的接口定义
* <br /> Author: vimx86
*/
public interface BeanFactory { /**
* 通过Bean的名称获取该 Bean 的实例对象
*
* @param beanName Bean 的名称
* @return Bean 的实例对象
*/
Object getBean(String name) throws BeansException; /**
* @param name
* @param requiredType
* @param <T>
* @return
* @throws BeansException
*/
<T> T getBean(String name, Class<T> requiredType) throws BeansException; }

DefaultListableBeanFactory

创建 DefaultListableBeanFactory 实现 BeanFactory


package org.galsang.bean; import lombok.extern.java.Log;
import org.apache.commons.beanutils.BeanUtils; import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; /**
* Description:
* <br /> Author: vimx86
*/
@Log
public class DefaultListableBeanFactory implements BeanFactory { public static final String SCOPE_SINGLETON = "singleton"; /**
* Map of bean definition objects, keyed by bean name
*/
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256); /**
* Cache of singleton objects: bean name --> bean instance
*/
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256); /**
* 配置 元数据文件地址
*/
private String configLocation; /**
* 构造方法
*
* @param configLocation
*/
protected DefaultListableBeanFactory(String configLocation) {
this.configLocation = configLocation;
this.loadBeanDefinitions();
this.iniBeanFacoty();
} /**
* @param name
* @return
* @throws BeansException
*/
@Override
public Object getBean(final String name) throws BeansException { // 单例
Object bean = singletonObjects.get(name);
if (bean != null) {
return bean;
} // 多例
return createBean(beanDefinitionMap.get(name));
} /**
* @param name
* @param requiredType
* @param <T>
* @return
* @throws BeansException
*/
@Override
public <T> T getBean(String name, Class<T> requiredType) throws BeansException {
return requiredType.cast(this.getBean(name));
} /**
* @param beanName
* @param beanDefinition
*/
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) {
if (null == beanName || null == beanDefinition) {
return;
}
this.beanDefinitionMap.put(beanName, beanDefinition);
} /**
* @param beanName
* @param singletonObject
*/
private void registerSingletonObject(String beanName, Object singletonObject) {
if (null == beanName || null == singletonObject) {
return;
}
this.singletonObjects.put(beanName, singletonObject);
} /**
* @return
*/
public int getBeanDefinitionCount() {
return this.beanDefinitionMap.size();
} /**
* @return
*/
public Map<String, BeanDefinition> getBeanDefinitionMap() {
return beanDefinitionMap;
} /**
* 初始化 bean 工厂
*
* @throws BeansException
*/
private void iniBeanFacoty() throws BeansException {
beanDefinitionMap.forEach((k, v) -> {
log.info("初始化 bean 工厂"); if (SCOPE_SINGLETON.equals(v.getScope())) {
createBean(v);
}
});
} /**
* 创建 Bean
*
* @param beanDefinition
* @return Bean 对象
* @throws BeansException
*/
private Object createBean(BeanDefinition beanDefinition) throws BeansException { log.info(" 创建 beanDefinition " + beanDefinition); // 创建该类对象
Class clazz = null;
try {
String className = beanDefinition.getClassName();
log.info("className === " + className);
clazz = Class.forName(className);
} catch (ClassNotFoundException e) {
e.printStackTrace();
throw new RuntimeException("没有找到该类" + beanDefinition.getClassName());
} Object beanObj = null;
try {
beanObj = clazz.newInstance();
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("没有提供无参构造器");
} // 获得 bean 的属性,将其注入,注入分两种情况
// 1、value 普通属性
// 2、ref Bean 属性
if (beanDefinition.getProperties() != null) {
for (PropertyValue prop : beanDefinition.getProperties()) { String name = prop.getName();
String value = prop.getValue();
String ref = prop.getRef(); // 使用 BeanUtils 工具类完成属性注入,可以自动完成类型转换
// 1、value 普通属性
if (value != null) {
Map<String, String[]> parmMap = new HashMap<>();
parmMap.put(name, new String[]{value});
try {
BeanUtils.populate(beanObj, parmMap);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("请检查你的" + name + "属性");
}
} // 2、ref Bean 属性
if (ref != null) { // 看一看当前IOC容器中是否已存在该bean,有的话直接设置没有的话使用递归,创建该bean对象
Object refBean = singletonObjects.get(ref); if (refBean == null) { BeanDefinition refBeanDefinition = beanDefinitionMap.get(ref); if (refBeanDefinition == null) {
throw new RuntimeException("没有找到 Bean " + ref + " 的定义");
} // 递归的创建一个bean
refBean = createBean(refBeanDefinition);
}
try {
BeanUtils.setProperty(beanObj, name, refBean);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("您的bean的属性" + name + "没有对应的set方法");
} } }
} // 当 scope="singleton" 时才放置到 singletonObjects 容器中
if (SCOPE_SINGLETON.equals(beanDefinition.getScope())) {
registerSingletonObject(beanDefinition.getName(), beanObj);
} return beanObj; } /**
* 解析 配置元数据
*/
private void loadBeanDefinitions() {
XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(configLocation);
xmlBeanDefinitionReader.loadBeanDefinitions(this);
} }

创建 XmlBeanDefinitionReader


package org.galsang.bean; import lombok.extern.java.Log;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader; import java.io.InputStream;
import java.util.List; /**
* Description: 解析 配置元数据 文件
* <br /> Author: vimx86
*/
@Log
public class XmlBeanDefinitionReader { private final String configPath; public XmlBeanDefinitionReader(String configPath) {
this.configPath = configPath;
} public void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) { log.info("XmlBeanDefinitionReader ==== loadBeanDefinitions "); // 1.创建解析器
SAXReader reader = new SAXReader();
// 2.加载配置文件,得到document对象
InputStream is = this.getClass().getResourceAsStream(configPath);
Document doc = null;
try {
doc = reader.read(is);
} catch (DocumentException e) {
e.printStackTrace();
throw new RuntimeException("请检查您的xml配置是否正确");
}
// 3.定义xpath表达式,取出所有Bean元素
String xpath = "//bean"; // 4.对Bean元素继续遍历
List<Element> list = doc.selectNodes(xpath);
if (list != null) {
// 4.1将Bean元素的name/class属性封装到 BeanDefinition 类属性中
for (Element bean : list) {
BeanDefinition beanDefinition = new BeanDefinition();
String name = bean.attributeValue("name");
String clazz = bean.attributeValue("class");
String scope = bean.attributeValue("scope"); beanDefinition.setName(name);
beanDefinition.setClassName(clazz);
if (scope != null) {
beanDefinition.setScope(scope);
}
// 4.2获得bean下的所有property子元素
List<Element> children = bean.elements("property"); // 4.3将属性name/value/ref分装到类Property类中
if (children != null) {
for (Element child : children) {
PropertyValue propertyValue = new PropertyValue();
String pName = child.attributeValue("name");
String pValue = child.attributeValue("value");
String pRef = child.attributeValue("ref");
propertyValue.setName(pName);
propertyValue.setRef(pRef);
propertyValue.setValue(pValue);
// 5.将property对象封装到 BeanDefinition 对象中
beanDefinition.getProperties().add(propertyValue);
}
}
//6.将Bean 对象注册到 beanFactory
beanFactory.registerBeanDefinition(name, beanDefinition);
}
} // 用于观察
int count = beanFactory.getBeanDefinitionCount();
log.info("count === " + count);
beanFactory.getBeanDefinitionMap().forEach((k, v) -> log.info("beanName: " + k + " == " + v)); } }

创建 ClassPathXmlApplicationContext


package org.galsang.bean; /**
* Description: BeanFactory 的一种实现方式
* <br /> Author: vimx86
*/
public class ClassPathXmlApplicationContext extends DefaultListableBeanFactory { public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
super(configLocation);
} @Override
public Object getBean(String name) throws BeansException {
return super.getBean(name);
} @Override
public <T> T getBean(String name, Class<T> requiredType) throws BeansException {
return super.getBean(name, requiredType);
}
}

创建 Car 类 和 Wheel 类


package org.galsang.bean.test; import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.java.Log; /**
* Description: 测试注入
* <br /> Author: vimx86
*/
@Log
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Car { /**
* 车名
*/
private String name; /**
* 车轮
*/
private Wheel wheel; } package org.galsang.bean.test; import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor; /**
* Description: 车轮
* <br /> Author: vimx86
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Wheel { /**
* 车轮名称
*/
private String name; }

applicationContext.xml


<?xml version="1.0" encoding="utf-8"?>
<beans>
<bean name="car" class="org.galsang.bean.test.Car">
<property name="name" value="小轿车"></property>
<property name="wheel" ref="wheel"></property>
</bean> <bean name="wheel" class="org.galsang.bean.test.Wheel" scope = "prototype">
<property name="name" value="四轮驱动"></property>
</bean>
</beans>

验证功能实现


package org.galsang.bean; import lombok.extern.java.Log;
import org.galsang.bean.test.Car;
import org.galsang.bean.test.Wheel; /**
* Description: 测试
* <br /> Author: vimx86
*/
@Log
public class App { public static void main(String[] args) { ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("/applicationContext.xml"); // Wheel wheel = (Wheel) applicationContext.getBean("wheel"); // Wheel wheel = applicationContext.getBean("wheel", Wheel.class);
//
// wheel.setName("sssss");
// log.info("wheel == " + wheel); /************************* === 单例 Start === *****************************/
// 单例
Car carA = applicationContext.getBean("car", Car.class);
Car carB = applicationContext.getBean("car", Car.class); log.info("(carA == carB) == " + (carA == carB)); //true // 原因参见: Spring 单例 多例相互注入部分
log.info("(carA.getWheel() == carB.getWheel()) == " + (carA.getWheel() == carB.getWheel())); //true
/************************* === 单例 End === *****************************/ /************************* === 多例 Start === *****************************/ // 多例
Wheel wheelA = applicationContext.getBean("wheel", Wheel.class);
Wheel wheelB = applicationContext.getBean("wheel", Wheel.class); log.info("(wheelA == wheelB) == " + (wheelA == wheelB)); //false
/************************* === 多例 End === *****************************/ } }

Spring IOC 剖析的更多相关文章

  1. 重新认识 Spring IOC

    spring IOC 剖析 再品IOC与DI IOC(Inversion of Control) 控制反转:所谓控制反转,就是把原先我们代码里面需要实现的对象创 建.依赖的代码,反转给容器来帮忙实现. ...

  2. Spring源码剖析1:初探Spring IOC核心流程

    本文大致地介绍了IOC容器的初始化过程,只列出了比较重要的过程和代码,可以从中看出IOC容器执行的大致流程. 接下来的文章会更加深入剖析Bean容器如何解析xml,注册和初始化bean,以及如何获取b ...

  3. Spring源码剖析2:初探Spring IOC核心流程

    本文转载自互联网,侵删 本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 https://github.com/h2pl/Java-Tutor ...

  4. Spring源码剖析2:Spring IOC容器的加载过程

    spring ioc 容器的加载流程 1.目标:熟练使用spring,并分析其源码,了解其中的思想.这篇主要介绍spring ioc 容器的加载 2.前提条件:会使用debug 3.源码分析方法:In ...

  5. Spring源码剖析3:Spring IOC容器的加载过程

    本文转自五月的仓颉 https://www.cnblogs.com/xrq730 本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 https ...

  6. [Spring框架]Spring IOC的原理及详解。

    这里感谢 CSDN 的原博客:http://blog.csdn.net/m13666368773/article/details/7802126 看后  受益匪浅,这里再重温一遍Spring IOC ...

  7. Spring IOC(三)依赖注入

    本系列目录: Spring IOC(一)概览 Spring IOC(二)容器初始化 Spring IOC(三)依赖注入 Spring IOC(四)总结 目录 1.AbstractBeanFactory ...

  8. Spring IOC 容器源码分析

    声明!非原创,本文出处 Spring 最重要的概念是 IOC 和 AOP,本篇文章其实就是要带领大家来分析下 Spring 的 IOC 容器.既然大家平时都要用到 Spring,怎么可以不好好了解 S ...

  9. Spring IOC 源码分析

    Spring 最重要的概念是 IOC 和 AOP,本篇文章其实就是要带领大家来分析下 Spring 的 IOC 容器.既然大家平时都要用到 Spring,怎么可以不好好了解 Spring 呢?阅读本文 ...

随机推荐

  1. 数据可视化之分析篇(十)Power BI应用:如何计算在职员工数量?

    ​https://zhuanlan.zhihu.com/p/128652582 经常碰到的一类问题是,如何根据起止日期来计算某个时间点的数量,比如: 已知合同的生效日期和到期日期,特定日期的有效合同有 ...

  2. Python数据分析实战:使用pyecharts进行数据可视化

    前言 本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 作者:刘早起 开始使用 基本套路就是先创建一个你需要的空图层,然后使用.s ...

  3. git和github连接权限(这是一个简便方法,不是很安全,建议大家还是用ssh解决)

    在使用,git从github上clone下来代码后. 我们再工作区进行编辑,然后提交. 最后我们想要将我们的改变推送到github上. 但是往往这个时候,我们可能会面临这样的问题. 我们没有权限将代码 ...

  4. 不是吧,阿sir,2020年程序员要不好过?

    自从网传程序员到了35岁之后必须要转行,现在又有人传言:“疫情之下,程序员今年要过苦日子了,降薪裁员是大趋势.” 不是,我就不明白了,你们怎么就看不得程序员好呢?天天巴望着程序员降薪.转行.裁员…   ...

  5. .NET Core 发布到 IIS

    ①右键项目->属性 将生成配置为Release ②右键项目->重新生成 ③提示生成成功后,右键项目->发布 点击新建,-> 这一步可以选择文件系统, 也可以选择IIS FT 以 ...

  6. 小书MybatisPlus第8篇-逻辑删除实现及API细节精讲

    本文为Mybatis Plus系列文章的第8篇,前7篇访问地址如下: 小书MybatisPlus第1篇-整合SpringBoot快速开始增删改查 小书MybatisPlus第2篇-条件构造器的应用及总 ...

  7. Btree索引和Hash索引

    B-Tree 索引 BTree索引是最常用的mysql数据库索引算法,因为它不仅可以被用在=,>,>=,<,<=和between这些比较操作符上,而且还可以用于like操作符, ...

  8. C语言中的 “>>”与“<<”

    1. ">>" int x = 16; printf("%d\n", x >> 1); 先将x转成二进制 10000, 不读最后一位, ...

  9. PHP exit() 函数

    实例 输出一条消息,并退出当前脚本: <?php$site = "http://www.w3cschool.cc/";fopen($site,"r")or ...

  10. C/C++编程笔记:C语言写推箱子小游戏,大一学习C语言练手项目

    C语言,作为大多数人的第一门编程语言,重要性不言而喻,很多编程习惯,逻辑方式在此时就已经形成了.这个是我在大一学习 C语言 后写的推箱子小游戏,自己的逻辑能力得到了提升,在这里同大家分享这个推箱子小游 ...