(一)IoC/DI
功能
- 配置解析:将配置文件解析为BeanDefinition结构,便于BeansFactory创建对象
- 对象创建:BeansFactory 根据配置文件通过反射创建对象,所有类对象都在一个工厂类中创建,采用反射机制动态加载类,避免代码膨胀
- 对象生命周期管理:在 BeanDefinition 中配置scope(每次返回新对象还是之前创建好的)、lazy-init(用到时创建还是启动时创建)属性
组成
- ApplicationContext:执行入口
- ClassPathXmlApplicationContext:ApplicationContext 的实现类
- BeanConfigParser:将配置文件解析为 BeanDefinition
- XmlBeanConfigParser:BeanConfigParser 的实现类
- BeanDefinition:类的定义,包括对象 id、classname、创建配置等
- BeansFactory:根据配置文件解析得到的 BeanDefinition 创建对象
- RateLimiter.java:要创建的对象
- RedisCounter.java:RateLImiter 的依赖类
- DiDemo:主程序
- beans.xml:配置文件
过程
- 写好要创建的类 RateLimiter
- 在 xml 配置文件中写明要创建的类信息
- 在客户端传入配置文件名 beans.xml,获取 applicationContext 对象的实例 ClassPathXmlApplicationContext
- 在 ClassPathXmlApplicationContext 中通过 XmlBeanConfigParser 解析配置文件,获取 BeanDefinition
- 通过 BeansFactory ,根据 BeanDefinition 创建目标类 rateLimiter、rediscounter 对象,及两者的依赖关系
代码
ApplicationContext
1 package di;
2
3 public interface ApplicationContext {
4 Object getBean(String beanId);
5 }
View Code
ClassPathXmlApplicationContext
1 package di;
2
3 import java.io.IOException;
4 import java.io.InputStream;
5 import java.util.List;
6
7 public class ClassPathXmlApplicationContext implements ApplicationContext{
8 private BeansFactory beansFactory;
9 private BeanConfigParser beanConfigParser;
10
11 public ClassPathXmlApplicationContext(String configLocation) {
12 this.beansFactory = new BeansFactory();
13 this.beanConfigParser = new XmlBeanConfigParser();
14 loadBeanDefinitions(configLocation);
15 }
16
17 private void loadBeanDefinitions(String configLocation) {
18 InputStream in = null;
19 try {
20 in = this.getClass().getResourceAsStream(configLocation);
21 if (in == null) {
22 throw new RuntimeException(("Can not find config file: " + configLocation));
23 }
24
25 List<BeanDefinition> beanDefinitions = beanConfigParser.parse(in);
26 beansFactory.addBeanDefinitions(beanDefinitions);
27 } finally {
28 if(in != null) {
29 try {
30 in.close();
31 } catch (IOException e) {
32 }
33 }
34 }
35 }
36
37 @Override
38 public Object getBean(String beanId) {
39 return beansFactory.getBean(beanId);
40 }
41 }
View Code
BeanConfigParser
1 package di;
2
3 import java.io.InputStream;
4 import java.util.List;
5
6 public interface BeanConfigParser {
7 List<BeanDefinition> parse(InputStream inputStream);
8 }
View Code
XmlBeanConfigParser
1 package di;
2
3 import org.w3c.dom.Document;
4 import org.w3c.dom.Element;
5 import org.w3c.dom.Node;
6 import org.w3c.dom.NodeList;
7
8 import javax.xml.parsers.DocumentBuilder;
9 import javax.xml.parsers.DocumentBuilderFactory;
10 import java.io.InputStream;
11 import java.util.ArrayList;
12 import java.util.List;
13
14 public class XmlBeanConfigParser implements BeanConfigParser{
15
16 @Override
17 public List<BeanDefinition> parse(InputStream inputStream) {
18 List beanDefinitions = new ArrayList<>();
19
20 try {
21 DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
22 DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
23 Document doc = documentBuilder.parse(inputStream);
24
25 // TODO: read it later, 关于 xml 为什么需要 normalize 一下
26 //optional, but recommended
27 //read this - http://stackoverflow.com/questions/13786607/normalization-in-dom-parsing-with-java-how-does-it-work
28 doc.getDocumentElement().normalize();
29
30 NodeList beanList = doc.getElementsByTagName("bean");
31
32 for (int i = 0; i < beanList.getLength(); i++) {
33 Node node = beanList.item(i);
34 if (node.getNodeType() != Node.ELEMENT_NODE) continue;
35
36 Element element = (Element) node;
37 BeanDefinition beanDefinition = new BeanDefinition(
38 element.getAttribute("id"),
39 element.getAttribute("class")
40 );
41 if (element.getAttribute("scope").equals("singleton")) {
42 beanDefinition.setScope(BeanDefinition.Scope.SINGLETON);
43 }
44 if (element.getAttribute("lazy-init").equals("true")) {
45 beanDefinition.setLazyInit(true);
46 }
47 loadConstructorArgs(
48 element.getElementsByTagName("constructor-arg"),
49 beanDefinition
50 );
51
52 beanDefinitions.add(beanDefinition);
53 }
54 } catch (Exception e) {
55 e.printStackTrace();
56 }
57
58 return beanDefinitions;
59 }
60
61 public void loadConstructorArgs(NodeList nodes, BeanDefinition beanDefinition) {
62 for (int i = 0; i < nodes.getLength(); i++) {
63 Node node = nodes.item(i);
64 if (node.getNodeType() != Node.ELEMENT_NODE) continue;
65 Element element = (Element) node;
66
67 BeanDefinition.ConstructorArg constructorArg = null;
68 if (!element.getAttribute("type").isEmpty()) {
69 constructorArg = new BeanDefinition.ConstructorArg.Builder()
70 .setArg(element.getAttribute("value"))
71 .setType(String.class)
72 .build();
73 }
74
75 if (!element.getAttribute("ref").isEmpty()) {
76 constructorArg = new BeanDefinition.ConstructorArg.Builder()
77 .setRef(true)
78 .setArg(element.getAttribute("ref"))
79 .build();
80 }
81
82 beanDefinition.addConstructorArg(constructorArg);
83 }
84 }
85 }
View Code
BeanDefinition
1 package di;
2
3 import java.util.ArrayList;
4 import java.util.List;
5
6 public class BeanDefinition {
7 private String id;
8 private String className;
9 private List<ConstructorArg> constructorArgs = new ArrayList();
10 private Scope scope = Scope.PROTOTYPE;
11 private boolean lazyInit = false;
12
13 public BeanDefinition(String id, String className) {
14 this.id = id;
15 this.className = className;
16 }
17
18 public Boolean isSingleton() {
19 return scope.equals(Scope.SINGLETON);
20 }
21
22 public boolean isLazyInit() {
23 return lazyInit;
24 }
25
26 public void addConstructorArg(ConstructorArg constructorArg) {
27 this.constructorArgs.add(constructorArg);
28 }
29
30 // getter && setter
31 public void setScope(Scope scope) {
32 this.scope = scope;
33 }
34 public void setLazyInit(Boolean lazyInit) {
35 this.lazyInit = lazyInit;
36 }
37 public String getId() {
38 return id;
39 }
40 public String getClassName() {
41 return className;
42 }
43 public List<ConstructorArg> getConstructorArgs() {
44 return constructorArgs;
45 }
46
47
48 // Static Below
49 public static enum Scope {
50 SINGLETON,
51 PROTOTYPE
52 }
53
54 public static class ConstructorArg {
55 private boolean isRef;
56 private Class type;
57 private Object arg;
58
59 /**
60 * 内部静态类,可以访问私有构造函数?
61 */
62 private ConstructorArg(Builder builder) {
63 this.isRef = builder.getIsRef();
64 this.type = builder.getType();
65 this.arg = builder.getArg();
66 }
67
68 public static class Builder {
69 private boolean isRef = false;
70 private Class type;
71 private Object arg;
72
73 public Builder setRef(Boolean isRef) {
74 this.isRef = isRef;
75 return this;
76 }
77
78 public Builder setType(Class type) {
79 this.type = type;
80 return this;
81 }
82
83 public Builder setArg(Object arg) {
84 this.arg = arg;
85 return this;
86 }
87
88 public ConstructorArg build() {
89 if (this.isRef) {
90 if (this.type != null) {
91 throw new IllegalArgumentException("当参数为引用类型时,无需设置 type 参数");
92 }
93
94 // null 是 string 实例妈?
95 if (!(arg instanceof String)) {
96 throw new IllegalArgumentException("请设置引用 ID");
97 }
98 } else {
99 if (this.type == null || this.arg == null) {
100 throw new IllegalArgumentException("当参数为非引用类型时,type 和 arg 参数必填");
101 }
102 }
103
104 return new ConstructorArg(this);
105 }
106
107 // Getter
108 public boolean getIsRef() {
109 return isRef;
110 }
111
112 public Class getType() {
113 return type;
114 }
115
116 public Object getArg() {
117 return arg;
118 }
119 }
120
121 public boolean isRef() {
122 return isRef;
123 }
124 public Class getType() {
125 return type;
126 }
127 public Object getArg() {
128 return arg;
129 }
130 }
131 }
View Code
BeansFactory
1 package di;
2
3 import java.lang.reflect.InvocationTargetException;
4 import java.util.List;
5 import java.util.concurrent.ConcurrentHashMap;
6
7 public class BeansFactory {
8 private ConcurrentHashMap<String, Object> singletonObjects = new ConcurrentHashMap<>();
9 private ConcurrentHashMap<String, BeanDefinition> beanDefinations = new ConcurrentHashMap<>();
10
11 public void addBeanDefinitions(List<BeanDefinition> beanDefinitionList) {
12 for (BeanDefinition beanDefinition: beanDefinitionList) {
13 this.beanDefinations.putIfAbsent(beanDefinition.getId(), beanDefinition);
14 }
15
16 for (BeanDefinition beanDefinition : beanDefinitionList) {
17 if (beanDefinition.isLazyInit() == false && beanDefinition.isSingleton()) {
18 createBean(beanDefinition);
19 }
20 }
21 }
22
23 public Object getBean(String beanId) {
24 BeanDefinition beanDefinition = beanDefinations.get(beanId);
25 if (beanDefinition == null) {
26 throw new NoSuchBeanDefinitionException("Bean is not defined: " + beanId);
27 }
28
29 return createBean(beanDefinition);
30 }
31
32 protected Object createBean(BeanDefinition beanDefinition) {
33 if (beanDefinition.isSingleton() && singletonObjects.containsKey(beanDefinition.getId())) {
34 return singletonObjects.get(beanDefinition.getId());
35 }
36
37 Object bean = null;
38 try {
39 Class beanClass = Class.forName(beanDefinition.getClassName());
40 List<BeanDefinition.ConstructorArg> args = beanDefinition.getConstructorArgs();
41 if (args.isEmpty()) {
42 bean = beanClass.newInstance();
43 } else {
44 Class[] argClasses = new Class[args.size()];
45 Object[] argObjects = new Object[args.size()];
46 for (int i = 0; i < args.size(); i++) {
47 BeanDefinition.ConstructorArg arg = args.get(i);
48 if (!arg.isRef()) {
49 argClasses[i] = arg.getType();
50 argObjects[i] = arg.getArg();
51 } else {
52 BeanDefinition refBeanDefinition = beanDefinations.get(arg.getArg());
53 if (refBeanDefinition == null) {
54 throw new NoSuchBeanDefinitionException("Bean is not defined: " + arg.getArg());
55 }
56 argObjects[i] = createBean(refBeanDefinition);
57 argClasses[i] = argObjects[i].getClass();
58 }
59 }
60
61 bean = beanClass.getConstructor(argClasses).newInstance(argObjects);
62 }
63 } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
64 e.printStackTrace();
65 }
66
67 if (bean != null && beanDefinition.isSingleton()) {
68 singletonObjects.putIfAbsent(beanDefinition.getId(), bean);
69 return singletonObjects.get(beanDefinition.getId());
70 }
71
72 return bean;
73 }
74 }
View Code
RateLimiter
1 package di;
2
3 public class RateLimiter {
4 private RedisCounter redisCounter;
5 public RateLimiter(RedisCounter redisCounter) {
6 this.redisCounter = redisCounter;
7 }
8 public boolean isValid() {
9 this.redisCounter.increamentAndGet();
10 return true;
11 }
12 }
View Code
RedisCounter
1 package di;
2
3 public class RedisCounter {
4 private String ipAddress;
5 private String port;
6 public RedisCounter(String ipAddress, String port) {
7 this.ipAddress = ipAddress;
8 this.port = port;
9 }
10
11 public int increamentAndGet() {
12 System.out.println("Connect to " + this.ipAddress + ":" + this.port);
13 return 10;
14 }
15 }
View Code
SpringDemo
1 package di;
2
3 public class DiDemo {
4 public static void main(String[] args) {
5 ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
6 RateLimiter rateLimiter = (RateLimiter) applicationContext.getBean("rateLimiter");
7 Boolean isValid = rateLimiter.isValid();
8 System.out.println("RateLimiter call isValid method, result: " + isValid);
9 }
10 }
View Code
beans.xml
1 <?xml version="1.0" encoding="UTF-8"?>
2 <beans>
3 <bean id="rateLimiter" class="di.RateLimiter">
4 <constructor-arg ref="redisCounter"></constructor-arg>
5 </bean>
6
7 <bean id="redisCounter" class="di.RedisCounter">
8 <constructor-arg type="String" value="127.0.0.1"></constructor-arg>
9 <constructor-arg type="int" value="1234" ></constructor-arg>
10 </bean>
11 </beans>
View Code
(二)AOP(待补充)
思想
- 业务代码,日志/安全/事务/性能代码
- 装饰器模式
- 切面
实现
- xml文件
- 注解
(三)事务(待补充)
(四)相关概念
Java bean
- 一个java bean 就是一个普通的java 类, 需满足以下要求
- 需要是public 的, 有个无参数的构造函数
- 属性是private 的, 通过setXXX()和getXXX()来访问
- 支持“事件”, 例如addXXXXListener(XXXEvent e),可以是Click事件,Keyboard事件,或自定义事件
- 提供自省/反射机制, 能在运行时查看java bean 的各种信息
- 可以序列化, 即可以把bean的状态保存的硬盘上, 以便以后来恢复