2.3.Spring的IOC解决程序耦合
2.3.1.创建工程
2.3.1.1.pom.xml
<?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引入到项目中。
Spring常用功能的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进行交互。
2.3.1.2.dao
/*** 持久层实现类*/
public class UserDaoImpl implements UserDao {@Overridepublic void addUser(){System.out.println("insert into tb_user......");}
}
2.3.1.3.service
/*** 业务层实现类*/
public class UserServiceImpl implements UserService {//此处有依赖关系private UserDao userDao = new UserDaoImpl();public void addUser(){userDao.addUser();}
}
2.3.2.IOC
2.3.2.1.applicationContext.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
2.3.2.2.测试
/*** 模拟表现层*/
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层仍然耦合
2.3.3.DI
概述:DI(Dependency Injection)依赖注入,在Spring创建对象的同时,为其属性赋值,称之为依赖注入。
2.3.3.1.构造函数注入
顾名思义,就是使用类中的构造函数,给成员变量赋值。注意,赋值的操作不是我们自己做的,而是通过配置的方式,让spring框架来为我们注入。具体代码如下:
/*** 业务层实现类*/
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 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>
2.3.3.2.set方法注入
顾名思义,就是在类中提供需要注入成员的set方法。具体代码如下:
/*** 业务层实现类*/
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 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>
2.3.3.3.自动注入
不用在配置中 指定为哪个属性赋值,由spring自动根据某个 “原则” ,在工厂中查找一个bean并为属性注入值。具体代码如下:
/*** 业务层实现类*/
public class UserServiceImpl implements UserService {private UserDao userDao;public void setUserDao(UserDao userDao) {this.userDao = userDao;}public void addUser(){userDao.addUser();}
}
<?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 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>
2.3.3.4.注入集合类型的属性
顾名思义,就是给类中的集合成员传值,它用的也是set方法注入的方式,只不过变量的数据类型都是集合。我们这里介绍注入数组,List,Set,Map。具体代码如下:
/*** 业务层实现类*/
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 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>
2.4.Spring中的工厂类
2.4.1.ApplicationContext
-
ApplicationContext的实现类,如下图:
- ClassPathXmlApplicationContext:加载类路径下 Spring 的配置文件
- FileSystemXmlApplicationContext:加载本地磁盘下 Spring 的配置文件
2.4.2.BeanFactory
-
spring中工厂的类结构图
-
区别:
-
ApplicationContext:只要一读取配置文件,默认情况下就会创建对象。
/*** 业务层实现类*/ 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();} }
/*** 模拟表现层*/ public class Client {public static void main(String[] args) {new ClassPathXmlApplicationContext("applicationContext.xml");System.out.println("Spring IOC容器创建好了");} }
-
-
BeanFactory:是在 getBean 的时候才会创建对象。
/*** 业务层实现类*/ 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();} }
/** * 模拟表现层 */ public class Client {public static void main(String[] args) {new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));System.out.println("Spring IOC容器创建好了");} }
2.5.bean的作用范围
2.5.1.概述
- 在Spring中,bean作用域用于确定bean实例应该从哪种类型的Spring容器中返回给调用者。
2.5.2.五种作用域
-
目前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的作用范围,其配置方式如下所示:<bean id="..." class="..." scope="singleton"/>
-
需要根据场景决定对象的单例、多例模式
单例:Service、DAO、SqlSessionFactory(或者是所有的工厂)
多例:Connection、SqlSession
2.6.bean的生命周期
2.6.1.单例bean
-
案例
<bean id="userService" class="com.by.service.UserServiceImpl"scope="singleton" init-method="init" destroy-method="destroy">
/*** 业务层实现类*/ 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();} }
/*** 模拟表现层*/ public class Client {public static void main(String[] args) {ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");//关闭容器ac.close();} }
-
生命周期:
[容器启动]—>构造方法(实例化)—>set方法(注入)—>init方法(初始化)—>[容器关闭]—>destroy方法(销毁)
2.6.2.多例bean
-
案例
<bean id="userService" class="com.by.service.UserServiceImpl"scope="prototype" init-method="init" destroy-method="destroy">
/*** 业务层实现类*/ 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();} }
/*** 模拟表现层*/ public class Client {public static void main(String[] args) {ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");//使用对象ac.getBean("userService");} }
-
生命周期:
[使用对象]---->构造方法(实例化)—>set方法(注入)—>init方法(初始化)—>[JVM垃圾回收]—>destroy方法(销毁)