手写IOC容器(xml,注解)
软件设计模式期末作业的设计内容:
在网上参考了资料,将两份资料,再加上自己的理解融合成一份!!
xml的参考资料’君君要上天’手写的IOC容器
注解的参考资料’楠哥教你学Java’手写的IOC容器
1.概念
IOC又称控制反转,把原先我们代码里面需要实现的对象的创建、依赖的代码,反转给容器来帮忙实现。
2.设计思路
A.配置文件配置包扫描路径
B.递归包扫描获取.class文件
C.反射确定需要交给IOC管理的类
D.对需要注入的类进行依赖注入
3.XMl配置
3.1导入依赖
<dependencies>
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>1.6.1</version>
</dependency>
<dependency>
<groupId>jaxen</groupId>
<artifactId>jaxen</artifactId>
<version>1.1.4</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
<scope>compile</scope>
</dependency>
</dependencies>
3.2编写Property
package com.myspring.bean;
/**
* @program: springioc-demo
* @description:
* @author: SmallNet
* @create: 2021-12-29 15:11
**/
public class Property {
private String name;
private String value;
private String ref;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public String getRef() {
return ref;
}
public void setRef(String ref) {
this.ref = ref;
}
}
3.3编写Bean
package com.myspring.bean;
import java.util.ArrayList;
import java.util.List;
/**
* @program: springioc-demo
* @description:
* @author: SmallNet
* @create: 2021-12-29 15:10
**/
public class Bean {
private String beanName;
private String className;
private Class beanClass;
private List<Property> properties = new ArrayList<Property>();//bean节点下可以有多个property节点
public Bean(){}
public Bean(String beanName,Class beanClass){
this.beanName=beanName;
this.beanClass=beanClass;
}
public String getBeanName() {
return beanName;
}
public void setBeanName(String beanName) {
this.beanName = beanName;
}
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
public Class getBeanClass() {
return beanClass;
}
public void setBeanClass(Class beanClass) {
this.beanClass = beanClass;
}
public List<Property> getProperties() {
return properties;
}
public void setProperties(List<Property> properties) {
this.properties = properties;
}
@Override
public String toString() {
return "Bean{" +
"beanName='" + beanName + '\'' +
", className='" + className + '\'' +
", beanClass=" + beanClass +
", properties=" + properties +
'}';
}
}
3.4编写工具类BeanUtil
package com.myspring.utils;
import java.lang.reflect.Method;
public class BeanUtil {
/**
* 获取obj类的name属性的setter方法
* @param obj
* @param name
* @return
*/
public static Method getSetterMethod(Object obj,String name){
Method method = null;
//setter方法名称(驼峰)
name = "set"+name.substring(0,1).toUpperCase()+name.substring(1);
try {
Method[] methods = obj.getClass().getMethods();
//遍历该类的所有方法
for(int i=0;i<methods.length;i++){
Method m = methods[i];
if(m.getName().equals(name)){
method = obj.getClass().getMethod(name,m.getParameterTypes());
break;
}
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
return method;
}
}
3.5编写扫描器XmlScanner
package com.myspring.scanner;
import com.myspring.bean.Bean;
import com.myspring.bean.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;
/**
* @program: springioc-demo-20220102
* @description:
* @author: SmallNet
* @create: 2022-01-02 19:34
**/
public class XmlScanner {
/**
* 读取配置文件
* @param path 配置文件路径
* @return
*/
public static Map<String, Bean> getBeanMap(String path){
Map<String, Bean> beanMap = new HashMap<String, Bean>();
//使用dom4j和xpath读取xml文件
//使用SAXReader需要导入dom4j-full.jar包。
//dom4j是一个Java的XML API,用来读写XML文件的。
// 定义一个文档
Document doc = null;
// 创建xml解析对象
SAXReader reader = new SAXReader();
//getResourceAsStream为读取文件,返回的是inputStream类型的(path传进来的参数)
InputStream in = XmlScanner.class.getResourceAsStream(path);
try {
//xml解析对象对输入流进行解析,解析完放在doc文档中
doc = reader.read(in);
} catch (DocumentException e) {
e.printStackTrace();
throw new RuntimeException("请检查您的xml配置文件路径是否正确!");
}
//定义xpath,取出所有的bean
String xpath = "//bean";
//对bean进行遍历 文档进行选则节点(bean)
List<Element> list = doc.selectNodes(xpath);
//当文件的bean有至少一个,则list!=null
if(list!=null){
//for加强循环,把list中的element取出来(bean)
for (Element beanEle : list) {
//new一个bean对象
Bean bean = new Bean();
//bean节点的id
String id = beanEle.attributeValue("id");
//bean节点的class属性
String className = beanEle.attributeValue("class");
//封装到bean对象中
bean.setBeanName(id);
bean.setClassName(className);
//获取bean节点下所有的property节点(属性)
List<Element> proList = beanEle.elements("property");
//当bean中的属性对象个数大于0,至少含有一个
if(proList != null){
for (Element pro : proList) {
Property prop = new Property();
String propName = pro.attributeValue("name");
String propValue = pro.attributeValue("value");
String propRef = pro.attributeValue("ref");
//封装到property属性中
prop.setName(propName);
prop.setValue(propValue);
prop.setRef(propRef);
bean.getProperties().add(prop);
}
}
//id是不应重复的(如果后注册的一个新Bean与之前注册过的存在重复)
if(beanMap.containsKey(id)){
throw new RuntimeException("bean节点ID重复:" + id);
}
//将bean封装到map中
beanMap.put(id, bean);
}
}
return beanMap;
}
}
3.6编写BeanFactory
package com.myspring.context;
/**
* @program: springioc-demo-20220102
* @description:
* @author: SmallNet
* @create: 2022-01-02 14:59
**/
public interface BeanFactory {
Object getBean(String beanName);
}
3.7编写ClassPathXmlApplicationContext
package com.myspring.context;
import com.myspring.bean.Bean;
import com.myspring.bean.Property;
import com.myspring.scanner.XmlScanner;
import com.myspring.utils.BeanUtil;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
/**
* @program: springioc-demo
* @description:
* @author: SmallNet
* @create: 2021-12-29 15:14
**/
public class ClassPathXmlApplicationContext implements BeanFactory {
//定义一个IOC容器
private Map<String, Object> iocMap;
//定义一个存储Bean的容器
private Map<String, Bean> beanMap;
/**
* 构造函数
* 1. 初始化IOC容器
* 2. 加载配置文件,生成bean对象放入IOC容器
* @param path
*/
public ClassPathXmlApplicationContext(String path){
//初始化IOC容器
iocMap = new HashMap<String, Object>();
//读取配置文件
beanMap = XmlScanner.getBeanMap(path);
if(beanMap!=null){
for(Entry<String, Bean> entry : beanMap.entrySet()){
String beanId = entry.getKey();
Bean bean = entry.getValue();
//根据bean生成相应的对象
Object object = createBean(bean);
iocMap.put(beanId, object);
}
}
}
/**
* 根据bean生成对象实例
* @param bean
* @return
*/
private Object createBean(Bean bean) {
String beanId = bean.getBeanName();
String className = bean.getClassName();
Class c = null;
Object object = null;
try {
//根据bean的class属性生成对象--》反射
c = Class.forName(className);
} catch (ClassNotFoundException e) {
throw new RuntimeException("您配置的class属性不合法:"+className);
}
try {
//该方法调用的是类的无参构造方法
object = c.newInstance();
} catch (Exception e) {
throw new RuntimeException("该类缺少一个无参构造方法:"+className);
}
//将bean的属性封装到对象中
if(bean.getProperties() != null){
for(Property p : bean.getProperties()){
//情况一:配置文件中使用的是value属性注入
if(p.getValue() != null){
//获取属性对应的setter方法
Method getMethod = BeanUtil.getSetterMethod(object,p.getName());
try {
//调用set方法注入
getMethod.invoke(object, p.getValue());
} catch (Exception e) {
throw new RuntimeException("属性名称不合法或者没有相应的getter方法:"+p.getName());
}
}
//情况二:配置文件中使用的是ref属性注入
if(p.getRef() != null){
//获取属性对应的setter方法
Method getMethod = BeanUtil.getSetterMethod(object,p.getName());
//从容器中找到依赖的对象
Object obj = iocMap.get(p.getRef());
if(obj == null){
throw new RuntimeException("没有找到依赖的对象:"+p.getRef());
}else{
//调用set方法注入
try {
getMethod.invoke(object, obj);
} catch (Exception e) {
throw new RuntimeException("属性名称不合法或者没有相应的getter方法:"+p.getName());
}
}
}
}
}
return object;
}
@Override
public Object getBean(String beanName) {
return iocMap.get(beanName);
}
}
3.8测试
3.8.1编写Cat类
package com.myspring.test;
public class Cat {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Cat{" +
"name='" + name + '\'' +
'}';
}
}
3.8.2编写People类
package com.myspring.test;
public class People {
private String name;
private Cat cat;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Cat getCat() {
return cat;
}
public void setCat(Cat cat) {
this.cat = cat;
}
@Override
public String toString() {
return "People{" +
"name='" + name + '\'' +
", cat=" + cat +
'}';
}
}
3.8.3编写ApplicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<bean id="cat" class="com.myspring.test.Cat">
<property name="name" value="小喵"></property>
</bean>
<bean id="people" class="com.myspring.test.People">
<property name="name" value="小明"></property>
<property name="cat" ref="cat"></property>
</bean>
</beans>
3.8.4编写Test类
package com.myspring.test;
import com.myspring.bean.Bean;
import com.myspring.context.BeanFactory;
import com.myspring.context.ClassPathXmlApplicationContext;
import com.myspring.scanner.XmlScanner;
import java.util.Map;
public class Test {
public static void main(String[] args) {
//测试xml
BeanFactory factory = new ClassPathXmlApplicationContext("/applicationContext.xml");
People people = (People) factory.getBean("people");
System.out.println(people.toString());
Cat cat = (Cat) factory.getBean("cat");
System.out.println(cat.toString());
}
}
测试成功!!
4.注解配置【注解所需要的Bean、BeanFactory不需要重写,故注解省略】
4.1编写注解
4.1.1编写注解@Autowired
package com.myspring.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 Autowired {
}
4.1.2编写注解@Component
package com.myspring.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 Component {
String value() default "";
}
4.1.3编写注解@Qualifier
package com.myspring.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 Qualifier {
String value();
}
4.1.4编写注解@Value
package com.myspring.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 Value {
String value();
}
4.2编写注解扫描器AnnotationScanner
package com.myspring.scanner;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.net.URL;
import java.net.URLDecoder;
import java.util.Enumeration;
import java.util.LinkedHashSet;
import java.util.Set;
/**
* @program: springioc-demo-20220102
* @description:
* @author: SmallNet
* @create: 2022-01-02 19:45
**/
public class AnnotationScanner {
public static Set<Class<?>> getClasses(String pack) {
// 第一个class类的集合
Set<Class<?>> classes = new LinkedHashSet<Class<?>>();
// 是否循环迭代
boolean flag = true;
// 获取包的名字 并进行替换
String packageName = pack;
String packageDirName = packageName.replace('.', '/');
// 定义一个枚举的集合 并进行循环来处理这个目录下的things
Enumeration<URL> dirs;
try {
dirs = Thread.currentThread().getContextClassLoader().getResources(packageDirName);
// 循环迭代下去
while (dirs.hasMoreElements()) {
// 获取下一个元素
URL url = dirs.nextElement();
// 得到协议的名称
String protocol = url.getProtocol();
// 如果是以文件的形式保存在服务器上
if ("file".equals(protocol)) {
// 获取包的物理路径
String filePath = URLDecoder.decode(url.getFile(), "UTF-8");
// 以文件的方式扫描整个包下的文件 并添加到集合中
findByFile(packageName, filePath, flag, classes);
}
}
} catch (IOException e) {
e.printStackTrace();
}
return classes;
}
private static void findByFile(String packageName, String packagePath, final boolean flag, Set<Class<?>> classes) {
// 获取此包的目录 建立一个File
File dir = new File(packagePath);
// 如果不存在或者 也不是目录就直接返回
if (!dir.exists() || !dir.isDirectory()) {
return;
}
// 如果存在 就获取包下的所有文件 包括目录
File[] dirfiles = dir.listFiles(new FileFilter() {
// 自定义过滤规则 如果可以循环(包含子目录) 或则是以.class结尾的文件(编译好的java类文件)
@Override
public boolean accept(File file) {
return (flag && file.isDirectory()) || (file.getName().endsWith(".class"));
}
});
// 循环所有文件
for (File file : dirfiles) {
// 如果是目录 则继续扫描
if (file.isDirectory()) {
findByFile(packageName + "." + file.getName(), file.getAbsolutePath(), flag, classes);
} else {
// 如果是java类文件 去掉后面的.class 只留下类名
String className = file.getName().substring(0, file.getName().length() - 6);
try {
// 添加到集合中去
classes.add(Thread.currentThread().getContextClassLoader().loadClass(packageName + '.' + className));
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
}
}
4.3编写AnnotationApplicationContext
package com.myspring.context;
import com.myspring.annotation.Autowired;
import com.myspring.annotation.Component;
import com.myspring.annotation.Qualifier;
import com.myspring.annotation.Value;
import com.myspring.bean.Bean;
import com.myspring.scanner.AnnotationScanner;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
/**
* @program: springioc-demo-20220102
* @description:
* @author: SmallNet
* @create: 2022-01-02 19:47
**/
public class AnnotationApplicationContext implements BeanFactory {
private Map<String,Object> iocMap = new HashMap<>();
private List<String> beanNames = new ArrayList<>();
public AnnotationApplicationContext(String pack) {
//遍历包,找到目标类(原材料)
Set<Bean> bean = findBean(pack);
//根据原材料创建bean
createObject(bean);
//自动装载
autowireObject(bean);
}
public void autowireObject(Set<Bean> bean){
Iterator<Bean> iterator = bean.iterator();
while (iterator.hasNext()) {
Bean beanDefinition = iterator.next();
Class clazz = beanDefinition.getBeanClass();
Field[] declaredFields = clazz.getDeclaredFields();
for (Field declaredField : declaredFields) {
Autowired annotation = declaredField.getAnnotation(Autowired.class);
if(annotation!=null){
Qualifier qualifier = declaredField.getAnnotation(Qualifier.class);
if(qualifier!=null){
//byName
try {
String beanName = qualifier.value();
Object beanOb = getBean(beanName);
String fieldName = declaredField.getName();
String methodName = "set"+fieldName.substring(0, 1).toUpperCase()+fieldName.substring(1);
Method method = clazz.getMethod(methodName, declaredField.getType());
Object object = getBean(beanDefinition.getBeanName());
method.invoke(object, beanOb);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
}
}
}
public Object getBean(String beanName){
return iocMap.get(beanName);
}
public String[] getBeanNames(){
return beanNames.toArray(new String[0]);
}
public Integer getBeanCount(){
return beanNames.size();
}
public void createObject(Set<Bean> bean){
Iterator<Bean> iterator = bean.iterator();
while (iterator.hasNext()) {
Bean beanDefinition = iterator.next();
Class clazz = beanDefinition.getBeanClass();
String beanName = beanDefinition.getBeanName();
try {
//创建的对象
Object object = clazz.getConstructor().newInstance();
//完成属性的赋值
Field[] declaredFields = clazz.getDeclaredFields();
for (Field declaredField : declaredFields) {
Value valueAnnotation = declaredField.getAnnotation(Value.class);
if(valueAnnotation!=null){
String value = valueAnnotation.value();
String fieldName = declaredField.getName();
String methodName = "set"+fieldName.substring(0, 1).toUpperCase()+fieldName.substring(1);
Method method = clazz.getMethod(methodName,declaredField.getType());
//完成数据类型转换
Object val = null;
switch (declaredField.getType().getName()){
case "java.lang.Integer":
val = Integer.parseInt(value);
break;
case "java.lang.String":
val = value;
break;
case "java.lang.Float":
val = Float.parseFloat(value);
break;
}
method.invoke(object, val);
}
}
//存入缓存
iocMap.put(beanName, object);
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
}
public Set<Bean> findBean(String pack){
//1、获取包下的所有类
Set<Class<?>> classes = AnnotationScanner.getClasses(pack);
Iterator<Class<?>> iterator = classes.iterator();
Set<Bean> bean = new HashSet<>();
while (iterator.hasNext()) {
//2、遍历这些类,找到添加了注解的类
Class<?> clazz = iterator.next();
Component componentAnnotation = clazz.getAnnotation(Component.class);
if(componentAnnotation!=null){
//获取Component注解的值
String beanName = componentAnnotation.value();
if("".equals(beanName)){
//获取类名首字母小写
String className = clazz.getName().replaceAll(clazz.getPackage().getName() + ".", "");
beanName = className.substring(0, 1).toLowerCase()+className.substring(1);
}
//3、将这些类封装成BeanDefinition,装载到集合中
bean.add(new Bean(beanName, clazz));
beanNames.add(beanName);
}
}
return bean;
}
}
4.4测试
4.4.1编写Book类
package com.myspring.test;
import com.myspring.annotation.Component;
import com.myspring.annotation.Value;
import lombok.Data;
@Component
//@Data
public class Book {
@Value("BOOK1001")
private String id;
@Value("软件设计模式")
private String name;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Book{" +
"id='" + id + '\'' +
", name='" + name + '\'' +
'}';
}
}
4.4.2编写Student类
package com.myspring.test;
import com.myspring.annotation.Autowired;
import com.myspring.annotation.Component;
import com.myspring.annotation.Qualifier;
import com.myspring.annotation.Value;
@Component
public class Student {
@Value("小红")
private String name;
@Value("18")
private Integer age;
@Autowired
@Qualifier("book")
private Book book;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Book getBook() {
return book;
}
public void setBook(Book book) {
this.book = book;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", book=" + book +
'}';
}
}
4.4.3编写Test类
package com.myspring.test;
import com.myspring.bean.Bean;
import com.myspring.context.AnnotationApplicationContext;
import com.myspring.context.BeanFactory;
import java.util.Map;
public class Test {
public static void main(String[] args) {
//测试注解
BeanFactory factory1 = new AnnotationApplicationContext("com.myspring.test");
Student student = (Student) factory1.getBean("student");
System.out.println(student);
Book book = (Book) factory1.getBean("book");
System.out.println("book->"+book.toString());
}
}
测试成功!!
有需要可以找我要源码!!侵权私聊删!!