Spring IOC
zzj
一、Spring框架
1.1 spring框架的概述
- 基于分层的javaEE应用一站式轻量级开源框架。
- 解决企业应用开发的复杂性,高内聚低耦合。
- 它是一个框架集,内部集成了很多的功能,可以根据需求选择对应的子框架使用。
- 主要核心:IOC和AOP
- IOC:
- 控制反转:将对象实例化的创建过程转交给外部容器(IOC容器 充当工厂角色)去负责,容器提供单例模式的支持。
- 依赖注入:属性赋值的操作。
- AOP:面向切面,不修改源码进行功能的增强,例如:权限拦截、运行期监控等功能。
- IOC:
1.2 Spring的特点
- 降低耦合,简化开发
- AOP编程支持
- 便于代码后期的维护和升级
- 便于和其它框架进行整合
- 便于与事务进行操作
- 降低API开发难度
1.3 Spring环境搭建
-
创建maven的普通java项目,选择maven-archetype-quickstart。
-
在pom.xml中修改JDK版本,改为1.8;修改单元测试JUnit版本,改为4.12;删除pluginManagement标签。
-
在pom.xml中添加spring框架的依赖。
<!-- 添加spring框架的核心依赖 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.4.RELEASE</version> </dependency>
-
编写bean对象
public class UserService { public void test(){ System.out.println("Hello Spring!"); } }
-
在main目录下创建resources文件夹,并将其标记为资源目录。
-
在resources文件夹下创建spring.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" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- 配置Bean标签 id:唯一标识,默认类名首字母小写 class:要实例化的类的类路径,默认调用空的构造器创建对象 --> <bean id="userService" class="com.shsxt.service.UserService"></bean> </beans>
-
加载配置文件,获取实例化对象
public class Starters { public static void main(String[] args) { BeanFactory factory=new ClassPathXmlApplicationContext("spring.xml"); UserService userService =(UserService) factory.getBean("userService"); userService.test(); } }
1.4 Spring IOC中的接口
- IOC思想基于IOC容器完成,IOC容器底层就是对象工厂。
- Spring提供IOC容器两种实现方式(两个接口):
- BeanFactory:IOC容器基本实现,是spring内部的使用接口。
- ApplicationContext:BeanFactory接口的子接口,提供更多强大的功能。
- BeanFactory和ApplicationContext的区别:
- BeanFactory:加载配置文件的时候不会创建对象,在获取对象的时候才去创建对象。
- ApplicationContext:加载配置文件的时候就会把在配置文件对象进行创建。
二、Spring IOC配置文件加载
2.1 Spring单配置文件加载
spring.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="userService" class="com.xxxx.service.UserService"></bean>
</beans>
相对路径加载资源:
ApplicationContext ac = new ClassPathXmlApplicationContext("spring.xml");
绝对路径加载资源:(了解)
ApplicationContext ac = new
FileSystemXmlApplicationContext("C:/IdeaWorkspace/spring01/src/main/resources/spring.xml");
2.2 Spring多配置文件加载
service.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="userService" class="com.xxxx.service.UserService"></bean>
</beans>
dao.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="userDao" class="com.xxxx.dao.UserDao"></bean>
</beans>
2.2.1 传入多个文件名
// 同时加载多个资源文件
ApplicationContext ac = new
ClassPathXmlApplicationContext("spring.xml","dao.xml");
2.2.2 通过总的xml文件import其它配置文件
spring.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--导入需要包含的资源文件-->
<import resource="service.xml"/>
<import resource="dao.xml"/>
</beans>
加载时只用加载总的配置文件:
ApplicationContext ac = new ClassPathXmlApplicationContext("spring.xml");
三、Spring IOC对Bean对象的三种实例化方式
3.1 构造器实例化
-
是实例化bean最简单的方式,Spring IOC容器既能使用默认的空构造器 也能使用有参构造器(需要使用构造器注入)。
<bean id="userService" class="com.xxxx.service.UserService"></bean>
-
获取实例化对象
ApplicationContext ac = new ClassPathXmlApplicationContext("spring.xml"); UserService userService = (UserService) ac.getBean("userService"); userService.test();
注意:通过构造器实例化时需要实例化的对象必须含有一个默认的空构造器;构造器实例化是通过反射实现对象创建的。
3.2 静态工厂实例化
-
工厂类中含有static工厂方法,通过这个方法直接返回一个new对象
-
定义静态工厂类:
public class StaticFactory { /** * 定义对应的静态⽅法,返回实例化对象 * @return */ public static UserService createUserService() { return new UserService(); } }
-
设置配置文件XML:(使用factory-method表示调用这个静态方法)
<!--静态⼯⼚--> <bean id="userService" class="com.xxxx.factory.StaticFactory" factory-method="createUserService"></bean>
3.3 实例工厂实例化
-
使用的是普通方法去返回一个new对象
-
定义工厂类:
public class InstanceFactory { /** * 定义⽅法,返回实例化对象 * @return */ public UserService createUserService() { return new UserService(); } }
-
设置配置文件xml:
<!-- 实例化⼯⼚ 1.定义实例化⼯⼚bean 2.引⽤⼯⼚bean 指定⼯⼚创建⽅法(⽅法为⾮静态) --> <bean id="instanceFactory" class="com.xxxx.factory.InstanceFactory"></bean> <bean id="userService" factory-bean="instanceFactory" factory-method="createUserService"></bean>
四、Spring IOC操作bean管理的三种依赖关系
4.1 继承关系
-
前提:在Spring容器中,如果多个Bean存在相同的配置信息,那么可以定义一个父Bean,子类Bean将自动继承父类Bean的配置信息。
-
注意:父Bean的主要功能就是简化子Bean的配置,一般声明为abstract=“true”;使用了abstract=“true”表示该Bean不会实例化一个对应的Bean(p为标签,值注入的一种方式)
<!--使用父子Bean的配置-->
<bean id="car1" class="xxx.xxx.Car1" p:brand="奔驰" p:price="2000" p:color="red" abstract="true"/>
<bean id="car2" class="xxx.xxx.Car2" p:color="blue" parent="car1"/>
<bean id="car3" class="xxx.xxx.Car3" p:color="yellow" parent="car1"/>
<!--未使用父子Bean的配置-->
<bean id="car1" class="xxx.xxx.Car1" p:brand="奔驰" p:price="2000" p:color="red" />
<bean id="car2" class="xxx.xxx.Car2" p:brand="奔驰" p:price="2000" p:color="blue" />
<bean id="car3" class="xxx.xxx.Car3" p:brand="奔驰" p:price="2000" p:color="yellow" />
4.2 依赖关系
前提:在Spring容器中,当使用标签建立对其它Bean的依赖关系,Spring容器富足管理这些Bean的关系,Spring容器负责管理这些Bean的关系。
<bean id="car1" class="xxx.xxx.Car1" depends-on="car2"/>
<bean id="car2" class="xxx.xxx.Car2"/>
4.3 引用关系
前提:在Spring容器中,当使用<ref>
标签引用另外一个Bean时,但实际上两者并没有建立引用关系,即使我们编写错误,也需要在运行期间才能够发现。因此Spring提供了<idref>
元素标签,通过<idref>
引用另一个Bean的名字,可以在容器启动的时候检查引用关系的正确性,这样可以提前发现错误的配置信息。
<bean id="car" class="xxx.xxx.Car"></bean>
<bean id="boss" class="xxx.xxx.Boss">
<property name="carId">
<idref bean="car"/>
</property>
</bean>
五、Spring IOC属性注入
5.1 Spring IOC手动注入
5.1.1 set方法注入
注意:属性字段需要提供set方法。
bean对象:
public class AccountService {
//set方法注入
private AccountDao accountDao;
private UserDao userDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
//字符串
private String host;
private int port;
public void setHost(String host) {
this.host = host;
}
public void setPort(int port) {
this.port = port;
}
//List
private List<String> strList;
public void setStrList(List<String> strList) {
this.strList = strList;
}
// 打印集合
public void printList(){
strList.forEach(s-> System.out.println(s));
}
//map
private Map<String,Object> map;
public void setMap(Map<String, Object> map) {
this.map = map;
}
public void printMap(){
map.forEach((k,v)-> System.out.println(k+","+v));
}
//Properties
private Properties properties;
public void setProperties(Properties properties) {
this.properties = properties;
}
public void printProperties(){
properties.forEach((k,v) -> System.out.println(k + ","+ v));
}
public void test(){
System.out.println("AccountService");
accountDao.test();
userDao.test();
System.out.println("Host:" + host + ",Port:" + port);
printList();
printMap();
printProperties();
}
}
设置xml配置文件:
<!--
set方法注入
通过property给属性资源赋值 (属性字段提供set方法)
name:当前Bean对象中的属性字段名
ref:指定的bean标签的id属性值 (注入法人是JavaBean时)
value:给属性字段赋值
-->
<bean id="accountService" class="com.shsxt.service.AccountService">
<property name="userDao" ref="userDao"/>
<property name="accountDao" ref="accountDao"/>
<property name="host" value="localhost"/>
<property name="port" value="8080"/>
<!--List-->
<property name="strList" >
<list>
<value>上海</value>
<value>深圳</value>
<value>北京</value>
</list>
</property>
<!--map-->
<property name="map">
<map>
<entry>
<key><value>sh</value></key>
<value>上海</value>
</entry>
<entry>
<key><value>sz</value></key>
<value>深圳</value>
</entry>
</map>
</property>
<!-- properties -->
<property name="properties">
<props>
<prop key="jay">周杰伦</prop>
<prop key="jj">林俊杰</prop>
<prop key="eason">陈奕迅</prop>
</props>
</property>
</bean>
<bean id="accountDao" class="com.shsxt.dao.AccountDao"></bean>
<bean id="userDao" class="com.shsxt.dao.UserDao"></bean>
5.1.2 构造器注入
5.1.2.1 单个bean对象作为参数
-
bean对象
public class TypeService { private TypeDao typeDao; public TypeService(TypeDao typeDao) { this.typeDao = typeDao; } public void test(){ System.out.println("TypeService"); typeDao.test(); } }
-
xml配置
<bean id="typeService" class="com.shsxt.service.TypeService"> <constructor-arg name="typeDao" ref="typeDao"/> </bean> <bean id="typeDao" class="com.shsxt.dao.TypeDao"></bean>
5.1.2.2 多个bean对象作为参数
-
bean对象
public class TypeService { private TypeDao typeDao;//javaBean对象 private UserDao userDao;//javaBean对象 private String uname;//常量 public TypeService(TypeDao typeDao, UserDao userDao,String uname) { this.typeDao = typeDao; this.userDao = userDao; this.uname=uname; } public void test(){ System.out.println("TypeService"); typeDao.test(); userDao.test(); System.out.println("uname"+uname); } }
-
xml配置
<bean id="typeService" class="com.shsxt.service.TypeService"> <constructor-arg name="typeDao" ref="typeDao"/> <constructor-arg name="userDao" ref="userDao"/> <constructor-arg name="uname" value="admin"/> </bean> <bean id="typeDao" class="com.shsxt.dao.TypeDao"></bean> <bean id="userDao" class="com.shsxt.dao.UserDao"></bean>
注意:构造器注入可能会产生依赖注入问题,即多个bean中互相注入,产生循环依赖问题,这时就可以使用set的方式注入解决。
5.1.3 p名称空间注入
使用p名称空间注入,可以简化基于xml配置方式。
-
javabean对象:
public class Book{ private String bname; private String bauthor; public void setBname(String bname) { this.bname = bname; } public void setBauthor(String bauthor) { this.bauthor = bauthor; } }
-
xml配置文件:
-
第一步:在xml头文件中添加约束
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> </beans>
-
第二步:进行属性注入,在bean标签里面进行操作
< bean id= "book" class= "com.atguigu.spring5.Book" p:bname= "九阳神功" p:bauthor= "无名氏" ></bean>
-
5.1.4 静态工厂注入
-
定义静态工厂类
public class StaticFactory { public static TypeDao createTypeDao() { return new TypeDao(); } }
-
javabean代码
public class TypeService { private TypeDao typeDao; public void setTypeDao(TypeDao typeDao) { this.typeDao = typeDao; } public void test() { System.out.println("TypeService Test..."); } }
-
xml文件配置
<bean id="typeService" class="com.xxxx.service.TypeService"> <property name="typeDao" ref="typeDao"/> </bean> <bean id="typeDao" class="com.xxxx.factory.StaticFactory" factory- method="createTypeDao"></bean>
5.4.5 动态工厂注入
-
定义工厂类
public class InstanceFactory { public TypeDao createTypeDao() { return new TypeDao(); } }
-
java代码
public class TypeService { private TypeDao typeDao; public void setTypeDao(TypeDao typeDao) { this.typeDao = typeDao; } public void test() { System.out.println("TypeService Test..."); } }
-
xml配置
<bean id="typeService" class="com.xxxx.service.TypeService"> <property name="typeDao" ref="typeDao"/> </bean> <bean id="instanceFactory" class="com.xxxx.factory.InstanceFactory"> </bean> <bean id="typeDao" factory-bean="instanceFactory" factory-method="createTypeDao"></bean>
5.2 Spring IOC自动注入
通过注解的方式注入bean,实际上是通过反射技术实现的。
-
第一步:修改xml配置文件的约束条件
<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 https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
-
第二步:xml中开启自动化注入
<context:annotation-config/> <bean id="userDao" class="com.xxxx.dao.UserDao"></bean> <bean id="userService" class="com.xxxx.service.UserService"></bean>
-
第三步:给需要注入的属性添加注解
5.2.1 @Resource注解
-
默认根据属性字段名查找 (属性字段名与bean标签的id属性值保持一致)
-
如果属性字段名未找到,则通过类型(Class类型)查找
-
属性字段可以提供set方法,也可以不提供
-
注解可以声明在属性字段上,也可以声明在set方法上
-
可以通过设置注解的name属性,通过指定值查找bean对象 (如果设置name属性值,则会找name属性值查找对应bean标签的id,要求name属性与bean标签的id保持一致)
-
如果注入的是接口,接口只有一个实现类时,可以正常使用;如果接口存在多个实现类,则需要使用name属性指定要注入的bean对象。
public class UserService { @Resource private UserDao userDao; //注入接口 @Resource(name="useDao01") private IUserDao iUserDao; public void test(){ System.out.println("UserService..."); userDao.test(); iUserDao.test(); } }
<context:annotation-config/> <bean id="useDao" class="com.shsxt.dao.UserDao"></bean> <bean id="userService" class="com.shsxt.service.UserService"></bean> <bean id="useDao01" class="com.shsxt.dao.UserDao01"/> <bean id="useDao02" class="com.shsxt.dao.UserDao02"/>
5.2.2 @Autowired注解
-
默认据类型(Class类型)查找
-
属性段可以提供set方法,也可以不提供
-
注解可以设置在属性字段级别或set方法级别
-
可以结合 @Qualifier 注解设置别名,但如果设置了@Qualifier注解,必须设置它的value属性。而且value属性值要与bean标签的id属性值保持一致。
public class AccountService { @Autowired private UserDao userDao; @Autowired @Qualifier(value = "useDao02") private IUserDao iUserDao; public void test(){ System.out.println("AccountService"); userDao.test(); iUserDao.test(); } }
<context:annotation-config/> <bean id="useDao" class="com.shsxt.dao.UserDao"></bean> <bean id="accountService" class="com.shsxt.service.AccountService"></bean> <bean id="useDao01" class="com.shsxt.dao.UserDao01"/> <bean id="useDao02" class="com.shsxt.dao.UserDao02"/>
5.3 注入其它类型的属性
5.3.1 字面量
- 属性值为null值
<!--null值-->
< property name= "address">
< null/>
</property>
- 属性值包含特殊符号
<!--属性值包含特殊符号
1 把<>进行转义 < >
2 把带特殊符号内容写到 CDATA
-->
< property name= "address">
< value><![CDATA[<<南京>>]]></value>
</property>
5.3.2 注入属性-外部bean
- 创建两个类 service 类和 dao 类
- 在 service 调用 dao 里面的方法
public class UserService {
//创建 UserDao 类型属性,生成 set 方法
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void add() {
System.out.println( "service add...............");
userDao.update();
}
}
- xml文件配置
和set手动注入属性的方式一致
5.3.3 注入属性-内部bean
- 在实体类之间表示一对多关系,员工表示所属部门,使用对象类型属性进行表示
//部门类
public class Dept {
private String dname;
public void setDname(String dname) {
this.dname = dname;
}
}
//员工类
public class Emp {
private String ename;
private String gender;
//员工属于某一个部门,使用对象形式表示
private Dept dept;
public void setDept(Dept dept) {
this.dept = dept;
}
public void setEname(String ename) {
this.ename = ename;
}
public void setGender(String gender) {
this.gender = gender;
}
}
- xml文件配置
<!--内部 bean-->
< bean id= "emp" class= "com.atguigu.spring5.bean.Emp">
<!--设置两个普通属性-->
< property name= "ename" value= "lucy" ></property>
< property name= "gender" value= "女" ></property>
<!--设置对象类型属性-->
< property name= "dept">
< bean id= "dept" class= "com.atguigu.spring5.bean.Dept">
< property name= "dname" value= "安保部" ></property>
</bean>
</property>
</bean>
六、Spring IOC扫描器
实际开发中,bean的数量会很多,所以我们需要使用注解和扫描器来进行开发。
6.1 Spring IOC扫描器的配置
spring IOC扫描器
作用: bean对象统一进行管理, 简化开发配置,提高开发效率
1、设置自动化扫描的范围
如果bean对象未在指定包范围,即使声明了注解,也无法实例化
2、使用指定的注解(声明在类级别) bean对象的id属性默认是类的首字母小写
Dao层:
@Repository
Service层:
@Service
Controller层;
@Controller
任意类:
@Component
注:开发过程中建议按照指定规则声明注解 注解开发时,默认bean对象都是单例对象
xml文件的配置:记得要加入context的约束
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!--设置自动化扫描的范围-->
<context:component-scan base-package="com.shsxt"/>
</beans>
七、 Bean的作用域和生命周期
7.1 Bean的作用域
默认情况下,从spring容器中拿到的对象都是单例的。
7.1.1 singleton作用域
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-m44WTiV6-1597623919988)(C:\Users\MECHREVO\Desktop\java总结笔记和思维导图\image\spring容器singleton作用域.png)]
注意:lazy-init是懒加载,若等于true时,则当程序调用时才能去实例化对象 ,当为false时,spring容器启动时就会实例化对象。默认情况为false。
7.1.2 prototype作用域
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-siwg77Oy-1597623919991)(C:\Users\MECHREVO\Desktop\java总结笔记和思维导图\image\spring容器prototype作用域.png)]
通过scope="prototype"设置bean的类型,每次向spring容器请求获取bean都返回一个全新的bean,不会缓存bean。
7.2 Bean的生命周期
Bean的生命周期包括Bean的定义、初始化、使用和销毁四个阶段。
7.2.1 Bean的定义
通过xml文件来定义Bean的,一个文件中可以定义多个Bean。
7.2.2 Bean的初始化
默认在IOC容器加载的时候实例化对象。
java代码:
public class RoleService {
// 定义初始化需要被调用的方法
public void init() {
System.out.println("RoleService init...");
}
}
xml文件配置:
<bean id="roleService" class="com.xxxx.service.RoleService" init-method="init"></bean>
7.2.3 Bean的使用
方式一:使用BeanFactory
BeanFactory factory = new ClassPathXmlApplicationContext("spring.xml");
RoleService roleService = (RoleService) factory.getBean("roleService");
BeanFactory在启动的时候不会去实例化Bean,中有从容器中拿Bean的时候才会去实例化。
方式二:使用ApplicationContext
ApplicationContext ac = new ClassPathXmlApplicationContext("spring.xml");
RoleService roleService = (RoleService) ac.getBean("roleService");
ApplicationContext在启动的时候就把所有的Bean全部实例化了。它还可以为Bean配置lazy-init=true来让Bean延迟实例化。
7.2.4 Bean的销毁
步骤一:
<bean id="roleService" class="com.xxxx.service.RoleService" destroy-method="destroy"></bean>
步骤二:调用相应的close方法实现销毁过程
AbstractApplicationContext ctx=new ClassPathXmlApplicationContext("spring.xml");
ctx.close();
xml文件配置:
<bean id="roleService" class="com.xxxx.service.RoleService" init-method="init"></bean>
7.2.3 Bean的使用
方式一:使用BeanFactory
BeanFactory factory = new ClassPathXmlApplicationContext("spring.xml");
RoleService roleService = (RoleService) factory.getBean("roleService");
BeanFactory在启动的时候不会去实例化Bean,中有从容器中拿Bean的时候才会去实例化。
方式二:使用ApplicationContext
ApplicationContext ac = new ClassPathXmlApplicationContext("spring.xml");
RoleService roleService = (RoleService) ac.getBean("roleService");
ApplicationContext在启动的时候就把所有的Bean全部实例化了。它还可以为Bean配置lazy-init=true来让Bean延迟实例化。
7.2.4 Bean的销毁
步骤一:
<bean id="roleService" class="com.xxxx.service.RoleService" destroy-method="destroy"></bean>
步骤二:调用相应的close方法实现销毁过程
AbstractApplicationContext ctx=new ClassPathXmlApplicationContext("spring.xml");
ctx.close();