Spring5基础(2)——Spring IoC控制反转(基础)
Spring IoC控制反转概念
csdn内容同步,id同名,csdn博客链接
Inversion of Control(IoC)控制反转:是一种通过描述(xml配置文件或注解),并通过第三方去产生或获取特定对象的方式。
实现控制反转的是:IoC容器。
操作方法是:依赖注入。
控制反转的结果是对象实例不再有调用者创建,而是由Spring容器来创建,即控制权由调用者转移到Spring容器。
Spring的依赖注入:Spring容器负责将被依赖的对象(需要的对象实例),赋值给调用者的成员变量,相当于为调用者注入了它所依赖的实例。
为什么要使用Spring IoC,作用是什么?
使用Spring IoC的作用是:消减程序间的耦合问题。
举个栗子:比如在B类中需要一个A类的实例对象,在B类中若使用New关键字来创建A类实例,按如下所示:
package com.lipiao.demo;
public class A {
String name;
public void setName(String name) {
this.name = name;
}
}
package com.lipiao.demo;
public class B {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
//修改前
A a=new A();
a.setName("A1");
System.out.println(a.name);
}
}
运行效果就是控制台输出A1:
解耦前
实际开发中肯定不止2个类,随着逻辑处理越来越多,这样的处理方式会使不同的类之间耦合越来越严重,代码的维护会比较困难。
耦合:程序间的依赖关系
包括:①类之间的依赖 ②方法之间的依赖
弊端 独立性很差
解耦:尽可能降低程序间的依赖关系(经可能是因为有的耦合没法避免)
实际开发中应该做到:编译期间不依赖,运行时才依赖
解耦思路:
第一步:使用反射来创建对象,而避免使用new关键字
第二步:通过读取配置文件来获取要创建的对象权限定类名
使用java的反射特性来修改上面的B类中的代码,假设A类所在包路径为:com.lipiao.demo.A
package com.lipiao.demo;
public class B {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
//修改前
A a=new A();
a.setName("A1");
System.out.println(a.name);
//使用反射
Class<?> classA = Class.forName("com.lipiao.demo.A");
A a2=(A)classA.newInstance();
a2.setName("A2");
System.out.println(a2.name);
}
}
运行效果如下:
使用java反射初步解耦
这样New关键字就没有啦,初步解耦就完成啦。
更多java反射特性的使用,本文不多撰写啦,我找了一片博客:Java的高级特性 - 反射
但是以上代码还没有做到编译期间不依赖,运行时才依赖。
进一步解耦就要使用Spring IoC控制反转啦,通过依赖注入,让IoC容器帮我们创建实例对象。
Spring IoC容器的基本使用
实现控制反转的是:IoC容器。
Spring IoC容器的设计基于两个接口:
①BeanFactory接口,使用绝对路径
//3.使用Spring IoC容器 BeanFactory接口 使用绝对路径
BeanFactory beanFactory = new XmlBeanFactory(new FileSystemResource
("C:\\Users\\11092\\IdeaProjects\\javaEE\\src\\main\\resources\\META-INF\\applicationContext.xml"));
//通过容器创建A类实例,xml中id为A,getBean传入A
A a3 = (A) beanFactory.getBean("A");
System.out.println(a3.name);
applicationContext.xml
<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.xsd">
<bean id="A" class="com.lipiao.demo.A">
<property name="name" value="A3"></property>
</bean>
</beans>
XmlBeanFactory
②ApplicationContext接口
1.使用ClassPathXmlApplicationContext创建ApplicationContext接口实例,使用相对路径(resources根目录)
//4.使用Spring IoC容器 ApplicationContext接口
//有3种创建ApplicationContext接口的方式:
//4.1 ClassPathXmlApplicationContext 使用相对路径(resources根目录)
ApplicationContext applicationContext4_1 =
new ClassPathXmlApplicationContext("META-INF/applicationContext.xml");
A a4_1= (A) applicationContext4_1.getBean("A4_1");
System.out.println(a4_1.name);
applicationContext.xml
<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.xsd">
<bean id="A4_1" class="com.lipiao.demo.A">
<property name="name" value="A4_1"></property>
</bean>
</beans>
会有两条红色的提示信息,一条是刷新ClassPathXmlApplicationContext,一条是从xml配置文件中加载对应的bean文件
运行结果:控制台输出A4_1
ClassPathXmlApplicationContext
2.使用FileSystemXmlApplicationContext创建ApplicationContext接口实例,使用绝对路径
//4.2 FileSystemXmlApplicationContext 使用绝对路径
ApplicationContext applicationContext4_2= new FileSystemXmlApplicationContext(
"C:\\Users\\11092\\IdeaProjects\\javaEE\\src\\main\\resources\\META-INF\\applicationContext.xml");
A a4_2= (A) applicationContext4_2.getBean("A4_2");
System.out.println(a4_2.name);
applicationContext.xml
<bean id="A4_2" class="com.lipiao.demo.A">
<property name="name" value="A4_2"></property>
</bean>
会有两条红色的提示信息,一条是刷新ClassPathXmlApplicationContext,一条是从xml配置文件中加载对应的bean文件
运行结果:控制台输出A4_2
3.使用web服务器实例化ApplicationContext容器
一般使用基于org.springframework.web.context.ContextLoaderListener的实现方式,在web.xml文件中添加:
<context-parm>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:applicationContext.xml
</param-value>
</context-parm>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
Spring IoC容器的依赖注入类型
①构造方法注入
Spring框架可以采用java的反射机制,通过构造方法完成依赖注入。
以下面这个C类为例:
package com.lipiao.demo;
//Spring IoC容器依赖注入 1.构造方法注入
public class C {
String name;
public C(String name) {
this.name = name;
}
}
applicationContext.xml中
<bean id="C5_1" class="com.lipiao.demo.C">
<constructor-arg index="0" value="C_strName_constructor"/>
</bean>
index用于定义参数的位置,value表示参数为常数,若为实例引用,将value替换为ref
运行效果:
会有两条红色的提示信息,一条是刷新ClassPathXmlApplicationContext,一条是从xml配置文件中加载对应的bean文件
控制台打印C_strName_constructor
信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@7d907bac: startup date [Wed Jul 17 21:12:40 CST 2019]; root of context hierarchy
七月 17, 2019 9:12:40 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from class path resource [META-INF/applicationContext.xml]
C_strName_constructor
②使用属性的setter方法注入(这是最主流的注入方式)
使用setter注入方法和构造方法注入类似,刚刚在介绍Spring IoC容器的基本使用的案例都是使用属性setter注入的。
还是看个例子吧,同样是上面的C类,修改C类代码,添加setter方法,删掉构造方法:
package com.lipiao.demo;
//Spring IoC容器依赖注入
// 1.构造方法注入
// 2.setter方法注入
public class C {
String name;
//构造方法注入
// public C(String name) {
// this.name = name;
// }
//setter方法注入
public void setName(String name) {
this.name = name;
}
}
修改applicationContext.xml配置信息:
<bean id="C5_2" class="com.lipiao.demo.C">
<property name="name" value="C_strName_setter"/>
</bean>
运行效果:
会有两条红色的提示信息,一条是刷新ClassPathXmlApplicationContext,一条是从xml配置文件中加载对应的bean文件
控制台打印C_strName_setter
信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@148080bb: startup date [Wed Jul 17 21:29:37 CST 2019]; root of context hierarchy
七月 17, 2019 9:29:37 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from class path resource [META-INF/applicationContext.xml]
C_strName_setter