文章目录
Spring的IOC从入门到深入
1.分析下面代码开发中存在的问题:
2. 针对上述代码存在的问题,我们该解决,
解决方案是: 将测试类与接口的耦合,通过第三方容器来解决
实现步骤分为两步:
第一步: 将耦合类配置到xml文件中, 比如: beans.xml
<?xml version="1.0" encoding="utf-8" ?>
<!--上面是:文档声明是xml文档-->
<!--XML有且仅有一个根标签: objs,由于是自定义的xml,标签可以任意定义-->
<objs>
<!--子标签: obj
id: 唯一标识,一般建议使用类名,首字母小写
class:类的全路径
-->
<obj id="dog" class="com.tedu.service.impl.Dog"/>
<obj id="cat" class="com.tedu.service.impl.Cat"/>
</objs>
第二步: 创建容器对象,加载配置文件,创建对象存储到容器中
/**
* 模拟Spring核心对象:
* 1.通过解析xml文件,读取到class属性的值
* 2.利用反射Class.newInstance()创建对象
* 3.把创建的对象存到map里面(map就是spring容器)
* map容器存对象有两种方式:
* 方式一: map中的key: 类名称(首字母小写), map的value: 类对象
* 方式二: map中的key: 类的class对象, map的value: 类对象
*/
public class MyApplicationContext {
//1.引入被加载的xml文件
private String resourcesXml;
//2.创建容器对象, 就是map, 底层是线程安全的map
private static Map<Object,Object> map = new ConcurrentHashMap<Object,Object>();
//3.通过构造方法给引入的xml文件赋值
public MyApplicationContext(String xmlPath){
this.resourcesXml = xmlPath;
//解析配置文件,创建对象,存到map容器中
try{
//5.解析配置文件的代码: dom4j技术解析xml,
SAXReader saxReader = new SAXReader();//核心解析器对象
//6.类加载器获取配置文件字节输入流
InputStream in = MyApplicationContext.class.getClassLoader().getResourceAsStream(resourcesXml);
//7.读取配置文件字节输入流,获取xml的文档对象
Document document = saxReader.read(in);
//8.根据document获取所有的obj标签对象
List<Node> list = document.selectNodes("//obj");
//9.遍历list集合,获取list集合里面的每一个obj标签
for (Node node : list) {
//10. 把node节点转换为 element标签
Element element = (Element)node;
//11. 根据element标签对象:获取id属性值,获取class属性值
String idValue = element.attributeValue("id");//比如:idValue = dog
String classPath = element.attributeValue("class");//比如:classPath = 包名.Dog
//12.利用反射创建对象
Class aClass = Class.forName(classPath);//得到类的字节码对象, 比如: Dog.class
Object obj = aClass.newInstance();//反射: 默认使用无参数的构造方法创建对象
//13.把创建好的对象存到容器中
//13.1比如: 根据 dog名称-- new Dog()对象
map.put(idValue,obj);
//13.2比如: 根据 Dog.class-- new Dog()对象
map.put(aClass,obj);
}
}catch (Exception e){
e.printStackTrace();
}
}
//4.提供一个根据名称: 也就是根据id的属性值获取 bean对象的方法
//直接从map容器中获取对象
public static Object getBean(String idValue){
return map.get(idValue);
}
//5.提供一个根据名称: 也就是根据Class字节码对象获取 bean对象的方法
//定义泛型的优点: 避免类型强转
//直接从map容器中获取对象
public static <T> T getBean(Class<T> clz){
return (T)map.get(clz);
}
}
第3步: 测试
public class Demo {
public static void main(String[] args) {
//1.创建核心对象:
//核心对象使用类加载器加载配置文件,
// 类加载器文件在resources根目录下面,所以和beans.xml同一级目录(相对路径写法)
MyApplicationContext map =
new MyApplicationContext("beans.xml");
//2.根据id属性值: 获取bean对象,这个方法没有使用泛型,需要强转
//解耦合: 看不到Demo类与 Pet接口,或者Dog实现类有耦合关系
Pet pet = (Pet)map.getBean("dog");
//3.根据class值: 获取bean对象,这个方法使用了泛型,不用强转
Pet pet2 = map.getBean(Dog.class);
pet.hello();
pet2.hello();
}
}
4. 总结
5. 下面我们看下spring框架的IOC是如何实现的
第一步: 在创建的maven项目中,导入spring-webmvc依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.16.RELEASE</version>
</dependency>
依赖解释:
第二步: 创建spring配置文件
<?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:context="http://www.springframework.org/schema/context"
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-4.0.xsd">
<!--
为什么写bean标签,不能写其它?
因为上述头中spring-beans.xsd: 这个是约束文件,规定了标签的名称以及属性的书写
id: 唯一标识
scope: 作用范围, 比如: 创建单例对象(只创建一个对象)
lazy-init: 是否立即创建对象,比如: false 懒加载,什么时候调用方法,什么时候创建对象
-->
<bean id="dog" scope="singleton" lazy-init="false"
class="com.tedu.service.impl.Dog"/>
<bean id="cat" class="com.tedu.service.impl.Cat"/>
</beans>
第三步: 使用spring核心对象,获取容器中的bean对象
public class Demo2 {
public static void main(String[] args) {
//1.创建容器对象
//在这里: 为什么直接写文件名称, 因为底层是类加载器加载spring配置文件的
//类加载器在resources根目录下面: 和spring.xml同一级目录
ApplicationContext map = new ClassPathXmlApplicationContext("spring.xml");
//2.根据类的名称获取对象: 这个方法没有使用泛型,必须强转
Pet pet = (Pet)map.getBean("dog");
//3.根据Class获取对象: 这个方法使用泛型,不用强转
Pet pet2 = map.getBean(Dog.class);
}
}
6. 对比spring框架的核心 与自定义的容器
通过下图我们能分析到一下三点:
-
Spring框架底层是通过类加载器加载spring的配置文件的,类加载器的位置在resources的根目录下面
所以和配置文件同一级目录,直接写目录名称 -
Spring框架的容器底层就是一个map集合
-
Spring容器在存储对象有两种方式
第一: 根据类名,存储该类的对象
第二:根据类的class字节码,存储该类的对象
7. Spring的注解开发
核心思路:
- 通过注解替代配置文件, Spring框架中配置自动扫描包机制,根据扫描的类,创建对象,存到容器中.
- Spring容器通过注解开发时,在存储对象依然是下面的两种方式
第一: 根据类名,存储该类的对象
第二:根据类的class字节码,存储该类的对象 - Spring的DI依赖注入: 其实就是给当前类的成员变量(成员属性)赋值
常见DI注入方式有两种:-
通过构造方法DI依赖注入
*比如:
-
通过set方法DI依赖注入
-
比如:
-
8. Spring整合junit进行单元测试
略