1. 回顾
经常前三天的学习,已初步掌握了spring ioc容器的实现原理,今天,试着自己来实现一个最简单的ioc容器;
2. bean定义
关于bean的定义,先使用最简单的定义方式,两个属性name和class,对应到xml的配置文件为:
<bean id="name" class="samples......."/>
代码定义如下:
public interface BeanDefinition {
String getBeanName();
String getBeanClassName();
}
public class DefaultBeanDefinition implements BeanDefinition {
private String beanName;
private String beanClassName;
@Override
public String getBeanName() {
return beanName;
}
@Override
public String getBeanClassName() {
return beanClassName;
}
public void setBeanClassName(String beanClassName){
this.beanClassName = beanClassName;
}
public void setBeanName(String beanName){
this.beanName = beanName;
}
}
3. xml解析
xml解析我们使用dom接口来实现,最简单的实现方式:
public class XmlBeanDefinitionReader {
private String configLocation;
private BeanRegistry beanRegistry;
public XmlBeanDefinitionReader(){
}
public void loadBeanDefinitions() throws ParserConfigurationException, IOException, SAXException {
URL configUrl = Thread.currentThread().getContextClassLoader().getResource(configLocation);
File configFile = new File(configUrl.getFile());
DocumentBuilder documentBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
Document document = documentBuilder.parse(configFile);
List<BeanDefinition> beanDefinitions = parseBeanDefinition(document);
if(null != beanDefinitions){
for (BeanDefinition beanDefinition : beanDefinitions) {
try {
beanRegistry.registry(beanDefinition.getBeanName(), beanDefinition);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
}
private List<BeanDefinition> parseBeanDefinition(Document document){
List<BeanDefinition> beanDefinitions = new ArrayList<>();
NodeList beanNodes = document.getElementsByTagName("bean");
for(int i=0; i<beanNodes.getLength(); i++){
Node node = beanNodes.item(i);
if(node.getNodeType() == Node.ELEMENT_NODE){
Element eleBean = (Element) node;
DefaultBeanDefinition defaultBeanDefinition = new DefaultBeanDefinition();
defaultBeanDefinition.setBeanName(eleBean.getAttribute("id"));
defaultBeanDefinition.setBeanClassName(eleBean.getAttribute("class"));
beanDefinitions.add(defaultBeanDefinition);
}
}
return beanDefinitions;
}
public BeanRegistry getBeanRegistry() {
return beanRegistry;
}
public void setBeanRegistry(BeanRegistry beanRegistry) {
this.beanRegistry = beanRegistry;
}
public String getConfigLocation(){
return configLocation;
}
public void setConfigLocation(String configLocation){
this.configLocation = configLocation;
}
}
主要完成的功能:
- 从
xml配置文件中读取bean的定义; - 回调
BeanRegistry的注册方法,完成bean的注册;
3.1. BeanRegistry
public interface BeanRegistry {
void registry(String name, Object beanObject) throws ClassNotFoundException;
}
4. 工厂定义
我们知道,ioc的核心其实是一个工厂,spring中我们常用的所有容器如:ClassPathXmlApplicationContext等都是BeanFactory工厂的实现类,spring的工厂很复杂,我们在这里只考虑最简单的实现:
public interface BeanFactory {
Object getBean(String name);
<T> T getBean(Class<T> requiredType);
}
public class DefaultBeanFactory implements BeanFactory, BeanRegistry {
private List<String> beanNames = new ArrayList<>();
private Map<String, BeanDefinition> beans = new ConcurrentHashMap<>();
private Map<Class<?>, BeanDefinition> typeBeans = new ConcurrentHashMap<>();
private String configLocation;
public String getConfigLocation() {
return configLocation;
}
protected void setConfigLocation(String configLocation) {
this.configLocation = configLocation;
}
public DefaultBeanFactory() {
}
protected void refresh() throws IOException, SAXException, ParserConfigurationException {
// 加载beanDefinition
XmlBeanDefinitionReader definitionReader = new XmlBeanDefinitionReader();
definitionReader.setConfigLocation(configLocation);
definitionReader.setBeanRegistry(this);
definitionReader.loadBeanDefinitions();
}
@Override
public Object getBean(String name) {
if (!beanNames.contains(name)) {
return null;
}
BeanDefinition beanDefinition = beans.get(name);
String beanClassName = beanDefinition.getBeanClassName();
Class<?> beanClass = null;
try {
beanClass = Class.forName(beanClassName);
Object obj = beanClass.newInstance();
return obj;
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
return null;
}
@Override
public <T> T getBean(Class<T> requiredType) {
if (!typeBeans.containsKey(requiredType)) {
return null;
}
BeanDefinition beanDefinition = typeBeans.get(requiredType);
String beanClassName = beanDefinition.getBeanClassName();
Class<?> beanClass = null;
try {
beanClass = Class.forName(beanClassName);
Object bean = beanClass.newInstance();
return (T) bean;
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
return null;
}
@Override
public void registry(String name, Object beanObject) throws ClassNotFoundException {
if (null == name && "".equals(name)) {
return;
}
if (beanNames.contains(name)) {
return;
}
beanNames.add(name);
BeanDefinition beanDefinition = (BeanDefinition) beanObject;
beans.put(name, beanDefinition);
Class<?> beanClass = Class.forName(beanDefinition.getBeanClassName());
typeBeans.put(beanClass, beanDefinition);
}
}
主要功能点:
- 使用
Map存储bean定义; - 调用
xml配置文件的BeanDefinition的解析; - 实现
BeanRegistry接口的注册方法; - 实现
BeanFactory的获取bean方法;
5. 容器包装
上面基本完成了一个Ioc容器,最后我们来一个假包装,更像spring一样操作:
public class ClassPathXmlApplicationContext extends DefaultBeanFactory {
public ClassPathXmlApplicationContext(String configLocation) {
setConfigLocation(configLocation);
try {
this.refresh();
} catch (IOException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
} catch (ParserConfigurationException e) {
e.printStackTrace();
}
}
}
6. 测试
创建一个HelloWorldService与实现类,并在classpath路径下创建xml配置文件,配置helloWorldService bean。
6.1. HelloWorldService
public interface HelloWorldService {
String sayHello(String name);
}
public class HelloWorldServiceImpl implements HelloWorldService {
@Override
public String sayHello(String name) {
return "hello: " + name;
}
}
6.2. beans.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<bean id="helloWorldService" class="samples.myioc.service.impl.HelloWorldServiceImpl"/>
</beans>
6.3. main启动类
public class App {
public static void main(String[] args) {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
HelloWorldService helloWorldService = applicationContext.getBean(HelloWorldServiceImpl.class);
System.out.println(helloWorldService.sayHello("zhangsan"));
HelloWorldService helloWorldService1 = (HelloWorldService) applicationContext.getBean("helloWorldService");
System.out.println(helloWorldService1.sayHello("lisi"));
}
}
查看运行结果:
hello: zhangsan
hello: lisi
7. 总结
经过四天的学习,从对spring一无所知开始,现在已经可以自己实现一个简单的ioc容器了。收获最大的可能就是,对于看起来复杂的东西,不要有畏难心理,只要你付出行动,并坚持下去,总会有收获。