Spring:简单实现IOC
2021-02-21 本文已影响0人
侧耳倾听y
使用反射实现简单的IOC,目标:
1.仅支持单例、set方法注入的IOC
2.解决循环依赖
解决循环依赖:
个人认为有两种思路,假设A与B相互依赖:
1.创建A的时候,发现A需要B,所以再去创建B,引入另外一个map来解决无限循环的问题
2.将A和B都创建出来,之后再注入A和B所需要的的依赖
下面xml方式使用第一种思路,注解方式使用第二种思路
- 使用XML实现IOC
基本思路:读取xml文件,根据读取的结果,使用反射创建对象实例,并放入静态变量map中。
该实现的大部分代码,参考了一位同学的代码:https://github.com/HuangFromJYU/JSpring-IoC
只是我认为他代码里面有两个地方,我做了一些修改:
1.A对象中注入的B,并不是容器中的B
2.A与B互相依赖,会导致stackOverFlow
下面是代码实现:
读取xml配置
package mySpring.ioc.xml.reader;
import mySpring.ioc.xml.tagEntity.Bean;
import mySpring.ioc.xml.tagEntity.Property;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class ConfigurationReader {
public static Map<String, Bean> getBeanConfig(String path) {
// 存放配置信息,返回结果
Map<String, Bean> result = new HashMap<String, Bean>();
// 创建解析器
SAXReader reader = new SAXReader();
// 加载配置文件,不以/开头时默认是此类所在包下面取资源,以/开头则是从classpath下获取
InputStream is = ConfigurationReader.class.getResourceAsStream(path);
Document doc;
try {
doc = reader.read(is);
} catch (DocumentException e) {
throw new RuntimeException("加载配置文件出错");
}
List<Element> beanNodes = doc.selectNodes("//bean");
// 遍历所有bean节点,并将信息封装到Bean对象中
for (Element ele : beanNodes) {
Bean bean = new Bean();
bean.setName(ele.attributeValue("name"));
bean.setClassName(ele.attributeValue("class"));
// 获取bean节点下所有的property节点
List<Element> propNodes = ele.elements("property");
if (propNodes != null) {
for (Element prop : propNodes) {
Property p = new Property();
p.setName(prop.attributeValue("name"));
p.setValue(prop.attributeValue("value"));
p.setRef(prop.attributeValue("ref"));
// 将property添加到所属bean中
bean.getProperties().add(p);
}
}
result.put(bean.getName(), bean);
}
return result;
}
}
xml中的标签对应的实体
package mySpring.ioc.xml.tagEntity;
import lombok.Getter;
import lombok.Setter;
import java.util.ArrayList;
import java.util.List;
@Getter
@Setter
public class Bean {
private String name;
private String className;
private List<Property> properties = new ArrayList<Property>();
}
package mySpring.ioc.xml.tagEntity;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class Property {
private String name;
private String value;
private String ref;
}
获取Bean的context
package mySpring.ioc.xml;
import mySpring.ioc.BeanFactory;
import mySpring.ioc.xml.reader.ConfigurationReader;
import mySpring.ioc.xml.tagEntity.Bean;
import mySpring.ioc.xml.tagEntity.Property;
import org.apache.commons.beanutils.BeanUtils;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class ClassPathXmlApplicationContext implements BeanFactory {
/**
* 存放单例对象的map
*/
private static Map<String, Object> singletonMap = new HashMap<>();
/**
* 用于解决循环依赖
*/
private static Map<String, Object> earlySingletonMap = new HashMap<>();
private static Map<String, Bean> config;
public ClassPathXmlApplicationContext(String path) {
config = ConfigurationReader.getBeanConfig(path);
for (Map.Entry<String, Bean> e : config.entrySet()) {
// 获取bean信息
Bean bean = e.getValue();
// 如果设置成单例的才创建好bean对象放进容器中
createBeanByConfig(bean);
}
}
public Object getBean(String name) {
return singletonMap.get(name);
}
private Object createBeanByConfig(Bean bean) {
// 如果已经单例对象已经在缓存中,则直接返回
Object beanObj = singletonMap.get(bean.getName());
if (beanObj != null) {
return beanObj;
}
try {
Class<?> clazz = Class.forName(bean.getClassName());
// 创建bean对象
beanObj = clazz.newInstance();
// 把半成品对象放入,以备后续使用
earlySingletonMap.put(bean.getName(), beanObj);
// 获取bean对象中的property配置
List<Property> properties = bean.getProperties();
// 遍历bean对象中的property配置,并将对应的value或者ref注入到bean对象中
for (Property property : properties) {
Map<String, Object> params = new HashMap<>();
if (property.getValue() != null) {
params.put(property.getName(), property.getValue());
// 将value值注入到bean对象中
BeanUtils.populate(beanObj, params);
}
if (property.getRef() != null) {
Object ref = singletonMap.get(property.getRef());
if (ref == null) {
// 查看半成品缓存中是否有需要的实体
ref = earlySingletonMap.get(property.getRef());
if (ref == null) {
ref = createBeanByConfig(config.get(property.getRef()));
}
}
params.put(property.getName(), ref);
// 将ref对象注入bean对象中
BeanUtils.populate(beanObj, params);
}
}
earlySingletonMap.remove(bean.getName());
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException();
}
singletonMap.put(bean.getName(), beanObj);
return beanObj;
}
}
xml文件
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<bean name="A" class="mySpring.pojo.A">
<property name="name" value="i am a"/>
<property name="b" ref="B"/>
</bean>
<bean name="B" class="mySpring.pojo.B">
<property name="name" value="i am b"/>
<property name="c" ref="C"/>
</bean>
<bean name="C" class="mySpring.pojo.C">
<property name="name" value="i am c"/>
<property name="a" ref="A"/>
</bean>
</beans>
运行测试
package mySpring.ioc;
import mySpring.ioc.xml.ClassPathXmlApplicationContext;
import mySpring.pojo.A;
import mySpring.pojo.B;
import mySpring.pojo.C;
public class Runner {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("/applicationContext-ioc.xml");
A a = (A) context.getBean("A");
A a1 = (A) context.getBean("A");
B b = (B) context.getBean("B");
C c = (C) context.getBean("C");
System.out.println(a);
System.out.println(b);
System.out.println(b);
System.out.println(a == a1);
System.out.println(a.getB() == b);
System.out.println(b.getC() == c);
System.out.println(c.getA() == a);
}
}
- 使用注解实现IOC
基本思路是扫描对应包下加了注解的实体类,并创建对象和注入依赖
注解类
package mySpring.ioc.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAutowired {
}
package mySpring.ioc.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyComponent {
}
获取对应包下所有的class对象,这个方法写的并不好,是东拼西凑的一个实现。
package mySpring.ioc.annotation;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
public class ClazzUtils {
public static List<Class<?>> getAllClasses(String packageName) {
List<String> result = new ArrayList<>();
String suffixPath = packageName.replaceAll("\\.", "/");
ClassLoader loader = Thread.currentThread().getContextClassLoader();
try {
Enumeration<URL> urls = loader.getResources(suffixPath);
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
if (url != null) {
String protocol = url.getProtocol();
if ("file".equals(protocol)) {
getAllClassesNames(new File(url.getPath()), result);
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
return result.stream().map(className -> {
try {
return Class.forName(className);
} catch (ClassNotFoundException e) {
e.printStackTrace();
return null;
}
}).collect(Collectors.toList());
}
private static void getAllClassesNames(File file, List<String> list) {
if (!file.exists()) {
return;
}
if (file.isDirectory()) {
for (File listFile : Objects.requireNonNull(file.listFiles())) {
getAllClassesNames(listFile, list);
}
} else {
list.add(file.getPath().split("classes.")[1].replace("\\", ".").replace(".class", ""));
}
}
}
获取Bean的context
package mySpring.ioc.annotation;
import mySpring.ioc.BeanFactory;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class AnnotationApplicationContext implements BeanFactory {
private Map<String, Object> singletonMap = new HashMap<>();
public AnnotationApplicationContext(String packageName) {
List<Class<?>> classes = ClazzUtils.getAllClasses(packageName);
for (Class<?> clazz : classes) {
// 如果加了@MyComponent注解,则实例化
Annotation annotation = clazz.getAnnotation(MyComponent.class);
if (annotation != null) {
createBean(clazz);
}
}
// 依赖注入
for (Class<?> clazz : classes) {
autowired(clazz);
}
}
@Override
public Object getBean(String name) {
return singletonMap.get(name);
}
/**
* 利用反射创建实体
*
* @param clazz
*/
private void createBean(Class<?> clazz) {
String name = clazz.getSimpleName();
Object o = singletonMap.get(name);
if (o == null) {
try {
o = clazz.newInstance();
} catch (InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
singletonMap.put(name, o);
}
}
/**
* 给实例化后的对象,依赖注入
*
* @param clazz
*/
private void autowired(Class<?> clazz) {
Object o = singletonMap.get(clazz.getSimpleName());
List<Field> fields = getAutowired(clazz);
if (fields.size() > 0) {
for (Field field : fields) {
field.setAccessible(true);
Object diObject = singletonMap.get(field.getType().getSimpleName());
if (diObject == null) {
throw new RuntimeException();
}
try {
field.set(o, diObject);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
/**
* 获取全部需要注入的字段
*
* @param clazz
* @return
*/
private List<Field> getAutowired(Class<?> clazz) {
List<Field> list = new ArrayList<>();
Field[] fields = clazz.getDeclaredFields();
if (fields.length > 0) {
for (Field field : fields) {
MyAutowired annotation = field.getAnnotation(MyAutowired.class);
if (annotation != null) {
list.add(field);
}
}
}
return list;
}
}
运行测试
package mySpring.ioc;
import mySpring.ioc.annotation.AnnotationApplicationContext;
import mySpring.ioc.xml.ClassPathXmlApplicationContext;
import mySpring.pojo.A;
import mySpring.pojo.B;
import mySpring.pojo.C;
public class Runner {
public static void main(String[] args) {
// ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("/applicationContext-ioc.xml");
AnnotationApplicationContext context = new AnnotationApplicationContext("mySpring.pojo");
A a = (A) context.getBean("A");
A a1 = (A) context.getBean("A");
B b = (B) context.getBean("B");
C c = (C) context.getBean("C");
System.out.println(a);
System.out.println(b);
System.out.println(b);
System.out.println(a == a1);
System.out.println(a.getB() == b);
System.out.println(b.getC() == c);
System.out.println(c.getA() == a);
}
}
- 前面两种实现方式,用到一些公共的类,下面贴出来
beanFactory接口
package mySpring.ioc;
public interface BeanFactory {
Object getBean(String name);
}
实体类
package mySpring.pojo;
import lombok.Getter;
import lombok.Setter;
import mySpring.ioc.annotation.MyAutowired;
import mySpring.ioc.annotation.MyComponent;
@Getter
@Setter
@MyComponent
public class A {
private String name;
@MyAutowired
private B b;
}
package mySpring.pojo;
import lombok.Getter;
import lombok.Setter;
import mySpring.ioc.annotation.MyAutowired;
import mySpring.ioc.annotation.MyComponent;
@Getter
@Setter
@MyComponent
public class B {
private String name;
@MyAutowired
private C c;
}
package mySpring.pojo;
import lombok.Getter;
import lombok.Setter;
import mySpring.ioc.annotation.MyAutowired;
import mySpring.ioc.annotation.MyComponent;
@Getter
@Setter
@MyComponent
public class C {
private String name;
@MyAutowired
private A a;
}
我把代码放到了github上:https://github.com/wxyyrain/spring-study/tree/master/spring-homework/src/main
有什么不对的地方,欢迎大家交流讨论