Spring 框架的意义
Spring 框架是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架,它的核心是控制反转和依赖注入。
控制反转(IoC):将对象的创建和对象之间的依赖关系交给 Spring 容器管理,降低了组件之间的耦合度。
举个例子看看如果没有 IoC
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
|
@Test public void test(){ UserServiceImpl userService = new UserServiceImpl(); UserDaoImpl userDao = new UserDaoImpl(); BasicDataSource dataSource = new BasicDataSource(); dataSource.setDriverClassName("com.mysql.jdbc.Driver"); dataSource.setUrl("jdbc:mysql://192.168.56.101:3306/test?characterEncoding=utf8"); dataSource.setUsername("root"); dataSource.setPassword("123456");
userDao.setDataSource(dataSource); userService.setUserDao(userDao);
Map<String,Object> map = new HashMap<>(); map.put("username","李四"); List<User> users = userService.queryUsers(map); System.out.println(users); }
|
如何实现IoC
- 使用配置文件(XML)解决扩展性问题,比如要创建的对象,配置对应的信息即可(类的全路径、类的属性信息)
a)类的全路径信息(要创建一个对象,就要对应一个信息)
b)配置类的属性信息
- 加载XML配置信息
- 创建对应的对象,将创建的对象放入map集合中(key: 唯一标识,value: 对象)
下面来手写一个简单的 Spring 框架,实现 IoC 和 AOP 的功能。
前置条件
存储bean的配置信息,使用 BeanDefinition
类来存储。
存储bean的实例,使用 singletonObjects
来存储。
1 2 3 4 5 6 7 8
| private Map<String, BeanDefinition> beanDefinitions = new HashMap<>();
private Map<String, Object> singletonObjects = new HashMap<>();
|
BeanDefinition 类代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
| public class BeanDefinition { private String clazzName; private Class<?> clazzType; private String beanName; private String initMethod; private String scope;
private List<PropertyValue> propertyValues = new ArrayList<>();
private static final String SCOPE_SINGLETON = "singleton"; private static final String SCOPE_PROTOTYPE = "prototype";
public BeanDefinition(String clazzName, String beanName) { this.beanName = beanName; this.clazzName = clazzName; this.clazzType = resolveClassName(clazzName); }
private Class<?> resolveClassName(String clazzName) { try { return Class.forName(clazzName); } catch (ClassNotFoundException e) { e.printStackTrace(); } return null; }
public String getBeanName() { return beanName; }
public void setBeanName(String beanName) { this.beanName = beanName; }
public String getInitMethod() { return initMethod; }
public void setInitMethod(String initMethod) { this.initMethod = initMethod; }
public List<PropertyValue> getPropertyValues() { return propertyValues; }
public void addPropertyValue(PropertyValue propertyValue) { this.propertyValues.add(propertyValue); }
public String getClazzName() { return clazzName; }
public void setClazzName(String clazzName) { this.clazzName = clazzName; }
public String getScope() { return scope; }
public void setScope(String scope) { this.scope = scope; }
public void setPropertyValues(List<PropertyValue> propertyValues) { this.propertyValues = propertyValues; }
public Class<?> getClazzType() { return clazzType; }
public boolean isSingleton() { return SCOPE_SINGLETON.equals(this.scope); }
public boolean isPrototype() { return SCOPE_PROTOTYPE.equals(this.scope); }
}
|
PropertyValue 类代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| public class PropertyValue { private String name;
private Object value;
public PropertyValue(String name, Object value) { super(); this.name = name; this.value = value; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public Object getValue() { return value; }
public void setValue(Object value) { this.value = value; } }
|
RuntimeBeanReference 类代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
public class RuntimeBeanReference {
private String ref;
public String getRef() { return ref; }
public void setRef(String ref) { this.ref = ref; }
public RuntimeBeanReference(String ref) { super(); this.ref = ref; } }
|
TypedStringValue 类代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
|
public class TypedStringValue {
private String value;
private Class<?> targetType;
public TypedStringValue(String value) { this.value = value; }
public String getValue() { return value; }
public void setValue(String value) { this.value = value; }
public Class<?> getTargetType() { return targetType; }
public void setTargetType(Class<?> targetType) { this.targetType = targetType; }
}
|
1. 加载配置文件
配置文件 applicationContext.xml
如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| <beans> <bean id="userService" class="com.ithawk.demo.spring.custom.demo.service.UserServiceImpl"> <property name="userDao" ref="userDao"></property> </bean>
<bean id="userDao" class="com.ithawk.demo.spring.custom.demo.dao.UserDaoImpl" init-method="init"> <property name="dataSource" ref="dataSource"></property> </bean>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" scope="singleton" > <property name="driverClassName" value="com.mysql.jdbc.Driver"></property> <property name="url" value="jdbc:mysql://192.168.56.101:3306/test?characterEncoding=utf-8"></property> <property name="username" value="root"></property> <property name="password" value="123456"></property> </bean> </beans>
|
加载配置文件的代码如下:
主要实现了解析 XML 文件,将解析的结果放入 beanDefinitions
中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| @Before public void before() { String location = "applicationContext.xml"; InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(location);
Document document = null; try { SAXReader reader = new SAXReader(); document = reader.read(inputStream); } catch (DocumentException e) { e.printStackTrace(); } List<Element> elements = document.getRootElement().elements(); for (Element element : elements) { String name = element.getName(); if (name.equals("bean")) { parseDefaultElement(element); } else { parseCustomElement(element); } } }
|
parseDefaultElement(element) 代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
|
@SuppressWarnings("unchecked") private void parseDefaultElement(Element beanElement) { try { if (beanElement == null) { return; } String id = beanElement.attributeValue("id");
String name = beanElement.attributeValue("name");
String clazzName = beanElement.attributeValue("class"); if (clazzName == null || "".equals(clazzName)) { return; }
String initMethod = beanElement.attributeValue("init-method"); String scope = beanElement.attributeValue("scope"); scope = scope != null && !scope.equals("") ? scope : "singleton";
String beanName = id == null ? name : id; Class<?> clazzType = Class.forName(clazzName); beanName = beanName == null ? clazzType.getSimpleName() : beanName; BeanDefinition beanDefinition = new BeanDefinition(clazzName, beanName); beanDefinition.setInitMethod(initMethod); beanDefinition.setScope(scope); List<Element> propertyElements = beanElement.elements(); for (Element propertyElement : propertyElements) { parsePropertyElement(beanDefinition, propertyElement); }
this.beanDefinitions.put(beanName, beanDefinition); } catch (ClassNotFoundException e) { e.printStackTrace(); } }
private void parsePropertyElement(BeanDefinition beanDefination, Element propertyElement) { if (propertyElement == null) return;
String name = propertyElement.attributeValue("name"); String value = propertyElement.attributeValue("value"); String ref = propertyElement.attributeValue("ref");
if (value != null && !value.equals("") && ref != null && !ref.equals("")) { return; }
PropertyValue pv = null;
if (value != null && !value.equals("")) { TypedStringValue typeStringValue = new TypedStringValue(value);
Class<?> targetType = getTypeByFieldName(beanDefination.getClazzName(), name); typeStringValue.setTargetType(targetType);
pv = new PropertyValue(name, typeStringValue); beanDefination.addPropertyValue(pv); } else if (ref != null && !ref.equals("")) {
RuntimeBeanReference reference = new RuntimeBeanReference(ref); pv = new PropertyValue(name, reference); beanDefination.addPropertyValue(pv); } else { return; } }
private Class<?> getTypeByFieldName(String beanClassName, String name) { try { Class<?> clazz = Class.forName(beanClassName); Field field = clazz.getDeclaredField(name); return field.getType(); } catch (Exception e) { e.printStackTrace(); } return null; }
|
2. Bean 对象创建
2.1 创建Bean对象
实现Bean的实例化、属性填充(依赖注入)、初始化操作。
1 2 3 4 5 6 7 8 9 10
| private Object createBean(BeanDefinition bd) throws Exception { Object bean = createBeanInstance(bd); populateBean(bean, bd); initializeBean(bean, bd); return bean; }
|
2.1.1 Bean的实例化
通过反射创建Bean的实例。
1 2 3 4 5 6 7 8 9 10
| private Object createBeanInstance(BeanDefinition bd) throws Exception { Class<?> clazzType = bd.getClazzType(); Constructor<?> constructor = clazzType.getDeclaredConstructor(); Object bean = constructor.newInstance(); return bean; }
|
2.1.2 Bean的属性填充(依赖注入)
通过反射设置Bean的属性值。
1 2 3 4 5 6 7 8 9 10 11 12 13
| private void populateBean(Object bean, BeanDefinition bd) throws Exception { List<PropertyValue> propertyValues = bd.getPropertyValues(); for (PropertyValue pv : propertyValues) { String name = pv.getName(); Object value = pv.getValue();
Object valueToUse = resolveValue(value);
setProperty(bean, name, valueToUse, bd); } }
|
通过反射获得属性值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| private Object resolveValue(Object value) throws Exception { Object valueToUse = null; if (value instanceof TypedStringValue) { TypedStringValue typedStringValue = (TypedStringValue) value; String stringValue = typedStringValue.getValue(); Class<?> targetType = typedStringValue.getTargetType();
valueToUse = handleType(stringValue, targetType); } else if (value instanceof RuntimeBeanReference) { RuntimeBeanReference beanReference = (RuntimeBeanReference) value; String ref = beanReference.getRef(); valueToUse = getBean(ref); } return valueToUse; }
private Object handleType(String stringValue, Class<?> targetType) { if (targetType == Integer.class) { return Integer.parseInt(stringValue); } else if (targetType == String.class) { return stringValue; } return null; }
|
通过反射设置属性值。
1 2 3 4 5 6
| private void setProperty(Object bean, String name, Object valueToUse, BeanDefinition bd) throws Exception { Class<?> clazzType = bd.getClazzType(); Field field = clazzType.getDeclaredField(name); field.setAccessible(true); field.set(bean, valueToUse); }
|
2.1.3 Bean的初始化
通过反射调用初始化方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| private void initializeBean(Object bean, BeanDefinition bd) throws Exception {
invokeInitMethod(bean, bd);
}
private void invokeInitMethod(Object bean, BeanDefinition bd) throws Exception { String initMethod = bd.getInitMethod(); if (initMethod == null) { return; } Class<?> clazzType = bd.getClazzType(); Method method = clazzType.getDeclaredMethod(initMethod);
method.invoke(bean); }
|
2.2 获取Bean对象
根据Bean的名称获取Bean对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| private Object getBean(String beanName) throws Exception {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject != null) { return singletonObject; }
BeanDefinition bd = this.beanDefinitions.get(beanName); if (bd == null) { return null; }
if (bd.isSingleton()) { singletonObject = createBean(bd); this.singletonObjects.put(beanName, singletonObject); } else if (bd.isPrototype()) { singletonObject = createBean(bd); }
return singletonObject; }
|
通过以上代码,我们实现了 Bean 对象的创建。