1. 前言
简单记录一次Spring框架IOC功能的实现。IOC和AOP功能都是基于java反射实现的
源码地址:https://gitee.com/ericecc/simple_-spring.git
2. IOC
2.1 项目架构
2.2 主要步骤
- 创建ioc.xml
<beans xmlns:context=" ">
<!-- user bean-->
<bean id="user" class="com.example.spring.ioc.beans.User">
<property name="name" value="zhangsan"/>
<property name="age" value="20"/>
</bean>
<!-- table bean -->
<bean id="table" class="com.example.spring.ioc.beans.Table">
<property name="tableName" value="销售"/>
<property name="id" value="000001"/>
<property name="description" value="Spring IOC testing"/>
</bean>
<!-- 配置注解扫描 -->
<context:component-scan base-package="com.example.spring.ioc.beans"></context:component-scan>
</beans>
- 创建相应的Bean
public class Table {
String id;
String tableName;
String description;
@Override
public String toString() {
return "Table{" +
"id='" + id + '\'' +
", tableName='" + tableName + '\'' +
", description='" + description + '\'' +
'}';
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getTableName() {
return tableName;
}
public void setTableName(String tableName) {
this.tableName = tableName;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
public class User {
private String name;
private String age;
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age='" + age + '\'' +
'}';
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
}
package com.example.spring.ioc.beans;
import com.example.spring.ioc.annonation.AutoWired;
import com.example.spring.ioc.annonation.Beans;
/**
* 该类是用注解方式实现依赖注入的类
* 如果只是xml方式实现注入可以不写
*/
@Beans
public class DIBean {
@AutoWired // 自动注入的bean是通过ioc配置文件方式生成的,没有改成注解实现
public User user;
@AutoWired
public Table table;
@Override
public String toString() {
return "DIBean{" +
"user=" + user +
", table=" + table +
'}';
}
}
- IOC两种方式实现
package com.example.spring.ioc.utils;
import com.example.spring.ioc.annonation.AutoWired;
import com.example.spring.ioc.annonation.Beans;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.File;
import java.io.FileInputStream;
import java.lang.reflect.Field;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/**
* IOC 功能主要实现
* 1. 包含注解方式和配置文件两种方式
*/
public class MyIoc {
Map<String, Object> beansMap = new HashMap<>(); // bean 容器
private static final Logger LOGGER = LoggerFactory.getLogger(MyIoc.class);
public MyIoc(String location) throws Exception {
LOGGER.info("location:{}", location);
loadBeans(location); // 通过配置文件加载bean
initAttrs(); // 通过注解自动注入
}
/**
* 注解方式实现依赖自动注入
* 1. 遍历容器中存储的对象
* 2. 获取对象的Fields,
* 1. 检查Fields是否有AutoWire注解
* 2. 如果有AutoWire注解
* 3. 通过setAccessible(true) 和 set方法为Field注入对象
*
*/
private void initAttrs() throws IllegalAccessException {
Set<String> strings = beansMap.keySet();
for (String s : strings){
// 依赖注入
attrsAssign(beansMap.get(s));
}
}
/** 注解方式具体实现 **/
private void attrsAssign(Object o) throws IllegalAccessException {
Field[] declaredFields = o.getClass().getDeclaredFields();
for(Field f: declaredFields){
AutoWired annotation = f.getAnnotation(AutoWired.class);
if (annotation != null){
String name = f.getName();
Object bean = getBean(name);
if (o != null){
f.setAccessible(true);
f.set(o, bean);
}
}
}
}
/**
* 通过xml实现bean加载
* 1. MySpringApplicationContext(String url) 传递配置文件的地址
* 2. 解析xml配置文件
* 1. 解析出 id 和 类名
* 1. 通过Class.forName(类名) 加载对象
* 2、 通过.newInstance()实例化
* 2. 解析properties 用于生成具体对象
* 1. 解析出name 和 value
* 2. 通过 bean.getDeclaredField(name) 获取相应的Field
* 3. 通过 Field.set(value) 为Field注入信息
* 3. 将实例化的bean存入容器中
* @param location
* @throws Exception
*/
private void loadBeans(String location) throws Exception {
// 1. 读取xml配置文件
FileInputStream fileInputStream = new FileInputStream(location);
System.out.println(location);
// 2. 获取xml文件中的配置信息
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse(fileInputStream);
Element root = doc.getDocumentElement();
LOGGER.info("Element:{}", root.toString());
NodeList nodes = root.getChildNodes();
for (int i = 0; i < nodes.getLength(); i++){
Node node = nodes.item(i);
/** xml 配置方式主逻辑**/
if (node instanceof Element && node.getNodeName().equals("bean")){
Element ele = (Element) node;
String id = ele.getAttribute("id");
String className = ele.getAttribute("class");
LOGGER.info("id:{}, className:{}", id, className);
// 加载类
Class beanClass = null;
try {
beanClass = Class.forName(className);
} catch (ClassNotFoundException e){
e.printStackTrace();
return;
}
// 实例化
Object bean = beanClass.newInstance();
registerBean(id, bean);
// 获取属性
NodeList property = ele.getElementsByTagName("property");
for (int j = 0; j < property.getLength(); j++){
Element pro = (Element)property.item(j);
String name = pro.getAttribute("name");
String value = pro.getAttribute("value");
LOGGER.info("name:{}, value:{}", name, value);
// 获取相应字段
Field field = bean.getClass().getDeclaredField(name);
field.setAccessible(true);
if (value != null && value.length() > 0){
// 将bean对象上的field 设置为value
field.set(bean, value);
} else {
String ref = pro.getAttribute("ref");
LOGGER.info("ref:{}", ref);
if (ref == null || ref.length() == 0){
throw new IllegalArgumentException("ref config error");
}
field.set(bean,getBean(ref));
}
}
}
/** 包扫描 **/
if (node instanceof Element && node.getNodeName().equals("context:component-scan")){
Element el = (Element) node;
String attribute = el.getAttribute("base-package"); // com.example.spring.ioc.beans
// 替换.
String path = attribute.replaceAll("\\.", "\\/");
// com/example/spring/ioc/beans
URL url = Thread.currentThread().getContextClassLoader().getResource(path);
if (url != null){
String protocol = url.getProtocol();
// 遍历包下所有类
if (protocol.equalsIgnoreCase("file")){
File file = new File(url.getFile());
ArrayList<String> beansName = new ArrayList<>();
File[] files = file.listFiles();
for (File f : files){
// 读取包名
String name = f.getName().substring(0, f.getName().length() - 6);
String packageName = path.replaceAll("\\/", "\\.") + "." + name;
// 获取类内部内容
Class<?> aClass = Class.forName(packageName);
Beans annotation = aClass.getAnnotation(Beans.class);
if (annotation != null){
String aName = aClass.getName();
String[] splits = aName.split("\\.");
String pack = splits[splits.length - 1].toLowerCase();
registerBean(pack, aClass.newInstance()); // 实例化并存入容器
}
}
}
}
}
}
Set<String> strings = beansMap.keySet();
LOGGER.info("------------------Beans容器内容-------------------");
for (String s : strings){
LOGGER.info("key:{}, value:{}]", s, beansMap.get(s));
}
LOGGER.info("-------------------------------------------------");
}
private void registerBean(String id, Object bean) {
beansMap.put(id, bean);
}
public Object getBean(String ref) {
return beansMap.get(ref);
}
}
- SpringContext实现
import com.example.spring.ioc.utils.MyIoc;
public class MySpringApplicationContext {
private String configName;
public MySpringApplicationContext(String configName){
this.configName = configName;
}
/**
* 通过该方法将bean转换成指定类型
* @param name bean name
* @param c bean 的 class
* @param <T>
* @return
* @throws Exception
*/
public <T> T getBean(String name, Class<T> c) throws Exception {
String file = MySpringApplicationContext.class.getClassLoader().getResource(configName).getFile();
System.out.println(file);
MyIoc myIoc = new MyIoc(file);
Object bean = myIoc.getBean(name);
return (T) bean;
}
}
- 测试
import com.example.spring.ioc.beans.DIBean;
import com.example.spring.ioc.beans.Table;
import com.example.spring.ioc.beans.User;
import org.junit.Test;
public class MyIocTest {
@Test
public void test() throws Exception {
// 创建上下文
MySpringApplicationContext ctx = new MySpringApplicationContext("ioc.xml");
// 获取对象
User user = ctx.getBean("user", User.class);
Table table = ctx.getBean("table", Table.class);
DIBean dibean = ctx.getBean("dibean", DIBean.class);
// xml配置实现
System.out.println(user);
System.out.println(table);
// Beans实现
System.out.println(dibean);
}
}
-
结果