前一段时间比较忙,手写系列停了一段时间,今天完成了springIOC系列,废话不多说,直接上干货。
下面是整个目录结构:
下面我多包下的类文件一一详解
annotation包下面是两个注解
下面是控制类
下面是模仿springIOC里的容器类(ApplicationContext)和Bean工厂(BeanFactory),以及他的初始化类(ClassPathXmlApplicationContext)
public class BeanFactory implements ApplicationContext {
public static final Map<Class<?>, Object> ioc = new ConcurrentHashMap<Class<?>, Object>();
List<Class<?>> classList = new ArrayList<Class<?>>();
public BeanFactory() {
String packageName = "com.max";
// 加载类文件
initClass(packageName);
// 初始化
instanceClass();
// 属性赋值
injectInstance();
}
@Override
public Object getBean(String name) {
// TODO Auto-generated method stub
return null;
}
public void initClass(String packageName) {
URL url = this.getClass().getClassLoader().getResource(packageName.replace(".", "/"));
File dir = new File(url.getFile());
for (File file : dir.listFiles()) {
if (file.isDirectory()) {
initClass(packageName.replace(".", "/") + "/" + file.getName());
} else {
try {
Class<?> clazz = Class
.forName(packageName.replace("/", ".") + "." + file.getName().replace(".class", ""));
classList.add(clazz);
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
public void instanceClass() {
for (Class<?> clazz : classList) {
if (clazz.isAnnotationPresent(MyComponent.class)) {
try {
ioc.put(clazz, clazz.newInstance());
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
private void injectInstance() {
for (Class<?> clazz : classList) {
if (clazz.isAnnotationPresent(MyComponent.class)) {
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
if (field.isAnnotationPresent(MyInject.class)) {
field.setAccessible(true);
try {
field.set(ioc.get(clazz), ioc.get(field.getType()));
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
}
@Override
public Object getBean(Class<?> name) {
return ioc.get(name);
}
}
public class ClassPathXmlApplicationContext implements ApplicationContext {
private Map<String, Object> ioc = new HashMap<>();
public ClassPathXmlApplicationContext(String fileName) {
URL url = this.getClass().getClassLoader().getResource(fileName);
File file = new File(url.getFile());
try {
xmlParse(file);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
public Object getBean(String name) {
// TODO Auto-generated method stub
return ioc.get(name);
}
/**
* 解析xml文件
*
* @param file
*/
private void xmlParse(File file) throws Exception {
SAXBuilder builder = new SAXBuilder();
Document document = builder.build(file);
// 创建XPath对象,反射获取XPath对象
XPathFactory factory = XPathFactory.instance();
// 获取所有的Bean节点,XPath解析写的写法可以了解一下
XPathExpression expression = factory.compile("//bean");
// 注释了旧版本的写法获取所有的Bean节点
// XPath xPath=XPath.newInstance("//bean");
// List beans=xPath.selectNodes(document);
List beans = expression.evaluate(document);
Iterator iterator = beans.iterator();
while (iterator.hasNext()) {
Element bean = (Element) iterator.next();
// 获取配置文件的id属性值
String id = bean.getAttributeValue("id");
String cls = bean.getAttributeValue("class");
// 反射拿到类的相应信息,首先是拿到类的实例对象
Class clazz = Class.forName(cls);
Object object = clazz.newInstance();
// 获取类的所有方法,然后通过set方法给这个对象设置属性值
Method[] methods = clazz.getDeclaredMethods();
// 遍历Bean节点下的所有属性和方法,一一匹配,反射设置对象的属性值
List<Element> list = bean.getChildren("property");
for (Element element : list) {
for (int i = 0; i < methods.length; i++) {
String methodName = methods[i].getName();
// 属性名
String temp = "";
// 这里检索set方法
if (methodName.startsWith("set")) {
// 这里就只截取set方法的方法名并且转换为小写的名字
temp = methodName.substring(3).toLowerCase();
// 属性为普通对象的属性
if (element.getAttribute("name") != null) {
if (temp.equals(element.getAttributeValue("name"))) {
// 反射给对象设置值
Class<?> parameterType = methods[i].getParameterTypes()[0];
if (parameterType == Integer.class || parameterType == int.class) {
methods[i].invoke(object, Integer.parseInt(element.getAttributeValue("value")));
} else if (parameterType == String.class) {
methods[i].invoke(object, element.getAttributeValue("value"));
}
}
} else { // 属性为引用对象的属性
methods[i].invoke(object, ioc.get(element.getAttributeValue("ref")));
}
}
}
}
// 将对象添加到容器里面
ioc.put(id, object);
}
}
@Override
public Object getBean(Class<?> name) {
// TODO Auto-generated method stub
return null;
}
}
这个是对象类,通过IOC根据配置文件初始化对象
public class Person {
private String name;
private int age;
private String sex;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
@Override
public String toString() {
System.out.println("to string " + "Person [name=" + name + ", age=" + age + ", sex=" + sex + "]");
return "Person [name=" + name + ", age=" + age + ", sex=" + sex + "]";
}
}
下面是业务逻辑层
@MyComponent
public class PersonService {
private Person person;
public Person getPerson() {
return person;
}
public void setPerson(Person person) {
this.person = person;
}
public void print(){
System.out.println("i am max");
}
}
IOC的配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<bean id="Person" class="com.max.po.Person">
<property name="name" value="Tom" />
<property name="sex" value="男" />
<property name="age" value="21" />
</bean>
<bean id="PersonService" class="com.max.service.PersonService">
<property ref="Person" />
</bean>
</beans>
到这里手写spring IOC就结束了,看上去简单,其实里面涉及的东西还是很多的,值得大家好好学习。