一、依赖注入方式
依赖注入描述了在容器中建立bean与bean之间的依赖关系的过程,Spring便提供了两种注入方式
- setter注入
- 构造器注入
以下将展示setter注入和构造器注入分别对引用类型和简单类型的四种注入方式
环境准备
创建一个Maven模块
在pom.xml中添加如下依赖
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.9</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
在java包下添加BookDao、BookDaoImpl、UserDao、UserDaoImpl、BookService和 BookServiceImpl类
BookDao
public interface BookDao {
public void save();
}
BookDaoImpl
public class BookDaoImpl implements BookDao {
@Override
public void save() {
System.out.println("book dao save ...");
}
}
UserDao
public interface UserDao {
public void save();
}
UserDaoImpl
public class UserDaoImpl implements UserDao {
@Override
public void save() {
System.out.println("user dao save ...");
}
}
BookService
public interface BookService {
public void save();
}
BookServiceImpl
public class BookServiceImpl implements BookService {
@Override
public void save() {
System.out.println("book service save ...");
}
}
在resources中创建spring的配置文件applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
</beans>
1.setter注入
-
引用类型
在BookServiceImpl中声明userDao和bookDao属性,并提供setter方法
public class BookServiceImpl implements BookService {
private BookDao bookDao;
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void setBookDao(BookDao bookDao) {
this.bookDao = bookDao;
}
@Override
public void save() {
System.out.println("book service save ...");
bookDao.save();
userDao.save();
}
}
在applicationContext.xml配置文件中使用property标签注入
name:表示BookServiceImpl里面那两个私有变量名
ref:表示所依赖的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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="bookDao" class="com.Dao.Impl.BookDaoImpl"/>
<bean id="userDao" class="com.Dao.Impl.UserDaoImpl"/>
<bean id="bookService" class="com.Service.Impl.BookServiceImpl">
<property name="bookDao" ref="bookDao"/>
<property name="userDao" ref="userDao"/>
</bean>
</beans>
在com包下编写一个Tset测试类
public class Test {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
BookService bookService = (BookService) ctx.getBean("bookService");
bookService.save();
}
}
控制台输出
-
简单类型
在BookDaoImpl类中声明两个简单数据类型变量,并提供setter方法
public class BookDaoImpl implements BookDao {
private String databaseName;
private int connectionNum;
public void setDatabaseName(String databaseName) {
this.databaseName = databaseName;
}
public void setConnectionNum(int connectionNum) {
this.connectionNum = connectionNum;
}
@Override
public void save() {
System.out.println("book dao save :"+databaseName+","+connectionNum);
}
}
在applicationContext.xml配置文件中使用property标签注入
name:表示BookServiceImpl里面那两个私有变量名
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="bookDao" class="com.Dao.Impl.BookDaoImpl">
<property name="databaseName" value="mysql"/>
<property name="connectionNum" value="666"/>
</bean>
<bean id="userDao" class="com.Dao.Impl.UserDaoImpl"/>
<bean id="bookService" class="com.Service.Impl.BookServiceImpl">
<property name="bookDao" ref="bookDao"/>
<property name="userDao" ref="userDao"/>
</bean>
</beans>
运行Test测试类,结果如下
2.构造器注入
-
引用类型
新建一个和环境准备相同的Maven模块
然后在BookServiceImpl中配置所对应私有变量的构造方法
public class BookServiceImpl implements BookService {
private BookDao bookDao;
private UserDao userDao;
public BookServiceImpl(BookDao bookDao, UserDao userDao) {
this.bookDao = bookDao;
this.userDao = userDao;
}
@Override
public void save() {
System.out.println("book service save ...");
bookDao.save();
userDao.save();
}
}
在applicationContext.xml配置文件中配置注入
name:表示构造函数中方法传入形参的参数名,必须要保持一致
ref:表示所依赖的bean的id
<bean id="bookDao" class="com.Dao.Impl.BookDaoImpl"/>
<bean id="userDao" class="com.Dao.Impl.UserDaoImpl"/>
<bean id="bookService" class="com.Service.Impl.BookServiceImpl">
<constructor-arg name="bookDao" ref="bookDao"/>
<constructor-arg name="userDao" ref="userDao"/>
</bean>
在com包下编写Test测试类
public class Test {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
BookService bookService = (BookService) ctx.getBean("bookService");
bookService.save();
}
}
控制台输出
-
简单类型
在BookDaoImpl类中声明两个简单数据类型变量,并添加两个构造方法
public class BookDaoImpl implements BookDao {
private String databaseName;
private int connectionNum;
public BookDaoImpl(String databaseName, int connectionNum) {
this.databaseName = databaseName;
this.connectionNum = connectionNum;
}
@Override
public void save() {
System.out.println("book dao save :"+databaseName+","+connectionNum);
}
}
在applicationContext.xml配置文件中配置注入
name:表示构造函数中方法传入形参的参数名,必须要保持一致
value:表示传入的参数值
<bean id="bookDao" class="com.Dao.Impl.BookDaoImpl">
<constructor-arg name="databaseName" value="mysql"/>
<constructor-arg name="connectionNum" value="777"/>
</bean>
<bean id="userDao" class="com.Dao.Impl.UserDaoImpl"/>
<bean id="bookService" class="com.Service.Impl.BookServiceImpl">
<constructor-arg name="bookDao" ref="bookDao"/>
<constructor-arg name="userDao" ref="userDao"/>
</bean>
运行Test测试类,结果如下
由于构造器注入的name属性使用的是传入形参的参数名,当参数名发生变化时,对应的name属性也需要跟着变,耦合性还是很强,Spring提供两种了不同的方式进行解决
1.将name属性更换为type,按照类型注入
<bean id="bookDao" class="com.Dao.Impl.BookDaoImpl">
<constructor-arg type="java.lang.String" value="mysql"/>
<constructor-arg type="int" value="777"/>
</bean>
这种方式解决了构造函数形参名发生变化带来的耦合问题
但如果构造方法参数中有类型相同的参数,这种方式就不再适用
2.将name属性更换为index属性,按照索引下标注入,下标从0开始
<bean id="bookDao" class="com.Dao.Impl.BookDaoImpl">
<constructor-arg index="0" value="mysql"/>
<constructor-arg index="1" value="777"/>
</bean>
这种方式可以解决参数类型重复问题
但如果构造方法参数顺序发生变化后,这种方式也存在耦合度高的情况
3.注入方式选择
在我们实际开发过程中,我们应根据实际情况选择合适的注入方式
- 强制依赖(指对象在创建的过程中必须要注入指定的参数)使用构造器注入,使用setter注入有概率不进行注入导致null对象出现
- 可选依赖(指对象在创建过程中注入的参数可有可无)使用setter注入,灵活性强
- Spring框架倡导使用构造器,第三方框架内部大多数采用构造器注入的形式进行数据初始化,相对严谨
- 如果有必要可以两者同时使用,使用构造器注入完成强制依赖的注入,使用setter注入完成可选依赖的注入
- 实际开发过程中还要根据实际情况分析,如果受控对象没有提供setter方法就必须使用构造器注入
- 自己开发的模块推荐使用setter注入
二、依赖自动装配
依赖自动装配是指IoC容器根据bean所依赖的资源在容器中自动查找并注入到bean中的过程称为自动装配
自动装配方式
- 按类型
- 按名称
- 按构造方法
- 不启用自动装配
新建一个与上文依赖注入方式中环境准备相同的Maven模块
在BookServiceImpl中声明bookDao属性,并提供setter方法
public class BookServiceImpl implements BookService {
private BookDao bookDao;
public void setBookDao(BookDao bookDao) {
this.bookDao = bookDao;
}
@Override
public void save() {
System.out.println("book service save ...");
bookDao.save();
}
}
在applicationContext.xml配置文件中配置对应bean并添加autowire属性
byType指按类型注入,但当一个类型在IOC中有多个对象,此时就不能使用byType,需使用byName,按名称注入
按名称注入是按照BookServiceImpl中的set方法按命名规则去找对应的bean对象,如果找不到则注入null
按类型注入
<bean id="bookDao" class="com.Dao.Impl.BookDaoImpl"/>
<bean id="bookService" class="com.Service.Impl.BookServiceImpl" autowire="byType"/>
按名称注入
<bean id="bookDao" class="com.Dao.Impl.BookDaoImpl" />
<bean id="bookService" class="com.Service.Impl.BookServiceImpl" autowire="byName"/>
在com包下编写Test测试类
public class Test {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
BookService bookService = (BookService) ctx.getBean("bookService");
bookService.save();
}
}
控制台输出
对于依赖自动装配来说,有以下几点注意
- 自动装配用于引用类型依赖注入,不能对简单类型进行操作
- 使用按类型装配时(byType)必须保证容器中相同类型的bean唯一(常用)
- 使用按名称装配时(byName)必须保证容器中具有指定名称的bean,但其变量名与配置耦合
- 自动装配优先级低于setter注入与构造器注入,同时出现时自动装配配置失效
三、集合注入
常见的集合类型有数组、List、Set、Map、Properties等类型,对于这些集合,我们可以通过以下方式注入
创建一个Maven模块,在pom.xml中添加如下依赖
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.9</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
在Java包下创建BookDao和BookDaoImpl类
BookDao
public interface BookDao {
public void save();
}
BookDaoImpl
public class BookDaoImpl implements BookDao {
private int[] array;
private List<String> list;
private Set<String> set;
private Map<String, String> map;
private Properties properties;
public void setArray(int[] array) {
this.array = array;
}
public void setList(List<String> list) {
this.list = list;
}
public void setSet(Set<String> set) {
this.set = set;
}
public void setMap(Map<String, String> map) {
this.map = map;
}
public void setProperties(Properties properties) {
this.properties = properties;
}
@Override
public void save() {
System.out.println("book dao save ...");
System.out.println("遍历数组:" + Arrays.toString(array));
System.out.println("遍历List:" + list);
System.out.println("遍历Set:" + set);
System.out.println("遍历Map:" + map);
System.out.println("遍历Properties:" + properties);
}
}
在resources中创建spring的配置文件applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="bookDao" class="com.Dao.Impl.BookDaoImpl">
<!--注入数组类型数据-->
<property name="array">
<array>
<value>10</value>
<value>20</value>
<value>30</value>
</array>
</property>
<!--注入List类型数据-->
<property name="list">
<list>
<value>王大</value>
<value>王二</value>
<value>王三</value>
<value>王四</value>
</list>
</property>
<!--注入Set类型数据-->
<property name="set">
<set>
<value>lida</value>
<value>lier</value>
<value>lisan</value>
<value>lisan</value>
</set>
</property>
<!--注入Map类型数据-->
<property name="map">
<map>
<entry key="country" value="China"/>
<entry key="province" value="sichuan"/>
<entry key="city" value="chengdu"/>
</map>
</property>
<!--注入Propertise类型数据-->
<property name="properties">
<props>
<prop key="country">China</prop>
<prop key="province">sichuan</prop>
<prop key="city">chengdu</prop>
</props>
</property>
</bean>
</beans>
在com包下创建一个Test类
public class Test {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
BookDao bookDao = (BookDao) ctx.getBean("bookDao");
bookDao.save();
}
}
控制台输出
如果Set集合中注入了两个相同的参数,则会“自动去重”
四、配置管理第三方bean(Druid)
在开发过程中有时需要我们去管理第三方jar包中的类,以下展示以数据源Druid为例的配置管理
创建一个新的Maven模块,在pom.xml中导入对应的spring和druid依赖
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.9</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.16</version>
</dependency>
</dependencies>
在resources中创建spring的配置文件applicationContext.xml
通过setter注入注入数据库连接四要素
<?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">
<!--管理DruidDataSource对象-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql:///library?useSSL=false"/>
<property name="username" value="root"/>
<property name="password" value="1234"/>
</bean>
</beans>
在Java包下创建一个Test测试类
public class test {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
DataSource dataSource = (DataSource) ctx.getBean("dataSource");
System.out.println(dataSource);
}
}
控制台输出
五、加载properties文件
.properties文件是用于作为一些参数的存储,使代码可以更灵活,以键值对方式存储。可以通过开启context命名空间将外部的properties文件加载到Spring中
新建一个Maven模块
在pom.xml中导入相关依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.9</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.16</version>
</dependency>
在resources下创建一个jdbc.properties文件,添加数据库连接四要素属性及name_test
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql:///library?useSSL=false
jdbc.username=root
jdbc.password=1234
jdbc.test=name_test
在applicationContext.xml中开context命名空间
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
</beans>
在配置文件中使用context命名空间下的标签来加载properties配置文件
system-properties-mode="NEVER"表示不加载系统属性,因为<context:property-placeholder/>标签会加载系统的环境变量,而且环境变量的值会被优先加载,如果properties文件中变量名和系统的环境变量名相同,可能就会导致取不到我们所想要取的properties配置文件里面的变量
<context:property-placeholder location="jdbc.properties" system-properties-mode="NEVER"/>
如果有多个properties文件,可通过如下几种方式插入
<!--1.如果配置文件多的话,每个都需要配置-->
<context:property-placeholder location="jdbc.properties,jdbc2.properties" system-properties-mode="NEVER"/>
<!--2.*.properties代表所有以properties结尾的文件都会被加载,但是不标准-->
<context:property-placeholder location="*.properties" system-properties-mode="NEVER"/>
<!--3.标准写法,classpath:代表的是从根路径下开始查找,但是只能查询当前项目的根路径-->
<context:property-placeholder location="classpath:*.properties" system-properties-mode="NEVER"/>
<!--4.不仅可以加载当前项目还可以加载当前项目所依赖的所有项目的根路径下的properties配置文件-->
<context:property-placeholder location="classpath*:*.properties" system-properties-mode="NEVER"/>
在Java包下创建BookDao和BookDaoImpl类
BookDao
public interface BookDao {
public void save();
}
BookDaoImpl
public class BookDaoImpl implements BookDao {
private String name;
public void setName(String name) {
this.name = name;
}
@Override
public void save() {
System.out.println("book dao save ..." + name);
}
}
在applicationContext.xml配置DruidDataSource和BookDao的bean对象
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<bean id="bookDao" class="com.Dao.Impl.BookDaoImpl">
<property name="name" value="${jdbc.test}"/>
</bean>
在com包下新建Test测试类
public class Test {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
//测试Druid连接池
DataSource dataSource = (DataSource) ctx.getBean("dataSource");
System.out.println(dataSource);
System.out.println();
//测试传入数据
BookDao bookDao = (BookDao) ctx.getBean("bookDao");
bookDao.save();
}
}
控制台输出