1.基本概念:
在这之前先介绍Spring及Spring IOC的基本概念:
Spring容器-一个IOC容器,用以管理程序中的各种对象以及他们之间的联系。
- IOC(Inversion Of Contorl)控制反转 ——原本是由应用程序管理对象之间的关系,现在把控制权交给了容器,称之为控制反转。通俗来说就是原本我们创建对象需要使用new,但是我们现在不直接跟对象打交道了,而是在配置文件中写好需要一个什么样的对象,由容器代替我们去新建对象。
比如说,我创建这样一个类:
public class OrderDao {
public void selcet(){
System.out.println("select");
}
}
然后在resource资源目录下新建config.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"
xmlns:contex="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- bean definitions here -->
</beans>
原本我新建一个它的实例需要通过new,但是有了IOC之后,我只需要在config.xml中声明
<bean id="OrderDao" class="com.github.hcsp.OrderDao"/>然后通过工厂方法获取:
//接受一个config.xml路径
BeanFactory beanFactory = new ClassPathXmlApplicationContext("classpath:db/mybatis/config.xml");
//最核心的API
OrderDao orderDao = (OrderDao) beanFactory.getBean("OrderDao");
//拿到的这个Bean默认情况下是单例的既同一时刻只能存在一个类的实例
也可以通过注解告诉SpringBoot要如何构造实例:
@Configuration
public class JavaConfiguration {
@Bean
public OrederDao orederDao(){
return new OrederDao();
}
}
同时还可以基于注解声明类之间存在的依赖关系,比如这种情况:
public class OrderService {
private OrderDao orderDao;
public void doSomething() {
orderDao.selcet();
}
}
OrderService中存在对OrderDao的引用,原本是需要我们自己去管理对象之间的依赖关系,但是有了Spring之后,我们只需要声明他们之间存在依赖关系,管理他们这种关系的工作就由Spring去完成了。如何实现这个过程呢?这时候要是你直接通过beanFactory去新建OrderService的实例,是没有这种依赖关系的:
OrderService orderService = (OrderService) beanFactory.getBean("OrderService");
public class OrderService {
@Resource
private OrderDao orderDao;
public void doSomething() {
orderDao.selcet();
}
}
接着在<beans>标签中添加<contex:annotation-config/>,将schemaLocation增加为:
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
情况就不一样了:
- Spring MVC——基于Spring和Servlet的web应用框架。是基于Spring但是比Spring更强大的一个应用。
SpringMVC架构图:
M-model(模型) 代表数据。
V-view(视图)代表网页,JSP等用以展示模型中的数据。
C-controller(控制器)将不同的数据显示在不同的视图上,这个过程由Servlet(小服务器)完成。
- SpringBoot——集成度和自动化程度更高的容器。(内嵌了Servlet)
SpringBoot封装程度更高,简化了各种配置过程,但是使得人们丧失了对底层的控制,更难以了解程序的实现细节。
2.手写一个最精简的IOC容器
从新建properties文件开始,记得前面讲Mybatis的动态SQL配置日志那里,新建了一个log4j.properties文件,其中都是一个个的
XXX=XXX。这其实类似于HashMap的键值对(Properties类继承了HashTable),前面是对象名,后面是对象的全限定类型。
在我们的IOC容器中,添加自己的映射。新建一个beans.properties文件,写入以下数据:
orderDao=com.github.hcsp.ioc.OrderDao
userDao=com.github.hcsp.ioc.UserDao
userService=com.github.hcsp.ioc.UserService
orderService=com.github.hcsp.ioc.OrderService
接下来用这样一个例子演示IOC加载类之间依赖关系的原理:
/**
* 从.properties文件中获取信息
*
* @return 一个properties类
*/
public static Properties getAndLoadProperties() {
Properties properties = new Properties();
try {
properties.load(MyIoCContainer.class.getResourceAsStream("/beans.properties"));
} catch (IOException e) {
throw new RuntimeException("properties路径有误"+e);
}
return properties;
}
/**
* 遍历.properties文件中的内容,生成value的实例,将<key,value的实例>逐个映射到HashMap中
*
* @param properties 加载后的properties实例
* @return 映射后的HashMap容器
*/
public static HashMap<String, Object> newInstance(Properties properties) {
HashMap<String, Object> hashMap = new HashMap<>();
properties.forEach((beanName, beanInstance) -> {
try {
Class<?> klass = Class.forName((String) beanInstance);
Object newBeanInstance = klass.getConstructor().newInstance();
hashMap.put((String) beanName, newBeanInstance);
} catch (Exception e) {
throw new RuntimeException();
}
});
return hashMap;
}
/**
* 为带有@Autowired标签的成员变量设置依赖关系
*
* @param beanName null
* @param beanInstance 被依赖的类的全限定类名
* @param beans 类与类名之间的映射关系
*/
@SuppressWarnings("unused")
public static void dependencyInstance(String beanName, Object beanInstance, HashMap<String, Object> beans) {
List<Field> fields = Stream.of(beanInstance.getClass().getDeclaredFields())
.filter(field -> field.getAnnotation(Autowired.class) != null)
.collect(Collectors.toList());
fields.forEach(field -> {
String filedName = field.getName();
Object filedInstance = beans.get(filedName);
field.setAccessible(true);
try {
//为beanInstance对象设置依赖关系
field.set(beanInstance, filedInstance);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
});
}
// 启动该容器
public void start() {
Properties properties = getAndLoadProperties();
hashMap = newInstance(properties);
hashMap.forEach((name, instance) -> {
dependencyInstance(name, instance, hashMap);
});
}
// 从容器中获取一个bean
public Object getBean(String beanName) {
return hashMap.get(beanName);
}
public class MyIoCContainer {
private HashMap<String, Object> hashMap;
public static void main(String[] args) {
MyIoCContainer container = new MyIoCContainer();
container.start();
OrderService orderService = (OrderService) container.getBean("orderService");
orderService.createOrder();
}
运行结果:
* A BeanDefinition describes a bean instance, which has property values,
* constructor argument values, and further information supplied by
* concrete implementations.
根据描述信息实现BeanDefinition的载入和解析,最后同样也是Bean的实例化跟依赖注入了。