概念和作用
程序耦合:
在软件工程中,耦合指的就是就是对象之间的依赖性。对象之间的耦合越高,维护成本越高。因此对象的设计应使类和构件之间的耦合最小。软件设计中通常用耦合度和内聚度作为衡量模块独立程度的标准。划分模块的一个准则就是高内聚低耦合。
解决耦合思路:反射 + 配置文件,减少 new
关键字的使用
工厂模式解耦:
实际开发中我们可以把三层的对象都使用配置文件配置起来,当启动服务器应用加载的时候,让一个类中的方法通过读取配置文件,把这些对象创建出来并存起来。在接下来的使用的时候,直接拿过来用。这个读取配置文件,创建和获取三层对象的类就是工厂。
控制反转:
问题一:对象存哪 ?
由于我们是很多对象,肯定要找个集合来存,这时候有 Map
和 List
供选择。由于我们有查找需求,所以选择 Map
,把这个 Map
称为容器
问题二:什么是工厂 ?
工厂就是负责给我们从容器中获取指定对象的类
问题三:怎么获取对象 ?
主动方式:采用 new
的方式
被动方式:向工厂要,工厂为我们查找或者创建对象(这就是控制反转思想)
IOC 入门案例
引入依赖
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
</dependencies>
创建接口及其配置类
public interface AccountService {
void saveAccount();
}
import com.service.AccountService;
public class AccountServiceImpl implements AccountService {
@Override
public void saveAccount() {
System.out.println("调用了 AccountServiceImpl 的 save 方法");
}
}
创建配置文件
<?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">
<bean id="accountService" class="com.service.impl.AccountServiceImpl"> </bean>
</beans>
主方法进行测试
import com.service.AccountService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class ApplicationTest {
public static void main(String[] args) {
// 使用 ApplicationContext
ApplicationContext application = new ClassPathXmlApplicationContext("application.xml");
AccountService accountService = application.getBean("accountService", AccountService.class);
accountService.saveAccount();
//使用 BeanFactory
Resource resoure = new ClassPathResource("application.xml");
BeanFactory factory = new XmlBeanFactory(resoure);
AccountService as = (AccountService) factory.getBean("accountService");
as.saveAccount();
}
}
执行结果
了解常用类
ApplicationCOntext
的三个常用类
ClassPathXmlApplicationContext
:它可以加载类路径下的配置文件,要求配置文件必须在类路径下
FileSystemXmlApplicationContext
:它可以加载磁盘任意路径下的配置文件(必须要有访问权限)
AnnotationConfigApplicationContext
:基于注解使用的,它不需要配置文件,采用配置类和各种注解来配置
核心容器的两个接口
ApplicationContext
:单例适用,它在构建核心容器时,创建对象采取的策略是采用立即加载,也就是说,只要已读取完配置文件马上就创建配置文件中配置的对象
BeanFactory
:多例适用,它在构建核心容器时,创建对象采取的策略是采用延迟加载的方式,也就是说,什么时候根据id获取对象了,什么时候才真正的创建对象
IOC 中的 Bean 标签
bean
标签的常用属性
属性 | 作用 |
---|---|
id | 给对象在容器中提供一个唯一标识,用于获取对象 |
class | 指定类的全限定类名。用于反射创建对象(默认调用无参构造函数) |
scope | 指定对象的作用范围(具体见下面的scope表) |
name | 可指定 id、name(用逗号、分号、空格隔开) |
autowire | 自动装配(具体见下面的autowire表) |
init-method | bean 属性设置完成后,会调用这个方法 |
destroy-method | bean 销毁后的回调方法 |
lazy-init | 是否懒加载(如果被非懒加载的bean依赖了,那么也就不能懒加载了) |
factory-bean | 用于指定实例工厂 bean 的 id |
factory-method | 用于指定实例工厂中创建对象的方法 |
property | 设置属性的值 |
constructor-arg | 指定构造参数 |
scope
属性详解
属性值 | 作用 |
---|---|
singleton | 默认值,表示单例的 |
prototype | 表示多例的 |
request | 在 web 项目中,Spring 创建一个 Bean 对象,将对象存在 request 域中 |
session | 在 web 项目中,Spring 创建一个 Bean 对象,将对象存在 session 域中 |
global session | 在 web 项目中,应用在 Portlet 环境,如果没有 Portlet 环境那么就相当于session |
证明scope
(使用上面定义的 accountService
):
<!--测试单例多例-->
<bean id="accountService" class="com.service.impl.AccountServiceImpl" scope="`singleton`"></bean>
//测试 scope:singleton
AccountService accountService1 = application.getBean("accountService", AccountService.class);
AccountService accountService2 = application.getBean("accountService", AccountService.class);
System.out.println(accountService1 == accountService2);*///true
//测试 scope : prototype
AccountService accountService3 = application.getBean("accountService", AccountService.class);
AccountService accountService4 = application.getBean("accountService", AccountService.class);
System.out.println(accountService3 == accountService4);//false
实例化bean
的方式
使用默认无参构造函数:在 spring
的配置文件中使用 bean
标签,配以 id
和 class
属性之后,且没有其他属性和标签时采用的是默认构造函数创建 bean
对象,此时如果类中没有默认构造函数,则对象无法创建
<bean id="accountService" class="com.service.impl.AccountServiceImpl" scope="singleton"></bean>
使用普通工厂中的方法创建对象:使用某个类中的方法创建对象,并存入 spring
容器中
<!-- factory-bean属性:用于指定实例工厂bean的id -->
<!-- factory-method属性:用于指定实例工厂中创建对象的方法 -->
<!--创建工厂 Bean-->
<bean id="instanceFactory" class="com.factory.InstanceFactory"></bean>
<!--使用工厂中的普通方法创建对象-->
<bean id="accountService" factory-bean="instanceFactory" factory-method="getAccountService"></bean>
// java 工厂
public class InstanceFactory {
private AccountService accountService = new AccountServiceImpl();
public AccountService getAccountService(){
return accountService;
}
}
使用工厂中的静态方法创建对象: 使某个类中的静态方法创建对象,并存入spring容器中
<!-- id属性:指定bean的id,用于从容器中获取 -->
<!-- class属性:指定静态工厂的全限定类名 -->
<!-- factory-method属性:指定生产对象的静态方法 -->
<!--使用静态方法创建对象-->
<bean id="accountService" class="com.factory.StaticFactory" factory-method="getAccountService"></bean>
// java 工厂
public class StaticFactory {
private static AccountService accountService = new AccountServiceImpl();
public static AccountService getAccountService(){
return accountService;
}
}
Spring 依赖注入
构造函数注入:
属性值 | 作用 |
---|---|
type | 用于指定要注入数据的数据类型,该数据类型也是构造函数中某个或某些参数的类型 |
index | 用于指定要注入的数据给构造函数中指定索引位置的参数赋值,索引的位置是从0开始 |
name | 用于指定给构造函数中指定名称的参数赋值(常用名称) |
value | 用于提供基本类型和 String 类型的数据 |
ref | 用于指定其他的 bean 类型数据,它指的就是在 Spring 的 IOC 核心容器中出现过的bean对象 |
测试下列情况所使用的 Person
类
import java.util.Date;
public class Person {
private String name;
private int age;
private Date date;
// 下列情况演示中使用的构造方法
public Person(String name,int age,Date date){
this.name = name;
this.age = age;
this.date = date;
}
}
情况一:index + value / index + ref
<bean id="date" class="java.util.Date"></bean>
<bean id="p" class="com.domain.Person">
<constructor-arg index="0" value="张三"></constructor-arg>
<constructor-arg index="1" value="15"></constructor-arg>
<constructor-arg index="2" ref="date"></constructor-arg>
</bean>
情况二:type + value / type + ref
<bean id="date" class="java.util.Date"></bean>
<bean id="p1" class="com.domain.Person">
<constructor-arg type="java.lang.String" value="张三"></constructor-arg>
<constructor-arg type="int" value="15"></constructor-arg>
<constructor-arg type="java.util.Date" ref="date"></constructor-arg>
</bean>
情况三:name + value / name + ref
<bean id="date" class="java.util.Date"></bean>
<bean id="p2" class="com.domain.Person">
<constructor-arg name="name" value="张三"></constructor-arg>
<constructor-arg name="age" value="15"></constructor-arg>
<constructor-arg name="date" ref="date"></constructor-arg>
</bean>
测试验证
// 测试的代码
ApplicationContext application = new ClassPathXmlApplicationContext("application.xml");
Person p = application.getBean("p", Person.class);
Person p1 = application.getBean("p1", Person.class);
Person p2 = application.getBean("p2", Person.class);
System.out.println(p);
System.out.println(p1);
System.out.println(p2);
Set 方法注入
属性值 | 作用 |
---|---|
name | 用于指定注入时调用的 set 方法的名称 |
value | 用于提供基本类型和 String 类型的数据 |
ref | 用于指定其他的 bean 类型数据,指的是在 Spring 的 IOC 核心容器中出现过的bean 对象 |
说明:使用的是上面的 Person
类,但是需要添加 Getter / Setter
方法,同时跟上面的验证使用的是同样的方法。
<bean id="p3" class="com.domain.Person">
<property name="name" value="张三"></property>
<property name="age" value="15"></property>
<property name="date" ref="date"></property>
</bean>
复杂类型的注入
给数组类型注入
测试下列情况所使用的的 School
类
import java.util.Arrays;
public class School {
private String[] schoolNames = new String[3];
public School() {
}
public School(String[] schoolNames) {
this.schoolNames = schoolNames;
}
public String[] getSchoolNames() {
return schoolNames;
}
public void setSchoolNames(String[] schoolNames) {
this.schoolNames = schoolNames;
}
toString(); // 自行补充
}
情况一 :使用构造方法
<bean id="s2" class="com.domain.School">
<constructor-arg name="schoolNames">
<array>
<value>重庆大学</value>
<value>重庆文理学院</value>
<value>重庆师范大学</value>
</array>
</constructor-arg>
</bean>
情况二:使用 set
方法
<bean id="s1" class="com.domain.School">
<property name="schoolNames">
<array>
<value>重庆大学</value>
<value>重庆师范大学</value>
<value>重庆文理学院</value>
</array>
</property>
</bean>
给 List
类型进行注入
测试下列情况所使用的的 Dates
类
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
public class Dates {
private List<Date> dates = new ArrayList<>();
public Dates() {
}
public Dates(List<Date> dates) {
this.dates = dates;
}
public List<Date> getDates() {
return dates;
}
public void setDates(List<Date> dates) {
this.dates = dates;
}
toString(); // 自行补充
}
情况一 :使用构造方法
<bean id="dates1" class="com.domain.Dates">
<constructor-arg name="dates">
<list>
<ref bean="date"></ref>
<ref bean="date"></ref>
<ref bean="date"></ref>
</list>
</constructor-arg>
</bean>
情况二:使用 set
方法
<bean id="dates2" class="com.domain.Dates">
<property name="dates">
<list>
<ref bean="date"></ref>
<ref bean="date"></ref>
</list>
</property>
</bean>
给 Set
类型进行注入
测试下列情况所使用的的 Maps
类
import java.util.*;
public class Dates {
private Set<Date> dates = new HashSet<>();
public Dates() {
}
public Dates(Set<Date> dates) {
this.dates = dates;
}
public Set<Date> getDates() {
return dates;
}
public void setDates(Set<Date> dates) {
this.dates = dates;
}
toString(); // 自行添加
}
情况一 :使用构造方法
<bean id="set2" class="com.domain.Dates">
<constructor-arg name="dates">
<set>
<ref bean="date"></ref>
<ref bean="date"></ref>
<ref bean="date"></ref>
</set>
</constructor-arg>
</bean>
情况二:使用 set
方法
<bean id="set1" class="com.domain.Dates">
<property name="dates">
<set>
<ref bean="date"></ref>
<ref bean="date"></ref>
<ref bean="date"></ref>
</set>
</property>
</bean>
给 Map
类型进行注入
测试下列情况所使用的的 Maps
类
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
public class Maps {
private Map<String, Date> map = new HashMap<>();
public Maps() {
}
public Maps(Map<String, Date> map) {
this.map = map;
}
public Map<String, Date> getMap() {
return map;
}
public void setMap(Map<String, Date> map) {
this.map = map;
}
toString(); // 自行添加
}
情况一 :使用构造方法
<bean id="m1" class="com.domain.Maps">
<constructor-arg name="map">
<map>
<entry key="1" value-ref="date"></entry>
<entry key="2">
<ref bean="date"></ref>
</entry>
</map>
</constructor-arg>
</bean>
情况二:使用 set
方法
<bean id="m2" class="com.domain.Maps">
<property name="map">
<map>
<entry key="1" value-ref="date"></entry>
<entry key="2">
<ref bean="date"></ref>
</entry>
</map>
</property>
</bean>
autowire
属性详解
属性值 | 作用 |
---|---|
no | 默认值,不启动自动装配 |
byName | 根据属性名自动装配。在容器中根据名字查找与属性名完全一致的 bean,并将其与属性自动装配 |
byType | 如果容器中存在一个与指定属性类型相同的 bean ,那么将与该属性自动装配;如果存在多个该类型 bean,那么抛出异常,并指出不能使用 byType 方式进行自动装配;如果没有找到相匹配的bean,则什么事都不发生,也可以通过设置 dependency-check=“objects” 让Spring抛出异常 |
constructor | 与 byType 方式类似,不同之处在于它应用于构造器参数。如果容器中没有找到与构造器参数类型一致的 bean, 那么抛出异常 |
default | 由上级标签的 default-autowire 属性确定。 |
证明 autowire
属性所使用的类
public class Man {
private Person person;
public Man() {
}
public Man(Person person) {
this.person = person;
}
public Person getPerson() {
return person;
}
public void setPerson(Person person) {
this.person = person;
}
toString(); // 自行添加
}
import java.util.Date;
public class Person {
private String name;
private int age;
private Date date;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public Person(String name,int age,Date date){
this.name = name;
this.age = age;
this.date = date;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
toString(); // 自行添加
}
测试代码:
ApplicationContext application = new ClassPathXmlApplicationContext("application.xml");
//测试 autowire
Person person = application.getBean("person",Person.class);
Man man = application.getBean("man",Man.class);
System.out.println(person);
System.out.println(man);
证明 byName
属性
<bean id="date" class="java.util.Date"></bean>
<bean id="person" class="com.domain.Person">
<property name="name" value="LEE"/>
<property name="age" value="20"/>
<property name="date" ref="date"/>
</bean>
<bean id="man" class="com.domain.Man" autowire="byName"></bean>
证明 byType
属性
情况一 :IOC
容器中只有一个该类型的 bean
<bean id="date" class="java.util.Date"></bean>
<bean id="person" class="com.domain.Person">
<property name="name" value="LEE"/>
<property name="age" value="20"/>
<property name="date" ref="date"/>
</bean>
<bean id="man" class="com.domain.Man" autowire="byType"></bean>
情况二:IOC
容器中有多个该类型的 bean
<bean id="date" class="java.util.Date"></bean>
<bean id="p3" class="com.domain.Person">
<property name="name" value="张三"></property>
<property name="age" value="15"></property>
<property name="date" ref="date"></property>
</bean>
<bean id="person" class="com.domain.Person">
<property name="name" value="LEE"/>
<property name="age" value="20"/>
<property name="date" ref="date"/>
</bean>
<bean id="man" class="com.domain.Man" autowire="byType"></bean>
测试 constructor
情况一:有相对应的构造器
<bean id="date" class="java.util.Date"></bean>
<bean id="person" class="com.domain.Person">
<property name="name" value="LEE"/>
<property name="age" value="20"/>
<property name="date" ref="date"/>
</bean>
<bean id="man" class="com.domain.Man" autowire="constructor"></bean>
情况二:没有相对应的构造器(还是使用情况一的配置,只是修改 Man
类)
public class Man {
private Person person;
public Man() {
}
public Person getPerson() {
return person;
}
public void setPerson(Person person) {
this.person = person;
}
toString(); // 自行添加
}
有问题可加QQ群一起交流:1076386005