自定义实现
1.定义实体类
<bean id="userDao" class="dao.impl.UserDaoImpl">
<property name="username" value="412"></property>
<property name="password" value="124"></property>
<property name="age" value="123"></property>
</bean>
<bean id="userService" class="service.impl.UserServiceImpl">
<property name="userDao" ref="userDao" ></property>
</bean>
property实例类
package com.itheima.beans;
/**
* @author:wuxie
* @date:2023/2/16
* @description:封装bean标签下property标签
*/
public class PropertyValue {
private String name;
private String ref;
private String value;
public PropertyValue(String name, String ref, String value) {
this.name = name;
this.ref = ref;
this.value = value;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getRef() {
return ref;
}
public void setRef(String ref) {
this.ref = ref;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
property集合
package com.itheima.beans;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* @author:wuxie
* @date:2023/2/16
* @description:存储管理多个propertyvalue对象
*/
public class MutablePropertyValues implements Iterable<PropertyValue> {
//property集合
private final List<PropertyValue> propertyValueList;
//通过构造函数进行初始化
public MutablePropertyValues() {
this.propertyValueList=new ArrayList<PropertyValue>();
}
//也可以通过传值进行初始化
public MutablePropertyValues(List<PropertyValue> propertyValueList) {
//进行一个null判断
this.propertyValueList = propertyValueList != null ? propertyValueList : new ArrayList<>();
}
//获得所有property(数组方式)
public PropertyValue[] getPropertyValues() {
// 返回类型
return this.propertyValueList.toArray(new PropertyValue[0]);
}
//根据名称获取property
public PropertyValue getPropertyValueByName(String name) {
for (PropertyValue propertyValue : propertyValueList) {
if (propertyValue.getName().equals(name)) {
return propertyValue;
}
}
return null;
}
//添加property
public MutablePropertyValues addProperty(PropertyValue propertyValue) {
for (int i = 0; i < propertyValueList.size(); i++) {
//先判断该property是否存在,如果存在就进行覆盖
if (propertyValueList.get(i).getName().equals(propertyValue.getName())) {
propertyValueList.set(i, propertyValue);
}
}
//如果不存在就直接添加
propertyValueList.add(propertyValue);
return this;
}
//判断是否有某个property
public boolean contains(String name) {
return getPropertyValueByName(name) != null;
}
//判断为空
public boolean isEmpty() {
return propertyValueList.isEmpty();
}
//继承Iterable,后续可以使用foreach遍历
@Override
public Iterator<PropertyValue> iterator() {
return propertyValueList.iterator();
}
}
bean实体类
package com.itheima.beans;
/**
* @author:wuxie
* @date:2023/2/16
* @description:封装Bean标签
*/
public class BeanDefinition {
private String id;
private String className;
//bean对象里面的property
private MutablePropertyValues mutablePropertyValues;
public BeanDefinition() {
mutablePropertyValues=new MutablePropertyValues();
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
public MutablePropertyValues getMutablePropertyValues() {
return mutablePropertyValues;
}
public void setMutablePropertyValues(MutablePropertyValues mutablePropertyValues) {
this.mutablePropertyValues = mutablePropertyValues;
}
}
2.定义注册表相关类
spring里面存在BeanDefinitionRegister注册表, 其作用是存储Bean,创建bean后就存储在注册表里面,并且实现对bean的增删等一系列操作
可以查看spring源码,其提供多种Register,所以设置一个接口
BeanDefinitionRegister 接口
package com.itheima.beans.factory.support;
import com.itheima.beans.BeanDefinition;
/**
* @Author:wuxie
* @Date:2023/2/16
* @Description:Bean注册中心
* @ClassName:BeanDefinitionRegistry
*/
public interface BeanDefinitionRegistry {
//注册BeanDefinition对象到注册表中
void registerBeanDefinition(String beanName, BeanDefinition beanDefinition);
//从注册表中删除指定名称的BeanDefinition对象
void removeBeanDefinition(String beanName) throws Exception;
//根据名称从注册表中获取BeanDefinition对象
BeanDefinition getBeanDefinition(String beanName) throws Exception;
boolean containsBeanDefinition(String beanName);
int getBeanDefinitionCount();
String[] getBeanDefinitionNames();
}
SimpleBeanDefinitionRegister 简单实现类
package com.itheima.beans.factory.support;
import com.itheima.beans.BeanDefinition;
import org.springframework.context.annotation.Bean;
import java.util.HashMap;
import java.util.Map;
/**
* @Author:wuxie
* @Date:2023/2/16
* @Description: 注册中心接口实现类
* @ClassName:SimpleBeanDefinitionRegistry
*/
public class SimpleBeanDefinitionRegistry implements BeanDefinitionRegistry {
//设置一个map集合,进行对bean的存储
private Map<String, BeanDefinition> beanDefinitionMap = new HashMap<>();
@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) {
beanDefinitionMap.put(beanName, beanDefinition);
}
@Override
public void removeBeanDefinition(String beanName) throws Exception {
beanDefinitionMap.remove(beanName);
}
@Override
public BeanDefinition getBeanDefinition(String beanName) throws Exception {
return beanDefinitionMap.get(beanName);
}
@Override
public boolean containsBeanDefinition(String beanName) {
return beanDefinitionMap.containsKey(beanName);
}
@Override
public int getBeanDefinitionCount() {
return beanDefinitionMap.size();
}
@Override
public String[] getBeanDefinitionNames() {
return beanDefinitionMap.keySet().toArray(new String[0]);
}
}
3.定义解析器相关类
根据上面介绍的过程得, 我们需要从application.xml里面获得相关数据, 就需要设置一个解析xml文件的工具, 即为解析器
spring里面的解析器也有很多, 所以也设置一个接口, 并且只实现一个xml的解析器
BeanDefinitionReader 接口
package com.itheima.beans.factory.support;
/**
* @Author:wuxie
* @Date:2023/2/16
* @Description:解析配置文件
* @ClassName:BeanDefinitionReader
*/
public interface BeanDefinitionReader {
//这里定义该方法,是为了解析bean后,将其放入注册表里面
BeanDefinitionRegistry getRegistr();
//加载配置
void loadBeanDefinitions(String configLocation) throws Exception;
}
解析xml使用到dom4j 工具
Dom4j通过XMLWriter将Document对象表示的XML树写入指定的文件,并使用OutputFormat格式对象指定写入的风格和编码方法。 ::: details
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>1.1</version>
</dependency>
XmlBeanDefinitionReader xml解析器
package com.itheima.beans.factory.xml;
import com.itheima.beans.BeanDefinition;
import com.itheima.beans.MutablePropertyValues;
import com.itheima.beans.PropertyValue;
import com.itheima.beans.factory.support.BeanDefinitionReader;
import com.itheima.beans.factory.support.BeanDefinitionRegistry;
import com.itheima.beans.factory.support.SimpleBeanDefinitionRegistry;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.io.InputStream;
import java.util.List;
/**
* @Author:wuxie
* @Date:2023/2/16
* @Description:针对xml文件解析器
* @ClassName:XmlBeanDefinitionReader
*/
public class XmlBeanDefinitionReader implements BeanDefinitionReader {
private BeanDefinitionRegistry beanDefinitionRegistry;
//通过构造,初始化注册表
public XmlBeanDefinitionReader() {
beanDefinitionRegistry = new SimpleBeanDefinitionRegistry();
}
//获得注册表
@Override
public BeanDefinitionRegistry getRegistr() {
return beanDefinitionRegistry;
}
//加载配置文件
@Override
public void loadBeanDefinitions(String configLocation) throws Exception {
//当使用Class.getClassLoader.getResourceAsStream()加载资源时,是从classPath路径下进行加载,放在resources下的文件加载时不能加(“/”)。
InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(configLocation);
//dom4j的工具
SAXReader saxReader = new SAXReader();
Document read = saxReader.read(inputStream);
//获得整个xml文件的根元素
Element rootElement = read.getRootElement();
parseBean(rootElement);
}
private void parseBean(Element rootElement) {
//获得名为bean的元素
List<Element> elements = rootElement.elements("bean");
for (Element element : elements) {
//获得bean里面的id和class
String id = element.attributeValue("id");
String className = element.attributeValue("class");
//创建bean
BeanDefinition beanDefinition = new BeanDefinition();
beanDefinition.setId(id);
beanDefinition.setClassName(className);
//再获取该bean下的property
List<Element> property = element.elements("property");
MutablePropertyValues propertyValues = new MutablePropertyValues();
for (Element element1 : property) {
String name = element1.attributeValue("name");
String value = element1.attributeValue("value");
String ref = element1.attributeValue("ref");
PropertyValue propertyValue = new PropertyValue(name, ref, value);
propertyValues.addProperty(propertyValue);
}
//最后在bean下存入property
beanDefinition.setMutablePropertyValues(propertyValues);
beanDefinitionRegistry.registerBeanDefinition(id, beanDefinition);
}
}
}
4.定义IOC容器相关类
前面做了对bean的获取和存储,剩下的就是获取,spring里面是设置一个IOC容器,来获得bean
BeanFactory接口
package com.itheima.beans.factory;
/**
* @Author:wuxie
* @Date:2023/2/16
* @Description:(TODO)描述该类
* @ClassName:BeanFactory
*/
public interface BeanFactory {
//根据bean名获得bean
Object getBean(String name) throws Exception;
//根据bean名称和类型获得bean
<T> T getBean(String name, Class<? extends T> clazz) throws Exception;
}
最初使用spring的时候,有两种创建容器的方法。一是ClassPathXmlApplicationContext,二是XmlBeanFactory。两者区别在前者是无延迟加载,默认行为是在启动服务器时将所有 singleton bean提前进⾏实例化。提前实例化意味着作为初始化过程的⼀部分,ApplicationContext 实例会创建并配置所有的singleton bean。
这里我们就需要新增方法
ApplicationContext
package com.itheima.context;
import com.itheima.beans.factory.BeanFactory;
/**
* @Author:wuxie
* @Date:2023/2/16
* @Description:非延时加载bean
* @ClassName:ApplicationContext
*/
public interface ApplicationContext extends BeanFactory {
//使用refresh进行实现对bean的加载和配置
void refresh() throws IllegalStateException, Exception;
}
AbstractApplicationContext
ApplicationContext接口的子实现类/,用于立即加载
设置为抽象类,实现方法交给子类
package com.itheima.context.support;
import com.itheima.beans.BeanDefinition;
import com.itheima.beans.factory.support.BeanDefinitionReader;
import com.itheima.beans.factory.support.BeanDefinitionRegistry;
import com.itheima.context.ApplicationContext;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
/**
* @Author:wuxie
* @Date:2023/2/16
* @Description:(TODO)描述该类
* @ClassName:AbstractApplicationContext
*/
public abstract class AbstractApplicationContext implements ApplicationContext {
//声明解析器
protected BeanDefinitionReader beanDefinitionReader;
//存储bean的容器
protected Map<String, Object> map = new HashMap<String, Object>();
//配置文件路径
protected String configLocation;
@Override
public void refresh() throws IllegalStateException, Exception {
beanDefinitionReader.loadBeanDefinitions(configLocation);
finishBeanInitialization();
}
private void finishBeanInitialization() throws Exception {
//获取注册表对象
BeanDefinitionRegistry registr = beanDefinitionReader.getRegistr();
String[] beanDefinitionNames = registr.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
BeanDefinition beanDefinition = registr.getBeanDefinition(beanDefinitionName);
//获得bean
getBean(beanDefinitionName);
}
}
}
ClassPathXmlApplicationContext IOC实现类
package com.itheima.context.support;
import com.itheima.beans.BeanDefinition;
import com.itheima.beans.MutablePropertyValues;
import com.itheima.beans.PropertyValue;
import com.itheima.beans.factory.support.BeanDefinitionRegistry;
import com.itheima.beans.factory.xml.XmlBeanDefinitionReader;
import com.itheima.utils.StringUtils;
import org.springframework.context.support.AbstractXmlApplicationContext;
import java.lang.reflect.Method;
import java.util.Iterator;
import java.util.Objects;
/**
* @Author:wuxie
* @Date:2023/2/16
* @Description:IOC容器实现类
* @ClassName:ClassPathXmlApplicationContext
*/
public class ClassPathXmlApplicationContext extends AbstractApplicationContext {
public ClassPathXmlApplicationContext(String configLocation) {
this.configLocation = configLocation;
beanDefinitionReader = new XmlBeanDefinitionReader();
//在构造时进行初始化bean
try {
this.refresh();
} catch (Exception e) {
}
}
//根据bean名称获取
@Override
public Object getBean(String name) throws Exception {
//从IOC容器里面的map里面获取bean
Object object = map.get(name);
if (object != null) {
return object;
}
//
BeanDefinitionRegistry registr = beanDefinitionReader.getRegistr();
BeanDefinition beanDefinition = registr.getBeanDefinition(name);
if (beanDefinition == null) {
return null;
}
//通过bean的class路径创建UserDao对象
String className = beanDefinition.getClassName();
Class<?> clazz = Class.forName(className);
Object beanObj = clazz.newInstance();
//依赖注入,将userDao数据传入userService里面
//<bean id="userService"class="service.impl.UserServiceImpl">
//<property name="userDao" ref="userDao" ></property>
//</bean>
MutablePropertyValues mutablePropertyValues = beanDefinition.getMutablePropertyValues();
for (PropertyValue mutablePropertyValue : mutablePropertyValues) {
String propertyName = mutablePropertyValue.getName();
String value = mutablePropertyValue.getValue();
String ref = mutablePropertyValue.getRef();
if (ref != null && !"".equals(ref)) {
//注意这里又使用到了自身,后续容易导致依赖循环
Object bean = getBean(ref);
String setterMethodNameByFieldName =
//通过名字来获得set方法
StringUtils.getSetterMethodNameByFieldName(propertyName);
//遍历clazz(UserDao)下的方法
Method[] methods = clazz.getMethods();
for (Method method : methods) {
if (method.getName().equals(setterMethodNameByFieldName)) {
//如果存在就执行
method.invoke(beanObj, bean);
//property里面ref和value只能二存一
break;
}
}
}
if (value != null && !"".equals(value)) {
String methodName = StringUtils.getSetterMethodNameByFieldName(propertyName);
Method method = clazz.getMethod(methodName, String.class);
method.invoke(beanObj, value);
}
}
//存入已获得的bean
map.put(name, beanObj);
return beanObj;
}
@Override
public <T> T getBean(String name, Class<? extends T> clazz) throws Exception {
//调用上方getbean
Object bean = getBean(name);
if (bean == null) {
return null;
}
//Class.cast(Object obj)方法 就是作用就是强制类型转换。将obj转化成T类型。
return clazz.cast(bean);
}
}
StringUtils 通过变量名字来获得对应的set方法
package com.itheima.utils;
/**
* @Author:wuxie
* @Date:2023/2/16
* @Description:通过变量名字来获得对应的set方法
* @ClassName:StringUtils
*/
public class StringUtils {
//userDao==>setUserDao
public static String getSetterMethodNameByFieldName(String filename) {
String methodName="set"+filename.substring(0,1).toUpperCase()+ filename.substring(1);
return methodName;
}
}
5.导入该框架
1.注入依赖
2.添加依赖
<dependencies>
<dependency>
<groupId>org.example</groupId>
<artifactId>spring_demo</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
3.测试
代码与一开始spring使用一样 :::details
userDao被创建了
userService被创建了
UserService ...
UserDao ...412==124age:123
测试是否是非延迟加载
设置断点进行调试,
可见
ApplicationContext applicationContext = new
ClassPathXmlApplicationContext("applicationContext.xml");
运行后就实例了bean
到此springIOC框架简单自定义就结束了,over