1.0 再次强调
IOC解耦只是削减计算机的耦合,但是不能消除,只能降低。
2.0 新建一个项目
填充基本的代码:
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.edp</groupId>
<artifactId>second_project_spring</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.3.RELEASE</version>
</dependency>
</dependencies>
</project>
bean.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来管理-->
<!-- id:获取时的唯一标志-->
<!-- class:反射要创建的权限定位名称-->
<bean id="accountService" class="com.edp.service.impl.IAccountServiceImpl"></bean>
<bean id="accountDao" class="com.edp.dao.impl.AccountDaoImpl"></bean>
</beans>
AccountDaoImpl.java
package com.edp.dao.impl;
import com.edp.dao.IAccountDao;
/**
* @author EdPeng
* @Title:
* @Package
* @Description: 账户持久层实现类
* @date 2020/2/23下午 5:45
*/
public class AccountDaoImpl implements IAccountDao {
public void saveAccount() {
System.out.println("保存了账户。");
}
}
IAccountDao.java
package com.edp.dao.impl;
import com.edp.dao.IAccountDao;
public class AccountDaoImpl implements IAccountDao {
public void saveAccount() {
System.out.println("保存了账户。");
}
}
IAccountServiceImpl .java
package com.edp.service.impl;
import com.edp.dao.impl.AccountDaoImpl;
import com.edp.service.IAccountService;
import com.edp.dao.IAccountDao;
public class IAccountServiceImpl implements IAccountService {
public IAccountServiceImpl(){
System.out.println("IAccountServiceImpl对象创建了!");
}
public void saveAccount() {
System.out.println("IAccountService中的saveAccount方法执行了……");
}
}
IAccountService .java
package com.edp.service;
public interface IAccountService {
void saveAccount();
}
Client .java
package com.edp.ui;
import com.edp.service.IAccountService;
import com.edp.dao.IAccountDao;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
public class Client {
public static void main(String[] args) {
//1.获取核心容器
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");
//根据id获取bean对象
IAccountService accountService = (IAccountService) applicationContext.getBean("accountService");
System.out.println(accountService);
accountService.saveAccount();
}
}
3.0 spring对bean对象管理的细节——创建bean的三种方式
3.1 第一种方式:使用默认构造函数创建
在spring的配置文件中使用bean标签,配以id和class属性之后,且没有其他属性和标签时,采用的就是默认构造函数创造bean对象,此时如果类中没有默认构造函数,则对象无法创建。
比如我们修改IAccountServiceImpl .java
public class IAccountServiceImpl implements IAccountService {
public IAccountServiceImpl(String value){
System.out.println("IAccountServiceImpl对象创建了!");
}
public void saveAccount() {
System.out.println("IAccountService中的saveAccount方法执行了……");
}
}
将原来的默认构造函数修改为有参构造函数,意味着构造函数没有了,运行Client类:
而且,这样没有无参构造函数的情况下,bean.xml都在会编译环境下报错:
3.2 第二种方式:使用普通工厂中的方法创建对象(使用某个类中的方法创建,并存入spring容器)
这个方法解决加入需要配置的类在class字节码中(即集成的jar包中的类。)
创建一个模拟的工厂类
InstanceFactory.java
package com.edp.factory;
import com.edp.service.IAccountService;
import com.edp.service.impl.IAccountServiceImpl;
/**
* @author EdPeng
* @Title: 模拟一个工厂类
* @Package
* @Description:模拟工厂类(该类可能是存在于jar包中的,我们无法通过修改源码的方式来提供默认构造函数)
* @date 2020/2/29下午 5:46
*/
public class InstanceFactory {
public IAccountService getAccountService(){
return new IAccountServiceImpl();
}
}
这时候,bean.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">
<bean id="instanceFactory" class="com.edp.factory.InstanceFactory"></bean>
<bean id="accountService" factory-bean="instanceFactory" factory-method="getAccountService"></bean>
</beans>
运行:
成功运行,证明这样配置没有问题。
3.3 第三种方式:使用工厂中的静态方法创建对象(使用某个类中的静态方法创建对象,并存入spring容器)
新建一个StaticFactory.java类:
package com.edp.factory;
import com.edp.service.IAccountService;
import com.edp.service.impl.IAccountServiceImpl;
/**
* @author EdPeng
* @Title:模拟一个工厂类
* @Package
* @Description:模拟工厂类(该类可能是存在于jar包中的,我们无法通过修改源码的方式来提供默认构造函数)
* @date 2020/2/29下午 9:00
*/
public class StaticFactory {
public static IAccountService getAccountService() {
return new IAccountServiceImpl();
}
}
bean.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">
<bean id="accountService" class="com.edp.factory.StaticFactory" factory-method="getAccountService"></bean>
</beans>
运行:
注意,上面看上去两个Factory类其实已经耦合了,但是我们只是在模拟jar包中的类,jar包中有普通方法也有静态方法,spring都给我们提供配置的方法。
在jar包中的类都是“.class”文件,是无法改动的,当我们需要获得某个方法的返回值存入spring容器中,就需要用到以上3种中的第2、3种bean对象创建方法。
4.0 spring对bean对象管理的细节——bean对象的作用范围
4.1 spring的bean对象默认情况下就是单例的。
重置一下bean.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">
<bean id="accountService" class="com.edp.service.impl.IAccountServiceImpl"></bean>
</beans>
修改Client.java类测试一下:
public class Client {
public static void main(String[] args) {
//1.获取核心容器
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");
//根据id获取bean对象
IAccountService accountService = (IAccountService) applicationContext.getBean("accountService");
IAccountService accountService2 = (IAccountService) applicationContext.getBean("accountService");
System.out.println(accountService);
System.out.println(accountService2);
accountService.saveAccount();
accountService2.saveAccount();
System.out.println(accountService==accountService2);
}
}
执行:
4.2 bean标签的scope属性
修改bean.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">
<!--bean标签的scope属性
作用:用于指定bean的作用范围
取值:singleton:单例的(默认值,常用)
prototype:多例的(常用)
request:作用于web应用的请求范围
session:作用于web应用的会话范围
global-session:作用于集群环境的会话范围(全局会话范围),当不是集群环境时,它就是session
-->
<bean id="accountService" scope="prototype" class="com.edp.service.impl.IAccountServiceImpl"></bean>
</beans>
执行:
其中
global-session:作用于集群环境的会话范围(全局会话范围),当不是集群环境时,它就是session
,一般情况下接触不到这个方面,但在此做以解释。
当我们用户访问服务器时,服务器往往都有一个访问地址,比如“192.168.0.1”,但在超大型项目中,一个服务器往往扛不住这么大的数据访问量,当在集群环境时,往往会有多个服务器ip地址供用户访问。
当我们访问一个大型网站,比如淘宝时,首先访问的是“https://www.taobao.com/”,用户打开的是一个login.jsp页面,然后网页就会寻找空闲的服务器ip地址提供数据服务。(专业名词叫“负载均衡”)
在用户注册账户时,将由一个空闲的服务器进行数据存储,但当用户登录的时候,服务器的情况已经瞬息万变,再次访问的时候,注册时的物理服务器已经不是现在登录的物理服务器,而这个时候,新的服务器不会存储用户的账户信息。
这时候就需要一个全局的集群环境的会话范围,能够让用户找到服务器所在信息的调度。这个会话模式在spring中的配置属性就是global-session。
5.0 spring对bean对象管理的细节——bean对象的生命周期
bean对象的生命周期
- 单例对象
- 出生:当容器创建时对象出生
- 活着:只要容器还在,对象一直活着
- 死亡:容器销毁,对象消亡
- 总结:单例对象的生命周期和容器相同
- 多例对象
- 出生:当我们使用对象时,spring框架为我们创建
- 活着:对象在使用过程中,一直活着
- 死亡:当对象长时间不用,且没有别的对象引用时,由java的垃圾回收器回收
6.0 Spring的依赖注入
6.1 什么是依赖注入(Dependency Injection)?
- IOC的作用:降低程序间的耦合(依赖关系),也就是降低程序间的依赖关系,而依赖关系的管理,以后都交给spring来维护。
- 在当前类需要用到其他类的对象,由spring为我们提供,我们只需要在配置文件中说明。
- 而针对这依赖关系的维护,就称之为依赖注入。
6.2 依赖注入分为三类
依赖注入能注入的数据分为三类:
- 基本类型和String
- 其他bean类型(在配置文件中或者注解配置过的bean)
- 复杂类型/集合类型
6.3 依赖注入的注入方式
总共有3种:
- 第1种:使用构造函数提供
- 第2种:使用set方法提供
- 第3种:使用注解提供
7.0 使用构造注入
首先在类中,定义一些可注入的变量。
修改IAccountServiceImpl.java
public class IAccountServiceImpl implements IAccountService {
//如果是经常变换的数据,并不适用于注入的方式
private String name;
private Integer age;
private Date birthday;
public IAccountServiceImpl(String name, Integer age, Date birthday) {
this.name = name;
this.age = age;
this.birthday = birthday;
}
public void saveAccount() {
System.out.println("IAccountService中的saveAccount方法执行了……"+name+" , "+age+" , "+birthday);
}
}
当然,我们不应该关注这些变量是不是应该出现在Dao层,只是说作为一个对象,以一种方便理解的方式命名。
配置bean.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">
<!--构造函数注入:
使用的标签:constructor-arg
标签出现的位置:bean标签内部
标签中的属性
type:用于指定要注入的数据的数据类型,同时该数据类型也是构造函数中某个或某些参数的类型
index:用于指定要注入的数据给构造函数中指定索引位置的参数赋值。索引的位置是从0开始的
name:用于指定给构造函数中指定名称的参数赋值(常用)
==============================以上三个用于指定给构造函数中那个参数赋值==============================
value:用于提供基本类型和String类型的数据
ref:用于指定其他的bean类型数据,指的是在spring的IOC核心容器中出现过得bean对象
优势:
在获取bean对象时,注入数据是必须的操作,否则对象无法创建成功。
弊端:
改变了bean对象的是开花方式,使我们在创建对象时,如果用不到这些数据,也必须提供。
-->
<bean id="accountService" class="com.edp.service.impl.IAccountServiceImpl">
<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>
</beans>
运行:
发现,没有出现乱码,这就是Spring的强大之处:能把涉及到的可能的它能处理的细节都提前替我们处理。
8.0 set方法注入
注入只需要set方法,不需要get方法。
创建一个com.edp.service.impl.IAccountServiceImpl2 .java
public class IAccountServiceImpl2 implements IAccountService {
//如果是经常变换的数据,并不适用于注入的方式
private String name;
private Integer age;
private Date birthday;
public void setName(String name) {
this.name = name;
}
public void setAge(Integer age) {
this.age = age;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public void saveAccount() {
System.out.println("IAccountService中的saveAccount方法执行了……"+name+" , "+age+" , "+birthday);
}
}
配置bean.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">
<!--配置一个日期对象-->
<bean id="now" class="java.util.Date"></bean>
<!--set方法注入:
设计的标签:property
标签出现的位置:bean标签内部
标签中的属性
name:用于指定注入时所调用的set方法名称
value:用于提供基本类型和String类型的数据
ref:用于指定其他的bean类型数据,指的是在spring的IOC核心容器中出现过得bean对象
优势:
创建对象时,没有明确的限制。可以直接使用默认构造函数
弊端:
如果有某个成员必须有值,则获取对象时,有可能set方法没有执行
-->
<bean id="accountService2" class="com.edp.service.impl.IAccountServiceImpl2">
<property name="name" value="泰斯特"></property>
<property name="age" value="18"></property>
<property name="birthday" ref="now"></property>
</bean>
</beans>
修改Client.java
public class Client {
public static void main(String[] args) {
//1.获取核心容器
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");
IAccountService accountService = (IAccountService) applicationContext.getBean("accountService2");
accountService.saveAccount();
}
}
执行:
set方法注入的优势就是就是构造函数注入的弊端,当然set方法注入的弊端就是构造函数注入的优势,但我们更常用的方式是set方法注入。
9.0 复杂类型的注入
创建com.edp.service.impl.IAccountServiceImpl3.java
package com.edp.service.impl;
import com.edp.service.IAccountService;
import java.util.*;
public class IAccountServiceImpl3 implements IAccountService {
private String[] myStr;
private List<String> myList;
private Set<String> mySet;
private Map<String, String> myMap;
private Properties myProps;
public void setMyStr(String[] myStr) {
this.myStr = myStr;
}
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 setMyProps(Properties myProps) {
this.myProps = myProps;
}
@Override
public String toString() {
return "IAccountServiceImpl3{" +
"\n myStr=" + Arrays.toString(myStr) +
",\n myList=" + myList +
",\n mySet=" + mySet +
",\n myMap=" + myMap +
",\n myProps=" + myProps +
'}';
}
public void saveAccount() {
System.out.println( toString());
}
}
修改bean.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">
<bean id="accountService3" class="com.edp.service.impl.IAccountServiceImpl3">
<property name="myStr">
<array>
<value>AAA</value>
<value>BBB</value>
<value>CCC</value>
</array>
</property>
</bean>
</beans>
修改Client.java
public class Client {
public static void main(String[] args) {
//1.获取核心容器
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");
IAccountService accountService = (IAccountService) applicationContext.getBean("accountService3");
accountService.saveAccount();
}
}
执行:
继续完善bean.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">
<bean id="accountService3" scope="prototype" class="com.edp.service.impl.IAccountServiceImpl3">
<property name="myStr">
<array>
<value>AAA</value>
<value>BBB</value>
<value>CCC</value>
</array>
</property>
<property name="myList">
<list>
<value>DDD</value>
<value>EEE</value>
<value>FFF</value>
</list>
</property>
<property name="mySet">
<set>
<value>GGG</value>
<value>HHH</value>
<value>III</value>
</set>
</property>
<property name="myMap">
<map>
<entry key="testA" value="aaaa"></entry>
<entry key="testB">
<value>bbbb</value>
</entry>
<entry key="testC" value="cccc"></entry>
</map>
</property>
<property name="myProps">
<props>
<prop key="testD"></prop>
<prop key="testE"></prop>
<prop key="testF"></prop>
</props>
</property>
</bean>
</beans>
执行:
接下来,我们如下操作,修改bean.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">
<bean id="accountService3" scope="prototype" class="com.edp.service.impl.IAccountServiceImpl3">
<property name="myStr">
<set>
<value>AAA</value>
<value>BBB</value>
<value>CCC</value>
</set>
</property>
<property name="myList">
<array>
<value>DDD</value>
<value>EEE</value>
<value>FFF</value>
</array>
</property>
<property name="mySet">
<list>
<value>GGG</value>
<value>HHH</value>
<value>III</value>
</list>
</property>
<property name="myMap">
<props>
<prop key="testD"></prop>
<prop key="testE"></prop>
<prop key="testF"></prop>
</props>
</property>
<property name="myProps">
<map>
<entry key="testA" value="aaaa"></entry>
<entry key="testB">
<value>bbbb</value>
</entry>
<entry key="testC" value="cccc"></entry>
</map>
</property>
</bean>
</beans>
执行:
将标签内的对应关系打乱,发现还是可以运行。
复杂类型(集合类型)的注入,用于给List结构集合注入的标签有:list、array、set;用于给map结构集合注入的标签有:map、props,结构相同的,标签可以互换。
10.0 使用注解注入
【Java中级】27.0 SSM之Spring框架(三)——注解注入
END