文章目录
Spring框架(第1天)-IoC容器和依赖注入
SSM框架阶段
- Spring(3天)
- SpringMVC(3天)
- maven高级(1天)
- Git(1天)
学习目标
- 能够描述Spring框架
- 能够理解Spring的IoC的容器
- 能够编写Spring的IoC的入门案例
- 能够说出Spring的bean标签的配置
- 能够理解Bean的实例化方法
- 能够理解Bean的属性注入方法
- 了解复杂(集合)类型的属性注入
- 使用注解代替相关XML配置
- 理解Spring相关注解的含义
1. IoC的概念
目标
IoC的概念
本质上就是工厂模式
原来
以前我们是由自己主动创建一个对象,主动获取一个资源
//Car是接口, 后面是实现类
Car bmw = new Bmw();
Car benZ = new BenZ();
Car audi = new Audi();
现在
使用工厂模式,我们需要的对象由工厂去创建,我们不再自己创建,我们只消费对象。
Car bmw = BeanFactory.getBean("bmw");
Car benZ = BeanFactory.getBean("benZ");
Car audi = BeanFactory.getBean("audi");
有什么好处?
- 降低类与类之间的耦合度
- 我们不再需要自己创建对象,只要使用对象就可以了
- 使用Spring框架就是使用工厂模式来创建对象,由Spring容器帮我们管理所有的Java对象
容器的概念
在项目开发中将对象存放在什么地方?
由于项目中对象是大量的, 所以考虑用集合 Map/List 存储。而在使用时需要根据名称查找, 所以通常使用 Map 存储
什么是工厂呢?
工厂就是负责创建对象,并且把对象放到容器中。在使用的时候,帮助我们从容器获取指定的对象。此时我们获取对象的方式就发生了改变。
概念:控制反转IoC(Inversion Of Control)
这种将创建对象的权利,由在程序代码中主动new对象的方式,转变为由工厂类创建并且提供给我们,我们使用的时候从容器中去取,变成被动接收的方式,称为控制反转。
控制反转也叫IoC(Inversion Of Control),即我们接下来要学习的Spring框架中的一个重要的特点。在这里我们首先明确一个事情:Spring的IoC解决的问题,就是工厂模式解耦解决的问题。
小结
IoC从哪里获取对象?与传统方式创建对象有什么区别?
从Spring容器中获取对象,本质上是Map集合,通过键获取值
以前是主动创建,现在是被动接收。这就是IoC
2. Spring框架介绍
目标
- 了解Spring框架的优点
- 了解Spring体系结构
Spring框架的由来
作者:Rod Johnson 是 Spring框架的创始人,著名作者。 Rod在悉尼大学不仅获得了计算机学位,更令人吃惊的是在回到软件开发领域之前,他还获得了音乐学的博士学位。 有着相当丰富的C/C++技术背景的Rod早在1996年就开始了对Java服务器端技术的研究。
Spring框架的介绍
- Spring是分层的Java SE/EE应用的full-stack轻量级开源框架。
- 它是以IOC(Inversion Of Control)控制反转和AOP(Aspect Oriented Programming)面向切面编程为核心
- 提供了表现层springmvc和持久层Spring JDBC以及业务层的事务管理等企业级应用解决方案。
- 将开源世界中众多优秀的第三方框架和类库整合,成为越来越受欢迎的Java EE企业级应用框架。
Spring体系结构
体系结构组成
Spring框架采用的是分层架构,它一系列功能要素被分成20个模块,以下列出了部分我们会用到的模块。
Core Container核心容器
- Beans模块:提供了BeanFactory,工厂模式的实现,Spring将所有管理的对象称为Bean。
- Core模块:提供了Spring框架的基本组成部分,包括IoC和DI依赖注入的功能。
- Context模块:访问和配置Bean对象的上下文对象,核心容器。如:ApplicationContext
Data Access/Integration数据访问与集成
- JDBC模块:提供了JDBC的支持,如:JdbcTemplate
- ORM模块:对主流的ORM框架提供了支持,如:JPA,JDO,Hibernate等
- Transaction模块:支持声明式事务的管理
Web
- Servlet模块:包含了SpringMVC和REST Web Services实现的Web应用程序
- Web模块:提供了基本的Web开发集成特性,如:文件上传,收发邮件等。
其他模块
- AOP模块:提供了面向切面编程的功能
- Aspects模块:提供了与AspectJ框架集成功能
- Test模块:提供了对单元测试和集成测试的支持
小结
Spring的优点
- IOC解耦,简化开发
- AOP面向切面编程支持
- 声明式事务支持
- 方便程序测试
- 集成第三方优秀框架
3. Spring入门案例
创建maven项目
检查工程结构
选择JDK1.8的版本
搭建Spring入门开发环境
添加Spring框架
项目结构
pom文件内容
部分内容:只是导入了依赖包
<dependencies>
<!--导入Spring基础框架-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
<!--junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
创建applicationContext.xml文件
-
该配置文件是Spring框架的主配置文件,文件位置为类的根路径下,文件名称可以修改。
-
在idea中使用菜单创建,会自动编写Schema的约束信息
-
配置applicationContext.xml文件,让Spring框架管理对象
编写持久层
编写CustomerService接口,添加保存客户的方法 void saveCustomer();
CustomerService.java
package com.itheima.service;
/**
* 业务层接口
*/
public interface CustomerService {
/**
* 保存客户对象
*/
void saveCustomer();
}
编写客户业务层实现类,实现接口中的方法,直接输出一句话"保存客户"
CustomerServiceImpl.java
package com.itheima.service.impl;
import com.itheima.service.CustomerService;
/**
* 业务层实现类
*/
public class CustomerServiceImpl implements CustomerService {
/**
* 保存客户对象
*/
@Override
public void saveCustomer() {
System.out.println("保存了客户");
}
}
applicationContext.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容器中
class:指定类全名,指定的是实现类,不是接口
id:容器中唯一标识,通过id获取这个对象
-->
<bean class="com.itheima.service.impl.CustomerServiceImpl" id="customerService"/>
</beans>
测试类
package com.itheima.test;
import com.itheima.service.CustomerService;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestCustomer {
@Test
public void testCustomer() {
//1.创建Spring容器,参数:指定spring的配置文件
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//2.从容器中获取对象
CustomerService customerService = (CustomerService) context.getBean("customerService");
//3.调用对象的方法
customerService.saveCustomer();
//4.关闭容器
context.close();
}
}
小结
bean标签的作用是?它有哪两个属性?
在容器中创建一个对象
<bean>标签 | 说明 |
---|---|
id | 容器中唯一标识 |
class | 类全名 |
4. IoC容器:创建容器三种方式
目标
- ClassPathXmlApplicationContext容器实现类【掌握】
- FileSystemXmlApplicationContext容器实现类【了解】
- AnnotationConfigApplicationContext容器实现类【掌握】
BeanFactory容器的类结构
案例演示
方式一:类路径配置文件创建容器
方式二:本地配置文件方式创建容器
方式三:注解的方式创建容器
代码
package com.itheima.test;
import com.itheima.service.CustomerService;
import com.itheima.service.impl.CustomerServiceImpl;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
public class TestCustomer {
/**
* 1.在类路径下读取配置文件,创建容器
*/
@Test
public void testCustomer() {
//1.创建Spring容器,参数:指定spring的配置文件
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//2.从容器中获取对象
CustomerService customerService = (CustomerService) context.getBean("customerService");
//3.调用对象的方法
customerService.saveCustomer();
//4.关闭容器
context.close();
}
/**
* 2. 从文件的绝对路径中获取配置文件,来创建容器
*/
@Test
public void testFileSystem() {
//1.创建Spring容器,参数:指定spring的配置文件
FileSystemXmlApplicationContext context = new FileSystemXmlApplicationContext("D:\\WorkSpace\\idea\\JavaEE148\\day45_01_HelloSpring\\src\\main\\resources\\applicationContext.xml");
//2.从容器中获取对象
CustomerService customerService = (CustomerService) context.getBean("customerService");
//3.调用对象的方法
customerService.saveCustomer();
//4.关闭容器
context.close();
}
/**
* 3.读取注解的配置文件创建容器
*/
@Test
public void testAnnotationConfig() {
//1.创建Spring容器,参数:类配置文件,通过注解去配置Spring
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(CustomerServiceImpl.class);
System.out.println(context);
}
}
小结
-
ClassPathXmlApplicationContext的作用?
从类路径下读取配置文件创建容器
-
FileSystemXmlApplicationContext的作用?
从文件系统的绝对路径中通过配置文件,创建容器
-
AnnotationConfigApplicationContext的作用?
读取类配置文件,使用注解的方式创建容器
5. bean标签的配置细节【重点】
目标
bean标签作用
作用:在Spring容器中创建一个对象
bean标签
bean标签的属性说明
属性 | 说明 |
---|---|
id | 容器中唯一的标识 |
name | 还可以有多个名字,使用逗号,空格,分号隔开都可以 |
class | 指定类全名,指定的是实现类,不是接口 |
scope | 指定bean在容器中作用范围 singleton:默认值,表示这个是单例对象,整个容器中只会创建一个对象 prototype:这是多例对象,每次获取一个新的对象 request:用于请求域 session:用于会话域 application: 用于上下文域 globalsession:用于全局的会话,用在分布式开发中 |
init-method | 创建对象时,执行的初始化的方法 |
destroy-method | 销毁对象时,执行的方法 |
lazy-init | 是否使用延迟加载,默认是不使用 |
关于GlobalSession的理解
6. bean的生命周期案例【重点】
目标
- scope属性中单例和多例的配置
- 生命周期的配置
scope属性【掌握】
bean的作用范围和生命周期的说明
scope取值 | 作用范围 | 生命周期 |
---|---|---|
singleton 单例对象 | 容器一创建就创建这个对象,只要容器不销毁就一直存在 | 出生:容器创建就出生 活着:只要容器没有销毁就一直存在 死亡:容器关闭的时候 |
prototype 多例对象 | 每次获取对象就创建一个新的对象,使用完毕会被GC回收 | 出生:获取对象的时候 活着:使用过程中 死亡:由GC去回收 |
scope取值
- 在applicationContext.xml中配置customerService,指定scope属性为singleton
- 在测试方法中得到2次customerService对象,判断是否是同一个对象
- 输出两个对象的地址和hashCode()
结果
singleton:
com.itheima.service.impl.CustomerServiceImpl@7a187f14
com.itheima.service.impl.CustomerServiceImpl@7a187f14
true
prototype:
com.itheima.service.impl.CustomerServiceImpl@7d70d1b1
com.itheima.service.impl.CustomerServiceImpl@2a742aa2
false
scope取值prototype
- 在applicationContext.xml中配置customerService,指定scope属性为prototype
- 在测试方法中得到2次customerService对象,判断是否是同一个对象
- 输出两个对象的地址和hashCode()
代码
applicationContext.xml
<!--
scope取值:
singleton:这是默认值,容器一创建就创建这个对象,只要容器不销毁就一直存在
prototype:每次获取对象就创建一个新的对象,使用完毕会被GC回收
-->
<bean class="com.itheima.service.impl.CustomerServiceImpl" id="customerService" scope="prototype"/>
测试类
@Test
public void testScope() {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//获取2次
CustomerService c1 = (CustomerService) context.getBean("customerService");
CustomerService c2 = (CustomerService) context.getBean("customerService");
System.out.println(c1);
System.out.println(c2);
System.out.println(c1 == c2);
}
init-method和destroy-method属性【了解】
步骤
- 改造客户dao实现类,增加初始化和销毁方法,名字随意。
- 配置applicationContext.xml文件,增加init-method和destroy-method属性。
- 每个方法中各输出一句话
- 注:销毁的方法只在单例模式下起作用。
- 编写测试类
- 创建容器对象
- 得到customerService对象
- 调用DAO中的方法
- 调用容器关闭的方法(需要转成子类)
CustomerServiceImpl
package com.itheima.service.impl;
import com.itheima.service.CustomerService;
/**
* 业务层实现类
*/
public class CustomerServiceImpl implements CustomerService {
/**
* 保存客户对象
*/
@Override
public void saveCustomer() {
System.out.println("保存了客户");
}
public void init() {
System.out.println("初始化的方法");
}
public void destroy() {
System.out.println("销毁的方法");
}
}
applicationContext.xml
<!--
scope取值:
singleton:这是默认值,容器一创建就创建这个对象,只要容器不销毁就一直存在
prototype:每次获取对象就创建一个新的对象,使用完毕会被GC回收
init-method:指定创建对象以后,在构造方法之后执行,调用的方法
destroy-method: 对象销毁前执行的方法
-->
<bean class="com.itheima.service.impl.CustomerServiceImpl" id="customerService" init-method="init" destroy-method="destroy"/>
单例的延迟加载
步骤
- 在业务对象中添加构造方法,输出一句话
- 修改applicationContext.xml文件,设置成单例模式
- 测试方法
- 未设置延迟加载,创建容器,不获取对象,也会实例化
- 使用lazy-init设置为true,在得到对象的时候才实例化
代码
-
改造实现类,增加初始化和销毁方法,名字随意
package com.itheima.service.impl; import com.itheima.service.CustomerService; /** * 业务层实现类 */ public class CustomerServiceImpl implements CustomerService { public CustomerServiceImpl() { System.out.println("无参构造方法"); } /** * 保存客户对象 */ @Override public void saveCustomer() { System.out.println("保存了客户"); } }
-
添加lazy-init属性
<!-- lazy-init 默认是及时加载,不使用延迟加载。延迟加载只用于单例对象 --> <bean class="com.itheima.service.impl.CustomerServiceImpl" id="customerService" lazy-init="true"/>
-
编写测试类
@Test public void testLazy() { //创建了容器就会创建所有的对象 ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); //获取对象的时候才去创建 CustomerService customerService = (CustomerService) context.getBean("customerService"); }
小结
7. 创建对象的三种方式
创建对象方式1:无参构造方法
目标
- 掌握使用无参构造方法创建对象
- 使用静态工厂方法
- 使用实例工厂方法
步骤
- 复制maven项目
- CustomerServiceImpl有保存客户的方法
- 在applicationContext.xml中customerService配置id和class的配置
- 在测试类中得到customerService,打印对象
- 添加1个有参的构造方法,不写无参的构造方法,再次测试
注:默认使用无参数构造方法创建对象。如果此时没有无参构造方法,创建对象会失败
代码
- CustomerServiceImpl
package com.itheima.service.impl;
import com.itheima.service.CustomerService;
/**
* 业务层实现类
*/
public class CustomerServiceImpl implements CustomerService {
public CustomerServiceImpl() {
}
public CustomerServiceImpl(String name) {
System.out.println("有参的构造方法");
}
/**
* 保存客户对象
*/
@Override
public void saveCustomer() {
System.out.println("保存了客户");
}
}
- applicationContext.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">
<!-- 默认使用无参的构造方法,如果没有无参构造方法,会出错:No default constructor found -->
<bean class="com.itheima.service.impl.CustomerServiceImpl" id="customerService"/>
</beans>
创建对象方式2:静态工厂方法【了解】
概述
该方式要求开发者创建一个静态工厂的方法来创建Bean的实例
- bean配置中的class属性所指定的不再是bean实例的类,而是静态工厂类
- 需要使用factory-method属性来指定创建对象的静态工厂方法。
创建静态工厂类
- 创建一个静态工厂类
- 创建一个静态方法,返回一个customerService对象
package com.itheima.utils;
import com.itheima.service.CustomerService;
import com.itheima.service.impl.CustomerServiceImpl;
/**
* 使用自己的静态工厂创建对象,而不使用Spring容器创建
*/
public class StaticBeanFactory {
/**
* 创建对象的静态方法
*/
public static CustomerService getBean() {
return new CustomerServiceImpl();
}
}
配置文件
- id为customerService,class属性指定为静态工厂类
- 指定factory-method属性为静态方法
<!--
2.使用静态工厂创建
class: 静态工厂类
factory-method:静态的获取对象的方法
id:对象的id -->
<bean class="com.itheima.utils.StaticBeanFactory" factory-method="getBean" id="customerService"/>
创建对象方式3:实例工厂方法【了解】
步骤
- 需要在容器中使用bean配置实例工厂对象,并且指定id。
- 在配置文件中,需要实例化的Bean通过factory-bean属性指向配置的实例工厂的id
- 使用factory-method属性指定使用工厂中的哪个方法
实例工厂创建对象
- 创建一个工厂类
- 通过成员方法创建customerService对象
package com.itheima.utils;
import com.itheima.service.CustomerService;
import com.itheima.service.impl.CustomerServiceImpl;
/**
* 实例工厂
*/
public class InstanceBeanFactory {
/**
* 不是静态方法
*/
public CustomerService getBean() {
return new CustomerServiceImpl();
}
}
配置文件
- 在容器中配置实例工厂对象,指定class和id属性
- 通过factory-bean指定实例工厂对象的id
- 通过factory-method指定实例工厂的方法
<!--
3. 使用实例工厂来创建
3.1 先创建实例工厂对象
3.2 调用实例工厂的方法创建我们所需的对象
-->
<bean class="com.itheima.utils.InstanceBeanFactory" id="instanceBeanFactory"/>
<bean id="customerService" factory-bean="instanceBeanFactory" factory-method="getBean"/>
小结
-
如果没有无参的构造方法,创建对象会如何?
-
使用静态工厂创建对象
- class属性:指定为静态工厂对象
- factory-method属性:指定工厂中静态方法
-
使用实例工厂创建对象
- factory-bean:指定为实例工厂对象
- factory-method:指定工厂中创建对象的方法
8. 依赖注入:构造函数
目标
使用构造函数注入Bean的属性
依赖注入介绍
依赖注入(Dependency Injection,简称DI)。当某个Java对象(调用者)需要调用另一个Java对象(被调用者,即被依赖对象)时,以前调用者通常采用"new 被调用者"的代码方式来创建对象,这种方式会导致调用者与被调用者之间的耦合性增加,不利于后期项目的升级和维护。
使用Spring框架之后,对象的实例不再由调用者来创建,而是由Spring容器来创建,由容器控制程序之间的关系,而不是由调用者的程序代码直接控制。由容器负责将被依赖对象赋值给调用者的成员变量,相当于为调用者注入了它的依赖的实例,这就是Spring的依赖注入。
简单理解:依赖注入就是由Spring创建对象,并且给成员变量赋值。
UserService由容器创建,并且注入给依赖它的对象
构造方法注入
开发步骤
- 创建项目
- 编写类Customer
- 包含属性( int id; String name;boolean male;Date birthday;)
- 编写无参的构造方法
- 添加全参的构造方法
- 生成toString()方法,不用创建set和get方法
- 配置applicationContext.xml
- 使用bean的constructor-arg子元素
- 编号属性使用index指定值
- 名字属性使用name和type指定
- 性别使用name属性指定
- 日期使用ref引用另一个日期对象,日期使用bean在容器中声明为java.util.Date对象
- 在测试类中得到客户对象,输出客户对象
代码
-
Customer
package com.itheima.entity; import java.util.Date; /** * 客户对象 */ public class Customer { private int id; private String name; private boolean male; private Date birthday; public Customer() { } public Customer(int id, String name, boolean male, Date birthday) { this.id = id; this.name = name; this.male = male; this.birthday = birthday; } @Override public String toString() { return "Customer{" + "id=" + id + ", name='" + name + '\'' + ", male=" + male + ", birthday=" + birthday + '}'; } }
-
applicationContext.xml
<!-- 1. 使用构造方法注入 子元素:constructor-arg 构造方法注入 index:每几个位置,从0开始 name:指定形参的名字 type:指定参数的类型 value:简单类型的值:8种基本类型+String类型 ref:赋值引用类型,指定对象的id --> <bean class="com.itheima.entity.Customer" id="customer"> <constructor-arg name="id" value="100"/> <constructor-arg name="name" value="白骨精"/> <constructor-arg name="male" value="true"/> <constructor-arg name="birthday" ref="birthday" type="java.util.Date"/> </bean> <!-- 引用类型:现在的时间 --> <bean class="java.util.Date" id="birthday"/>
小结
constructor-arg标签的属性 | 描述 |
---|---|
index | 构造方法参数的位置 |
name | 参数名 |
type | 指定参数的类型 |
value | 赋值简单类型=基本类型+String类型 |
ref | 赋值引用类型 |
9. 依赖注入:set方法【重点】
目标
就是通过类中的set方法,给成员变量赋值
步骤
- 给Customer添加set注入
- 配置applicationContext.xml,通过property元素给所有的属性赋值
- 运行测试类,输出customer对象
代码
-
Customer
package com.itheima.entity; import java.util.Date; /** * 客户对象 */ public class Customer { private int id; private String name; private boolean male; private Date birthday; public Customer() { } public Customer(int id, String name, boolean male, Date birthday) { this.id = id; this.name = name; this.male = male; this.birthday = birthday; } public void setId(int id) { this.id = id; } public void setName(String name) { this.name = name; } public void setMale(boolean male) { this.male = male; } public void setBirthday(Date birthday) { this.birthday = birthday; } @Override public String toString() { return "Customer{" + "id=" + id + ", name='" + name + '\'' + ", male=" + male + ", birthday=" + birthday + '}'; } }
-
applicationContext.xml
<!-- 2.使用set方法注入 --> <bean class="com.itheima.entity.Customer" id="customer"> <!-- 属性名:为id value为200 --> <property name="id" value="200"/> <property name="name" value="猪八戒"/> <property name="male" value="true"/> <property name="birthday" ref="birthday"/> </bean> <!-- 引用类型:现在的时间 --> <bean class="java.util.Date" id="birthday"/>
结果
默认的构造方法
Customer{id=200, name='猪八戒', male=true, birthday=Mon Dec 28 10:45:05 CST 2020}
小结
<property>的属性 | 描述 |
---|---|
name | 属性名 |
value | 简单类型的值 |
ref | 引用类型的值 |
10. 依赖注入:p命名空间
目标
使用p命名空间注入
概述
注:先要在xml中导入p命名空间,本质仍然是调用类中的set方法实现注入功能。
步骤
- 配置applicationContext.xml
- 导入p命名空间(xmlns:p=“http://www.springframework.org/schema/p”)
- 通过"p:属性名"或者"p:属性名-ref"注入属性
- 测试类,从容器中得到客户对象输出
代码
applicationContext.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"
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">
<!-- 使用p命名空间注入,本质上还是set注入
1. 导入p命名空间
2. 简单类型的格式:p:属性名="值"
引用类型的格式:p:属性名-ref="id"
-->
<bean class="com.itheima.entity.Customer" id="customer" p:id="300" p:name="孙悟空" p:male="false" p:birthday-ref="birthday"/>
<!-- 引用类型:现在的时间 -->
<bean class="java.util.Date" id="birthday"/>
</beans>
小结
p名称空间 | 描述 |
---|---|
p:属性名 | 注入简单类型 |
p:属性名-ref | 注入引用类型 |
11. 集合属性的注入
目标
各种集合属性的注入
概述
顾名思义,就是给类中的集合成员变量赋值,只不过变量的数据类型都是集合。
我们这里介绍注入数组,List,Set,Map,Properties。
执行效果
编写集合属性注入类
- 创建实体类,Person
- 创建以下属性:
- 字符串数组:array
- 字符串类型的List集合:list
- 字符串类型的Set集合:set
- 字符串的键和值Map集合:map
- Properties属性集合
- 生成toString方法
package com.itheima.entity;
import java.util.*;
public class Person {
//字符串数组
private String[] array;
//字符串类型的List集合
private List<String> list;
//字符串类型的Set集合
private Set<String> set;
//字符串的键和值Map集合
private Map<String, String> map;
//Properties属性集合
private Properties prop;
public String[] getArray() {
return array;
}
public void setArray(String[] array) {
this.array = array;
}
public List<String> getList() {
return list;
}
public void setList(List<String> list) {
this.list = list;
}
public Set<String> getSet() {
return set;
}
public void setSet(Set<String> set) {
this.set = set;
}
public Map<String, String> getMap() {
return map;
}
public void setMap(Map<String, String> map) {
this.map = map;
}
public Properties getProp() {
return prop;
}
public void setProp(Properties prop) {
this.prop = prop;
}
@Override
public String toString() {
return "Person{" +
"array=" + Arrays.toString(array) +
", list=" + list +
", set=" + set +
", map=" + map +
", prop=" + prop +
'}';
}
}
配置applicationContext.xml
-
标签使用说明:
- 单列集合:使用标签 array/list/set
- 双列集合:使用标签 map/prop
- 只要数据结构相同,标签可以互用
-
使用set注入,给所有的属性使用相应的标签赋值
- 数组:array,每个元素是value或ref
- List集合:list,每个元素是value或ref
- set集合:set,每个元素是value或ref
- map集合:map,其中每个元素是entry,entry再指定key和value
- prop集合:props,其中每个元素是prop,包含key属性,没有value属性,标签体的内容是值
<!-- 注入属性集合:所有的单列集合可以通用,只是语义上区别 -->
<bean class="com.itheima.entity.Person" id="person">
<!--数组类型-->
<property name="array">
<array>
<value>孙悟空</value>
<value>猪八戒</value>
<value>白骨精</value>
</array>
</property>
<!-- list类型 -->
<property name="list">
<list>
<value>张飞</value>
<value>关羽</value>
<value>刘备</value>
</list>
</property>
<!-- set类型 -->
<property name="set">
<set>
<value>贾宝玉</value>
<value>林黛玉</value>
<value>薛宝钗</value>
</set>
</property>
<!-- map集合,所有的双引集合也是可以通用的 -->
<property name="map">
<map>
<entry key="cn" value="中国"/>
<entry key="usa" value="美国"/>
<entry key="jp" value="日本"/>
</map>
</property>
<!-- property集合 -->
<property name="prop">
<props>
<prop key="gz">广州</prop>
<prop key="sh">上海</prop>
<prop key="bj">北京</prop>
</props>
</property>
</bean>
测试
得到person对象,输出对象
/**
* 集合注入
*/
@Test
public void testPerson() {
//1.创建Spring容器,参数:指定spring的配置文件
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//2.从容器中获取对象,指定对象类型
Person person = context.getBean("person", Person.class);
System.out.println(person);
//3.关闭容器
context.close();
}
小结
标签 | 描述 |
---|---|
<array> | 注入数组 |
<list> | 注入List |
<set> | 注入set |
<map> | Map集合 |
<props> | properties集合 |
12. @Component注解【重点】
目标
- 使用注解配置对象
- 从Spring容器中得到对象并输出
关于IoC配置说明
注解配置和 xml 配置要实现的目的是一样的,都是要降低程序间的耦合。只是配置的形式不一样。关于实际的开发中到底使用 xml 还是注解,每家公司有不同的习惯。所以这两种配置方式我们都需要掌握。
注意:SpringIoC容器中XML配置与注解可以混合使用。即:如果Dao用注解创建的对象;service用xml创建的对象一样可以注入DAO。
技术点
标签的作用
<context:component-scan base-package="com.itheima"/>
案例:使用注解的IoC配置
创建新项目
需求
- 使用注解配置Account对象
- 从Spring容器中得到Account对象并且输出
步骤
- 创建项目:添加依赖spring-context和junit
- 创建实体类Account,使用@Component注解。
- 在applicationContext.xml 配置中开启注解扫描的基包
- 在测试类中得到Account类并且输出
代码
pom.xml
- 导入spring-context包,使用5.2.0.RELEASE
- 导入junit 4.12
<dependencies>
<dependency>
<!--spring的上下文-->
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
Account实体类
- 创建实体类Account,属性(Integer id, String name,Double money)
- 只有toString()方法,没有get和set,没有构造方法。
- 使用@Component注解
package com.itheima.entity;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
/**
* 被扫描到以后,由Spring创建这个对象放到容器中去,默认是类名首字母小写做为名字
* 也可以指定value属性,就是它的名字
@Component:用于普通的类
@Service:用于业务层的类
@Repository:用于持久层
@Controller:用于控制器
注:以上四个注解的功能一样,只是语义上区别
*/
@Component
public class Account {
private Integer id;
private String name;
private Double money;
public Account() {
}
public Account(Integer id, String name, Double money) {
this.id = id;
this.name = name;
this.money = money;
}
@Override
public String toString() {
return "Account{" +
"id=" + id +
", name='" + name + '\'' +
", money=" + money +
'}';
}
}
applicationContext.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
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!-- 扫描哪个包下所有的类,指定基包的名字,使用context命名空间 -->
<context:component-scan base-package="com.itheima"/>
</beans>
测试类
- 从容器中得到对象
- 输出对象,看对象是否为空
package com.itheima.test;
import com.itheima.entity.Account;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestAccount {
@Test
public void testAccount() {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//从容器中获取对象
Account account = (Account) context.getBean("account");
System.out.println(account);
}
}
小结
-
@Component注解的作用是什么?
放在类上,一个标记,表示这个类会被创建,将对象放在容器中
-
<context:component-scan base-package=“com.itheima.entity”/> 有什么用?
指定扫描哪个基包,它的子包全部会被扫描,可以使用逗号分隔多个基包
13. @Autowired注解【重点】
目标
- 掌握@Autowired注解的作用
- 使用@Autowired修饰属性和方法
@Autowired介绍
- 位置: 用于成员变量和成员方法上
- 作用: 将属性注入相应的值
- 按类型匹配的方式从容器中去查找对应的值注入
- 如果有多个匹配的类型,按名字匹配的方式注入
- 如果找不到匹配的名字,就会抛出异常
@Autowired修饰属性
案例演示
步骤
- 创建User1对象,包含字符串的属性username,有toString()方法
- 给User1添加@Component注解,指定注解的值为user,后面这个名字要重复使用。
- 给username添加@Autowired注解
- 在applicationContext.xml中添加一个id为man的字符串。在子标签中,使用构造器注入值。
- 运行测试,从容器中得到User1对象,并且输出对象。
- 在applicationContext.xml中再添加一个id为username的字符串,再运行测试,则注入新的值。
@Autowired修饰方法
演示案例
步骤:@Autowired修饰方法
- 将User1复制成User2对象,去掉User1中的@Component
- 包含字符串的属性username
- 有方法input(String username),接收注入的值
- 有toString()方法。
- 给input方法添加@Autowired注解
- 在applicationContext.xml中添加一个id为man的字符串,使用构造器注入值
- 运行测试,从容器中得到User2对象,并且输出对象。
- 在applicationContext.xml中再添加一个id为username的字符串,再运行测试,则注入新的值。
@Autowired的属性required
作用:默认为true,这个属性是否必须,如果为false,则在容器中找不到这个属性匹配的类型,也不抛出异常,属性值为空
步骤:required属性
- 去掉applicationContext.xml中所有字符串的配置
- 运行测试,找不到所需的字符串,则注入失败出现错误。因为@Autowired的required属性默认为true。
- 将User2中的@Autowired的requried属性设置为false,则注入失败没有错误。输出User2的username为null。
代码
User1.java
package com.itheima.entity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component("user")
public class User1 {
private String username;
/**
* 在默认的情况下:@Autowired的值必须要注入,否则抛出异常
* 属性:required 默认是true,设置为false表示这个属性不是必须注入的
*/
@Autowired(required = false)
private void inputUsername(String username) {
this.username = username;
}
@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
'}';
}
}
applicationContext.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"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!-- 扫描哪个包下所有的类,指定基包的名字,使用context命名空间 -->
<context:component-scan base-package="com.itheima"/>
<!-- 有一个字符串类型要注入 -->
<bean class="java.lang.String" id="man">
<!-- 相当于 new String("NewBoy") -->
<constructor-arg value="NewBoy"/>
</bean>
<!--
<bean class="java.lang.String" id="username">
<constructor-arg value="Rose"/>
</bean>
-->
</beans>
小结
-
@Autowired注入方法按什么去匹配的?
按类型去匹配
-
如果出现参数类型相同,则按什么匹配?
按名字去匹配
-
如果设置@Autowired属性为false会怎么样?
这个属性可以不注入
14. @Qualifier注解
目标
学习@Qualifier的作用
作用
- 必须与@Autowired配置使用,不能单独使用
- 位置: 放在成员变量或成员方法上
- 作用: 按名字匹配的方式注入
- 属性: value指定要注入的名字名
案例演示
User2.java
package com.itheima.entity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
@Component("user")
public class User2 {
@Autowired
@Qualifier("woman") //指定bean的id进行入
private String username;
@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
'}';
}
}
applicationContext.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"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!-- 扫描哪个包下所有的类,指定基包的名字,使用context命名空间 -->
<context:component-scan base-package="com.itheima"/>
<!-- 有一个字符串类型要注入 -->
<bean class="java.lang.String" id="man">
<!-- 相当于 new String("NewBoy") -->
<constructor-arg value="NewBoy"/>
</bean>
<bean class="java.lang.String" id="woman">
<constructor-arg value="Rose"/>
</bean>
</beans>
小结
@Qualifier注解的作用是什么?
1. 必须与@Autowired配合使用
2. 按名字匹配的方式注入
15. @Value注解
目标
学习@Value注解的作用
作用
用于一些简单类型的注入,也可以注入日期类型
以后主要用于从Java属性文件中读取键,将值注入到成员变量
案例演示
步骤
- 复制User3,删除User2的@Component注解
- 给对象添加String name,boolean sex,java.util.Date birthday三个属性
- 使用@Value注入不同类型的值,其中生日使用"yyyy/MM/dd"的格式
- 重写toString()方法,输出结果。
- 执行测试方法,得到User3对象,并且输出对象。
代码
User3.java
package com.itheima.entity;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.util.Date;
@Component("user")
public class User3 {
@Value("嫦娥")
private String name;
@Value("true")
private boolean sex;
@Value("2000/11/11")
private Date birthday;
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", sex=" + sex +
", birthday=" + birthday +
'}';
}
}
16. 对象范围与生命周期的注解
目标
- @Scope注解
- @PostConstruct注解
- @PreDestroy注解
- @Lazy注解
注解说明
@Scope
作用:用在类上,用来指定当前对象在容器中是单例对象还是多例对象
步骤
- 复制User4,删除类中所有的内容。删除其它类的@Component注解
- 使用@Scope属性,分别设置为singleton和prototype属性,在测试类中得到两次User4对象,输出对象。看是否是同一个对象。
@Lazy
作用:用在类上,用来指定当前对象是否延迟加载,在默认的情况下,容器加载对象就加载了。如果为true表示延迟加载。
相当于:
<bean lazy-init="true/false"/>
步骤
- 给User4添加无参构造方法,输出一句话。
- 只加载容器,不从容器中得到对象。默认单例模式,只要容器加载就会实例化所有的对象。
- 给User4设置@Lazy注解。加载容器看是否实例化对象,然后再从容器中得到对象。
注:@Lazy只在单例模式下有效
@PostConstruct
作用:用在方法上,指定这是一个初始化的方法
相当于:
<bean init-method="方法名"/>
@PreDestroy
作用: 用在方法上,指定这是对象销毁前执行的方法,只用于单例对象。
相当于:
<bean destroy-method="方法名"/>
步骤
- 在User4中创建init()方法,使用@PostConstruct注解,看这个方法什么时候执行。
- 创建方法destroy(),使用@PreDestroy注解,看这个方法什么时候执行
- 注:测试的时候需要关闭容器
代码
User4.java
package com.itheima.entity;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
@Component("user")
@Scope("singleton")
@Lazy //延迟加载
public class User4 {
public User4() {
System.out.println("user4被创建了");
}
@PostConstruct
public void init() {
System.out.println("初始化的方法");
}
@PreDestroy
public void destroy() {
System.out.println("销毁的方法");
}
}
测试类
/**
* 与生命周期有关的注解
*/
@Test
public void testLifeCycle() {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//从容器中获取对象
User4 user1 = (User4) context.getBean("user");
User4 user2 = (User4) context.getBean("user");
System.out.println(user1);
System.out.println(user2);
System.out.println(user1 == user2);
}
@Test
public void testLazy() {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//从容器中获取对象
User4 user1 = (User4) context.getBean("user");
System.out.println(user1);
//必须关闭容器才会看到销毁的方法
context.close();
}
小结
注解 | 作用 |
---|---|
@Scope | 单例或多例 |
@PostConstruct | 初始化的方法 |
@PreDestory | 销毁的方法 |
@Lazy | 延迟加载 |
学习总结
-
能够描述Spring框架
- Spring是分层的Java SE/EE应用的full-stack轻量级开源框架。
- 它是以IOC(Inversion Of Control)控制反转和AOP(Aspect Oriented Programming)面向切面编程为核心
- 提供了表现层springmvc和持久层spring JDBC以及业务层的事务管理等企业级应用解决方案。
- 将开源世界中众多优秀的第三方框架和类库整合,成为越来越受欢迎的Java EE企业级应用框架。
-
能够理解Spring的IoC的容器
以前的主动获取变成了现在的被动接收,对象从Spring容器中拿
-
能够说出Spring的bean标签的配置
属性 说明 id 指定唯一标识 name 可以多个名字,使用逗号,空格或分号分隔 class 指定类全名 scope singleton:单例对象
prototype:多例对象 -
能够理解Bean的实例化方法
- 无参的构造方法
- 静态工厂对象
- 实例工厂对象
-
能够理解Bean的属性注入方法
<property>的属性 描述 name 属性名字 value 属性值 ref 注入引用类型 -
使用注解代替相关的XML配置
-
理解Spring相关注解的含义
将对象实例化以后放在容器中,要配置扫描基包:
context:component-scan base-package="基包名"
创建对象的注解 | 说明 |
---|---|
@Component | 放在普通的类上 |
@Controller | 放在控制器 |
@Service | 放在业务对象 |
@Repository | 放在持久层 |
依赖注入的注解 | 说明 |
---|---|
@Autowired | 1. 按类型匹配的方式 2.如果有多个按名字 3.如果找不到抛出异常 |
@Qualifier | 按名字匹配 |
@Value | 注入一些简单类型,主要用于读取配置文件中值注入 |
对象范围与生命周期 | 说明 |
---|---|
@Scope | 对象类型,单例或多例 |
@Lazy | 延迟加载 |
@PostConstruct | 初始化方法 |
@PreDestroy | 销毁的方法 |
Memorial Day is 513 days |