目录
一:Spring对IoC的实现
1. IoC 控制反转
2. 依赖注入
2.1 set注入
2.2 构造注入
3. set注入专题
3.1 注入外部Bean
3.2 注入内部Bean
3.3 注入简单类型
3.4 级联属性赋值(了解)
3.5 注入数组
3.6 注入List集合和Set集合
3.7 注入Map和Properties集合
3.8 注入null和空字符串
3.9 注入的值中含有特殊符号
4. p命名空间注入
5. c命名空间注入
6. util命名空间
7. 基于XML的自动装配(byName & byType)
7.1 根据名称(byName)自动装配
7.2 根据类型(byType)自动装配
8. Spring引入外部属性配置文件(使用context命名空间)
一:Spring对IoC的实现
前面我们已经学会了如何用spring创建管理对象,接下来就要学习如何让对象和对象产生关系,使用依赖注入!
1. IoC 控制反转
(1)控制反转是一种思想,一种新型的设计模式!
(2)控制反转是为了降低程序耦合度,提高程序扩展力,达到OCP原则,达到DIP原则。
(3)控制反转,反转的是什么?
①将对象的创建权利交出去,交给第三方容器负责。
②将对象和对象之间关系的维护权交出去,交给第三方容器负责。
(4)控制反转这种思想如何实现呢?
DI(Dependency Injection):依赖注入
2. 依赖注入
依赖注入实现了控制反转的思想!
Spring通过依赖注入的方式来完成Bean管理的。Bean管理说的是:Bean对象的创建,以及Bean对象中属性的赋值(或者叫做Bean对象之间关系的维护)。
依赖注入:
- 依赖指的是对象和对象之间的关联关系。
- 注入指的是一种数据传递行为,通过注入行为来让对象和对象产生关系。
依赖注入常见的实现方式包括两种:
- 第一种:set注入
- 第二种:构造注入
2.1 set注入
set注入,基于set方法实现的,底层会通过反射机制调用属性对应的set方法然后给属性赋值;这种方式要求属性必须对外提供set方法!
pom.xml配置
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.bjpowernode</groupId>
<artifactId>spring6-002-dependency-injection</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<!--配置多个仓库-->
<repositories>
<!--spring6里程碑的仓库-->
<repository>
<id>repository.spring.milestone</id>
<name>Spring Milestone Repository</name>
<url>https://repo.spring.io/milestone</url>
</repository>
</repositories>
<dependencies>
<!--spring context依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>6.0.0-M2</version>
</dependency>
<!--单元测试依赖-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!--log4j2的依赖-->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.19.0</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j2-impl</artifactId>
<version>2.19.0</version>
</dependency>
</dependencies>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>
</project>
log4j2.xml日志配置
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<loggers>
<root level="DEBUG">
<appender-ref ref="spring6log"/>
</root>
</loggers>
<appenders>
<console name="spring6log" target="SYSTEM_OUT">
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss SSS} [%t] %-3level %logger{1024} - %msg%n"/>
</console>
</appenders>
</configuration>
UserDao类:连接数据库的操作
package com.bjpowernode.spring6.dao;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class UserDao {
// 一般声明为常量
private static final Logger logger = LoggerFactory.getLogger(UserDao.class);
// 使用日志进行打印,用System.out.println也可以
public void insert(){
logger.info("数据库正在保存用户信息!");
}
}
UserService类:调用UserDao中的方法
①set注入的话,必须提供一个set方法;Spring容器会调用这个set方法,来给userDao属性赋值。
②这个set方法不符合javabean规范也可以,但是必须以set开头,例如:setUD也是可以的;这里我使用的是IDEA自动生成的符合javabean规范的。
package com.bjpowernode.spring6.service;
import com.bjpowernode.spring6.dao.UserDao;
public class UserService {
private UserDao userDao;
// set注入,必须提供一个set方法
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void saveUser(){
// 调用UserDao保存用户信息
userDao.insert();
}
}
spring.xml配置
①配置userDaoBean和UserService,让spring管理这两个类。
②对于UserService,想让Spring调用对应的set方法,需要配置property标签:
name属性值:set方法的方法名,去掉set,然后把剩下的单词首字母变小写
ref属性值:翻译为引用,英语单词references,后面指定的是要注入的bean的id
<?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 https://www.springframework.org/schema/util/spring-util.xsd">
<!--配置dao-->
<bean id="userDaoBean" class="com.bjpowernode.spring6.dao.UserDao"/>
<!--配置service-->
<bean id="userServiceBean" class="com.bjpowernode.spring6.service.UserService">
<property name="userDao" ref="userDaoBean" />
</bean>
</beans>
③另外,对于property标签来说,ref属性也可以采用标签的方式,但使用ref属性是多数的:
<bean id="userServiceBean" class="com.powernode.spring6.service.UserService">
<property name="userDao">
<ref bean="userDaoBean"/>
</property>
</bean>
编写测试类
package com.bjpowernode.spring6.test;
import com.bjpowernode.spring6.service.UserService;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class DITest {
@Test
public void testSetDI(){
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
UserService userServiceBean = applicationContext.getBean("userServiceBean", UserService.class);
userServiceBean.saveUser();
}
}
执行结果:
正常输出日志信息了,说明两个问题:
①spring正常创建UserDao和UserService对象了!
②spring关联了对象与对象之间的关系了!
总结:set注入的核心实现原理是通过反射机制调用set方法来给属性赋值,让两个对象之间产生关系。
2.2 构造注入
核心原理:通过调用构造方法来给属性赋值。
①set注入:是先创建对象,才能执行set方法,给属性赋值。
②构造注入:是在创建对象的同时,给属性赋值,时机是不同的。
在定义一个VipDao类
package com.bjpowernode.spring6.dao;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class VipDao {
private static final Logger logger = LoggerFactory.getLogger(VipDao.class);
public void delete(){
logger.info("正在删除信息!");
}
}
ConstructService类
构造方法注入,必须要提供一个构造方法!
package com.bjpowernode.spring6.service;
import com.bjpowernode.spring6.dao.UserDao;
import com.bjpowernode.spring6.dao.VipDao;
public class ConstructService {
private UserDao userDao;
private VipDao vipDao;
// 构造注入,必须有构造方法
public ConstructService(UserDao userDao, VipDao vipDao) {
this.userDao = userDao;
this.vipDao = vipDao;
}
public void save(){
userDao.insert();
vipDao.delete();
}
}
bean.xml配置
访问的方式有三种:使用的是constructor-arg标签
①第一种方式是根据下标index的方式,下标的顺序是构造方法参数的顺序。
②第二种方式是根据构造方法参数的名字name的方式。
③第三种方式是根据类型进行注入,不指定,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="userDaoBean" class="com.bjpowernode.spring6.dao.UserDao"/>
<bean id="vipDaoBean" class="com.bjpowernode.spring6.dao.VipDao"/>
<bean id="constructServiceBean" class="com.bjpowernode.spring6.service.ConstructService">
<!--第一种方式-->
<constructor-arg index="0" ref="userDaoBean"/>
<constructor-arg index="1" ref="vipDaoBean" />
<!--第二种方式-->
<constructor-arg name="userDao" ref="userDaoBean"/>
<constructor-arg name="vipDao" ref="vipDaoBean"/>
<!--第三种方式-->
<constructor-arg ref="userDaoBean"/>
<constructor-arg ref="vipDaoBean" />
</bean>
</beans>
编写测试
package com.bjpowernode.spring6.test;
import com.bjpowernode.spring6.service.ConstructService;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class ConstructTest {
@Test
public void testConstructDI(){
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");
ConstructService constructServiceBean = applicationContext.getBean("constructServiceBean", ConstructService.class);
constructServiceBean.save();
}
}
执行结果:
3. set注入专题
set注入和构造注入中,set注入用的比较多,所以下面就学习一下set注入的专题!
3.1 注入外部Bean
(1)在之前我们使用的案例一直就是注入外部Bean的方式!
(2)外部Bean的特点:bean定义到外面,在property标签中使用ref属性进行注入;通常这种方式是常用!
<?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 https://www.springframework.org/schema/util/spring-util.xsd">
<bean id="userDaoBean" class="com.bjpowernode.spring6.dao.UserDao"/>
<bean id="userServiceBean" class="com.bjpowernode.spring6.service.UserService">
<property name="userDao" ref="userDaoBean" />
</bean>
</beans>
3.2 注入内部Bean
内部Bean的方式:在bean标签中直接嵌套bean标签,不需要ref属性引入。
<?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 https://www.springframework.org/schema/util/spring-util.xsd">
<bean id="userServiceBean" class="com.bjpowernode.spring6.service.UserService">
<property name="userDao">
<bean class="com.bjpowernode.spring6.dao.UserDao"/>
</property>
</bean>
</beans>
3.3 注入简单类型
(1)之前在进行注入的时候,对象的属性都是另一个对象;那如果对象的属性是int类型呢?也可以通过set注入的方式给该属性赋值,实际上只要能够调用set方法就可以给属性赋值。
(2)重点:如果给简单类型赋值,就不能使用ref属性,需要使用value属性!
User类:定义了两个简单类型,写上set方法
package com.bjpowernode.spring6.bean;
public class User {
private String name;
private int age;
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
set-di.xm配置
注:既可以使用value标签的方式,也可以使用value属性的方式(常用)
<?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.bjpowernode.spring6.bean.User">
<property name="age" value="18"/>
<property name="name" value="张三"/>
</bean>
</beans>
编写测试
package com.bjpowernode.spring6.test;
import com.bjpowernode.spring6.bean.User;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SetDITest {
@Test
public void testSimpleTypeSet(){
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("set-di.xml");
User user = applicationContext.getBean("userBean", User.class);
System.out.println(user);
}
}
执行结果:默认会调用toString方法
需要特别注意:如果给简单类型赋值,使用value属性或value标签,而不是ref!
(3)那么简单类型包括哪些呢?可以通过Spring的源码来分析一下!
双击shift搜索BeanUtils类,ctrl+F12搜索isSimpleValueType方法,里面都是简单类型:
(4)这里重点说一下Date类型,如果硬要把Date类型当做简单类型,使用value赋值的话,这个日期的格式有要求:Thu Jan 12 21:05:49 CST 2023 ,所以在实际的开发中,我们一般采用ref属性的方式给Date类型的属性赋值!
(5)简单类型注入的经典案例:给数据源的属性注入值:
假设我们现在要自己手写一个数据源(能够提供Connection对象的),我们都知道所有的数据源都要实现javax.sql.DataSource接口,并且数据源中应该有连接数据库的信息,例如:driver、url、username、password等。
数据源MyDataSource
package com.bjpowernode.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;
public class MyDateSource 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 "MyDateSource{" +
"driver='" + driver + '\'' +
", url='" + url + '\'' +
", username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
@Override
public Connection getConnection() throws SQLException {
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;
}
}
spring-datasource.xml:使用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 name="myDataSource" class="com.bjpowernode.spring6.jdbc.MyDateSource">
<property name="driver" value="com.mysql.jdbc.driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/spring6"/>
<property name="username" value="root"/>
<property name="password" value="123"/>
</bean>
</beans>
编写测试
package com.bjpowernode.spring6.test;
import com.bjpowernode.spring6.bean.User;
import com.bjpowernode.spring6.jdbc.MyDateSource;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SetDITest {
@Test
public void testMyDataSource(){
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-datasource.xml");
MyDateSource myDataSource = applicationContext.getBean("myDataSource", MyDateSource.class);
System.out.println(myDataSource);
}
}
测试结果:成功注入连接数据库的信息
3.4 级联属性赋值(了解)
我们先回顾一下原来使用的注入方式,然后在使用级联属性赋值,进行对比!
clazz班级类
package com.bjpowernode.spring6.bean;
public class Clazz {
// 班级名称
private String name;
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Clazz{" +
"name='" + name + '\'' +
'}';
}
}
Student学生类
package com.bjpowernode.spring6.bean;
public class Student {
// 学生姓名
private String name;
// 班级
private Clazz clazz;
public void setName(String name) {
this.name = name;
}
public void setClazz(Clazz clazz) {
this.clazz = 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 name="clazzBean" class="com.bjpowernode.spring6.bean.Clazz">
<property name="name" value="高三一班"/>
</bean>
<bean name="studentBean" class="com.bjpowernode.spring6.bean.Student">
<property name="name" value="张三"/>
<property name="clazz" ref="clazzBean"/>
</bean>
</beans>
第二种方式:级联注入方式
使用级联属性赋值需要注意两点:
①配置的顺序不能颠倒,先配置student在配置clazz
②clazz属性必须提供getClazz()方法(所以要在Student类当中要增加getter方法)
<?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 name="studentBean" class="com.bjpowernode.spring6.bean.Student">
<property name="name" value="张三"/>
<property name="clazz" ref="clazzBean"/>
<!--级联属性赋值-->
<property name="clazz.name" value="高三一班"/>
</bean>
<bean name="clazzBean" class="com.bjpowernode.spring6.bean.Clazz" />
</beans>
执行结果:
3.5 注入数组
这里主要学习两种情况:数组中的元素是简单类型和当数组中的元素是非简单类型!
Woman类,作为非简单类型
package com.bjpowernode.spring6.bean;
public class Woman {
private String name;
@Override
public String toString() {
return "Woman{" +
"name='" + name + '\'' +
'}';
}
public void setName(String name) {
this.name = name;
}
}
QY类,里面包含简单类型和非简单类型的数组属性
package com.bjpowernode.spring6.bean;
import java.util.Arrays;
public class QY {
// 简单类型的数组
private String[] loves;
// 非简单类型的数组
private Woman[] women;
@Override
public String toString() {
return "QY{" +
"loves=" + Arrays.toString(loves) +
", women=" + Arrays.toString(women) +
'}';
}
public void setLoves(String[] loves) {
this.loves = loves;
}
public void setWomen(Woman[] women) {
this.women = women;
}
}
spring-array.xml配置
当属性是数组时,需要先使用一下array标签,在array标签中再写value和ref标签进行赋值!
<?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="w1" class="com.bjpowernode.spring6.bean.Woman">
<property name="name" value="小花"/>
</bean>
<bean id="w2" class="com.bjpowernode.spring6.bean.Woman">
<property name="name" value="小红"/>
</bean>
<!--简单类型-->
<bean id="yqBean" class="com.bjpowernode.spring6.bean.QY">
<!-- 注入简单类型-->
<property name="loves">
<array>
<value>抽烟</value>
<value>喝酒</value>
<value>烫头</value>
</array>
</property>
<!--注入非简单类型-->
<property name="women" >
<array>
<ref bean="w1"/>
<ref bean="w2"/>
</array>
</property>
</bean>
</beans>
编写测试
package com.bjpowernode.spring6.test;
import com.bjpowernode.spring6.bean.Clazz;
import com.bjpowernode.spring6.bean.QY;
import com.bjpowernode.spring6.bean.Student;
import com.bjpowernode.spring6.bean.User;
import com.bjpowernode.spring6.jdbc.MyDateSource;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SetDITest {
@Test
public void testArraySet(){
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-array.xml");
QY yqBean = applicationContext.getBean("yqBean", QY.class);
System.out.println(yqBean);
}
}
执行结果:
要点:
如果数组中是简单类型,使用value标签。
如果数组中是非简单类型,使用ref标签。
3.6 注入List集合和Set集合
Person类
package com.bjpowernode.spring6.bean;
import java.util.List;
import java.util.Set;
public class Person {
// 注入List
private List<String> names;
// 注入Set集合
private Set<String> addrs;
@Override
public String toString() {
return "Person{" +
"names=" + names +
", addrs=" + addrs +
'}';
}
public void setNames(List<String> names) {
this.names = names;
}
public void setAddrs(Set<String> addrs) {
this.addrs = addrs;
}
}
spring-collection.xml配置
如果是List集合或者Set集合的属性,需要先使用<list>标签和<set>标签,标签中再写value和ref标签进行赋值!
<?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="personBean" class="com.bjpowernode.spring6.bean.Person">
<!--List集合-->
<property name="names">
<list>
<value>张三</value>
<value>李四</value>
<value>张三</value>
<value>王五</value>
</list>
</property>
<!--Set集合-->
<property name="addrs">
<set>
<value>张三</value>
<value>李四</value>
<value>张三</value>
<value>王五</value>
</set>
</property>
</bean>
</beans>
编写测试
package com.bjpowernode.spring6.test;
import com.bjpowernode.spring6.bean.*;
import com.bjpowernode.spring6.jdbc.MyDateSource;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SetDITest {
@Test
public void testCollectionSet(){
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-collection.xml");
Person personBean = applicationContext.getBean("personBean", Person.class);
System.out.println(personBean);
}
}
执行结果:
从执行结果上看,可以得出:List集合是有序可重复、Set集合是无序不可重复!
注意:注入List集合的时候使用list标签,注入Set集合的时候使用set标签,如果集合中是简单类型使用value标签,反之使用ref标签。
3.7 注入Map和Properties集合
Properties集合本质上也是一个Map集合,但是Properties集合的key和value只能是String类型,并且注入的方式也是与Map集合不同的!
Man类
package com.bjpowernode.spring6.bean;
import java.util.Map;
import java.util.Properties;
public class Man {
// 注入Map集合
private Map<String,Integer> phones;
// 注入Properties
private Properties properties;
@Override
public String toString() {
return "Man{" +
"phones=" + phones +
", properties=" + properties +
'}';
}
public void setProperties(Properties properties) {
this.properties = properties;
}
public void setPhones(Map<String, Integer> phones) {
this.phones = phones;
}
}
spring-collection.xml配置
如果是Map集合的属性,使用map标签嵌套entry子标签(不使用ref和value标签了)
如果是Map集合的属性,使用props标签嵌套pro标签
<?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="manBean" class="com.bjpowernode.spring6.bean.Man">
<!--Map集合-->
<property name="phones">
<map>
<entry key="张三" value="123"/>
<entry key="李四" value="456"/>
<entry key="王五" value="789"/>
</map>
</property>
<!-- Properties集合-->
<property name="properties">
<props>
<prop key="driver">com.mysql.jdbc.driver</prop>
<prop key="url">jdbc:mysql://localhost:3306/spring6</prop>
</props>
</property>
</bean>
</beans>
执行结果:
要点:
对于Map集合使用<map>标签,对于Properties使用<props>标签嵌套<prop>标签完成。
如果key是简单类型,使用 key 属性,反之使用 key-ref 属性。
如果value是简单类型,使用 value 属性,反之使用 value-ref 属性。
3.8 注入null和空字符串
Cat类
package com.bjpowernode.spring6.bean;
public class Cat {
private String name;
private int age;
@Override
public String toString() {
return "Cat{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
}
set-di.xml配置
①注入空字符串使用:<value/> 或者 value=""。
②注入null使用:<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="carBean" class="com.bjpowernode.spring6.bean.Cat">
<!--注入null-->
<!--第一种方法:不为该属性赋值-->
<property name="name" value="张三"/>
<!--第二种方法:使用null标签,手动注入null-->
<property name="name">
<null/>
</property>
<property name="age" value="18"/>
<!-- 注入空字符串-->
<!--第一种方法-->
<property name="name" value=""/>
<!--第二种方法-->
<property name="name">
<value/>
</property>
<property name="age" value="20"/>
</bean>
</beans>
3.9 注入的值中含有特殊符号
(1)XML中有5个特殊字符,分别是:<、>、'、"、&
(2)以上5个特殊符号在XML中会被特殊对待,会被当做XML语法的一部分进行解析,如果这些特殊符号直接出现在注入的字符串当中,会报错。
Math类
package com.bjpowernode.spring6.bean;
public class Math {
private String result;
@Override
public String toString() {
return "Math{" +
"result='" + result + '\'' +
'}';
}
public void setResult(String result) {
this.result = result;
}
}
math.xml配置
解决方案包括两种:
第一种:特殊符号使用转义字符代替。
第二种:将含有特殊符号的字符串放到:<![CDATA[]]> 当中。因为放在CDATA区中的数据不会被XML文件解析器解析。
注:使用<![CDATA[]]>的方式只能使用value标签的形式,不能使用value属性!
注:<![CDATA[]]>是XML的语法,放在这里面的东西不会被XML解析器解析!
5个特殊字符对应的转义字符分别是:
特殊字符 | 转义字符 |
> | > |
< | < |
' | ' |
" | " |
& | & |
<?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.bjpowernode.spring6.bean.Math">
<!--直接写2<3会报错-->
<!-- <property name="result" value="2<3"/>-->
<!--第一种解决方案:使用实体符号代替特殊符号-->
<property name="result" value="2 < 3"/>
<!--第二种解决方案:<![CDATA[]]>-->
<property name="result">
<!--只能使用value标签-->
<value><![CDATA[2<3]]></value>
</property>
</bean>
</beans>
4. p命名空间注入
(1)p命名空间是简化set方法注入的。
(2)使用p命名空间注入的前提条件包括两个:
第一:在XML头部信息中添加p命名空间的配置信息:xmlns:p="http://www.springframework.org/schema/p"
第二:p命名空间注入还是基于set注入的,只不过p命名空间注入可以让spring配置变的更加简单;所以需要对应的属性提供setter方法。
Dog类:提供了setter方法
package com.bjpowernode.spring6.bean;
import java.util.Date;
public class Dog {
private String name;
private int age;
// 虽然简单类型,但是一般都是当做非简单类型对待
private Date date;
@Override
public String toString() {
return "Dog{" +
"name='" + name + '\'' +
", age=" + age +
", date=" + date +
'}';
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
public void setDate(Date date) {
this.date = date;
}
}
spring-p.xml配置
①在spring的配置文件头部添加p命名空间。
②使用:在<bean>标签中的class属性后面直接使用,对于简单类型属性赋值 p:属性名 = "属性值";对于非简单类型属性赋值p:属性名-ref = "属性值"。
<?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">
<!--原来注入的方式-->
<bean id="dogBean" class="com.bjpowernode.spring6.bean.Dog">
<property name="name" value="大黄"/>
<property name="age" value="3"/>
<property name="date" ref="nowDate"/>
</bean>
<!--p命名注入方式-->
<bean id="dogBean" class="com.bjpowernode.spring6.bean.Dog" p:name="大黄" p:age="3" p:date-ref="nowDate"/>
<bean id="nowDate" class="java.util.Date"/>
</beans>
测试程序
package com.bjpowernode.spring6.test;
import com.bjpowernode.spring6.bean.Dog;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class PTest {
@Test
public void testPTest(){
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-p.xml");
Object dogBean = applicationContext.getBean("dogBean", Dog.class);
System.out.println(dogBean);
}
}
执行结果:
如果把setter方法注释掉,会报错
5. c命名空间注入
(1)c命名空间是简化构造方法注入的。
(2)使用c命名空间的两个前提条件:
①第一:需要在xml配置文件头部添加信息: xmlns:c="http://www.springframework.org/schema/c"
②第二:需要提供构造方法。
MyTime类:提供了构造方法
package com.bjpowernode.spring6.bean;
public class MyTime {
private int year;
private int month;
private int day;
public MyTime(int year, int month, int day) {
this.year = year;
this.month = month;
this.day = day;
}
@Override
public String toString() {
return "MyTime{" +
"year=" + year +
", month=" + month +
", day=" + day +
'}';
}
}
spring-c.xml配置
①在spring的配置文件头部添加c命名空间。
②使用:c:_0 下标方式或者c:name 参数名方式。
<?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="myTimeBean" class="com.bjpowernode.spring6.bean.MyTime">
<constructor-arg index="0" value="2022"/>
<constructor-arg index="1" value="1"/>
<constructor-arg index="2" value="14"/>
</bean>
<!--c命名注入方式-->
<bean id="myTimeBean" class="com.bjpowernode.spring6.bean.MyTime" c:_0="2022" c:_1="1" c:_2="14" />
</beans>
测试程序
package com.bjpowernode.spring6.test;
import com.bjpowernode.spring6.bean.MyTime;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class CTest {
@Test
public void testCDI(){
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-c.xml");
MyTime myTimeBean = applicationContext.getBean("myTimeBean", MyTime.class);
System.out.println(myTimeBean);
}
}
执行结果:
如果把构造方法注释掉
注意:不管是p命名空间还是c命名空间,注入的时候都可以注入简单类型以及非简单类型。
6. util命名空间
(1)使用util命名空间可以让配置复用。
(2)使用util命名空间的前提是:在spring配置文件头部添加配置信息。如下:
(3)假设系统集成不同厂家的连接池,这里用自己写的数据源来代替;里面的连接数据库的配置实际上是相同的,所以我们就可以使用util命名空间进行配置复用!
数据源MyDataSource1
package com.bjpowernode.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;
public class MyDateSource1 implements DataSource {
// 连接数据库的信息,放到成员变量里
/*private String driver;
private String url;
private String username;
private String password;*/
// 当然也可以放到一个Properties集合当中
private Properties properties;
public void setProperties(Properties properties) {
this.properties = properties;
}
@Override
public String toString() {
return "MyDateSource1{" +
"properties=" + properties +
'}';
}
@Override
public Connection getConnection() throws SQLException {
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;
}
}
spring-util.xml配置:未使用util命名空间
<?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">
<!--数据源1-->
<bean id="ds1" class="com.bjpowernode.spring6.jdbc.MyDateSource1">
<property name="properties">
<props>
<prop key="dirver">com.mysql.jdbc.Driver</prop>
<prop key="url">jdbc:mysql://localhost:3306/spring6</prop>
<prop key="username">root</prop>
<prop key="password">123456</prop>
</props>
</property>
</bean>
<!--数据源2-->
<bean id="ds2" class="com.bjpowernode.spring6.jdbc.MyDateSource2">
<property name="properties">
<props>
<prop key="dirver">com.mysql.jdbc.Driver</prop>
<prop key="url">jdbc:mysql://localhost:3306/spring6</prop>
<prop key="username">root</prop>
<prop key="password">123456</prop>
</props>
</property>
</bean>
</beans>
spring-util.xml配置:使用util命名空间,把公共的配置使用util命名
使用util命名空间后,把重复的配置放到util:properties标签里面,并设置唯一标识id;后面如果想使用,直接使用ref属性直接引入id即可。
<?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命名-->
<util:properties id="prop">
<prop key="dirver">com.mysql.jdbc.Driver</prop>
<prop key="url">jdbc:mysql://localhost:3306/spring6</prop>
<prop key="username">root</prop>
<prop key="password">123456</prop>
</util:properties>
<!--数据源1-->
<bean id="ds1" class="com.bjpowernode.spring6.jdbc.MyDateSource1">
<property name="properties" ref="prop" />
</bean>
<!--数据源2-->
<bean id="ds2" class="com.bjpowernode.spring6.jdbc.MyDateSource2">
<property name="properties" ref="prop"/>
</bean>
</beans>
测试代码
package com.bjpowernode.spring6.test;
import com.bjpowernode.spring6.jdbc.MyDateSource1;
import com.bjpowernode.spring6.jdbc.MyDateSource2;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class UtilTest {
@Test
public void testUtilTest(){
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-util.xml");
MyDateSource1 ds1 = applicationContext.getBean("ds1", MyDateSource1.class);
MyDateSource2 ds2 = applicationContext.getBean("ds2", MyDateSource2.class);
System.out.println(ds1);
System.out.println(ds2);
}
}
执行结果:
实际上util命名空间主要是针对集合的:
7. 基于XML的自动装配(byName & byType)
Spring还可以完成自动化的注入,自动化注入又被称为自动装配。它可以根据名字(byName)进行自动装配,也可以根据类型(byType)进行自动装配。
UserDao类
package com.bjpowernode.spring6.dao;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class UserDao {
private static final Logger logger = LoggerFactory.getLogger(UserDao.class);
public void insert(){
logger.info("数据库正在保存用户信息!");
}
}
UserDaoService类
package com.bjpowernode.spring6.service;
import com.bjpowernode.spring6.dao.UserDao;
public class UserService {
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void saveUser(){
// 调用UserDao保存用户信息
userDao.insert();
}
}
7.1 根据名称(byName)自动装配
下面这个配置起到关键作用:
(1)UserService Bean中需要添加autowire="byName",表示通过名称进行装配。
(2)如果是正常的装配,UserDao的id随便写,只要和上面ref的值对应着就行!
(3)如果是自动装配,UserDao的id必须是UserService类中的UserDao属性对应的setUserDao(set方法)方法去掉前面的set,后面首字母变成小写的值:userDao!
(4)所以根据名称自动配置本质上也是set注入!
spring-autowire.xml配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--正常的装配-->
<bean id="userServiceBean" class="com.bjpowernode.spring6.service.UserService">
<property name="userDao" ref="userDaoBean"/>
</bean>
<bean id="userDaoBean" class="com.bjpowernode.spring6.dao.UserDao"/>
<!--根据名称自动装配-->
<bean id="userServiceBean" class="com.bjpowernode.spring6.service.UserService" autowire="byName"/>
<bean id="userDao" class="com.bjpowernode.spring6.dao.UserDao"/>
</beans>
编写测试
package com.bjpowernode.spring6.test;
import com.bjpowernode.spring6.service.UserService;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class AutowireTest {
@Test
public void testAutowireTest(){
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-autowire.xml");
UserService userServiceBean = applicationContext.getBean("userServiceBean", UserService.class);
userServiceBean.saveUser();
}
}
执行结果:
正常执行,说明如果根据名称装配(byName),底层会调用set方法进行注入!例如:setAge() 对应的名字是age,setPassword()对应的名字是password,setEmail()对应的名字是email。
7.2 根据类型(byType)自动装配
(1)其实无论是根据名称自动装备byName还是根据类型制动装备byType,在装配的时候都是基于set方法的,所以set方法是必须要提供的!
(2)根据byType自动装配时,对于被注入的对象,只需要使用bean标签指定要注入的类型,不需要再指定id。
spring-autowire.xml配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--根据类型自动装备-->
<bean id="userServiceBean" class="com.bjpowernode.spring6.service.UserService" autowire="byType"/>
<bean class="com.bjpowernode.spring6.dao.UserDao"/>
</beans>
执行结果:
如果byType根据类型装配时,如果配置文件中有两个类型一样的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="x" class="com.bjpowernode.spring6.dao.UserDao"/>
<bean id="y" class="com.bjpowernode.spring6.dao.UserDao"/>
<bean id="userServiceBean" class="com.bjpowernode.spring6.service.UserService" autowire="byType"/>
</beans>
执行结果:
测试结果说明了,当byType进行自动装配的时候,配置文件中某种类型的Bean必须是唯一的,不能出现多个!
8. Spring引入外部属性配置文件(使用context命名空间)
我们都知道编写数据源的时候是需要连接数据库的信息的,例如:driver、url、username password等信息。这些信息可以单独写到一个属性配置文件中吗?这样用户修改起来会更加的方便,当然是可以的,使用context命名空间!
第一步:写一个数据源类,提供相关属性
package com.bjpowernode.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;
public class MyDateSource 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 "MyDateSource{" +
"driver='" + driver + '\'' +
", url='" + url + '\'' +
", username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
@Override
public Connection getConnection() throws SQLException {
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文件,并配置信息
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/spring6
username=root
password=123456
第三步:在spring-properties.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 http://www.springframework.org/schema/context/spring-context.xsd">
</beans>
第四步:在spring中配置使用jdbc.properties文件
第一步:引入context命名空间,前面已经引过了。
第二步:使用<context:property-placeholder>标签的location属性来指定属性配置文件的路径。 location默认从类的根路径下开始加载资源。
<?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"/>
<bean id="dataSource" class="com.bjpowernode.spring6.jdbc.MyDateSource">
<!--使用$元符号,${key}-->
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</bean>
</beans>
测试程序:
package com.bjpowernode.spring6.test;
import com.bjpowernode.spring6.jdbc.MyDateSource;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class JDBCPropertiesTest {
@Test
public void testProperties(){
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-properties.xml");
MyDateSource dataSource = applicationContext.getBean("dataSource", MyDateSource.class);
System.out.println(dataSource);
}
}
执行结果:
这里username怎么不是我们配置文件里的?spring通过${}加载,默认是是先加载windows系统的环境变量!
怎么解决?一般在所有配置前面加上jdbc前缀
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/spring6
jdbc.username=root
jdbc.password=123456
执行结果: