Spring-01
本帖最后由 Mrchen1144 于 2022-6-5 15:32 编辑## 内容
- `Spring`概念
- `Spring`快速入门
- `Spring`的IoC 和 DI
- `Bean`基本配置
- `Bean`生命周期
- `Bean`实例化
- `Bean`注入
## 1. `Spring`概念
### 1.1 概念
- Spring是分层的JavaSE/EE应用 full-stack轻量级开源框架
### 1.2 Spring全家桶
目前学习的是`Spring Framework`,是`Spring`全家桶的基础。
### 1.3 Spring体系
- Spring体系结构图
- SSM的提供的解决方案
- `Mybatis`:`Dao`层框架
- `SpringMVC`:`web`层框架
- `Spring`:软件三层中都可以看到`Spring`的应用场景
### 1.4 发展历程
- Spring之父 `Rod Johnson`
- EJB ` Enterprise Java Bean`,`sun`公司提出来的大型web项目的架构
- Expert One-on-One J2EE Design and Development 2002年手把手教你怎么使用EJB开发大型web项目
- Expert one-on-one J2EE Development without EJB2004 手把手教你怎么不使用EJB开发大型web项目(Spring的原型)
- 官网:https://spring.io/projects
- 课堂版本5.x
### 1.5 Spring优势
- 方便解耦,简化开发
- 方便集成各种优秀框架(不排斥、慢慢整合)
- AOP(面向切面编程)编程的支持
- 声明式事务的支持
- Java源码是经典学习范例
- 方便程序的测试(Spring整合junit)
### 1.6 Spring两大核心思想
- `IoC`:Inverse of Control控制反转,目的:解耦
- `AOP`:Aspect Oriented Programming面向切面编程
- Spring演化过程
**基于接口编程**:`service`和`dao`之间高度耦合
**工厂+配置文件**:解除了耦合,留下配置文件和dao/工厂耦合,这种耦合是我们期望的一种耦合方式
- `IoC` 控制反转
- Inverse of Control
把对象的创建权、依赖的注入权从程序员手中,反转到了`Spring`容器创建并提供
## ==2.Spring quick Start==
### 2.0 准备工作:配置私服
```xml
<!--略-->
```
### ==2.1 IOC实现步骤==
1. 导入`Spring`依赖坐标:`spring-context-5.2.10.RELEASE`
2. 编写`service/dao`接口和实现类
3. `Spring`核心配置文件中配置`service/dao`实现类
4. 测试类中创建`Spring`容器对象,并通过对象获取`service/dao`实现类对象
5. 调用`service/dao`实现类对象的方法测试
#### 2.1.1 配置依赖坐标
新建模块/导入模块
```xml
<dependencies>
<!--
配置Spring的依赖坐标
会根据依赖传传递自动导入其依赖的其他坐标
-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
</dependencies>
```
#### 2.1.2 编写service/dao实现类和接口
`BookDao.java`接口
```java
package com.cy.dao;
public interface BookDao {
public void save();
}
```
`BookDaoImpl.java`实现类
```java
package com.cy.dao.impl;
import com.cy.dao.BookDao;
public class BookDaoImpl implements BookDao {
public void save() {
System.out.println("book dao save ...");
}
}
```
`BookService`接口
```java
package com.cy.service;
public interface BookService {
public void save();
}
```
`BooKServiceImpl`实现类
```java
package com.cy.service.impl;
import com.cy.dao.BookDao;
import com.cy.dao.impl.BookDaoImpl;
import com.cy.service.BookService;
public class BookServiceImpl implements BookService {
private BookDao bookDao = new BookDaoImpl();
public void save() {
System.out.println("book service save ...");
bookDao.save();
}
}
```
#### 2.1.3新建`Spring`的配置文件并配置
`resources 右键选择 new--》xml configration file --》Spring config(前提是Spring的依赖已经成功导入)`
配置文件名称任意,习惯写法`applicationcontext.xml`。本案中使用`beans.xml`
```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装配进Spring容器
id 唯一标识,任意起名,但是尽量使用提示的名字
class全限定类名(用于反射创建对象)
小提示:先写class,只写类名有提示;再写id,也有提示
-->
<bean id="bookDao" class="com.cy.dao.impl.BookDaoImpl"/>
<bean id="bookService" class="com.cy.service.impl.BookServiceImpl">
</beans>
```
#### 2.1.4 创建`Spring`容器对象并获取`bean`对象
```java
/**
* @Description: 测试Spring容器创建和获取对象
*/
package com.cy;
import com.cy.dao.BookDao;
import com.cy.service.BookService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App2 {
public static void main(String[] args) {
//3.获取IoC容器
ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
//4.获取bean(根据bean配置id获取)
// BookDao bookDao = (BookDao) ctx.getBean("bookDao");
// bookDao.save();
BookService bookService = (BookService) ctx.getBean("bookService");
bookService.save();
}
}
```
### ==2.2 `DI`实现步骤==
#### 2.2.1 DI概念
全称:`Dependency Injection`。`Service`用到`dao`,`Service`就依赖了`Dao`,就需要把`Dao`注入到`Service`中。
之前的注入方式:
- 之前`Service`中的`dao`都是我们自己`new`的;
使用`Spring`中的依赖注入
- 我们就不需要自己`new`,`Spring`会自动找到(创建)`Dao`对象,并设置给`Service`中的成员变量。
#### 2.2.2 实现步骤
0. 基于`IoC`快速入门代码,完成下面操作。
1. 删除被依赖对象的new创建代码,并提供`setter`
2. `Spring`配置文件中配置依赖关系
#### 2.2.3 修改代码
`BookServiceImpl.java`
```java
public class BookServiceImpl implements BookService {
//5.删除业务层中使用new的方式创建的dao对象
private BookDao xxx;
public void save() {
System.out.println("book service save ...");
xxx.save();
}
//6.提供对应的set方法
public void setBookDao(BookDao xxx) {
this.xxx = xxx;
}
}
```
#### 2.2.4 修改配置
```xml
<!--
把Bean装配进Spring容器
id 唯一标识,任意起名,但是尽量使用提示的名字
class全限定类名(用于反射创建对象)
小提示:先写class,只写类名有提示;再写id,也有提示
-->
<bean id="bookDao" class="com.cy.dao.impl.BookDaoImpl"/>
<bean id="bookService" class="com.cy.service.impl.BookServiceImpl">
<bean id="bookService" class="com.cy.service.impl.BookServiceImpl">
<!--7.配置server与dao的关系-->
<!--
property标签表示配置当前bean的属性
name属性值为对应类中成员变量名。本质上是通过成员变量的setter为其赋值。
ref属性值为spring容器中已经存在的另一bean的id
-->
<property name="bookDao" ref="bookDao"/>
</bean>
</bean>
```
其他代码保持不变,直接运行测试方法即可。
## ==3. Spring配置-Bean标签==
### ==3.1 id&class属性==
- `id`:唯一标识,同一个`Spring`容器中不允许重复
- `class`:全限定类名,用于反射创建对象
XML配置
```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装配进Spring容器
id 唯一标识,任意起名,但是尽量使用提示的名字
class全限定类名(用于反射创建对象)
-->
<bean class="com.cy.dao.impl.BookDaoImpl" id="bookDao"/>
</beans>
```
测试代码
```java
/**
* @Description: 测试Spring容器创建和获取对象
*/
public class UserServiceImpl {
public static void main(String[] args) {
// 创建容器对象,并加载配置文件
ApplicationContext app = new ClassPathXmlApplicationContext("beans.xml");
// 从容器对象中获取bean对象
BookDao bookDao = (BookDao) app.getBean("bookDao");
// 使用获取的bean对象
bookDao.save();
}
}
```
### 3.2 name 属性
为`bean`其别名,可以配置多个值,多个值之间使用`空格/逗号/分号`分隔
- XML配置
```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">
<!--name:为bean指定别名,别名可以有多个,使用逗号,分号,空格进行分隔-->
<bean id="bookService" name="service service4 bookEbi" class="com.itheima.service.impl.BookServiceImpl">
<property name="bookDao" ref="bookDao"/>
</bean>
</beans>
```
- 演示代码
```java
package com.cy;
import com.cy.dao.BookDao;
import com.cy.service.BookService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class AppForName {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
// 可以通过bean标签name属性的值,获取对应的bean对象
// BookService bookService = (BookService) ctx.getBean("service");
// 如果传递参数不能成功匹配id和name的值,则会报错。NoSuchBean***
BookService bookService = (BookService) ctx.getBean("service");
bookService.save();
}
}
```
### 3.3 scope 属性
这个很重要,但是一般使用默认值,单例!
- 概念:单例:在整个项目中,某个类的对象有且仅有一个,这种情况,就是单(个实)例。
- `Spring`中的`Bean`默认是单例的。
- `XML`配置
```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">
<!--scope:为bean设置作用范围,可选值为单例singloton,非单例prototype-->
<bean id="bookDao" name="dao" class="com.cy.dao.impl.BookDaoImpl" scope="prototype"/>
</beans>
```
- 演示代码
```java
package com.cy;
import com.cy.dao.BookDao;
import com.cy.service.BookService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class AppForScope {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
BookDao bookDao1 = (BookDao) ctx.getBean("bookDao");
BookDao bookDao2 = (BookDao) ctx.getBean("bookDao");
System.out.println(bookDao1);
System.out.println(bookDao2);
}
}
```
- `Spring`可以帮我们维护已有项目中哪些类和对象
可以单例的
- Dao
- Service
- Servlet/Controller
- ~~Utils~~
不是单例的一般不会`让Spring`维护
- 实体类(数据传输的载体)
## ==4. `Bean`的实例化==
### 4.1 `Spring`之前对象实例化方式
- 方式1:通过构造方法直接创建
```java
// 方式1:通过构造方法直接创建
BookDaoImpl bookDao = new BookDaoImpl();
```
- 方式2:实例工厂方式
```java
/* 工厂类 */
public class BookDaoImplFactory {
// 提供方法,返回一个BookDaoImpl的对象
public BookDaoImpl getObject(){
// 假装这里有非常复杂的流程
return new BookDaoImpl();
}
}
```
```java
/** 测试代码***/
// 方式2:实例工厂方式
// 2.1 创建工厂对象
BookDaoImplFactory factory = new BookDaoImplFactory();
// 2.2 通过工厂对象获取目标对象
BookDaoImpl booDao1 = factory.getObject();
```
- 方式3:静态工厂方式
```java
/* 静态工厂类 */
class BookDaoImplFactoryStatic {
// 提供静态方法,返回一个BookDaoImpl的对象
public static BookDaoImpl getObject(){
// 假装这里有非常复杂的流程
return new BookDaoImpl();
}
}
```
```java
// 方式3:静态工厂方式
BookDaoImpl bookDao2 = BookDaoImplFactoryStatic.getObject();
```
### ==4.2 `Spring`中构造方法实例化==
- 快速入门案例中,`Spring`使用的就是通过反射无参构造创建的对象。
- 无参构造
```java
OrderDao OrderDao2 = new OrderDaoImpl();
```
对应`Spring`中的配置
```xml
<!--
如下配置的效果:
通过class的值获取全限定类名,然后通过反射调用无参构造方法,
创建对象并装配进Spring容器,起名为id的值。
-->
<bean id="bookDao" class="com.cy.dao.impl.OrderDaoImpl"/>
```
- 如果未提供给无参构造方法,会因为找不到该方法而报错。`NoSuchMethodException *** init()`
### 4.3 `Spring`中静态工厂实例化
- 静态工厂
```java
// 方式3:静态工厂方式
OrderDao OrderDao2 = OrderDaoFactory.getOrderDao();
```
对应Spring中配置
```xml
<!--
通过OrderDaoFactory的静态方法,获取OrderDao对象
Spring容器中会产生一个orderDao对象
该对象的名字是id的值
-->
<bean id="orderDao" class="com.itheima.factory.OrderDaoFactory" factory-method="getOrderDao"/>
```
从`Spring`容器中,获取方式和之前一样,通过`id`获取对象并使用。
- **应用场景**
1. 兼容老的项目
2. 创建过程比较复杂的对象,一般都是使用工厂模式
### 4.4 `Spring`中实例工厂实例化
- 实例工厂
```java
// 方式3:实例工厂方式
// 创建工厂对象
UserDaoFactory factory = new UserDaoFactory();
// 通过工厂对象,得到目标对象
UserDao userDao = factory.getUserDao();
```
对应Spring中配置
```xml
<!--
通过UserDaoFactory的成员方法,获取UserDao对象
Spring容器中会产生一个UserDaoFacotry对象、UserDao对象
UserDaoFacotry对象的名字是其id的值
UserDao=对象的名字是其id的值
-->
<!--方式三:使用实例工厂实例化bean-->
<!-- 装配工厂Bean,得到一个工厂的Bean对象 -->
<bean id="userFactory" class="com.cy.factory.UserDaoFactory"/>
<!-- 通过工厂Bean对象获取目标Bean对象 -->
<bean id="userDao" factory-method="getUserDao" factory-bean="userFactory"/>
```
从`Spring`容器中,获取方式和之前一样,通过`id`获取对象并使用。
### ==4.5 `Spring`中`FactoryBean`实例化==
- 该方式是在第三种方法的简化改良。编码基本雷同,配置相对简单。
- 实现步骤
1. 编写工厂类实现`FactoryBean`。类名一般为`xxxFactoryBean`,泛型为目标对象类型`xxx`
2. 实现`getObject()`方法、`getObjectType()`方法。
3. `Spring`配置文件中简单配置。
- `xxxFactoryBean`
```java
package com.cy.factory;
import com.cy.dao.UserDao;
import com.cy.dao.impl.UserDaoImpl;
import org.springframework.beans.factory.FactoryBean;
//FactoryBean创建对象
public class UserDaoFactoryBean implements FactoryBean<UserDao> {
//代替原始实例工厂中创建对象的方法
public UserDao getObject() throws Exception {
return new UserDaoImpl();
}
public Class<?> getObjectType() {
return UserDao.class;
}
/**
*该方法一般不用重写
*决定获取的Bean是否为单例。默认返回true。
*true 单例 singleton
*false 多例(非单例) prototype
* @return
*/
public boolean isSingleton() {
return true;
}
}
```
- `XML`配置
```xml
<!--
方式四:使用FactoryBean实例化bean
配置方式和第一种使用构造方法实例化凡是类似,但是class属性值是一个FactoryBean的实现类
Spring容器中会产生一个UserDaoFactoryBean对象、UserDao对象
UserDao对象的名字是改标签的id值
-->
<bean id="userDao" class="com.itheima.factory.UserDaoFactoryBean"/>
```
- **应用场景**
`Spring`在整合其他框架时,多用该方式。
## 5 生命周期
**生命周期**:对象从出生到死亡的整个过程。
### 5.1 配置实现
1. 提前准备好两个方法,方法名任意 xxxyyy
```java
package com.cy.dao.impl;
import com.cy.dao.BookDao;
public class BookDaoImpl implements BookDao {
public void save() {
System.out.println("book dao save ...");
}
//表示bean初始化对应的操作
public void xxx(){
System.out.println("init...");
}
//表示bean销毁前对应的操作
public void yyy(){
System.out.println("destory...");
}
}
```
2. `Spring`配置文件对应`bean`标签上添加两个属性,属性值不用带小括号
```xml
<!--init-method:设置bean初始化生命周期回调函数-->
<!--destroy-method:设置bean销毁生命周期回调函数,仅适用于单例对象-->
<bean id="bookDao" class="com.cy.dao.impl.BookDaoImpl" init-method="xxx" destroy-method="yyy"/>
```
### 5.2 实现接口
```java
package com.cy.dao.impl;
import com.cy.dao.BookDao;
public class BookDaoImpl implements BookDao, InitializingBean, DisposableBean {
public void save() {
System.out.println("book dao save ...");
}
// 实现InitializingBean中抽象方法,该方法会在init-method指定的方法执行后执行
public void afterPropertiesSet() throws Exception {
System.out.println("service init");
}
// 实现DisposableBean充抽象方法
public void destroy() throws Exception {
System.out.println("service destroy");
}
}
```
### 5.3 注意
需要手动关闭容器才能看到销毁代码的执行。
- `Spring`容器关闭的功能,在`Config***`的子接口中规定的,需要向下转型到其子类才能调用,一般直接使用`ClasspathXMLApplicationContext`
- 也可以注册关闭的钩子`ctx.registerShutdownHook()`,会在执行完代码后自动关闭。
#### 5.4 声明周期(11步)
- 初始化容器
- 创建`Bean`对象(内存分配)
- 执行构造方法
- 执行属性注入(set操作)
- 执行bean初始化方法
- 使用`bean`
- 执行业务操作
- 关闭/销毁容器
- 执行bean销毁方法
## ==6. 依赖注入==
### 6.1 概念
`Dependency Injection,DI`
**依赖**:在A类中用到了B类,就说A依赖B,需要在A类中添加一个B类型的成员变量;
**依赖注入**:把**B类对象**设置进**A中B类型的成员变量**的过程,称为依赖注入。
`Spring`会把**B类对象**注入到**A类中B类型的属性**上。自动完成注入。
```java
// 伪代码演示
class A{
// 为B类型的b变量初始化的过程,称为依赖注入
B b;
}
```
依赖注入可以理解成`IoC`的一种应用场景,反转的是对象间`依赖关系维护注入权`。
### 6.2`IoC`和`DI`的关系(面试题)
`IoC`是一种思想(规范),可以把对象的创建权、对象间依赖关系的维护注入权等从程序员手中,反转到了`Spring`容器中;
`DI`依赖注入,只是`IoC`在某个方面的一个具体实现,在 **依赖关系维护注入** 方面的一个实现。
### 6.3 注入方式
本质上就是为某个类的成员变量赋值的方式:setter/有参构造
```java
/*
1. 提供有参构造方法,通过构造方法在创建对象的同时为成员变量赋值
2. 提供setter,通过setter为成员变量赋值
*/
```
### ==6.4 Setter方式注入==
**注入引用类型(容器中已经存在的其他Bean对象)**
1. 保证被注入的`Bean`和目标`Bean`都已经装配到`Spring`容器
```xml
<bean id="bookDao" class="com.cy.dao.impl.BookDaoImpl"></bean>
<bean id="userDao" class="com.cy.dao.impl.UserDaoImpl"/>
<bean id="bookService" class="com.cy.service.impl.BookServiceImpl"></bean>
```
2. 在`BookServiceImpl`类中添加`UserDao/BookDao`的属性并提供setter
```java
package com.cy.service.impl;
import com.cy.dao.BookDao;
import com.cy.dao.UserDao;
import com.cy.service.BookService;
public class BookServiceImpl implements BookService{
private BookDao bookDao;
private UserDao userDao;
//setter注入需要提供要注入对象的set方法
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
//setter注入需要提供要注入对象的set方法
public void setBookDao(BookDao bookDao) {
this.bookDao = bookDao;
}
public void save() {
System.out.println("book service save ...");
bookDao.save();
userDao.save();
}
}
```
3. 通过`<property>`子标签完成注入
```xml
<bean id="bookDao" class="com.cy.dao.impl.BookDaoImpl"></bean>
<bean id="userDao" class="com.cy.dao.impl.UserDaoImpl"/>
<!--注入引用类型-->
<bean id="bookService" class="com.cy.service.impl.BookServiceImpl">
<!--property标签:设置注入属性-->
<!--name属性:设置注入的属性名,实际是set方法对应的名称-->
<!--ref属性:Spring容器中已经存在的符合要求的Bean-->
<property name="bookDao" ref="bookDao"/>
<property name="userDao" ref="userDao"/>
</bean>
```
注入简单数据类型数据
`String` + 基本数据类型
只需要把生面的配置文件中`ref`属性换成`value`属性即可。
```xml
<!--注入简单类型-->
<bean id="bookDao" class="com.cy.dao.impl.BookDaoImpl">
<!--property标签:设置注入属性-->
<!--name属性:设置注入的属性名,实际是set方法对应的名称-->
<!--value属性:设置注入简单类型数据值-->
<property name="connectionNum" value="100"/>
<property name="databaseName" value="mysql"/>
</bean>
```
```java
package com.cy.dao.impl;
import com.cy.dao.BookDao;
public class BookDaoImpl implements BookDao {
private String databaseName;
private int connectionNum;
//setter注入需要提供要注入对象的set方法
public void setConnectionNum(int connectionNum) {
this.connectionNum = connectionNum;
}
//setter注入需要提供要注入对象的set方法
public void setDatabaseName(String databaseName) {
this.databaseName = databaseName;
}
public void save() {
System.out.println("book dao save ..."+databaseName+","+connectionNum);
}
}
```
### 6.5 构造器注入
不常用
- 被装配的类中提供有参构造,而不需要提供`setter`
```java
package com.cy.service.impl;
import com.cy.dao.BookDao;
import com.cy.dao.UserDao;
import com.cy.service.BookService;
public class BookServiceImpl implements BookService{
private BookDao bookDao;
private UserDao userDao;
// 有参构造,不需要提供setter
public BookServiceImpl(BookDao bookDao, UserDao userDao) {
this.bookDao = bookDao;
this.userDao = userDao;
}
public void save() {
System.out.println("book service save ...");
bookDao.save();
userDao.save();
}
}
```
- `XML`中通过子标签配置`constructor-arg`
```xml
<!-- 标准书写 -->
<bean id="bookDao" class="com.cy.dao.impl.BookDaoImpl"></bean>
<bean id="userDao" class="com.cy.dao.impl.UserDaoImpl"/>
<bean id="bookService" class="com.cy.service.impl.BookServiceImpl">
<!-- 根据构造方法参数名称注入
name值只为有参构造中形参的名称
ref值为Spring容器中已近存在的其他Bean的id
value:简单类型使用value注入
-->
<constructor-arg name="userDao" ref="userDao"/>
<constructor-arg name="bookDao" ref="bookDao"/>
</bean>
```
- value属性注入简单类型
```xml
<bean id="bookDao" class="com.cy.dao.impl.BookDaoImpl">
<!-- 根据构造方法参数类型注入 -->
<constructor-arg type="int" value="10"/>
<constructor-arg type="java.lang.String" value="mysql"/>
</bean>
```
### ==6.6 自动装配==
自动为当前`Bean`完成依赖注入,简称**自动装配**,可取值`byName | byType | constructor`,分别代表按照
1. `byType`:按照**属性类型**和**Spring容器中bean**进行匹配,完成自动装配注入
2. `byName`:按照**属性名**和**Spring容器中bean的id**进行匹配,完成自动装配注入
3. `constructor`:按照**构造方法的形参类型**和**Spring容器中bean**进行匹配,完成自动装配注入
注意:上述中属性值的是`setter`或者是`构造形参`,而非成员变量本身。
`xml`中配置:把所有的`Bean`都配置进`Spring`容器。
```xml
<bean id = "bookDao" class="com.cy.dao.impl.BookDaoImpl"/>
<!-- <bean class="com.cy.dao.impl.BookDaoImpl"/> -->
<!-- autowire属性:开启自动装配,通常使用按类型装配-->
<bean id="bookService" class="com.cy.service.impl.BookServiceImpl" autowire="byType"/>
```
`java`代码
```java
package com.cy.service.impl;
import com.cy.dao.BookDao;
import com.cy.service.BookService;
public class BookServiceImpl implements BookService{
private BookDao bookDao;
public void setBookDao(BookDao bookDao) {
this.bookDao = bookDao;
}
public void save() {
System.out.println("book service save ...");
bookDao.save();
}
}
```
### ==6.7 注意事项==
0. **优选自动装配**
1. 使用**按类型装配**时(byType)必须保障容器中相同类型的bean唯一,推荐使用
2. 使用**按名称装配**时(byName)必须保障容器中具有指定名称的bean,因变量名与配置耦合,不推荐使用
3. 自动装配优先级低于setter注入与构造器注入,同时出现时自动装配配置失效
### 6.8 集合注入
了解即可。
```xml
<bean id="bookDao" class="com.cy.dao.impl.BookDaoImpl">
<!--
单例集合listsetarray
配置通用,建议使用list
双列集合map properties
配置通用 ,建议使用Map
-->
<!--set集合注入-->
<property name="set">
<array>
<value>100</value>
<value>200</value>
<value>300</value>
</array>
</property>
<!--map集合注入-->
<property name="map">
<props>
<prop key="country">china</prop>
<prop key="province">henan</prop>
<prop key="city">kaifeng</prop>
</props>
</property>
<!--Properties注入-->
<property name="properties">
<map>
<entry key="country" value="china"/>
<entry key="province" value="henan"/>
<entry key="city" value="kaifeng"/>
</map>
</property>
</bean>
```
```java
package com.cy.dao.impl;
import com.cy.dao.BookDao;
import java.util.*;
public class BookDaoImpl implements BookDao {
private String[] array;
private List<String> list;
private Set<String> set;
private Map<String,String> map;
private Properties properties;
public void setArray(String[] array) {
this.array = array;
}
public void setList(List<String> list) {
this.list = list;
}
public void setSet(Set<String> set) {
this.set = set;
}
public void setMap(Map<String, String> map) {
this.map = map;
}
public void setProperties(Properties properties) {
this.properties = properties;
}
public void save() {
System.out.println("book dao save ...");
System.out.println("遍历数组:" + Arrays.toString(array));
System.out.println("遍历List" + list);
System.out.println("遍历Set" + set);
System.out.println("遍历Map" + map);
System.out.println("遍历Properties" + properties);
}
}
```
## ==7. 案例练习==
无论是自己写的类要装配进`Spring`容器,还是第三方的类要装配进`Spring`容器,只需要遵循`Spring`的配置规则即可。
后者与前者的不同,仅仅是把**自己编写代码**变成了**导入依赖坐标**,配置方式一致。
### 7.1 `DruidDataSource`
#### 7.1.1 导入依赖坐标
```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.cy</groupId>
<artifactId>ssm01_spring01_11_datasource</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.16</version>
</dependency>
</dependencies>
</project>
```
#### 7.1.2 `Spring`装配配置
```xml
<bean class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/spring_db"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
```
#### 7.1.3 测试
```java
package com.itheima;
import com.itheima.dao.BookDao;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import javax.sql.DataSource;
public class App {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
DataSource dataSource = (DataSource) ctx.getBean("dataSource");
System.out.println(dataSource);
}
}
```
### 7.2 C3P0数据源
老牌数据源管理工具,曾经久不衰。
#### 7.2.1 导入依赖坐标
```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.itheima</groupId>
<artifactId>ssm01_spring01_11_datasource</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
</dependencies>
</project>
```
#### 7.2.2 `Spring`装配配置
```xml
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/spring_db"/>
<property name="user" value="root"/>
<property name="password" value="root"/>
<property name="maxPoolSize" value="1000"/>
</bean>
```
#### 7.2.3 测试
```java
package com.cy;
import com.cy.dao.BookDao;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import javax.sql.DataSource;
public class App {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
DataSource dataSource = (DataSource) ctx.getBean("dataSource");
System.out.println(dataSource);
}
}
```
## ==8 引入properties文件==
### 8.1 创建properties文件
- `jdbc.properties`
```properties
# 不要使用username,否则会错误的获取到当前系统的用户名。建议使用jdbc.username
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/spring_db
jdbc.username=root
jdbc.password=root
```
### 8.2 `beans.xml`文件引入 `jdbc.properties`文件
```xml
<!-- 在类路径下加载指定名称的properties配置文件,classpath:可以省略不写。下同-->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 在类路径下加载多个指定名称的properties配置文件 -->
<context:property-placeholder location="classpath:jdbc.properties,jdbc2.properties"/>
<!-- 在类路径下加载所有的properties配置文件 -->
<context:property-placeholder location="classpath:*.properties"/>
<!--
在类路径+jar包中(整个项目下)加载所有的properties配置文件
统配会造成效率降低,速度变慢
-->
<context:property-placeholder location="classpath*:*.properties"/>
<!--
使用属性占位符${},可以通过properties文件中的key读取到对应的值
OGNL
${} 所有的框架都支持这个种写法,从当前容器对象中根据key获取值
Spring容器会把当前系统的内置变量整合进自己的容器 username=当前系统的用户名
且优先级高于我们自己配置的。
${username} 会获取当前系统的用户名
解决方案:
1. 不要使用username,使用诸如jdbc.username。推荐写法。
2. 通过其属性system-properties-mode="NEVER"配置不加载系统属性。但是不推荐。
-->
<bean class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<bean id="bookDao" class="com.cy.dao.impl.BookDaoImpl">
<property name="name" value="${username}"/>
</bean>
```
### 8.3 代码测试
```java
package com.cy;
import com.cy.dao.BookDao;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import javax.sql.DataSource;
public class App {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
BookDao bookDao = (BookDao) ctx.getBean("bookDao");
bookDao.save();
}
}
``` xml方式有一年没用过了
页:
[1]