- Spring对IoC的实现
- IoC控制反转
- DI依赖注入
- set注入专题
- p命名空间注入
- c命名空间注入
- util命名空间注入
- 基于XML的自动装配
- spring引入外部属性配置文件
四、★★★Spring对IoC的实现
4.1 IoC控制反转
- 控制反转是一种思想。
- 控制反转是为了降低程序耦合度,提高程序扩展力,达到
OCP
原则,达到DIP
原则。 - 控制反转,反转的是什么?
- 将对象的创建权利交出去,交给第三方容器负责。
- 将对象和对象之间关系的维护权交出去,交给第三方容器负责。
- 控制反转思想的实现:
DI
(Dependency Injection
,依赖注入)。
4.2 DI依赖注入
- 依赖注入实现了控制反转的思想。
- Spring 通过依赖注入的方式来完成 Bean管理。
- Bean管理:Bean对象的创建,以及 Bean 对象中属性的赋值(或者叫做 Bean 对象之间关系的维护)。
- 依赖注入:
- 依赖指的是对象和对象之间的关联关系。
- 注入指的是一种数据传递行为,通过注入行为来让对象和对象产生关系。
- 依赖注入常见的实现方式包括两种:
- 第一种:
set
注入 - 第二种:构造注入
- 第一种:
4.2.1 set注入
set
注入,基于set
方法实现的,底层会通过反射机制调用属性相对应的set
方法来给属性赋值。- 这种方式要求属性必须对外提供
set
方法。
- 这种方式要求属性必须对外提供
set
注入的使用:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17package com.f.spring6.dao;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author fzy
* @date 2024/1/17 14:36
*/
public class UserDao {
private static final Logger logger = LoggerFactory.getLogger(UserDao.class);
public void insert() {
logger.info("数据库正在保存用户信息...");
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27package com.f.spring6.service;
import com.f.spring6.dao.UserDao;
/**
* @author fzy
* @date 2024/1/17 14:39
*/
public class UserService {
private UserDao userDao;
// set注入的话,必须提供一个set方法
// spring容器会调用这个set方法,来给userDao属性赋值
// 下面这个set方法是IDEA工具生成的,符合javabean规范
/*public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}*/
// 我自己写一个set方法,不使用IDEA工具生成的,不符合javabean的规范,可以吗?
// 也可以,但没必要。这里是演示如果这么写了,相应地在spring配置文件中该怎么写。
public void setXyz(UserDao xyz) {
this.userDao = xyz;
}
public void saveUser() {
userDao.insert();
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<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">
<!--配置dao-->
<bean id="userDaoBean" class="com.f.spring6.dao.UserDao"/>
<!--配置service-->
<bean id="userServiceBean" class="com.f.spring6.service.UserService">
<!--想让spring调用对应的set方法,需要配置property标签-->
<!--name属性怎么指定值:相应的set方法的方法名,去掉set,然后把剩下的单词的首字母变小写-->
<!--ref翻译为引用,ref后面指定的是要注入的bean的id-->
<property name="xyz" ref="userDaoBean"/>
<!--如果用IDEA工具生成的set方法,那么直接将属性名写在name的位置即可-->
<!--<property name="userDao" ref="userDaoBean"/>-->
</bean>
</beans>1
2
3
4
5
6
public void testSetDI() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
UserService userServiceBean = applicationContext.getBean("userServiceBean", UserService.class);
userServiceBean.saveUser();
}总结:为了让对象和对象之间能产生依赖关系,如果使用
set
注入的话,需要满足以下条件:- 在类中需要提供
set
方法。 - 在 spring 配置文件中需要配置
property
标签,property
标签中:name
属性指定相应的set
方法,值为相应的set
方法的方法名,去掉set
,然后把剩下的单词的首字母变小写。ref
属性指定要注入的bean
的id
,也就是set
方法要传参的对象。
- 在类中需要提供
4.2.2 构造注入
通过调用构造方法来给属性赋值。
与
set
注入相比,构造注入是在创建对象的同时进行注入,进行属性的赋值,而set
注入是在对象创建之后。构造注入的使用:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19package com.f.spring6.service;
import com.f.spring6.dao.UserDao;
/**
* @author fzy
* @date 2024/1/17 15:16
*/
public class UserService2 {
private UserDao userDao;
public UserService2(UserDao userDao) {
this.userDao = userDao;
}
public void saveUser() {
userDao.insert();
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<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="userDaoBean" class="com.f.spring6.dao.UserDao"/>
<bean id="userService2Bean" class="com.f.spring6.service.UserService2">
<!--
构造注入
index属性指定参数下标,第一个参数是0,第二个参数是1...
ref属性指定注入的bean的id
-->
<!--指定构造方法的第一个参数,下标是0-->
<!--<constructor-arg index="0" ref="userDaoBean"/>-->
<!--指定构造方法的第二个参数,下标是1-->
<!--<constructor-arg index="1" ref=""/>-->
<!--也可以用name属性来指定参数-->
<constructor-arg name="userDao" ref="userDaoBean"/>
</bean>
</beans>1
2
3
4
5
6
public void testConstructorDI() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring2.xml");
UserService2 userService2Bean = applicationContext.getBean("userService2Bean", UserService2.class);
userService2Bean.saveUser();
}- 通过构造方法进行注入有三种形式:
- 可以通过下标
index
。 - 可以通过名称
name
。 - 也可以不指定下标和参数名,让 spring 进行类型推断。
- 可以通过下标
- 通过构造方法进行注入有三种形式:
4.3 ★set注入专题
4.3.1 注入外部Bean(常用)
外部
Bean
:要进行注入的Bean
定义在需要被注入的Bean
的外面。对于外部
Bean
,在property
标签中使用ref
属性进行注入,或者使用ref
标签进行注入,其中通过ref
属性进行注入更常用,就如4.2.1
小节所用的那样。1
2
3
4
5
6
7
8
9
10
11
<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-->
<bean id="userDaoBean" class="com.f.spring6.dao.UserDao"/>
<bean id="userServiceBean" class="com.f.spring6.service.UserService">
<!--使用ref属性来引入外部Bean,这就是注入外部Bean-->
<property name="xyz" ref="userDaoBean"/>
</bean>
</beans>
4.3.2 注入内部Bean(少用)
内部
Bean
:在Bean
标签中嵌套Bean
标签,即需要进行注入的Bean
通过Bean
标签声明在需要被注入的Bean
中,然后直接注入到需要被注入的Bean
中。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16package com.f.spring6.dao;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author fzy
* @date 2024/1/17 15:37
*/
public class OrderDao {
private static final Logger logger = LoggerFactory.getLogger(OrderDao.class);
public void generate() {
logger.info("正在生成订单...");
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20package com.f.spring6.service;
import com.f.spring6.dao.OrderDao;
/**
* @author fzy
* @date 2024/1/17 16:05
*/
public class OrderService {
private OrderDao orderDao;
// 通过set方法给属性赋值
public void setOrderDao(OrderDao orderDao) {
this.orderDao = orderDao;
}
public void generate() {
this.orderDao.generate();
}
}1
2
3
4
5
6
7
8
9
10
11
<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="orderServiceBean" class="com.f.spring6.service.OrderService">
<property name="orderDao">
<!--在property标签中使用嵌套的bean标签,这就是内部Bean-->
<bean class="com.f.spring6.dao.OrderDao"/>
</property>
</bean>
</beans>1
2
3
4
5
6
7
public void testSetDI2() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("set-di.xml");
OrderService orderServiceBean = applicationContext.getBean("orderServiceBean", OrderService.class);
System.out.println(orderServiceBean);
orderServiceBean.generate();
}
4.3.3 注入简单类型
如果给简单类型赋值,即**给属性注入简单类型,使用
value
属性或value
标签,而不是ref
**。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32package com.f.spring6.bean;
/**
* @author fzy
* @date 2024/1/17 16:18
*/
public class User {
private String username;
private String password;
private int age;
public void setUsername(String username) {
this.username = username;
}
public void setPassword(String password) {
this.password = password;
}
public void setAge(int age) {
this.age = age;
}
public String toString() {
return "User{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
", age=" + age +
'}';
}
}1
2
3
4
5
6
7
8
9
10
11
12
<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="userBean" class="com.f.spring6.bean.User">
<!--如果是给简单类型赋值,就不能使用ref了,而是使用value-->
<property name="username" value="root"/>
<property name="password" value="root"/>
<property name="age" value="100"/>
</bean>
</beans>1
2
3
4
5
6
public void testSetDI3() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("set-di.xml");
User userBean = applicationContext.getBean("userBean", User.class);
System.out.println(userBean);
}
String中的简单类型
通过分析
org.springframework.beans.BeanUtils
的源码得知,简单类型包括:- 基本数据类型
- 基本数据类型对应的包装类
- String 或其他的 CharSequence 子类
- Number 子类
- Date 子类,
java.util.Date
是简单类型 - Enum 子类
- URI
- URL
- Temporal 子类,Temporal 是 Java8 提供的时间和时区类型
- Locale,Locale 是语言类,也是简单类型
- Class
- 另外还包括以上简单值类型对应的数组类型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46public class BeanUtils{
//.......
/**
* Check if the given type represents a "simple" property: a simple value
* type or an array of simple value types.
* <p>See {@link #isSimpleValueType(Class)} for the definition of <em>simple
* value type</em>.
* <p>Used to determine properties to check for a "simple" dependency-check.
* @param type the type to check
* @return whether the given type represents a "simple" property
* @see org.springframework.beans.factory.support.RootBeanDefinition#DEPENDENCY_CHECK_SIMPLE
* @see org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#checkDependencies
* @see #isSimpleValueType(Class)
*/
public static boolean isSimpleProperty(Class<?> type) {
Assert.notNull(type, "'type' must not be null");
return isSimpleValueType(type) || (type.isArray() && isSimpleValueType(type.getComponentType()));
}
/**
* Check if the given type represents a "simple" value type: a primitive or
* primitive wrapper, an enum, a String or other CharSequence, a Number, a
* Date, a Temporal, a URI, a URL, a Locale, or a Class.
* <p>{@code Void} and {@code void} are not considered simple value types.
* @param type the type to check
* @return whether the given type represents a "simple" value type
* @see #isSimpleProperty(Class)
*/
public static boolean isSimpleValueType(Class<?> type) {
return (Void.class != type && void.class != type &&
(ClassUtils.isPrimitiveOrWrapper(type) ||
Enum.class.isAssignableFrom(type) ||
CharSequence.class.isAssignableFrom(type) ||
Number.class.isAssignableFrom(type) ||
Date.class.isAssignableFrom(type) ||
Temporal.class.isAssignableFrom(type) ||
URI.class == type ||
URL.class == type ||
Locale.class == type ||
Class.class == type));
}
//........
}- 需要注意的是:
- 如果把
Date
当做简单类型的话,日期字符串格式就不能随便写,格式必须符合Date
的toString()
方法格式。显然这就比较鸡肋了,如果我们提供一个这样的日期字符串:2010-10-11
,那么是无法赋值给Date
类型的属性的。所以一般就不会用简单类型来给Date
做set
注入,而是用ref
给Date
类型的属性赋值。 - Spring6 之后,当注入的是 URL,那么这个 url 字符串是会进行有效性检测的。如果是一个存在的 url,那就没问题。如果不存在则报错。
- 如果把
注入简单类型的经典案例
给数据源的属性注入值:
假设我们现在要自己手写一个数据源,我们都知道所有的数据源都要实现
javax.sql.DataSource
接口,并且数据源中应该有连接数据库的信息,例如:driver
、url
、username
、password
等。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49package com.f.spring6.jdbc;
import javax.sql.DataSource;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.logging.Logger;
/**
* 所有的数据源都要实现java规范:javax.sql.DataSource
* 什么是数据源:能够给你提供Connection对象的,都是数据源
*
* @author fzy
* @date 2024/1/17 19:26
*/
public class MyDataSource implements DataSource { // 把数据源交给spring容器来管理
private String driver;
private String url;
private String username;
private String password;
public String toString() {
return "MyDataSource{" +
"driver='" + driver + '\'' +
", url='" + url + '\'' +
", username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
public void setDriver(String driver) {
this.driver = driver;
}
public void setUrl(String url) {
this.url = url;
}
public void setUsername(String username) {
this.username = username;
}
public void setPassword(String password) {
this.password = password;
}
......
}1
2
3
4
5
6
7
8
9
10
11
12
<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="myDataSourceBean" class="com.f.spring6.jdbc.MyDataSource">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/spring6"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
</beans>1
2
3
4
5
6
public void testMyDataSourceDI() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("set-di.xml");
MyDataSource myDataSourceBean = applicationContext.getBean("myDataSourceBean", MyDataSource.class);
System.out.println(myDataSourceBean);
}
4.3.4 注入数组
数组元素是简单类型
数组元素是非简单类型
- 注入数组的时候使用
array
标签,如果数组中是简单类型使用value
标签,反之使用ref
标签。
1 | package com.f.spring6.bean; |
1 | package com.f.spring6.bean; |
1 |
|
1 |
|
- 如果数组是简单类型,使用
<value>xxx</value>
标签。 - 如果数组是非简单类型,使用
<ref bean="bean-id"/>
标签。
4.3.5 注入List、Set集合
List
集合:有序可重复。- 注意:注入
List
集合的时候使用list
标签,如果List
集合中是简单类型使用value
标签,反之使用ref
标签。
- 注意:注入
Set
集合:无序不可重复。- 注意:注入
set
集合的时候使用set
标签,如果set
集合中元素是简单类型使用value
标签,反之使用ref
标签。
- 注意:注入
1 | package com.f.spring6.bean; |
1 | package com.f.spring6.bean; |
1 |
|
1 |
|
4.3.6 注入Map集合
Map
集合:- 使用
map
标签嵌套entry
标签。 - 如果
key
是简单类型,就使用key
属性,反之使用key-ref
属性。 - 如果
value
是简单类型,就使用value
属性,反之使用value-ref
属性。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22package com.f.spring6.bean;
import java.util.Map;
/**
* @author fzy
* @date 2024/1/17 20:26
*/
public class School {
private Map<Clazz, Student> school;
public void setSchool(Map<Clazz, Student> school) {
this.school = school;
}
public String toString() {
return "School{" +
"school=" + school +
'}';
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<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="schoolBean" class="com.f.spring6.bean.School">
<!--注入Map集合-->
<property name="school">
<map>
<!--
如果key和value是简单类型,就用这种配置:
<entry key="" value=""/>
-->
<!--如果key和value是非简单类型,就用下面这种配置-->
<entry key-ref="clazzBean" value-ref="studentBean"/>
</map>
</property>
</bean>
<bean id="clazzBean" class="com.f.spring6.bean.Clazz">
<property name="name" value="高三一班"/>
</bean>
<bean id="studentBean" class="com.f.spring6.bean.Student">
<property name="name" value="张三"/>
<property name="clazz" ref="clazzBean"/>
</bean>
</beans>1
2
3
4
5
6
public void testMapDI() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("collection.xml");
School schoolBean = applicationContext.getBean("schoolBean", School.class);
System.out.println(schoolBean);
}- 使用
4.3.7 注入Properties
java.util.Properties
继承java.util.Hashtable
,所以Properties
也是一个Map
集合。Properties
使用props
标签嵌套prop
标签完成。- 在
Properties
中,key
和value
的类型都是String
类型。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26package com.f.spring6.bean;
import java.util.Properties;
/**
* @author fzy
* @date 2024/1/17 20:38
*/
public class DataSourceProperties {
// Properties本质上也是一个Map集合
// Properties的父类Hashtable,Hashtable实现了Map接口
// 虽然这个也是一个Map集合,但是和Map的注入方式不同
// Properties的key和value只能是String类型
private Properties properties;
public void setProperties(Properties properties) {
this.properties = properties;
}
public String toString() {
return "DataSourceProperties{" +
"properties=" + properties +
'}';
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<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="dataSourcePropertiesBean" class="com.f.spring6.bean.DataSourceProperties">
<!--注入properties属性-->
<property name="properties">
<props>
<prop key="driver">com.mysql.cj.jdbc.Driver</prop>
<prop key="url">jdbc:mysql://localhost:8080/javaweb</prop>
<prop key="username">root</prop>
<prop key="password">root</prop>
</props>
</property>
</bean>
</beans>1
2
3
4
5
6
public void testPropertiesDI() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("collection.xml");
DataSourceProperties dataSourcePropertiesBean = applicationContext.getBean("dataSourcePropertiesBean", DataSourceProperties.class);
System.out.println(dataSourcePropertiesBean);
}
4.3.8 注入null和空字符串
- 注入
null
:<null/>
或者不为该属性赋值。 - 注入空字符串:
<value/>
或者value=""
。
1 | package com.f.spring6.bean; |
1 |
|
1 |
|
4.3.9 注入的值中含有特殊符号
XML 中有 5 个特殊字符,分别是:<、>、’、”、&
以上 5 个特殊符号在 XML 中会被特殊对待,会被当做 XML 语法的一部分进行解析,如果这些特殊符号直接出现在注入的字符串当中,会报错。

解决方案包括两种:
第一种:特殊符号使用转义字符代替。
特殊字符 转义字符 > > < < ‘ ' “ " & & 第二种:将含有特殊符号的字符串放到:
<![CDATA[]]>
当中。放在 CDATA 区中的数据不会被 XML 文件解析器解析,使用 CDATA 时,不能使用value
属性,只能使用value
标签。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20package com.f.spring6.bean;
/**
* @author fzy
* @date 2024/1/17 21:56
*/
public class Math {
private String result;
public void setResult(String result) {
this.result = result;
}
public String toString() {
return "Math{" +
"result='" + result + '\'' +
'}';
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
<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="mathBean" class="com.f.spring6.bean.Math">
<!--第一种方案:使用实体符号代替特殊符号-->
<!--<property name="result" value="2 < 3"/>-->
<!--第二种方案:使用<![CDATA[]]>-->
<property name="result">
<!--只能使用value标签-->
<value><![CDATA[2 < 3]]></value>
</property>
</bean>
</beans>1
2
3
4
5
6
public void testSpecial() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("set-di.xml");
Object mathBean = applicationContext.getBean("mathBean", Math.class);
System.out.println(mathBean);
}
4.4 p命名空间注入
目的:简化
set
注入。p 命名空间实际上是对
set
注入的简化。- p 命名空间注入底层还是
set
注入,只不过 p 命名空间注入可以让 spring 配置变得更加简单。
- p 命名空间注入底层还是
使用 p 命名空间注入的前提条件包括两个:
- 第一:在 XML 头部信息中添加 p 命名空间的配置信息:
xmlns:p="http://www.springframework.org/schema/p"
- 第二:p 命名空间注入是基于
setter
方法的,所以需要提供对应的属性的setter
方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34package com.f.spring6.bean;
import java.util.Date;
/**
* @author fzy
* @date 2024/1/17 22:09
*/
public class Dog {
private String name;
private int age;
private Date birth;
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
public void setBirth(Date birth) {
this.birth = birth;
}
public String toString() {
return "Dog{" +
"name='" + name + '\'' +
", age=" + age +
", birth=" + birth +
'}';
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
<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">
<!--
第一步:在spring的配置文件头部添加p命名空间:xmlns:p="http://www.springframework.org/schema/p"
第二步:使用 p:属性名 = "属性值"
-->
<bean id="dogBean" class="com.f.spring6.bean.Dog" p:name="小黄" p:age="2" p:birth-ref="birthBean"/>
<!--这里获取的是当前系统时间-->
<bean id="birthBean" class="java.util.Date"/>
</beans>1
2
3
4
5
6
public void testP() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-p.xml");
Dog dogBean = applicationContext.getBean("dogBean", Dog.class);
System.out.println(dogBean);
}- 第一:在 XML 头部信息中添加 p 命名空间的配置信息:
4.5 c命名空间注入
c 命名空间是简化构造方法注入的。
使用 c 命名空间的两个前提条件:
- 第一:需要在 xml 配置文件头部添加 c 命名空间的配置信息:
xmlns:c="http://www.springframework.org/schema/c"
- 第二:需要提供构造方法。
c 命名空间是依靠构造方法的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23package com.f.spring6.bean;
/**
* @author fzy
* @date 2024/1/18 10:34
*/
public class Bird {
private String name;
private String color;
public Bird(String name, String color) {
this.name = name;
this.color = color;
}
public String toString() {
return "Bird{" +
"name='" + name + '\'' +
", color='" + color + '\'' +
'}';
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:c="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--
第一步:在spring配置文件头部添加c命名空间:xmlns:c="http://www.springframework.org/schema/c"
第二步:使用
c:_0 下标方式
c:name 参数名方式
-->
<bean id="birdBean" class="com.f.spring6.bean.Bird" c:name="黄雀" c:color="yellow"/>
</beans>1
2
3
4
5
6
public void testC() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-c.xml");
Bird birdBean = applicationContext.getBean("birdBean", Bird.class);
System.out.println(birdBean);
}- 第一:需要在 xml 配置文件头部添加 c 命名空间的配置信息:
注意:不管是 p 命名空间还是 c 命名空间,注入的时候都可以注入简单类型以及非简单类型。
4.6 util命名空间注入
使用 util 命名空间可以让配置复用。
使用 util 命名空间的前提是:在 spring 配置文件头部添加配置信息。
xmlns:util="http://www.springframework.org/schema/util"
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31package com.f.spring6.jdbc;
import javax.sql.DataSource;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.Properties;
import java.util.logging.Logger;
/**
* @author fzy
* @date 2024/1/18 10:52
*/
public class MyDataSource1 implements DataSource {
// properties属性类对象,这是一个Map集合,key和value都是String类型
private Properties properties;
public void setProperties(Properties properties) {
this.properties = properties;
}
public String toString() {
return "MyDataSource2{" +
"properties=" + properties +
'}';
}
......
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31package com.f.spring6.jdbc;
import javax.sql.DataSource;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.Properties;
import java.util.logging.Logger;
/**
* @author fzy
* @date 2024/1/18 10:49
*/
public class MyDataSource2 implements DataSource {
// properties属性类对象,这是一个Map集合,key和value都是String类型
private Properties properties;
public void setProperties(Properties properties) {
this.properties = properties;
}
public String toString() {
return "MyDataSource2{" +
"properties=" + properties +
'}';
}
......
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
<!--
第一步:在spring配置文件头部添加:
xmlns:util="http://www.springframework.org/schema/util"
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
第二步:通过ref引用
-->
<util:properties id="p">
<prop key="driver">com.mysql.cj.jdbc.Driver</prop>
<prop key="url">jdbc:mysql://localhost:3306/javaweb</prop>
<prop key="username">root</prop>
<prop key="password">root</prop>
</util:properties>
<!--数据源1,不使用util命名空间-->
<bean id="myDataSource1" class="com.f.spring6.jdbc.MyDataSource1">
<property name="properties">
<props>
<prop key="driver">com.mysql.cj.jdbc.Driver</prop>
<prop key="url">jdbc:mysql://localhost:3306/javaweb</prop>
<prop key="username">root</prop>
<prop key="password">root</prop>
</props>
</property>
</bean>
<!--数据源2,使用util命名空间-->
<bean id="myDataSource2" class="com.f.spring6.jdbc.MyDataSource2">
<property name="properties" ref="p"/>
</bean>
</beans>1
2
3
4
5
6
7
8
public void testUtil() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-util.xml");
MyDataSource1 myDataSource1 = applicationContext.getBean("myDataSource1", MyDataSource1.class);
MyDataSource2 myDataSource2 = applicationContext.getBean("myDataSource2", MyDataSource2.class);
System.out.println(myDataSource1);
System.out.println(myDataSource2);
}
4.7 基于XML的自动装配
Spring 还可以完成自动化的注入,自动化注入又被称为自动装配。
- 它可以根据名字进行自动装配,也可以根据类型进行自动装配。
建议还是少用,因为当数据多起来以后,可能容易出错。
4.7.1 根据名称自动装配
如果根据名称装配 (
autowire="byName"
),底层会调用set
方法进行注入。- 根据名字进行自动装配的时候,被注入的对象的
bean
的id
不能随便写。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16package com.f.spring6.dao;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author fzy
* @date 2024/1/17 15:37
*/
public class OrderDao {
private static final Logger logger = LoggerFactory.getLogger(OrderDao.class);
public void generate() {
logger.info("正在生成订单...");
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20package com.f.spring6.service;
import com.f.spring6.dao.OrderDao;
/**
* @author fzy
* @date 2024/1/17 16:05
*/
public class OrderService {
private OrderDao orderDao;
// 通过set方法给属性赋值
public void setOrderDao(OrderDao orderDao) {
this.orderDao = orderDao;
}
public void generate() {
this.orderDao.generate();
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<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="orderServiceBean" class="com.f.spring6.service.OrderService" autowire="byName"/>
<!--id一般也叫做bean的名称-->
<!--根据名字进行自动装配的时候,被注入的对象的bean的id不能随便写。
set方法的方法名去掉set,剩下单词首字母小写。-->
<bean id="orderDao" class="com.f.spring6.dao.OrderDao"/>
<!--<bean id="orderServiceBean" class="com.f.spring6.service.OrderService">-->
<!-- <property name="orderDao" ref="orderDaoBean"/>-->
<!--</bean>-->
<!--<bean id="orderDaoBean" class="com.f.spring6.dao.OrderDao"/>-->
</beans>1
2
3
4
5
6
public void testAutoWire() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-autowire.xml");
OrderService orderServiceBean = applicationContext.getBean("orderServiceBean", OrderService.class);
orderServiceBean.generate();
}- 根据名字进行自动装配的时候,被注入的对象的
4.7.2 根据类型自动装配
如果根据类型装配 (
autowire="byType"
),底层会调用set
方法进行注入。即:无论是byName
还是byType
,在装配的时候都是基于set
方法的,所以set
方法是必须要提供的,提供构造方法是不行的。如果
byType
,在根据类型装配时,如果配置文件中有两个类型一样的bean
会报错。当使用
byType
进行自动装配的时候,配置文件中某种类型的Bean
必须是唯一的,不能出现多个。如果存在多个类型一样的
Bean
,则 Spring 会不知道使用哪个Bean
进行注入。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17public class CustomerService {
private UserDao userDao;
private VipDao vipDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void setVipDao(VipDao vipDao) {
this.vipDao = vipDao;
}
public void save(){
userDao.insert();
vipDao.insert();
}
}1
2
3
4
5
6
7
8<!--根据类型进行自动装配-->
<!--自动装配是基于set方法的-->
<!--根据类型进行自动装配的时候,在有效的配置文件当中,某种类型的实例只能有一个。-->
<bean class="com.powernode.spring6.dao.VipDao"></bean>
<bean id="x" class="com.powernode.spring6.dao.UserDao"></bean>
<!--如果byType,根据类型装配时,如果配置文件中有两个类型一样的bean会报错-->
<!--<bean id="y" class="com.powernode.spring6.dao.UserDao"></bean>-->
<bean id="cs" class="com.powernode.spring6.service.CustomerService" autowire="byType"></bean>
4.8 spring引入外部属性配置文件
我们都知道编写数据源的时候是需要连接数据库的信息的,例如:driver url username password 等信息。这些信息可以单独写到一个属性配置文件中,这样用户修改起来会更加的方便。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49package com.f.spring6.jdbc;
import javax.sql.DataSource;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.logging.Logger;
/**
* 所有的数据源都要实现java规范:javax.sql.DataSource
* 什么是数据源:能够给你提供Connection对象的,都是数据源
*
* @author fzy
* @date 2024/1/17 19:26
*/
public class MyDataSource implements DataSource { // 把数据源交给spring容器来管理
private String driver;
private String url;
private String username;
private String password;
public String toString() {
return "MyDataSource{" +
"driver='" + driver + '\'' +
", url='" + url + '\'' +
", username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
public void setDriver(String driver) {
this.driver = driver;
}
public void setUrl(String url) {
this.url = url;
}
public void setUsername(String username) {
this.username = username;
}
public void setPassword(String password) {
this.password = password;
}
......
}1
2
3
4jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/javaweb
jdbc.username=root
jdbc.password=root1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<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 http://www.springframework.org/schema/context/spring-context.xsd">
<!--
引入外部的jdbc.properties文件:
第一步:引入context命名空间
xmlns:context="http://www.springframework.org/schema/context"
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
第二步:使用标签context:property-placeholder的location属性来指定属性配置文件的路径
location默认从类的根路径下开始加载资源
第三步:从配置文件中取值:${key}
${key}会优先从系统环境变量中加载值,所以一般在properties中会给key加上前缀
-->
<context:property-placeholder location="jdbc.properties"/>
<!--配置数据源-->
<bean id="myDataSource" class="com.f.spring6.jdbc.MyDataSource">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
</beans>1
2
3
4
5
6
public void testProperties() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-properties.xml");
MyDataSource myDataSource = applicationContext.getBean("myDataSource", MyDataSource.class);
System.out.println(myDataSource);
}