Spring 对 IoC 的实现
依赖注入
Spring 通过依赖注入的方式来完成 Bean 的管理,Bean 的管理是指 Bean对象的创建以及 Bean 对象之间关系的维护。
set 注入
在 UserService 类中需要配置 UserDao 对象的依赖关系。
public class UserService {
private UserDao userDao;
// 使用set方式注入,必须提供set方法。
// 反射机制要调用这个方法给属性赋值的。
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void save(){
userDao.insert();
}
}
然后需要在 Spring 配置文件中声明 UserService 的这个 Bean 中如何使用 set 注入的方式管理 UserDao 的依赖关系。
<?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="userDaoBean" class="com.powernode.spring6.dao.UserDao"/>
<bean id="userServiceBean" class="com.powernode.spring6.service.UserService">
<!-- 想让 Spring 调用对应的 set 方法,需要配置 property 标签 -->
<!-- name 属性:set方法的方法名去掉“set“,然后把剩下的单词首字母变小写 -->
<!-- ref 后面指定的是 bean 的 id -->
<!-- <property name="mySQLUserDao" ref="userDaoBean" />-->
<property name="userDao" ref="userDaoBean"/>
</bean>
</beans>
构造注入
首先要定义好构造函数
public class CustomerService {
private UserDao userDao;
private VipDao vipDao;
CustomerService(UserDao userDao, VipDao vipDao) {
this.userDao = userDao;
this.vipDao = vipDao;
}
public void save() {
userDao.insert();
vipDao.insert();
}
}
然后在配置文件中配置构造函数中各个参数关联的 bean
<?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="userDaoBean" class="com.powernode.spring6.dao.UserDao" />
<bean id="vipDaoBean" class="com.powernode.spring6.dao.VipDao" />
<bean id="customerServiceBean" class="com.powernode.spring6.service.CustomerService">
<!-- 构造注入(第一种方式,通过下标) -->
<!-- index = 0 用于指定构造方法的第一个参数 -->
<constructor-arg index="0" ref="userDaoBean" />
<constructor-arg index="1" ref="vipDaoBean" />
</bean>
<bean id="customerServiceBean2" class="com.powernode.spring6.service.CustomerService">
<!-- 构造注入(第二种方式,通过参数名称) -->
<!-- name = userDao 用于指定构造方法中名为 userDao 的参数 -->
<constructor-arg name="userDao" ref="userDaoBean" />
<constructor-arg name="vipDao" ref="vipDaoBean" />
</bean>
<bean id="customerServiceBean3" class="com.powernode.spring6.service.CustomerService">
<!-- 构造注入(第三种方式,根据类型自动判断,不推荐这种方式,可读性太差) -->
<!-- 不指定下标,也不指定参数名,让 spring 自己做类型匹配 -->
<constructor-arg ref="userDaoBean" />
<constructor-arg ref="vipDaoBean" />
</bean>
</beans>
set注入专题
注入外部 Bean(推荐)
<?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 -->
<bean id="userDaoBean" class="com.powernode.spring6.dao.UserDao"/>
<bean id="userServiceBean" class="com.powernode.spring6.service.UserService">
<!-- 注入外部 bean(推荐): 使用 ref 属性来引入 -->
<property name="userDao" ref="userDaoBean"/>
</bean>
</beans>
注入内部 Bean
在 bean 标签中嵌套 bean 标签
<?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="userServiceBean" class="com.powernode.spring6.service.UserService">
<property name="userDao">
<!-- 注入内部 bean:在 property 标签中使用嵌套的 bean 标签 -->
<bean class="com.powernode.spring6.dao.UserDao"/>
</property>
</bean>
</beans>
注入简单类型
public class User{
private String username;
private int age;
public void setUsername(String username) {
this.username = username;
}
public void setAge(int age){
this.age = age;
}
}
可以通过 set 注入给 String、int 等简单类型赋值。
<?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="userBean" class="com.powernode.spring6.beans.User">
<!--如果像这种int类型的属性,我们称为简单类型,这种简单类型在注入的时候要使用value属性,不能使用ref-->
<!--<property name="age" value="20"/>-->
<property name="age" value="20">
</property>
</bean>
</beans>
简单类型包括哪些呢?可以通过Spring的源码来分析一下:BeanUtils类
public 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));
}
//........
}
通过源码分析得知,简单类型包括:
- 基本数据类型
- 基本数据类型对应的包装类
- String 或其他的 CharSequence 子类
- Number 子类
- Date 子类
- Enum 子类
- URI
- URL
- Temporal 子类
- Locale
- Class
- 另外还包括以上简单值类型对应的数组类型
注意:spring6之后,当注入的是URL,那么这个url字符串是会进行有效性检测的。如果是一个存在的url,那就没问题。如果不存在则报错。
级联属性赋值
package com.powernode.spring6.beans;
/**
* @author 动力节点
* @version 1.0
* @className Clazz
* @since 1.0
**/
public class Clazz {
private String name;
public Clazz() {
}
public Clazz(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Clazz{" +
"name='" + name + ''' +
'}';
}
}
package com.powernode.spring6.beans;
/**
* @author 动力节点
* @version 1.0
* @className Student
* @since 1.0
**/
public class Student {
private String name;
private Clazz clazz;
public Student() {
}
public Student(String name, Clazz clazz) {
this.name = name;
this.clazz = clazz;
}
public void setName(String name) {
this.name = name;
}
public void setClazz(Clazz clazz) {
this.clazz = clazz;
}
// 使用级联属性赋值,需要 getter 方法
public Clazz getClazz() {
return clazz;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + ''' +
", clazz=" + clazz +
'}';
}
}
<?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="clazzBean" class="com.powernode.spring6.beans.Clazz"/>
<bean id="student" class="com.powernode.spring6.beans.Student">
<property name="name" value="张三"/>
<!--要点1:以下两行配置的顺序不能颠倒-->
<property name="clazz" ref="clazzBean"/>
<!--要点2:clazz属性必须有getter方法-->
<property name="clazz.name" value="高三一班"/>
</bean>
</beans>
注入数组
当数组中的元素是简单类型
package com.powernode.spring6.beans;
import java.util.Arrays;
public class Person {
private String[] favariteFoods;
public void setFavariteFoods(String[] favariteFoods) {
this.favariteFoods = favariteFoods;
}
@Override
public String toString() {
return "Person{" +
"favariteFoods=" + Arrays.toString(favariteFoods) +
'}';
}
}
<?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="person" class="com.powernode.spring6.beans.Person">
<property name="favariteFoods">
<array>
<value>鸡排</value>
<value>汉堡</value>
<value>鹅肝</value>
</array>
</property>
</bean>
</beans>
当数组中的元素是非简单类型:一个订单中包含多个商品。
package com.powernode.spring6.beans;
/**
* @author 动力节点
* @version 1.0
* @className Goods
* @since 1.0
**/
public class Goods {
private String name;
public Goods() {
}
public Goods(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Goods{" +
"name='" + name + ''' +
'}';
}
}
package com.powernode.spring6.beans;
import java.util.Arrays;
/**
* @author 动力节点
* @version 1.0
* @className Order
* @since 1.0
**/
public class Order {
// 一个订单中有多个商品
private Goods[] goods;
public Order() {
}
public Order(Goods[] goods) {
this.goods = goods;
}
public void setGoods(Goods[] goods) {
this.goods = goods;
}
@Override
public String toString() {
return "Order{" +
"goods=" + Arrays.toString(goods) +
'}';
}
}
<?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="goods1" class="com.powernode.spring6.beans.Goods">
<property name="name" value="西瓜"/>
</bean>
<bean id="goods2" class="com.powernode.spring6.beans.Goods">
<property name="name" value="苹果"/>
</bean>
<bean id="order" class="com.powernode.spring6.beans.Order">
<property name="goods">
<array>
<!--这里使用ref标签即可-->
<ref bean="goods1"/>
<ref bean="goods2"/>
</array>
</property>
</bean>
</beans>
注入 List 集合
package com.powernode.spring6.beans;
import java.util.List;
/**
* @author 动力节点
* @version 1.0
* @className People
* @since 1.0
**/
public class People {
// 一个人有多个名字
private List<String> names;
public void setNames(List<String> names) {
this.names = names;
}
@Override
public String toString() {
return "People{" +
"names=" + names +
'}';
}
}
<?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="peopleBean" class="com.powernode.spring6.beans.People">
<property name="names">
<list>
<value>铁锤</value>
<value>张三</value>
<value>张三</value>
<value>张三</value>
<value>狼</value>
</list>
</property>
</bean>
</beans>
注入 Set 集合
Set集合:无序不可重复
package com.powernode.spring6.beans;
import java.util.List;
import java.util.Set;
/**
* @author 动力节点
* @version 1.0
* @className People
* @since 1.0
**/
public class People {
// 一个人有多个电话
private Set<String> phones;
public void setPhones(Set<String> phones) {
this.phones = phones;
}
//......
@Override
public String toString() {
return "People{" +
"phones=" + phones +
", names=" + names +
'}';
}
}
<?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="peopleBean" class="com.powernode.spring6.beans.People">
<property name="phones">
<set>
<!--非简单类型可以使用ref,简单类型使用value-->
<value>110</value>
<value>110</value>
<value>120</value>
<value>120</value>
<value>119</value>
<value>119</value>
</set>
</property>
</bean>
</beans>
注入 Map 集合
注意:
- 如果key是简单类型,使用 key 属性,反之使用 key-ref 属性。
- 如果value是简单类型,使用 value 属性,反之使用 value-ref 属性。
package com.powernode.spring6.beans;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* @author 动力节点
* @version 1.0
* @className People
* @since 1.0
**/
public class People {
// 一个人有多个住址
private Map<Integer, String> addrs;
public void setAddrs(Map<Integer, String> addrs) {
this.addrs = addrs;
}
//......
@Override
public String toString() {
return "People{" +
"addrs=" + addrs +
", phones=" + phones +
", names=" + names +
'}';
}
}
<?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="peopleBean" class="com.powernode.spring6.beans.People">
<property name="addrs">
<map>
<!--如果key不是简单类型,使用 key-ref 属性-->
<!--如果value不是简单类型,使用 value-ref 属性-->
<entry key="1" value="北京大兴区"/>
<entry key="2" value="上海浦东区"/>
<entry key="3" value="深圳宝安区"/>
</map>
</property>
</bean>
</beans>
注入 Properties
java.util.Properties 继承 java.util.Hashtable,所以 Properties 也是一个 Map 集合。
虽然这是一个 Map 集合,但是和 Map 的注入方式不太一样。
Properties 的 key 和 value 只能是 String,不能是其他类型。
package com.powernode.spring6.beans;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
/**
* @author 动力节点
* @version 1.0
* @className People
* @since 1.0
**/
public class People {
private Properties properties;
public void setProperties(Properties properties) {
this.properties = properties;
}
//......
@Override
public String toString() {
return "People{" +
"properties=" + properties +
", addrs=" + addrs +
", phones=" + phones +
", names=" + names +
'}';
}
}
<?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="peopleBean" class="com.powernode.spring6.beans.People">
<property name="properties">
<props>
<prop key="driver">com.mysql.cj.jdbc.Driver</prop>
<prop key="url">jdbc:mysql://localhost:3306/spring</prop>
<prop key="username">root</prop>
<prop key="password">123456</prop>
</props>
</property>
</bean>
</beans>
注入 NULL 和空字符串
注入空字符串使用:<value/> 或者 value="";
注入null使用:<null/> 或者 不为该属性赋值。
- 我们先来看一下,怎么注入空字符串。
package com.powernode.spring6.beans;
/**
* @author 动力节点
* @version 1.0
* @className Vip
* @since 1.0
**/
public class Vip {
private String email;
public void setEmail(String email) {
this.email = email;
}
@Override
public String toString() {
return "Vip{" +
"email='" + email + ''' +
'}';
}
}
<?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="vipBean" class="com.powernode.spring6.beans.Vip">
<!--空串的第一种方式(idea推荐使用第一种方式)-->
<!--<property name="email" value=""/>-->
<!--空串的第二种方式-->
<property name="email">
<value/>
</property>
</bean>
</beans>
怎么注入null呢?
第一种方式:不给属性赋值
<?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="vipBean" class="com.powernode.spring6.beans.Vip" />
</beans>
第二种方式:使用 <null/>
<?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="vipBean" class="com.powernode.spring6.beans.Vip">
<property name="email">
<null/>
</property>
</bean>
</beans>
注入的值含有特殊符号
XML 中有 5 个特殊字符,分别是:<、>、'、"、&,这 5 个特殊符号在 XML 中会被特殊对待,会被当做 XML 语法的一部分进行解析,如果这些特殊符号直接出现在注入的字符串当中,会报错。
解决方案包括两种:
- 第一种:特殊符号使用转义字符代替。
- 第二种:将含有特殊符号的字符串放到:
<![CDATA[]]>当中。因为放在 CDATA 区中的数据不会被 XML 文件解析器解析。
5个特殊字符对应的转义字符分别是:
| 特殊字符 | 转义字符 |
|---|---|
| > | > |
| < | < |
| ' | ' |
| " | " |
| & | & |
先用转义字符代替。
package com.powernode.spring6.beans;
/**
* @author 动力节点
* @version 1.0
* @className Math
* @since 1.0
**/
public class Math {
private String result;
public void setResult(String result) {
this.result = result;
}
@Override
public String toString() {
return "Math{" +
"result='" + result + '\'' +
'}';
}
}
<?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="mathBean" class="com.powernode.spring6.beans.Math">
<property name="result" value="2 < 3"/>
</bean>
</beans>
再来使用CDATA方式。
<?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="mathBean" class="com.powernode.spring6.beans.Math">
<property name="result">
<!--只能使用value标签-->
<value><![CDATA[2 < 3]]></value>
</property>
</bean>
</beans>
注意:使用CDATA时,不能使用value属性,只能使用value标签。
p 命名空间注入
这种注入方式也是基于 set 方法注入的,只不过这种方式可以简化配置。
使用p命名空间注入的前提条件包括两个:
- 第一:在 XML 头部信息中添加 p 命名空间的配置信息:
xmlns:p="http://www.springframework.org/schema/p"; - 第二:p 命名空间注入是基于 setter 方法的,所以需要对应的属性提供 setter 方法。
package com.powernode.spring6.bean;
import java.util.Date;
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;
}
@Override
public String toString() {
return "Dog{" +
"name='" + name + '\'' +
", age=" + age +
", birth=" + birth +
'}';
}
}
<?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">
<!--
step1:在 spring 配置文件的头部添加 p 命名空间
如果是简单类型,直接 p:属性=;
如果是饮用烈性,p:属性-ref=“bean”。
-->
<bean id="dogBean" class="com.powernode.spring6.bean.Dog" p:name="小花" p:age="3" p:birth-ref="birthBean" />
<!-- 这里会调用无参的构造函数,既获取当前的日期 -->
<bean id="birthBean" class="java.util.Date"/>
</beans>
c 命名空间注入
c命名空间是简化构造方法注入的。
使用c命名空间的两个前提条件:
- 第一:需要在xml配置文件头部添加信息:
xmlns:c="http://www.springframework.org/schema/c"; - 第二:需要提供构造方法。
public class People {
private String name;
private int age;
private boolean sex;
public People(String name, int age, boolean sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
@Override
public String toString() {
return "People{" +
"name='" + name + '\'' +
", age=" + age +
", sex=" + sex +
'}';
}
}
<?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:c="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 第一种注入方式 -->
<bean id="peopleBean" class="com.powernode.spring6.bean.People" c:_0="小花" c:_1="13" c:_2="true"/>
<!-- 第二种注入方式 -->
<bean id="peopleBean2" class="com.powernode.spring6.bean.People" c:name="小花" c:age="13" c:sex="true"/>
</beans>
util 命名空间
使用util命名空间可以让配置复用。
使用util命名空间的前提是:在spring配置文件头部添加配置信息。如下:
package com.powernode.spring6.beans;
import java.util.Properties;
/**
* @author 动力节点
* @version 1.0
* @className MyDataSource1
* @since 1.0
**/
public class MyDataSource1 {
private Properties properties;
public void setProperties(Properties properties) {
this.properties = properties;
}
@Override
public String toString() {
return "MyDataSource1{" +
"properties=" + properties +
'}';
}
}
package com.powernode.spring6.beans;
import java.util.Properties;
/**
* @author 动力节点
* @version 1.0
* @className MyDataSource2
* @since 1.0
**/
public class MyDataSource2 {
private Properties properties;
public void setProperties(Properties properties) {
this.properties = properties;
}
@Override
public String toString() {
return "MyDataSource2{" +
"properties=" + properties +
'}';
}
}
<?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: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">
<util:properties id="prop">
<prop key="driver">com.mysql.cj.jdbc.Driver</prop>
<prop key="url">jdbc:mysql://localhost:3306/spring</prop>
<prop key="username">root</prop>
<prop key="password">123456</prop>
</util:properties>
<bean id="dataSource1" class="com.powernode.spring6.beans.MyDataSource1">
<property name="properties" ref="prop"/>
</bean>
<bean id="dataSource2" class="com.powernode.spring6.beans.MyDataSource2">
<property name="properties" ref="prop"/>
</bean>
</beans>
基于 XML 的自动装配
Spring 还可以完成自动化的注入,自动化注入又被称为自动装配。它可以根据名字进行自动装配,也可以根据类型进行自动装配。
根据名称自动装配
public class UserDao {
private static final Logger logger = LoggerFactory.getLogger(UserDao.class);
public void insert() {
// System.out.println("数据库正在保存用户信息");
logger.info("数据库正在保存用户信息");
}
}
package com.powernode.spring6.service;
import com.powernode.spring6.dao.UserDao;
public class UserService {
private UserDao aaa;
// 这个set方法非常关键
public void setAaa(UserDao aaa) {
this.aaa = aaa;
}
public void save(){
aaa.insert();
}
}
<?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="userService" class="com.powernode.spring6.service.UserService" autowire="byName"/>
<bean id="aaa" class="com.powernode.spring6.dao.UserDao"/>
</beans>
UserService 类中有一个 UserDao 属性,而 UserDao 属性的名字是 aaa,对应的 set 方法是 setAaa(),正好和UserDao Bean 的 id 是一样的。
测试一下 byName 装配是和属性名有关还是和 set 方法名有关系:
package com.powernode.spring6.service;
import com.powernode.spring6.dao.UserDao;
/**
* @author 动力节点
* @version 1.0
* @className UserService
* @since 1.0
**/
public class UserService {
// 这里没修改
private UserDao aaa;
/*public void setAaa(UserDao aaa) {
this.aaa = aaa;
}*/
// set方法名变化了
public void setDao(UserDao aaa){
this.aaa = aaa;
}
public void save(){
aaa.insert();
}
}
在执行测试程序:
通过测试得知,aaa 属性并没有赋值成功,也就是没有装配成功。
我们将spring配置文件修改以下:
<?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="userService" class="com.powernode.spring6.service.UserService" autowire="byName"/>
<!--这个id修改了-->
<bean id="dao" class="com.powernode.spring6.dao.UserDao"/>
</beans>
测试程序就可以正常执行了。这说明,如果根据名称装配,底层会调用 set 方法进行注入。
根据类型自动装配
package com.powernode.spring6.dao;
public class AccountDao {
public void insert() {
System.out.println("Inserting a new account");
}
}
package com.powernode.spring6.service;
import com.powernode.spring6.dao.AccountDao;
public class AccountService {
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
public void save() {
accountDao.insert();
}
}
<?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 class="com.powernode.spring6.dao.AccountDao" />
<bean id="accountService" class="com.powernode.spring6.service.AccountService" autowire="byType"/>
</beans>
当注释掉 AccountService 的set方法时,再执行会报错:
可以看到无论是 byName 还是 byType,在装配的时候都是基于 set 方法的。所以 set 方法是必须要提供的,提供构造方法是不行的。
当byType进行自动装配的时候,配置文件中某种类型的Bean必须是唯一的,不能出现多个。
Spring 引入外部属性配置文件
我们都知道编写数据源的时候是需要连接数据库的信息的,例如:driver、url、username、password等信息。
这些信息可以单独写到一个配置文件中,这样用户修改起来会更加的方便。
第一步:写一个数据源类,提供相关属性。
package com.powernode.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 对象的,都是数据源
* 可以把数据源交给 Spring 容器来管理
*/
public class MyDataSource implements DataSource {
private String driver;
private String url;
private String username;
private String 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;
}
@Override
public String toString() {
return "MyDataSource{" +
"driver='" + driver + '\'' +
", url='" + url + '\'' +
", username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
@Override
public Connection getConnection() throws SQLException {
// 获取数据库连接对象的时候需要 4 个信息:driver、URL、username、password
return null;
}
@Override
public Connection getConnection(String username, String password) throws SQLException {
return null;
}
@Override
public PrintWriter getLogWriter() throws SQLException {
return null;
}
@Override
public void setLogWriter(PrintWriter out) throws SQLException {
}
@Override
public void setLoginTimeout(int seconds) throws SQLException {
}
@Override
public int getLoginTimeout() throws SQLException {
return 0;
}
@Override
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
return null;
}
@Override
public <T> T unwrap(Class<T> iface) throws SQLException {
return null;
}
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
return false;
}
}
第二步:在类路径下新建 jdbc.properties文件,并配置信息。
jdbc.driverClass=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/spring6
jdbc.username=root
jdbc.password=123455
第三步:在 spring 配置文件中引入 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 http://www.springframework.org/schema/context/spring-context.xsd">
</beans>
第四步:在 spring 中配置使用 jdbc.properties 文件。
<?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 http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 引入外部的 properties 文件
-->
<context:property-placeholder location="jdbc.properties" />
<!-- 第三步:使用 ${key} 配置数据源 -->
<bean id="ds" class="com.powernode.spring6.jdbc.MyDataSource">
<property name="driver" value="${jdbc.driverClass}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
</beans>