概述
IOC(Inversion of Control)“控制反转”,不过更流行的叫法是“依赖注入”(DI - Dependency Injection)。
什么是“控制反转”呢?其实就是将控制权(创建对象和对象之间的依赖关系的权利)交给spring容器。以前我们写代码的需要某个对象的时候直接使用 new XXXImpl();,有了Spring IOC容器之后,它负责对象的创建和依赖注入,当我们需要某个对象时直接跟Spring IOC容器要就好了。
IOC 听起来很高大上,其实实现起来并不复杂。本文主要介绍基于XML配置的方式来实现一个IOC容器,后面会有单独的一章介绍如何通过注解的方式来实现IOC容器。
用法
具体用法与Spring IOC类似,如下:
1、beans.xml 配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<bean id="userDao" class="com.ricky.ioc.sample.dao.UserDaoImpl" scope="singleton" init-method="init" ></bean>
<bean id="userService" class="com.ricky.ioc.sample.service.UserServiceImpl">
<property name="userDao" ref="userDao"></property>
</bean>
<bean id="userController" class="com.ricky.ioc.sample.controller.UserController">
<property name="userService" ref="userService"></property>
</bean>
</beans>
2、添加maven依赖
<dependency>
<groupId>com.ricky.framework</groupId>
<artifactId>ioc</artifactId>
<version>1.0.0</version>
</dependency>
3、加载bean配置文件
ApplicationContext ctx = new ClasspathXmlApplicationContext("beans.xml");
//通过id获取Bean
UserController userController =(UserController)ctx.getBean("userController");
userController.login("ricky","123");
//通过Class获取Bean
UserService userService = ctx.getBean(UserService.class);
System.out.println(userService);
userService.login("ricky","abc");
ctx.close();
运行结果如下:
UserController login name->ricky,password->123
UserServiceImpl login name->ricky,password->123
UserDaoImpl find name->ricky
com.ricky.ioc.sample.service.UserServiceImpl@214c265e
UserServiceImpl login name->ricky,password->abc
UserDaoImpl find name->ricky
container close…
具体实现
思路:
解析beans.xml获取Bean列表以及相互之间的依赖关系,然后通过反射技术构造出Bean实例,并根据Bean之间的依赖关系进行Bean装配。
首先,看看ApplicationContext类,代码如下:
package com.ricky.framework.ioc;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import org.apache.commons.lang3.StringUtils;
import com.ricky.framework.ioc.model.BeanDeFinition;
import com.ricky.framework.ioc.model.PropertyDeFinition;
import com.ricky.framework.ioc.util.ReflectionUtils;
public abstract class ApplicationContext {
public abstract Object getBean(String id);
public abstract <T> T getBean(Class<T> clazz);
public abstract void close();
protected abstract BeanDeFinition getBeanDeFinition(String id);
protected Object createBean(BeanDeFinition bd) {
try {
Object bean = ReflectionUtils.newInstance(bd.getClassName());
if(StringUtils.isNotEmpty(bd.getinitMethodName())){
ReflectionUtils.invokeMethod(bean,bd.getinitMethodName());
}
return bean;
} catch (ClassNotFoundException | InstantiationException
| illegalaccessexception | IllegalArgumentException
| InvocationTargetException | NoSuchMethodException e) {
throw new RuntimeException("create bean error,class->"+bd.getClassName(),e);
}
}
protected void injectBeanProperties(Object bean,BeanDeFinition beanDeFinition){
try {
PropertyDescriptor[] ps = Introspector.getBeanInfo(bean.getClass()).getPropertyDescriptors();
for(PropertyDeFinition propertyDeFinition : beanDeFinition.getProperties()){
for(PropertyDescriptor propertyDescriptor:ps){
if(propertyDescriptor.getName().equals(propertyDeFinition.getName())){
Method setter = propertyDescriptor.getWriteMethod();
setter.setAccessible(true);
setter.invoke(bean,getBean(propertyDeFinition.getRef()));
}
}
}
} catch (SecurityException | illegalaccessexception
| IllegalArgumentException | InvocationTargetException
| IntrospectionException e) {
throw new RuntimeException("inject bean properties error",e);
}
}
}
在这涉及到两个关键类:BeanDeFinition 和 PropertyDeFinition,它们分别是用来描述 javabean的定义和javabean 属性的定义,一个BeanDeFinition 可以有1个或多个PropertyDeFinition,它们之间是1:N的关系。代码如下:
BeanDeFinition.java
package com.ricky.framework.ioc.model;
import java.util.List;
public class BeanDeFinition {
private String id;
private String className;
private String scope; //singleton|prototype
private String initMethodName;
private List<PropertyDeFinition> properties;
public BeanDeFinition(String id,String className) {
this.id = id;
this.className = className;
}
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 String getScope() {
return scope;
}
public void setScope(String scope) {
this.scope = scope;
}
public String getinitMethodName() {
return initMethodName;
}
public void setinitMethodName(String initMethodName) {
this.initMethodName = initMethodName;
}
public List<PropertyDeFinition> getProperties() {
return properties;
}
public void setProperties(List<PropertyDeFinition> properties) {
this.properties = properties;
}
@Override
public String toString() {
return "BeanDeFinition [id=" + id + ",className=" + className
+ ",scope=" + scope + ",initMethodName=" + initMethodName
+ ",properties=" + properties + "]";
}
}
PropertyDeFinition.java
package com.ricky.framework.ioc.model;
public class PropertyDeFinition {
private String name;
private String ref;
public PropertyDeFinition(String name,String ref) {
this.name = name;
this.ref = ref;
}
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;
}
@Override
public String toString() {
return "PropertyDeFinition [name=" + name + ",ref=" + ref + "]";
}
}
2、接下来是ClasspathXmlApplicationContext类,代码如下:
package com.ricky.framework.ioc;
import java.io.FileNotFoundException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import org.dom4j.DocumentException;
import com.ricky.framework.ioc.model.BeanDeFinition;
import com.ricky.framework.ioc.parser.BeanXmlConfigParser;
import com.ricky.framework.ioc.util.BeanScope;
import com.ricky.framework.ioc.util.ReflectionUtils;
public class ClasspathXmlApplicationContext extends ApplicationContext {
private Map<String,BeanDeFinition> beanDeFinitionMap = new HashMap<String,BeanDeFinition>();
protected Map<String,Object> beanInstanceMap = new HashMap<String,Object>();
public ClasspathXmlApplicationContext(String xmlFilePath) {
System.out.println("****************container init begin****************");
readxml(xmlFilePath);
initBeans();
injectBeans();
System.out.println("****************container init end****************");
}
private void readxml(String xmlFilePath) {
BeanXmlConfigParser beanXmlConfigParser = new BeanXmlConfigParser();
List<BeanDeFinition> bean_def_list = null;
try {
bean_def_list = beanXmlConfigParser.parse(xmlFilePath);
} catch (FileNotFoundException e) {
throw new RuntimeException("not found bean xml,file->"+xmlFilePath,e);
} catch (DocumentException e) {
throw new RuntimeException("bean xml format error,e);
}
for (BeanDeFinition beanDeFinition : bean_def_list) {
if(StringUtils.isEmpty(beanDeFinition.getId()) || StringUtils.isEmpty(beanDeFinition.getClassName())){
throw new IllegalArgumentException("bean deFinition is empty!");
}
if (beanDeFinitionMap.containsKey(beanDeFinition.getId())) {
throw new IllegalArgumentException(
"duplicated bean id,id->"
+ beanDeFinition.getId());
}
beanDeFinitionMap.put(beanDeFinition.getId(),beanDeFinition);
}
}
private void initBeans() {
for (Map.Entry<String,BeanDeFinition> me : beanDeFinitionMap.entrySet()) {
BeanDeFinition bd = me.getValue();
if(StringUtils.isEmpty(bd.getScope()) || bd.getScope().equals(BeanScope.SINGLetoN)){
try {
Object bean = createBean(bd);
beanInstanceMap.put(bd.getId(),bean);
} catch (Exception e) {
throw new IllegalArgumentException("create bean error,class->"+bd.getClassName(),e);
}
}
}
}
private void injectBeans() {
for (Map.Entry<String,BeanDeFinition> me : beanDeFinitionMap.entrySet()) {
BeanDeFinition beanDeFinition = me.getValue();
//判断有没有注入属性
if (beanDeFinition.getProperties() != null && beanDeFinition.getProperties().size()>0) {
Object bean = beanInstanceMap.get(beanDeFinition.getId());
try {
injectBeanProperties(bean,beanDeFinition);
} catch (Exception e) {
e.printstacktrace();
}
}
}
}
@Override
public Object getBean(String id) {
// System.out.println("get bean by id:"+id);
if (StringUtils.isEmpty(id)) {
return null;
}
if (beanDeFinitionMap.containsKey(id)) {
BeanDeFinition bd = beanDeFinitionMap.get(id);
if(StringUtils.isEmpty(bd.getScope()) || bd.getScope().equals(BeanScope.SINGLetoN)){
return beanInstanceMap.get(id);
}
Object bean = null;
try {
bean = createBean(bd);
injectBeanProperties(bean,bd);
beanInstanceMap.put(bd.getId(),bean);
} catch (Exception e) {
e.printstacktrace();
}
return bean;
}
throw new IllegalArgumentException("unkNown bean,id->" + id);
}
@SuppressWarnings("unchecked")
@Override
public <T> T getBean(Class<T> clazz) {
// System.out.println("get bean by type:"+clazz.getName());
for(Map.Entry<String,BeanDeFinition> me : beanDeFinitionMap.entrySet()){
BeanDeFinition bd = me.getValue();
Class<?> beanClass = null;
try {
beanClass = ReflectionUtils.loadClass(bd.getClassName());
} catch (ClassNotFoundException e) {
e.printstacktrace();
}
if(beanClass!=null && clazz.isAssignableFrom(beanClass)){
// System.out.println("find bean by type,class->"+clazz.getName());
return (T) getBean(bd.getId());
}
}
return null;
}
@Override
protected BeanDeFinition getBeanDeFinition(String id) {
return beanDeFinitionMap.get(id);
}
@Override
public void close() {
System.out.println("container close...");
// release resource
beanDeFinitionMap.clear();
beanDeFinitionMap = null;
beanInstanceMap.clear();
beanInstanceMap = null;
}
}
在ClasspathXmlApplicationContext类中,主要负责三大功能:解析XML配置文件、通过反射构建Bean实例以及对Bean进行装配。
小结
以上所有代码均已上传到GitHub上,欢迎大家fork。另外由于时间比较仓促,代码设计上有不合理的地方还请包涵,后面会抽时间对代码进行重构。
原文地址:https://www.jb51.cc/xml/295215.html
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。