接着上一篇,在上一篇完成了有关IOC的注解实现,这一篇用XML的方式实现IOC,并且完成AOP。
简易的IOC框图
注解的方式实现了左边的分支,那么就剩下右边的XML分支:
XmlContext:
这个类是也是AbstractApplicationContext的子类,和AnnotationContext相似,只不过这里是要解析XML文件而不是注解:
(关于XML文件的解析之前给过一篇博客:【Java】XML文件的解析
对于XML文件的处理,是不太容易的,会产生很多问题,后面只是实现核心步骤,很多属性就不考虑了!
首先给出XmlBean,和AnnotationBean一样,都是继承自BeanElement
复制代码
1 public class XmlBean implements BeanElement {
2 private boolean DI;
3 private Object object;
4 private Object proxy;
5 private Map
wiredMap;
6 // key:object的为注入成员 value:依赖的className
7 // 将不能注入的成员先保存起来
8
9 protected XmlBean() {
10 this(true, null, null);
11 }
12
13 protected XmlBean(Object object, Object proxy) {
14 this(true, object, proxy);
15 }
16
17 protected XmlBean(boolean dI, Object object, Object proxy) {
18 DI = dI;
19 this.object = object;
20 this.proxy = proxy;
21 }
22
23 protected void addWiredElement(Field field, String ref) throws RepeatProperty {
24 if (wiredMap == null) {
25 wiredMap = new HashMap<>();
26 }
27 if (wiredMap.containsKey(field)) {
28 throw new RepeatProperty(object.getClass() + "成员:" + field.getName() + "已定义!");
29 }
30 wiredMap.put(field, ref);
31 }
32
33 protected void setDI(boolean DI) {
34 this.DI = DI;
35 }
36
37 protected Map getWiredMap() {
38 return wiredMap;
39 }
40
41 @Override
42 @SuppressWarnings("unchecked")
43 public E getProxy() {
44 return (E) proxy;
45 }
46
47 @Override
48 public Object getObject() {
49 return object;
50 }
51
52 @Override
53 public boolean isDI() {
54 return DI;
55 }
56
57 }
复制代码
XmlContext
复制代码
1 public class XmlContext extends AbstractApplicationContext {
2 protected XmlContext() {
3 }
4
5 protected XmlContext(String xmlPath) {
6 innerParseXml(xmlPath);
7 }
8
9 // 和注解方式中的做法一样,只不过产生的是XML方式的BeanElement
10 private XmlBean addXmlBean(Class> klass, Object object, String classId, String className) throws BeansException {
11 Object proxy = aopFactory.creatCGLibProxy(klass, object);
12 XmlBean bean = new XmlBean(object, proxy);
13 add(classId, className, bean);
14 return bean;
15 }
16
17 protected void innerParseXml(String xmlPath) {
18 // 找到根标签
19 new XMLReader() {
20 @Override
21 public void dealElment(Element element, int index) {
22 // 处理bean标签
23 new XMLReader() {
24 @Override
25 public void dealElment(Element element, int index) {
26 // 得到id属性和class属性的值
27 String classId = element.getAttribute("id");
28 String className = element.getAttribute("class");
29 try {
30 // 由class得到类
31 Class> klass = Class.forName(className);
32 // 处理constructor标签
33 new XMLReader() {
34 @Override
35 public void dealElment(Element element, int index) {
36 // TODO 处理有参数的构造方法,这里就会遇到许多问题,在这里我就不处理了,后面会给出解决思路
37 }
38 }.parse(element, "constructor-arg");
39 // 由于上面没有处理带参数的构造方法,这里直接通过反射机制调用无参构造产生对象
40 // 并且利用产生的对象生成代理对象,最后得到Bean放入beanMap中
41 Object object = klass.newInstance();
42 XmlBean bean = addXmlBean(klass, object, classId, className);
43
44 // 处理property标签
45 new XMLReader() {
46 @Override
47 public void dealElment(Element element, int index) {
48 try {
49 dealProperty(element, klass, bean);
50 } catch (XmlPropertyMustNeedNameException e) {
51 e.printStackTrace();
52 } catch (Exception e) {
53 e.printStackTrace();
54 }
55 }
56 }.parse(element, "property");
57 } catch (Exception e1) {
58 e1.printStackTrace();
59 }
60 }
61 }.parse(element, "bean");
62 }
63 }.parse(XMLReader.openDocument(xmlPath), "SimpleSpring");
64 }
65
66 private void dealProperty(Element element, Class> klass, XmlBean bean)
67 throws XmlPropertyMustNeedNameException, Exception {
68 // 得到property标签name属性的值
69 String fieldName = element.getAttribute("name");
70 if (fieldName.length() <= 0) {
71 throw new XmlPropertyMustNeedNameException("Bean" + klass.getName() + "的Property标签必须声明name属性!");
72 }
73 // 通过反射机制得到成员
74 Field field = klass.getDeclaredField(fieldName);
75 // 得到该成员的类型
76 Class> fieldType = field.getType();
77 // 得到value属性
78 String value = element.getAttribute("value");
79 // 得到ref属性
80 String ref = element.getAttribute("ref");
81
82 // 判断ref和value是否同时存在
83 if (value.length() > 0 && ref.length() > 0) {
84 throw new CanNotJudgeParameterException("value:" + value + " ref:" + ref + "只能存在一个!");
85 }
86 Object arg = null;
87 // value存在,则直接通过类型转换给成员赋值
88 if (value.length() > 0) {
89 if (!fieldType.isPrimitive() && !fieldType.equals(String.class)) {
90 throw new ValueOnlyPrimitiveType("Value只能用于八大基本类型!");
91 }
92 // TypeConversion是我自己写的,将字符串转换为基本类型的工具
93 arg = TypeConversion.getValue(value, fieldType.getSimpleName());
94 field.setAccessible(true);
95 field.set(bean.getObject(), arg);
96 }
97 if (ref.length() > 0) {
98 // ref属性存在,由于存在相互依赖关系,所以现在不做处理,只是将其保存起来
99 // 设置该bean的状态为尚未注入
100 bean.setDI(false);
101 bean.addWiredElement(field, ref);
102 }
103 }
104
105 }
复制代码
XmlContext能做的工作也十分有限,只能完成简单的注入,剩下的注入工作留给下一级处理!
在这里之所以没有处理constructor标签,是因为对与构造方法的处理存在许多因素:
比如:
复制代码
1 public class Test {
2 public Test(String one, int two) {
3 ......
4 }
5 public Test(int two, String one) {
6 ......
7 }
8 }
复制代码
通过XML文件读取出来的都是字符串,如何区分它是字符串“123”,而不是int类型123?这两个构造方法到底执行哪个?
再比如说:
复制代码
1 public Test(int one, int two, Student student) {
2 ......
3 }
4
5 public Test(String one, int two, Student student) {
6 ......
7 }
8
9 public Test(int two, String one, Student student) {
10 ......
11 }
复制代码
通过反射机制,我们就需要得到构造方法的集合getConstructors();然后筛选出参数个数符合要求的子集,再遍历这个子集的每一个构造方法,然后遍历当前构造方法的所有参数,一个一个比对参数类型是否符合要求,直到找到符合要求的那一个为止,但是,如果说我们是想执行第三个构造方法,它却找到的是第一个,完全就出问题了!
所以Spring的解决办法是给出一个type属性
复制代码
1
2
3
4
5
复制代码
只有这样做才能真真区分,所以以后在使用Spring的constructor标签时,当构造方法有歧义时,一定要给出type属性,避免出错,也减少了查找时的遍历!
接下来就是最后一个类,xml分支的最高容器:
ClassPathXmlApplicationContext
上面的XmlContext只是完成了基本的注入问题,还有后续有关于注入之间的依赖关系,甚至是依赖循环(关于依赖循环在我的上一篇中有专门介绍,这里就不再介绍了)
复制代码
1 public class ClassPathXmlApplicationContext extends XmlContext {
2 public ClassPathXmlApplicationContext() {
3 }
4
5 public ClassPathXmlApplicationContext(String xmlPath) {
6 super(xmlPath);
7 }
8
9 public ClassPathXmlApplicationContext parseXml(String xmlPath) {
10 innerParseXml(xmlPath);
11 return this;
12 }
13
14 @Override
15 public T getBean(Class klass) throws BeansException {
16 String className = klass.getName();
17 BeanElement bean = beanMap.get(className);
18
19 if (bean == null) {
20 throw new BeansException("Bean :" + klass + "不存在!");
21 }
22 // 在这里还是只考虑XmlBean的注入,不考虑AnnotationBlean注解的完成情况
23 if (!bean.isDI() && bean instanceof XmlBean) {
24 autowired(className, (XmlBean)bean);
25 }
26
27 return bean.getProxy();
28 }
29
30 private void autowired(String klassName, XmlBean bean) throws BeansException {
31 // 和AnnotationBean的解决思路一样,先设置状态为已注入,防止循环依赖的无限递归
32 bean.setDI(true);
33 // 得到尚未注入的成员map
34 Map wiredMap = bean.getWiredMap();
35 if (wiredMap == null || wiredMap.isEmpty()) return;
36 // 遍历map
37 for (Field field : wiredMap.keySet()) {
38 String ref = wiredMap.get(field);
39 String tagClassName = beanNameMap.get(ref);
40 // ref如果是id则在beanNameMap中找,如果是className就在beanMap中找
41 BeanElement wiredBean = tagClassName == null ? beanMap.get(ref) : beanMap.get(tagClassName);
42 if (bean == null) {
43 return;
44 }
45 if (!wiredBean.isDI() && wiredBean instanceof XmlBean) {
46 autowired(ref, (XmlBean)wiredBean);
47 }
48 field.setAccessible(true);
49 try {
50 field.set(bean.getObject(), wiredBean.getObject());
51 } catch (Exception e) {
52 throw new BeansException(klassName + "依赖关系不正确!");
53 }
54 }
55 wiredMap.clear();
56 }
57
58 }
复制代码
看过注解方式的话再看XML就会发现两者其实是一回事,都是通过两者提供的映射关系,利用反射机制完成注入!
只不过两者提供的映射关系在解析起来时各有各的特点!
Xml方式的实现这里就简单实现了,来看看使用情况:
复制代码
1 public class StudentA {
2 String name;
3 private StudentB B;
4
5 public StudentA() {
6 }
7
8 @Override
9 public String toString() {
10 return "A:" + name + "->" + B;
11 }
12
13 }
14
15 @Component
16 public class StudentB {
17 private String name;
18 private StudentC C;
19
20 public StudentB() {
21 }
22
23 @Override
24 public String toString() {
25 return "B:" + name + "->" + C;
26 }
27
28 }
29
30 @Component
31 public class StudentC {
32 private String name;
33 private StudentA A;
34
35 public StudentC() {
36 }
37
38 @Override
39 public String toString() {
40 return "C:" + name;
41 }
42
43 }
复制代码
xml的配置:
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
复制代码
主函数:
复制代码
1 public static void main(String[] args) throws BeansException {
2 // 或者是使用BeanFactory beanFactory = new ClassPathXmlApplicationContext("/test_simple_spring.xml");
3 ApplicationContext applicationContext = new ClassPathXmlApplicationContext("/test_simple_spring.xml");
4 StudentA bean = applicationContext.getBean(StudentA.class);
5 System.out.println(bean);
6 }
复制代码
输出:
那么试一试注解和Xml方式的混合使用:
复制代码
1 @Component
2 public class StudentA {
3 @Value(value="我是A")
4 String name;
5 @Autowired
6 private StudentB B;
7
8 public StudentA() {
9 }
10
11 @Override
12 public String toString() {
13 return "A:" + name + "->" + B;
14 }
15
16 }
17
18 @Component
19 public class StudentB {
20 @Value(value="我是B")
21 private String name;
22 @Autowired
23 private StudentC C;
24
25 public StudentB() {
26 }
27
28 @Override
29 public String toString() {
30 return "B:" + name + "->" + C;
31 }
32
33 }
34 @Component
35 public class StudentC {
36 @Value(value="我是C")
37 private String name;
38 @Autowired
39 private StudentD D;
40
41 @Autowired
42 private StudentA A;
43
44 public StudentC() {
45 }
46
47 @Override
48 public String toString() {
49 return "C:" + name + "->" + D;
50 }
51
52 }
53
54 public class StudentD {
55 private String name;
56
57 public StudentD() {
58 }
59
60 @Override
61 public String toString() {
62 return "D:" + name;
63 }
64
65 }
复制代码
Xml配置:
复制代码
1
2
3
关键字: