简单手写SpringIOC框架
- 环境搭建
- 基于XML方式
- 项目结构
- 项目代码
- 运行结果
- 基于注解方式
- 项目结构
- 项目代码
- 运行结果
- 简单手写SpringIOC框架
- 核心原理
- 基于XML方式
- 原理
- 项目结构
- 项目代码
- 运行结果
- 基于注解方式
- 原理
- 项目结构
- 项目代码
- 运行结果
环境搭建
基于XML方式
项目结构
项目代码
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<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>com</groupId>
<artifactId>spring</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.2.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.2.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.1.RELEASE</version>
</dependency>
</dependencies>
</project>
UserBean.java
package com.spring.bean;
/**
* @author honey
* @date 2023-08-08 14:58:16
*/
public class UserBean {
}
spring.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 id="userBean" class="com.spring.bean.UserBean"/>
</beans>
SpringTest01.java
package com.spring.test;
import com.spring.bean.UserBean;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @author honey
* @date 2023-08-08 14:59:56
*/
public class SpringTest01 {
public static void main(String[] args) {
// 读取spring.xml
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
// 从IOC容器中读取对象
UserBean userBean = applicationContext.getBean("userBean", UserBean.class);
System.out.println(userBean);
}
}
运行结果
基于注解方式
项目结构
项目代码
ScanBean.java
package com.spring.bean.scan;
import org.springframework.stereotype.Component;
/**
* @author honey
* @date 2023-08-08 16:37:26
*/
@Component
public class ScanBean {
}
SpringConfig.java
package com.spring.config;
import com.spring.bean.UserBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
/**
* @author honey
* @date 2023-08-08 16:30:21
*/
@Configuration
@ComponentScan(value = {"com.spring.bean.scan"})
public class SpringConfig {
@Bean(name = "user")
public UserBean userBean() {
return new UserBean();
}
}
SpringTest02.java
package com.spring.test;
import com.spring.bean.UserBean;
import com.spring.bean.scan.ScanBean;
import com.spring.config.SpringConfig;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
/**
* @author honey
* @date 2023-08-08 16:31:25
*/
public class SpringTest02 {
public static void main(String[] args) {
// 加载SpringConfig
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);
// 从IOC容器中读取对象
UserBean userBean = applicationContext.getBean("user", UserBean.class);
System.out.println(userBean);
ScanBean scanBean = applicationContext.getBean("scanBean", ScanBean.class);
System.out.println(scanBean);
}
}
运行结果
简单手写SpringIOC框架
核心原理
底层使用map集合管理对象,key=beanId,value=实例对象
private final Map<String, Object> beanMap = new ConcurrentHashMap<>();
基于XML方式
原理
基于反射+工厂模式+DOM技术
- 使用DOM技术解析spring.xml文件;
- 获取bean的id和class属性;
- 根据类的完整路径使用反射技术初始化对象;
- 使用工厂模式管理初始化对象;
项目结构
项目代码
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<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>com</groupId>
<artifactId>ext-spring-ioc</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.2.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.2.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.1.RELEASE</version>
</dependency>
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>1.6.1</version>
</dependency>
</dependencies>
</project>
UserBean.java
package com.spring.bean;
/**
* @author honey
* @date 2023-08-08 16:56:32
*/
public class UserBean {
}
spring.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 id="userBean" class="com.spring.bean.UserBean"/>
</beans>
SpringIocXml.java
package com.spring.ext;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.springframework.core.io.ClassPathResource;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author honey
* @date 2023-08-08 16:57:17
*/
public class SpringIocXml {
private final Map<String, Object> beanMap = new ConcurrentHashMap<>();
public SpringIocXml() throws IOException, DocumentException {
init();
}
public <T> T getBean(String name) {
return (T) beanMap.get(name);
}
/**
* 初始化IOC容器
*/
private void init() throws IOException, DocumentException {
// 解析spring.xml配置
ClassPathResource classPathResource = new ClassPathResource("spring.xml");
File xmlFile = classPathResource.getFile();
SAXReader saxReader = new SAXReader();
Document doc = saxReader.read(xmlFile);
// 获取根节点
Element rootElement = doc.getRootElement();
// 获取bean节点信息
List<Element> beans = rootElement.elements("bean");
for (Element bean : beans) {
try {
String beanId = bean.attribute("id").getValue();
String classPath = bean.attribute("class").getValue();
// 使用反射机制初始化对象,并将对象存入Map集合
Class<?> clazz = Class.forName(classPath);
Object object = clazz.newInstance();
beanMap.put(beanId, object);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
SpringTest01.java
package com.spring.test;
import com.spring.bean.UserBean;
import com.spring.ext.SpringIocXml;
import org.dom4j.DocumentException;
import java.io.IOException;
/**
* @author honey
* @date 2023-08-08 17:04:35
*/
public class SpringTest01 {
public static void main(String[] args) throws DocumentException, IOException {
SpringIocXml springIocXml = new SpringIocXml();
UserBean userBean = springIocXml.getBean("userBean");
System.out.println(userBean);
}
}
运行结果
基于注解方式
原理
基于反射+工厂模式实现
- 判断配置类上是否有@Configuration注解;
- 获取配置类中的所有方法,判断方法上是否有@Bean注解,如果有则获取方法的返回值作为实例对象;
- 判断配置类上是否有@ComponentScan注解,如果有则扫描指定包下的所有类,并判断类上是否有@Component注解,如果有则通过反射技术初始化对象;
- 使用工厂模式管理初始化对象/实例对象;
项目结构
项目代码
pom.xml
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.7.7</version>
</dependency>
ScanBean.java
package com.spring.bean.scan;
import org.springframework.stereotype.Component;
/**
* @author honey
* @date 2023-08-09 14:28:33
*/
@Component
public class ScanBean {
}
SpringConfig.java
package com.spring.config;
import com.spring.bean.UserBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
/**
* @author honey
* @date 2023-08-09 14:26:40
*/
@Configuration
@ComponentScan(value = {"com.spring.bean.scan"})
public class SpringConfig {
@Bean
public UserBean userBean() {
return new UserBean();
}
}
SpringIocAnnotation.java
package com.spring.ext;
import cn.hutool.core.lang.ClassScanner;
import cn.hutool.core.util.StrUtil;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author honey
* @date 2023-08-09 14:12:21
*/
public class SpringIocAnnotation {
private final Object config;
private final Map<String, Object> beanMap = new ConcurrentHashMap<>();
public SpringIocAnnotation(Object config) {
this.config = config;
init();
}
/**
* 初始化IOC容器
*/
public void init() {
// 判断配置类上是否有@Configuration注解
Configuration configuration = this.config.getClass().getDeclaredAnnotation(Configuration.class);
if (configuration == null) {
return;
}
// 处理@Bean注解
Class<?> clazz = config.getClass();
Method[] declaredMethods = clazz.getDeclaredMethods();
for (Method method : declaredMethods) {
// 判断方法上是否有@Bean注解
Bean bean = method.getDeclaredAnnotation(Bean.class);
if (bean != null) {
try {
// 获取beanId
String[] value = bean.value();
String beanId = value.length > 0 ? value[0] : method.getName();
// 获取方法的返回值
Object object = method.invoke(config);
beanMap.put(beanId, object);
} catch (Exception e) {
e.printStackTrace();
}
}
}
// 处理@Component注解
ComponentScan componentScan = clazz.getDeclaredAnnotation(ComponentScan.class);
if (componentScan != null) {
for (String packageName : componentScan.value()) {
try {
// 扫描指定包下的所有类
Set<Class<?>> classes = ClassScanner.scanPackage(packageName);
for (Class<?> c : classes) {
// 判断类上是否有@Component注解
Annotation component = c.getDeclaredAnnotation(Component.class);
if (component != null) {
try {
// 获取beanId
String beanId = StrUtil.lowerFirst(c.getSimpleName());
// 通过反射技术初始化对象
Object beanObject = c.newInstance();
beanMap.put(beanId, beanObject);
} catch (Exception e) {
e.printStackTrace();
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
public <T> T getBean(String name) {
return (T) beanMap.get(name);
}
}
SpringTest02.java
package com.spring.test;
import com.spring.bean.UserBean;
import com.spring.bean.scan.ScanBean;
import com.spring.config.SpringConfig;
import com.spring.ext.SpringIocAnnotation;
/**
* @author honey
* @date 2023-08-09 14:24:36
*/
public class SpringTest02 {
public static void main(String[] args) {
SpringIocAnnotation springIocAnnotation = new SpringIocAnnotation(new SpringConfig());
UserBean userBean = springIocAnnotation.getBean("userBean");
System.out.println(userBean);
ScanBean scanBean = springIocAnnotation.getBean("scanBean");
System.out.println(scanBean);
}
}