IoC概念和作用
控制反转的引入和分析
存储实体类中对象的数据,肯定要找个集合来存。这时候有 Map 和 List 供选择。到底选 Map 还是 List 就看我们有没有查找需求。有查找需求,选 Map。所以我们的答案就是在应用加载时,创建一个 Map,用于存放三层对象。我们把这个 map 称之为容器。工厂就是负责给我们从容器中获取指定对象的类。这时候我们获取对象的方式发生了改变。
原来: 我们在获取对象时,都是采用 new 的方式,是主动的。
现在:我们获取对象时,同时跟工厂要,有工厂为我们查找或者创建对象,是被动的。这种被动接收的方式获取对象的思想就是控制反转,它是 Spring 框架的核心之一。
控制反转的概念和作用
控制反转(Inversion of Control,英文缩写为IoC)把创建对象的权利交给框架,是Spring框架的重要特征,并非面向对象编程的专用术语。它包括依赖注入(Dependency Injection,简称DI)和依赖查找(Dependency Lookup)。
IoC的作用:削减计算机程序的耦合(解除我们代码中的依赖关系)。
使用Spring的IoC解耦
案例:业务层和持久层的依赖关系解决。
Spring改造工厂模式
下载地址:http://repo.springsource.org/libs-release-local/org/springframework/spring。
解压:(Spring目录结构:docs:API 和开发规范、libs:jar包和源码、schema:约束)
注意:Spring5 版本是用 JDK8 编写的,所以要求我们的 JDK 版本是 8 及以上。同时 Tomcat 的版本要求 8.5 及以上。
Spring相关Jar包(核心包):
操作步骤
1、创建Maven工程,导入Spring依赖。
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
</dependencies>
2、创建三层架构的包结构,编写dao层和service层接口以及实现类。做到service层调用dao层。
private UserDao userDao = new UserDaoImpl();
UserService userService = new UserServiceImpl();
3、编写Spring的配置文件。
在resources目录下创建名为appicationContext.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
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--把对象的创建交给spring来管理-->
<bean id="userService" class="com.coydone.service.impl.UserServiceImpl"></bean>
<bean id="userDao" class="com.coydone.dao.impl.UserDaoImpl"></bean>
</beans>
4、测试。
public static void main(String[] args) {
//获取spring的IOC核心容器,并根据id获取对象
//1、获取核心容器对象
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
//ApplicationContext ac = new FileSystemXmlApplicationContext("D:\\bean.xml");
//2、根据id获取Bean对象
UserService userService = (UserService) ac.getBean("userService");
UserDao userDao = ac.getBean("userDao", UserDao.class);
userDao.deleteUser("1");
userService.deleteUser("1");
}
基于XML的IoC细节
Spring 中工厂的类结构图
BeanFactory和ApplicationContext的区别
BeanFactory才是Spring 容器中的顶层接口。ApplicationContext是它的子接口。
区别:创建对象的时间点不一样。
ApplicationContext:单例对象适用,它在构建核心容器时,创建对象采取的策略是采用立即加载的方式。也就是说,只要一读取完配置文件马上就创建配置文件中配置的对象。
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
BeanFactory:什么使用什么时候创建对象。多例对象使用。它在构建核心容器时,创建对象采取的策略是采用延迟加载的方式。也就是说,什么时候根据id获取对象了,什么时候才真正的创建对象。
Resource resource = new ClassPathResource("bean.xml");
BeanFactory factory = new XmlBeanFactory(resource);
UserService service = factory.getBean("userService", UserService.class);
ApplicationContext接口的实现类
ApplicationContext的三个常用实现类:
-
ClassPathXmlApplicationContext:它可以加载类路径下的配置文件,要求配置文件必须在类路径下。不在的话,加载不了。(更常用)
-
FileSystemXmlApplicationContext:它可以加载磁盘任意路径下的配置文件(必须有访问权限)。
-
AnnotationConfigApplicationContext:它是用于读取注解创建容器的。
IoC中bean标签和管理对象细节
bean标签作用:用于配置对象让 Spring 来创建的。
创建Bean的三种方式
1、使用默认构造函数创建。
在Spring的配置文件中使用bean标签,配以id和class属性之后,且没有其他属性和标签时。采用的就是默认构造函数创建bean对象,此时如果类中没有默认构造函数,则对象无法创建。
<bean id="userService" class="com.coydone.service.impl.UserServiceImpl">
</bean>
2、使用普通工厂中的方法创建对象(使用某个类中的方法创建对象,并存入Spring容器)。
package com.coydone.factory;
import com.coydone.service.UserService;
import com.coydone.service.impl.UserServiceImpl;
public class InstanceFactory {
public UserService getUserService(){
return new UserServiceImpl();
}
}
<bean id="instanceFactory" class="com.coydone.factory.InstanceFactory">
</bean>
<bean id="userService" factory-bean="instanceFactory" factory-method="getUserService">
</bean>
3、使用工厂中的静态方法创建对象(使用某个类中的静态方法创建对象,并存入Spring容器)。
public class StaticFactory {
public static UserService getUserService(){
return new UserServiceImpl();
}
}
<bean id="accountService" class="com.coydone.factory.StaticFactory" factory-method="getAccountService">
</bean>
bean的属性
-
id:给对象在容器中提供一个唯一标识。用于获取对象。
-
class:指定类的全限定类名。用于反射创建对象。默认情况下调用无参构造函数。
-
scope:指定对象的作用范围。
-
singleton:默认值,单例的。
-
prototype:多例的。
-
request:Web项目中,Spring创建一个Bean的对象,将对象存入到 request 域中。
-
session:Web项目中,Spring创建一个Bean的对象,将对象存入到 session 域中。
-
global-session:Web项目中,应用在 Portlet(集群) 环境。如果没有 Portlet 环境,那么globalSession 相当于 session。
-
-
init-method:指定类中的初始化方法名称。
-
destroy-method:指定类中销毁方法名称。
bean对象的生命周期
单例对象:
-
出生:当容器创建时对象出生。
-
活着:只要容器还在,对象一直活着。
-
死亡:容器销毁,对象消亡。
总结:单例对象的生命周期和容器相同。
多例对象:
-
出生:当我们使用对象时Spring框架为我们创建。
-
活着:对象只要是在使用过程中就一直活着。
-
死亡:当对象长时间不用,且没有别的对象引用时,由Java的垃圾回收器回收。
<bean id="userService" class="com.coydone.service.impl.UserServiceImpl"
scope="prototype" init-method="init" destroy-method="destroy">
</bean>
public static void main(String[] args) {
//1.获取核心容器对象
//ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
//2.根据id获取Bean对象
UserService as = (UserService)ac.getBean("userService");
as.saveAccount();
//手动关闭容器
ac.close();
}
依赖注入(DI)
依赖注入的概念
依赖注入:Dependency Injection。它是Spring框架核心IoC的具体实现。
我们的程序在编写时,通过控制反转,把对象的创建交给了Spring,但是代码中不可能出现没有依赖的情况。IoC解耦只是降低他们的依赖关系,但不会消除。
例如:我们的业务层仍会调用持久层的方法。那这种业务层和持久层的依赖关系,在使用Spring之后,就让Spring来维护了。
在当前类需要用到其他类的对象,由Spring为我们提供,我们只需要在配置文件中说明依赖关系的维护,就称之为依赖注入。
简单的说,就是坐等框架把持久层对象传入业务层,而不用我们自己去获取。
依赖注入能注入的数据有三类:
-
基本类型和String。
-
其他bean类型(在配置文件中或者注解配置过的bean)。
-
复杂类型/集合类型。
注入的方式
-
使用构造函数提供。
-
使用set方法提供。
-
使用注解提供。
构造函数注入
顾名思义,就是使用类中的构造函数,给成员变量赋值。注意,赋值的操作不是我们自己做的,而是通过配置的方式,让Spring框架来为我们注入。
使用的标签:constructor-arg
标签出现的位置:bean标签的内部
标签中的属性
-
type:用于指定要注入的数据的数据类型,该数据类型也是构造函数中某个或某些参数的类型。
-
index:用于指定要注入的数据给构造函数中指定索引位置的参数赋值。索引的位置是从0开始。
-
name:用于指定给构造函数中指定名称的参数赋值(常用)。
以上三个用于指定给构造函数中哪个参数赋值。
-
value:用于提供基本类型和String类型的数据。
-
ref:用于指定其他的bean类型数据。它指的就是在spring的Ioc核心容器中出现过的bean对象。
优势:在获取bean对象时,注入数据是必须的操作,否则对象无法创建成功。
弊端:改变了bean对象的实例化方式,使我们在创建对象时,如果用不到这些数据,也必须提供。
public class UserServiceImpl implements UserService {
//如果是经常变化的数据,并不适用于注入的方式
private String name;
private Integer age;
private Date birthday;
public AccountServiceImpl(String name,Integer age,Date birthday){
this.name = name;
this.age = age;
this.birthday = birthday;
}
}
<bean id="userService" class="com.coydone.service.impl.UserServiceImpl">
<constructor-arg name="name" value="泰斯特"></constructor-arg>
<constructor-arg name="age" value="18"></constructor-arg>
<constructor-arg name="birthday" ref="now"></constructor-arg>
</bean>
<!-- 配置一个日期对象 -->
<bean id="now" class="java.util.Date"></bean>
set方法注入(常用)
涉及的标签:property。
出现的位置:bean标签的内部。
标签的属性
-
name:用于指定注入时所调用的set方法名称。
-
value:用于提供基本类型和String类型的数据。
-
ref:用于指定其他的bean类型数据。它指的就是在Spring的IoC核心容器中出现过的bean对象。
优势:创建对象时没有明确的限制,可以直接使用默认构造函数。
弊端:如果有某个成员必须有值,则获取对象是有可能set方法没有执行。
public class UserServiceImpl2 implements UserService {
//如果是经常变化的数据,并不适用于注入的方式
private String name;
private Integer age;
private Date birthday;
//添加set()方法
}
<bean id="userService2" class="com.coydone.service.impl.UserServiceImpl2">
<property name="name" value="TEST" ></property>
<property name="age" value="21"></property>
<property name="birthday" ref="now"></property>
</bean>
set方法注入复杂类型/集合类型
用于给List结构集合注入的标签:list、array、set。
用于个Map结构集合注入的标签:map、props。
结构相同,标签可以互换。
public class UserServiceImpl3 implements UserService {
private String[] myStrs;
private List<String> myList;
private Set<String> mySet;
private Map<String,String> myMap;
private Properties myProps;
//省略setter()方法
}
bean.xml编写
<bean id="accountService3" class="com.coydone.service.impl.UserServiceImpl3">
<property name="myStrs">
<set>
<value>AAA</value>
<value>BBB</value>
<value>CCC</value>
</set>
</property>
<property name="myList">
<array>
<value>AAA</value>
<value>BBB</value>
<value>CCC</value>
</array>
</property>
<property name="mySet">
<list>
<value>AAA</value>
<value>BBB</value>
<value>CCC</value>
</list>
</property>
<property name="myMap">
<props>
<prop key="testC">ccc</prop>
<prop key="testD">ddd</prop>
</props>
</property>
<property name="myProps">
<map>
<entry key="testA" value="aaa"></entry>
<entry key="testB">
<value>BBB</value>
</entry>
</map>
</property>
</bean>
自动装配
自动装配:Spring在实例化bean的时候从Spring容器中找到匹配的实例赋值给当前bean的属性。
自动装配的策略有两种:
byName:根据当前Bean的属性名在Spring容器中寻找匹配的对象。如果根据name找到了 bean但是类型不匹配则抛出异常。
byType:根据当前Bean的属性类型在Spring容器中寻找匹配的对象。如果根据类型找到了多个bean会抛出异常。
<!--Student中有Clazz属性-->
<bean id="clazz" class="com.coydone.bean.Clazz"></bean>
<bean id="stu2" class="com.coydone.bean.Student" autowire="byName"></bean>
<bean id="clazz2" class="com.coydone.bean.Clazz"></bean>
<bean id="stu2" class="com.coydone.bean.Student" autowire="byType"></bean>
评论区