工程模式的IOC解决程序耦合
什么是IOC
-
IOC (Inverse of Control)即控制反转:正传是自己创建依赖对象;反正是有IOC工厂来创建依赖对象;
-
原来:
我们在获取对象时,都是采用new的方式。是主动的。
-
现在:
我们获取对象时,同时跟工厂要,有工厂为我们查找或者创建对象。是被动的。
这种被动接收的方式获取对象的思想就是控制反转,它是spring框架的核心之一。
工厂模式的IOC解耦
-
案例一
java/** * bean工厂 */ public class BeanFactory_v1 { /** * 获得UserServiceImpl对象 * @return */ public static UserService getUserService(){ return new UserServiceImpl(); } /** * 获得UserDaoImpl对象 * @return */ public static UserDao getUserDao(){ return new UserDaoImpl(); } }
问题:我们在开发中会有很多个service和dao,此时工厂类就要添加无数个方法。
-
案例二
properties#1、配置要使用的dao和service UserDao=com.by.dao.UserDaoImpl UserService=com.by.service.UserServiceImpl
java/** * bean工厂 */ public class BeanFactory_v2 { private static Properties prop = new Properties(); /** * 根据全类名获取bean对象 * @param beanName * @return * @throws ClassNotFoundException */ public static Object getBean(String beanName) { try { //不能使用:web工程发布后没有src目录 //InputStream is = new FileInputStream("src/bean.properties"); InputStream is = BeanFactory_v2.class.getClassLoader() .getResourceAsStream("bean.properties"); prop.load(is); return Class.forName(prop.getProperty(beanName)).newInstance(); } catch (Exception e) { e.printStackTrace(); } return null; } public static void main(String[] args) { System.out.println(prop.get("UserService")); System.out.println(getBean("UserService")); } }
java/** * 业务层实现类 */ public class UserServiceImpl implements UserService { private UserDao userDao = (UserDao) BeanFactory.getBean("UserDao"); public void addUser(){ userDao.addUser(); } }
测试:
java/** * 模拟表现层 */ public class Client { public static void main(String[] args) { //直接引用接口实现类 for (int i = 0; i < 5; i++) { UserService userService = (UserService)BeanFactory.getBean("UserService"); System.out.println(userService); } } }
问题:
- 每次都会创建新的对象
- 程序运行时才创建对象(读取配置文件)
-
案例三
javapackage com.by.factory; import java.io.InputStream; import java.util.HashMap; import java.util.Map; import java.util.Properties; import java.util.Set; /** * bean工厂 */ public class BeanFactory_v3 { //定义一个容器,用于存放对象 private static Map<String, Object> beans = new HashMap<>(); /** * 加载配置文件 */ static { try { //2、读取配置文件 //不能使用:web工程发布后没有src目录 //InputStream is = new FileInputStream("src/bean.properties"); InputStream is = BeanFactory_v3.class.getClassLoader() .getResourceAsStream("bean.properties"); //3、通过反射创建对象,把对象存到容器中 Properties prop = new Properties(); prop.load(is); Set<Map.Entry<Object, Object>> entrySet = prop.entrySet(); for (Map.Entry<Object, Object> entry : entrySet) { String key = entry.getKey().toString(); String beanName = entry.getValue().toString(); Object value = Class.forName(beanName).newInstance(); beans.put(key, value); } } catch (Exception e) { e.printStackTrace(); } } /** * 4、在使用的时候,直接从工厂拿 * @param beanName * @return */ public static Object getBean(String beanName) { try { return beans.get(beanName); } catch (Exception e) { e.printStackTrace(); } return null; } public static void main(String[] args) { System.out.println(getBean("UserService")); } }
Spring的IOC解决程序耦合
创建工程
pom.xml
java
?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.by</groupId>
<artifactId>Spring_IOC_Xml</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<!-- 项目源码及编译输出的编码 -->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<!-- 项目编译JDK版本 -->
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<!-- Spring常用依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.8.RELEASE</version>
</dependency>
</dependencies>
</project>
注意:Jar包彼此存在依赖,只需引入最外层Jar即可由Maven自动将相关依赖Jar引入到项目中。
核心容器由 beans、core、context 和 expression(Spring Expression Language,SpEL)4个模块组成。
- spring-beans和spring-core模块是Spring框架的核心模块,包含了控制反转(Inversion of Control,IOC)和依赖注入(Dependency Injection,DI)。BeanFactory使用控制反转对应用程序的配置和依赖性规范与实际的应用程序代码进行了分离。BeanFactory属于延时加载,也就是说在实例化容器对象后并不会自动实例化Bean,只有当Bean被使用时,BeanFactory才会对该 Bean 进行实例化与依赖关系的装配。
- spring-context模块构架于核心模块之上,扩展了BeanFactory,为它添加了Bean生命周期控制、框架事件体系及资源加载透明化等功能。此外,该模块还提供了许多企业级支持,如邮件访问、远程访问、任务调度等,ApplicationContext 是该模块的核心接口,它的超类是 BeanFactory。与BeanFactory不同,ApplicationContext实例化后会自动对所有的单实例Bean进行实例化与依赖关系的装配,使之处于待用状态。
- spring-expression 模块是统一表达式语言(EL)的扩展模块,可以查询、管理运行中的对象,同时也可以方便地调用对象方法,以及操作数组、集合等。它的语法类似于传统EL,但提供了额外的功能,最出色的要数函数调用和简单字符串的模板函数。EL的特性是基于Spring产品的需求而设计的,可以非常方便地同Spring IoC进行交互。
dao
java
/**
* 持久层实现类
*/
public class UserDaoImpl implements UserDao {
@Override
public void addUser(){
System.out.println("insert into tb_user......");
}
}
service
java
/**
* 业务层实现类
*/
public class UserServiceImpl implements UserService {
//此处有依赖关系
private UserDao userDao = new UserDaoImpl();
public void addUser(){
userDao.addUser();
}
}
IOC
applicationContext.xml
xml
<?xml version="1.0" encoding="UTF-8"?>
<!--1、注意:要导入schema约束-->
<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">
<!--
2、把对象交给spring来创建
id:给对象在容器中提供一个唯一标识。用于获取对象
class:指定类的全限定类名。用于反射创建对象。默认情况下调用无参构造函数
-->
<bean id="userDao" class="com.by.dao.UserDaoImpl"></bean>
<bean id="userService" class="com.by.service.UserServiceImpl"></bean>
</beans>
注意:命名无限制,约定俗成命名有:spring-context.xml、applicationContext.xml、beans.xml
测试
java
/**
* 模拟表现层
*/
public class Client {
public static void main(String[] args) {
//1.使用ApplicationContext接口,就是在获取spring容器
ApplicationContext ac = new
ClassPathXmlApplicationContext("applicationContext.xml");
//2.根据bean的id获取对象
UserDao userDao = (UserDao) ac.getBean("userDao");
System.out.println(userDao);
UserService userService = (UserService) ac.getBean("userService");
System.out.println(userService);
userService.addUser();
}
}
- 问题:service层仍然耦合
DI
概述:DI(Dependency Injection)依赖注入,在Spring创建对象的同时,为其属性赋值,称之为依赖注入。
构造函数注入
顾名思义,就是使用类中的构造函数,给成员变量赋值。注意,赋值的操作不是我们自己做的,而是通过配置的方式,让spring框架来为我们注入。具体代码如下:
java
/**
* 业务层实现类
*/
public class UserServiceImpl implements UserService {
private UserDao userDao;
private String name;
private Integer age;
public UserServiceImpl(UserDao userDao, String name, Integer age) {
this.userDao = userDao;
this.name = name;
this.age = age;
}
public void addUser(){
System.out.println(name+","+age);
userDao.addUser();
}
}
xml
<?xml version="1.0" encoding="UTF-8"?>
<!--1、注意:要导入schema约束-->
<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">
<!--2、把对象交给spring来创建-->
<bean id="userDao" class="com.by.dao.UserDaoImpl"></bean>
<bean id="userService" class="com.by.service.UserServiceImpl">
<!--
要求:类中需要提供一个对应参数列表的构造函数。
标签:constructor-arg
==给谁赋值:==
index:指定参数在构造函数参数列表的索引位置
name:指定参数在构造函数中的名称
==赋什么值:==
value:它能赋的值是基本数据类型和String类型
ref:它能赋的值是其他bean类型,也就是说,必须得是在配置文件中配置过的bean
-->
<constructor-arg name="userDao" ref="userDao"></constructor-arg>
<constructor-arg name="name" value="张三"></constructor-arg>
<constructor-arg name="age" value="18"></constructor-arg>
</bean>
</beans>
set方法注入
顾名思义,就是在类中提供需要注入成员的set方法。具体代码如下:
java
/**
* 业务层实现类
*/
public class UserServiceImpl implements UserService {
private UserDao userDao;
private String name;
private Integer age;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void setName(String name) {
this.name = name;
}
public void setAge(Integer age) {
this.age = age;
}
public void addUser(){
System.out.println(name+","+age);
userDao.addUser();
}
}
xml
<?xml version="1.0" encoding="UTF-8"?>
<!--1、注意:要导入schema约束-->
<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">
<!--2、把对象交给spring来创建-->
<bean id="userDao" class="com.by.dao.UserDaoImpl"></bean>
<bean id="userService" class="com.by.service.UserServiceImpl">
<!--
要求:property
标签:constructor-arg
==给谁赋值:==
name:找的是类中set方法后面的部分
==赋什么值:==
value:它能赋的值是基本数据类型和String类型
ref:它能赋的值是其他bean类型,也就是说,必须得是在配置文件中配置过的bean
-->
<property name="userDao" ref="userDao"></property>
<property name="name" value="张三"></property>
<property name="age" value="18"></property>
</bean>
</beans>
自动注入
不用在配置中 指定为哪个属性赋值,由spring自动根据某个 "原则" ,在工厂中查找一个bean并为属性注入值。具体代码如下:
java
/**
* 业务层实现类
*/
public class UserServiceImpl implements UserService {
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void addUser(){
userDao.addUser();
}
}
xml
<?xml version="1.0" encoding="UTF-8"?>
<!--1、注意:要导入schema约束-->
<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">
<!--2、把对象交给spring来创建-->
<bean id="userDao" class="com.by.dao.UserDaoImpl"></bean>
<!--autowire="byType":按照类型自动注入值-->
<bean id="userService" class="com.by.service.UserServiceImpl" autowire="byType">
</bean>
</beans>
xml
<?xml version="1.0" encoding="UTF-8"?>
<!--1、注意:要导入schema约束-->
<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">
<!--2、把对象交给spring来创建-->
<bean id="userDao" class="com.by.dao.UserDaoImpl"></bean>
<!--autowire="byType":按照类型自动注入值-->
<bean id="userService" class="com.by.service.UserServiceImpl" autowire="byName">
</bean>
</beans>
注入集合类型的属性
顾名思义,就是给类中的集合成员传值,它用的也是set方法注入的方式,只不过变量的数据类型都是集合。我们这里介绍注入数组,List,Set,Map。具体代码如下:
java
/**
* 业务层实现类
*/
public class UserServiceImpl implements UserService {
private UserDao userDao;
private String[] myStrs;
private List<String> myList;
private Set<String> mySet;
private Map<String,String> myMap;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void setMyStrs(String[] myStrs) {
this.myStrs = myStrs;
}
public void setMyList(List<String> myList) {
this.myList = myList;
}
public void setMySet(Set<String> mySet) {
this.mySet = mySet;
}
public void setMyMap(Map<String, String> myMap) {
this.myMap = myMap;
}
public void addUser(){
System.out.println(Arrays.toString(myStrs));
System.out.println(myList);
System.out.println(mySet);
System.out.println(myMap);
userDao.addUser();
}
}
xml
<?xml version="1.0" encoding="UTF-8"?>
<!--1、注意:要导入schema约束-->
<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">
<!--2、把对象交给spring来创建-->
<bean id="userDao" class="com.by.dao.UserDaoImpl"></bean>
<bean id="userService" class="com.by.service.UserServiceImpl">
<!--
要求:property
标签:constructor-arg
==给谁赋值:==
name:找的是类中set方法后面的部分
==赋什么值:==
value:它能赋的值是基本数据类型和String类型
ref:它能赋的值是其他bean类型,也就是说,必须得是在配置文件中配置过的bean
-->
<property name="userDao" ref="userDao"></property>
<!-- 给mySet集合注入数据 -->
<property name="mySet">
<set>
<value>AAA</value>
<value>BBB</value>
<value>CCC</value>
</set>
</property>
<!-- 注入array数组数据 -->
<property name="myArray">
<array>
<value>AAA</value>
<value>BBB</value>
<value>CCC</value>
</array>
</property>
<!-- 注入list集合数据 -->
<property name="myList">
<list>
<value>AAA</value>
<value>BBB</value>
<value>CCC</value>
</list>
</property>
<!-- 注入Map数据 -->
<property name="myMap">
<map>
<entry key="testA" value="aaa"></entry>
<entry key="testB" value="bbb"></entry>
</map>
</property>
</bean>
</beans>
Spring中的工厂类
ApplicationContext
-
ApplicationContext的实现类,如下图:
- ClassPathXmlApplicationContext:加载类路径下 Spring 的配置文件
- FileSystemXmlApplicationContext:加载本地磁盘下 Spring 的配置文件
BeanFactory
-
spring中工厂的类结构图
-
区别:
-
ApplicationContext:只要一读取配置文件,默认情况下就会创建对象。
java/** * 业务层实现类 */ public class UserServiceImpl implements UserService { private UserDao userDao; public UserServiceImpl() { System.out.println("UserServiceImpl对象创建了..."); } public void setUserDao(UserDao userDao) { this.userDao = userDao; } public void addUser(){ userDao.addUser(); } }
java/** * 模拟表现层 */ public class Client { public static void main(String[] args) { new ClassPathXmlApplicationContext("applicationContext.xml"); System.out.println("Spring IOC容器创建好了"); } }
-
-
BeanFactory:是在 getBean 的时候才会创建对象。
java/** * 业务层实现类 */ public class UserServiceImpl implements UserService { private UserDao userDao; public UserServiceImpl() { System.out.println("UserServiceImpl对象创建了..."); } public void setUserDao(UserDao userDao) { this.userDao = userDao; } public void addUser(){ userDao.addUser(); } }
java/** * 模拟表现层 */ public class Client { public static void main(String[] args) { new XmlBeanFactory(new ClassPathResource("applicationContext.xml")); System.out.println("Spring IOC容器创建好了"); } }
bean的作用范围
概述
- 在Spring中,bean作用域用于确定bean实例应该从哪种类型的Spring容器中返回给调用者。
五种作用域
-
目前Spring Bean的作用域或者说范围主要有五种:
作用域 说明 singleton 默认值,Bean以单例方式存在spring IoC容器 prototype 每次从容器中调用Bean时都返回一个新的实例,相当于执行newInstance() request WEB 项目中,Spring 创建一个 Bean 的对象,将对象存入到 request 域中 session WEB 项目中,Spring 创建一个 Bean 的对象,将对象存入到 session 域中 application WEB 项目中,Spring 创建一个 Bean 的对象,将对象存入到 ServletContext 域中 -
可以通过
<bean>
标签的scope
属性控制bean的作用范围,其配置方式如下所示:xml<bean id="..." class="..." scope="singleton"/>
-
需要根据场景决定对象的单例、多例模式
单例:Service、DAO、SqlSessionFactory(或者是所有的工厂)
多例:Connection、SqlSession
bean的生命周期
单例bean
-
案例
xml<bean id="userService" class="com.by.service.UserServiceImpl" scope="singleton" init-method="init" destroy-method="destroy">
java/** * 业务层实现类 */ public class UserServiceImpl implements UserService { private UserDao userDao; public UserServiceImpl() { System.out.println("调用构造方法创建bean..."); } public void setUserDao(UserDao userDao) { System.out.println("调用set方法注入值..."); this.userDao = userDao; } public void init(){ System.out.println("调用init方法初始化bean..."); } public void destroy(){ System.out.println("调用destroy方法销毁bean..."); } public void addUser(){ userDao.addUser(); } }
java/** * 模拟表现层 */ public class Client { public static void main(String[] args) { ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml"); //关闭容器 ac.close(); } }
-
生命周期:
[容器启动]--->构造方法(实例化)--->set方法(注入)--->init方法(初始化)--->[容器关闭]--->destroy方法(销毁)
多例bean
-
案例
xml<bean id="userService" class="com.by.service.UserServiceImpl" scope="prototype" init-method="init" destroy-method="destroy">
java/** * 业务层实现类 */ public class UserServiceImpl implements UserService { private UserDao userDao; public UserServiceImpl() { System.out.println("调用构造方法创建bean..."); } public void setUserDao(UserDao userDao) { System.out.println("调用set方法注入值..."); this.userDao = userDao; } public void init(){ System.out.println("调用init方法初始化bean..."); } public void destroy(){ System.out.println("调用destroy方法销毁bean..."); } public void addUser(){ userDao.addUser(); } }
java/** * 模拟表现层 */ public class Client { public static void main(String[] args) { ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml"); //使用对象 ac.getBean("userService"); } }
-
生命周期:
[使用对象]---->构造方法(实例化)--->set方法(注入)--->init方法(初始化)--->[JVM垃圾回收]--->destroy方法(销毁)