Spring IOC 剖析
模拟实现 Spring Ioc 控制反转功能
使用 => 原理 => 源码 => 模拟实现
使用:了解
原理:熟悉
源码 And 模拟实现: 精通
- 对照 Spring 功能点
- Spring 功能点原理
- Spring 功能点源码
- 模拟实现功能点核心逻辑
开发环境:
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 剖析的更多相关文章
- 重新认识 Spring IOC
spring IOC 剖析 再品IOC与DI IOC(Inversion of Control) 控制反转:所谓控制反转,就是把原先我们代码里面需要实现的对象创 建.依赖的代码,反转给容器来帮忙实现. ...
- Spring源码剖析1:初探Spring IOC核心流程
本文大致地介绍了IOC容器的初始化过程,只列出了比较重要的过程和代码,可以从中看出IOC容器执行的大致流程. 接下来的文章会更加深入剖析Bean容器如何解析xml,注册和初始化bean,以及如何获取b ...
- Spring源码剖析2:初探Spring IOC核心流程
本文转载自互联网,侵删 本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 https://github.com/h2pl/Java-Tutor ...
- Spring源码剖析2:Spring IOC容器的加载过程
spring ioc 容器的加载流程 1.目标:熟练使用spring,并分析其源码,了解其中的思想.这篇主要介绍spring ioc 容器的加载 2.前提条件:会使用debug 3.源码分析方法:In ...
- Spring源码剖析3:Spring IOC容器的加载过程
本文转自五月的仓颉 https://www.cnblogs.com/xrq730 本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 https ...
- [Spring框架]Spring IOC的原理及详解。
这里感谢 CSDN 的原博客:http://blog.csdn.net/m13666368773/article/details/7802126 看后 受益匪浅,这里再重温一遍Spring IOC ...
- Spring IOC(三)依赖注入
本系列目录: Spring IOC(一)概览 Spring IOC(二)容器初始化 Spring IOC(三)依赖注入 Spring IOC(四)总结 目录 1.AbstractBeanFactory ...
- Spring IOC 容器源码分析
声明!非原创,本文出处 Spring 最重要的概念是 IOC 和 AOP,本篇文章其实就是要带领大家来分析下 Spring 的 IOC 容器.既然大家平时都要用到 Spring,怎么可以不好好了解 S ...
- Spring IOC 源码分析
Spring 最重要的概念是 IOC 和 AOP,本篇文章其实就是要带领大家来分析下 Spring 的 IOC 容器.既然大家平时都要用到 Spring,怎么可以不好好了解 Spring 呢?阅读本文 ...
随机推荐
- 数据可视化之分析篇(十)Power BI应用:如何计算在职员工数量?
https://zhuanlan.zhihu.com/p/128652582 经常碰到的一类问题是,如何根据起止日期来计算某个时间点的数量,比如: 已知合同的生效日期和到期日期,特定日期的有效合同有 ...
- Python数据分析实战:使用pyecharts进行数据可视化
前言 本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 作者:刘早起 开始使用 基本套路就是先创建一个你需要的空图层,然后使用.s ...
- git和github连接权限(这是一个简便方法,不是很安全,建议大家还是用ssh解决)
在使用,git从github上clone下来代码后. 我们再工作区进行编辑,然后提交. 最后我们想要将我们的改变推送到github上. 但是往往这个时候,我们可能会面临这样的问题. 我们没有权限将代码 ...
- 不是吧,阿sir,2020年程序员要不好过?
自从网传程序员到了35岁之后必须要转行,现在又有人传言:“疫情之下,程序员今年要过苦日子了,降薪裁员是大趋势.” 不是,我就不明白了,你们怎么就看不得程序员好呢?天天巴望着程序员降薪.转行.裁员… ...
- .NET Core 发布到 IIS
①右键项目->属性 将生成配置为Release ②右键项目->重新生成 ③提示生成成功后,右键项目->发布 点击新建,-> 这一步可以选择文件系统, 也可以选择IIS FT 以 ...
- 小书MybatisPlus第8篇-逻辑删除实现及API细节精讲
本文为Mybatis Plus系列文章的第8篇,前7篇访问地址如下: 小书MybatisPlus第1篇-整合SpringBoot快速开始增删改查 小书MybatisPlus第2篇-条件构造器的应用及总 ...
- Btree索引和Hash索引
B-Tree 索引 BTree索引是最常用的mysql数据库索引算法,因为它不仅可以被用在=,>,>=,<,<=和between这些比较操作符上,而且还可以用于like操作符, ...
- C语言中的 “>>”与“<<”
1. ">>" int x = 16; printf("%d\n", x >> 1); 先将x转成二进制 10000, 不读最后一位, ...
- PHP exit() 函数
实例 输出一条消息,并退出当前脚本: <?php$site = "http://www.w3cschool.cc/";fopen($site,"r")or ...
- C/C++编程笔记:C语言写推箱子小游戏,大一学习C语言练手项目
C语言,作为大多数人的第一门编程语言,重要性不言而喻,很多编程习惯,逻辑方式在此时就已经形成了.这个是我在大一学习 C语言 后写的推箱子小游戏,自己的逻辑能力得到了提升,在这里同大家分享这个推箱子小游 ...