自定义IOC:
概述:
Spring通过一个配置文件描述Bean与Bean之间的依赖关系,利用java语言的反射功能实例化Bean,并建立Bean之间的依赖关系。Spring的IOC容器在完成这些底层工作的基础上,还提供了Bean实例缓存生命周期管理、Bean实例代理、事件发布、资源加载等高级服务
接下来我们通过这一段话,去自己实现一个简单的Spring框架,通过反射和XML解析来建立对象及对象之间的关系
1.导入所需依赖
<dependencies>
<!--DOM4J 的支持:解析XML文件-->
<dependency>
<groupId>org.dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>2.1.1</version>
</dependency>
<!--junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.22</version>
<scope>provided</scope>
</dependency>
</dependencies>
2.BeanFactory是提供了最基本的IOC容器功能的一个接口
自定义BeanFactory
//Bean工厂,作用就是从容器中返回特定名称的 Bean
public interface BeanFactory {
/**
* 从容器中获取bean
* @param beanName
* @return
*/
public Object getBean(String beanName);
}
3.编写程序运行所需资源
实体类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Person {
private String name;
private int age;
}
数据访问层接口
public interface PersonDao {
/**
* 添加一个person
* @param person
*/
public void addPerson(Person person);
}
数据访问层实现类
public class PersonDaoImpl implements PersonDao {
@Override
public void addPerson(Person person) {
System.out.println("获取person信息,姓名"+person.getName()+",年龄:"+person.getAge());
System.out.println("用户保存成功");
}
}
业务层接口
public interface PersonService {
public void addPerson(Person person);
}
业务层实现
public class PersonServiceImpl implements PersonService {
//注入dao层
private PersonDao personDao;
//添加setter方法,模拟spring依赖注入中的set注入方式
public void setPersonDao(PersonDao personDao) {
this.personDao = personDao;
}
@Override
public void addPerson(Person person) {
personDao.addPerson(person);
}
}
4.创建beans.xml文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE PUBLIC>
<beans>
<!--在容器中注入person对象-->
<bean id="person" class="com.lqz.pojo.Person"/>
<!--在容器中注入personDao对象:实现类,接口不能创建对象-->
<bean id="personDao" class="com.lqz.dao.impl.PersonDaoImpl"/>
<bean id="personService" class="com.lqz.service.impl.PersonServiceImpl">
<!--引入注入的dao对象:ref即引用那个类-->
<property name="personDao" ref="personDao"/>
</bean>
</beans>
5.解析xml文件
public class ClassPathXmlApplicationContext implements BeanFactory{
//存放bean容器的Map
Map<String,Object> beans = new HashMap<>();
public ClassPathXmlApplicationContext() throws Exception{
//使用dom4j解析自己写的beans.xml文件
SAXReader reader = new SAXReader();
//指定解析的配置文件
String path = "beans.xml";
//获取DOM树
Document document = reader.read(this.getClass().getClassLoader().getResourceAsStream(path));
//利用树获取beans.xml的beans根节点
Element rootElement = document.getRootElement();
//测试获取的根节点是否为beans
// System.out.println(rootElement.getName());//beans ok
//获取根节点下的所有元素
List<Element> elements = rootElement.elements();
for (Element element : elements) {
//测试获取元素名称是否为bean
//System.out.println(element.getName().equals("bean"));//true ok
//通过每个bean元素获取到id这个属性,即beanName
String id = element.attributeValue("id");
//拿到全限定类名,以便反射调用方法
String aClass = element.attributeValue("class");
//测试
//System.out.println("id=" + id + " ,aClass=" + aClass);//ok
//获取Class对象
Class<?> clazz = Class.forName(aClass);
//通过全限定类名实例化对象:控制反转
Object object = clazz.newInstance();
//测试
//System.out.println("object = " + object);//ok
//将实例化的对象存入map中
beans.put(id,object);
//遍历bean元素下的子元素
List<Element> sonEl = element.elements();
for (Element e2 : sonEl) {
String name = e2.attributeValue("name");
//引用的那个bean对象
String refBean = e2.attributeValue("ref");
//测试
//System.out.println(name+"---------"+refBean);//ok
//要调用service中注入的setPersonDao方法:拼接set方法
String invokeMethod = "set" + name.substring(0,1).toUpperCase()+name.substring(1);
//测试
//System.out.println(invokeMethod);//setPersonDao ok
//通过key获取value值,对象
Object bean = getBean(refBean);
//反射,获取方法对象,反向调用
Method method = object.getClass().getMethod(invokeMethod, bean.getClass().getInterfaces()[0]);
//测试
//System.out.println(method);//ok
//反向调用
method.invoke(object,bean);//依赖注入
}
}
}
@Override
public Object getBean(String beanName) {
return this.beans.get(beanName);
}
}
6.测试
public class MyTest {
@Test
public void test() throws Exception {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext();
//从容器中拿到id为person的beanName
Person person = (Person)context.getBean("person");
person.setName("张三");
person.setAge(12);
System.out.println(person);
PersonServiceImpl personService = (PersonServiceImpl) context.getBean("personService");
personService.addPerson(person);
//Person(name=张三, age=12)
//获取person信息,姓名张三,年龄:12
//用户保存成功 ok
}
}
总结
ioc是一种思想,也叫控制反转
传统的开发,创建对象会使用到了new关键字
在Spring中,将创建对象和对象之间的依赖交给了IOC容器管理,由IOC容器进行注入、组合对象,处理对象之间的依赖关系
这样对象与对象之间是松耦合、便于测试、功能可复用(减少对象的创建和内存消耗),程序更好维护、灵活度高、扩展性变高