● 控制反转,反转的是什么?
○ 将对象的创建权利交出去,交给第三方容器负责。
○ 将对象和对象之间关系的维护权交出去,交给第三方容器负责。
● 控制反转这种思想如何实现呢?
○ DI(Dependency Injection):依赖注入
依赖注入
依赖注入:
● 依赖指的是对象和对象之间的关联关系。
● 注入指的是一种数据传递行为,通过注入行为来让对象和对象产生关系。
依赖注入常见的实现方式包括两种:
● 第一种:set注入
● 第二种:构造注入
在第一个程序中,尽管通过Spring容器创建并管理了一个对象,但以第一章为例,表现层创建了一个Userservice控制层对象:
private Userservice userService = new UserServiceImp();
而在第二章中
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
Object userBean = applicationContext.getBean("userDaoBean");
//这两行代码完成了"new UserServiceImp"的功能,还剩另一半功能,即将容器创建的对象赋值给任意层的对象,将这个具体的张三放到工作岗位上,这另一半就是依赖注入.
set注入
set注入,基于set方法实现的,底层会通过反射机制调用属性对应的set方法然后给属性赋值。这种方式要求属性必须对外提供set方法。
新建模块:spring6-002-dependency-injection
本例中,方便起见,不再采用接口-实现类的方式,而是每一层直接定义一个抽象类.
<?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.powernode</groupId>
<artifactId>spring6-002-dependency-injection</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<repositories>
<repository>
<id>repository.spring.milestone</id>
<name>Spring Milestone Repository</name>
<url>https://repo.spring.io/milestone</url>
</repository>
</repositories>
<dependencies>
<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.13.2</version>
<scope>test</scope>
</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>
结构
resources-spring.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">
<!-- 提前规划好本次项目是2层_持久层&&控制层,并且每层是一个类,等一下直接去创建对应的包,类-->
<bean id="userDaoBean" class="com.sunsplanter.spring6.dao.UserDao"/>
<bean id="userServiceBean" class="com.sunsplanter.spring6.service.UserService">
<!-- set哪个对象(注入哪个对象),就在spring.xml中找到对应管理该对象的标签,在bean标签中增加一个<property>子标签
该子标签的name属性必须满足规范:去掉该set方法的set,剩下单词采用驼峰命名法,即mySySQLUserDao
该子标签的ref属性,指定要注入的bean的id
此处,在控制层利用spring容器创建了持久层UserDao对象,而后自定义了一个setMySQLUserDao的set方法进行注入
且注入的是持久层对象UserDao,因此ref处填userDaoBean
实现原理:
通过property标签获取到set方法:setMySQLUserDao()
通过反射机制调用setMySQLUserDao()方法给属性赋值
property标签的name是提示set方法名。
property标签的ref是要注入的bean对象的id。
(通过ref属性来完成bean的装配,这是bean最简单的一种装配方式。装配指的是:创建系统组件之间关联的动作)-->
<property name="mySQLUserDao" ref="userDaoBean"/>
</bean>
</beans>
package com.sunsplanter.spring6.dao;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class UserDao {
//log4j2可以选择输出到控制台/文件,还可以选择各种输出等级
private static final Logger logger = LoggerFactory.getLogger(UserDao.class);
public void insert(){
logger.info("saving...");
}
}
package com.sunsplanter.spring6.service;
import com.sunsplanter.spring6.dao.UserDao;
public class UserService {
//创建一个持久层对象,但这次不再依赖调用下层创建,而是用Spring容器创建,并且用Set依赖注入的方法进行赋值
private UserDao userDao;
//
//ALT+insert用Idea生成set方法
// public void setUserDao(UserDao userDao) {
// this.userDao = userDao;
// }
//自定义set方法
//要求1:必须是set开始
//要求2:set哪个对象(注入哪个对象),就在spring.xml中找到对应管理该对象的标签,在bean标签中增加一个<property>子标签
//该子标签的name属性必须满足规范:去掉该set方法的set,剩下单词采用驼峰命名法,即mySySQLUserDao
public void setMySQLUserDao(UserDao xyz){
this.userDao = xyz;
}
public void saveuser(){
userDao.insert();
}
}
package com.sunsplanter.spring6.test;
import com.sunsplanter.spring6.service.UserService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringDITest {
@Test
public void testSetDI(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
UserService userService = applicationContext.getBean("userServiceBean", UserService.class);
userService.saveuser();
}
}
总结:set注入的核心实现原理:通过反射机制调用set方法来给属性赋值,让两个对象之间产生关系。
构造注入
核心原理:通过调用构造方法来给属性赋值。
与依赖注入的核心区别:构造注入是在对象实例化的过程中注入的.
结构为:
其中,Userservice,spring_dependency.xml,SpringDITest_dependency不再使用
package com.sunsplanter.spring6.service;
import com.sunsplanter.spring6.dao.UserDao;
import com.sunsplanter.spring6.dao.VipDao;
public class CustomerService {
//先声明两个持久层的对象,然后利用spring容器构造出后,用构造方法注入
private UserDao userDao;
private VipDao vipDao;
public CustomerService(UserDao userDao, VipDao vipDao) {
//按照先后顺序,对应spring2_constructor第三个bean中0号和1号constructor标签
this.userDao = userDao;
this.vipDao = vipDao;
}
public void saveUsers(){
userDao.insert();
vipDao.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="userDaoBean" class = "com.sunsplanter.spring6.dao.UserDao"/>
<bean id="vipDaoBean" class = "com.sunsplanter.spring6.dao.VipDao"/>
<bean id="customerServiceBean" class = "com.sunsplanter.spring6.service.CustomerService">
<!-- 构造注入的标签的第一种方法
index属性指定参数下标,序号从0开始,对应着CustomerService的构造方法中的两个先后对象
ref属性指定要注入的bean的id-->
<!-- <constructor-arg index = "0" ref="userDaoBean"/>-->
<!-- <constructor-arg index = "1" ref="vipDaoBean"/>-->
<!-- 构造注入的标签的第二种方法
index属性指定参数下标,序号从0开始,对应着CustomerService的构造方法中的两个先后参数(即对象名)-->
<constructor-arg name = "userDao" ref="userDaoBean"/>
<constructor-arg name = "vipDao" ref="vipDaoBean"/>
</bean>
</beans>
package com.sunsplanter.spring6.test;
import com.sunsplanter.spring6.service.CustomerService;
import com.sunsplanter.spring6.service.UserService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringDITest_constructor {
@Test
public void testConstructorDI(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring2_constructor.xml");
CustomerService custormerService = applicationContext.getBean("customerServiceBean", CustomerService.class);
custormerService.saveUsers();
}
}