1、什么是IOC
IOC是Inversion of Control的首字母,叫做控制反转,这不是一门什么很高大上的技术,而是一种设计思想,我们初学者要转变传统javase时候的思维。
在传统的Java应用中,一个类想要调用另一个类中的属性或者方法,一般都会在代码中先new出来一个对象,然后通过对象再去调用属性或方法,这个时候,新对象,也就是被调用者的控制权掌握在调用者身上,更为具体一点的说是掌握在我们程序员手上,下面写个简单的例子体会一下。
一般开发程序有dao层,service层,这边创建一个接口,里边有一个获取用户的方法,再写一个实现类,去实现这个接口
public interface UserDao {
void getUser();
}
public class UserDaoImpl implements UserDao{
@Override
public void getUser() {
System.out.println("默认获取用户的数据");
}
}
这是dao 层的接口和实现类,然后我们去service层写个接口跟实现类,调用dao层,然后客户端就调用service层
public interface UserService {
void getUser();
}
public class UserServiceImpl implements UserService{
private UserDao userDao = new UserDaoImpl();
@Override
public void getUser() {
userDao.getUser();
}
}
这里我们在这里new了一个userdao的实现类,然后客户端就去调userServiceImpl这个类去实现功能就可以
public class UserDAOTest {
@Test
public void test(){
//用户实际上是去调用的Service层
UserService userService = new UserServiceImpl();
userService.getUser();
}
}
通过上面这些代码会发现UserdaoImpl的对象的创建控制权是在我们程序员手上的,假如这个时候userdao有很多个实现类,因为我们不知道用户的需求是通过哪一个实现类来实现的,那么我们就需要把这些实现类的对象都私有到userServiceImpl类中,然后都得写进去,这个时候调用就会特别麻烦,这个时候我们可以通过一个set方法,来把对象创建的控制权交给客户端(也就是测试方法),假设我们这时候新增一个UserDao的实现类,注意看两段代码的区别
新增的实现类
public class UserDaoMysqlImpl implements UserDao{
@Override
public void getUser() {
System.out.println("mysql获取用户数据");
}
}
原来这里的userdao是在这里就new死了的,然后这时候我们通过一个set方法,把这个要创建的对象,你要用啥,你就给我一个相对应的对象,然后我去调用相应对象的实现方法就可以
public class UserServiceImpl implements UserService{
private UserDao userDao;
//通过set方法实现动态注入
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void getUser() {
userDao.getUser();
}
}
客户端调用方法 ,通过set方法,客户端要用哪个对象就给程序哪个对象,这时候userdao对象的创建控制权就已经是在客户端手上了,这里对象创建的控制权由服务端也就是程序员转向了客户端,也就是用户
public class UserDAOTest {
@Test
public void test(){
//用户实际上是去调用的Service层
UserService userService = new UserServiceImpl();
//bean的控制交给了service层
//使用了set动态注入,程序不再具有主动性,而是变成了被动的接收对象
((UserServiceImpl)userService).setUserDao(new UserDaoMysqlImpl());
userService.getUser();
}
}
2、IOC容器
在spring中,一个个的对象(也就是bean)的控制权是掌握在IOC容器中的,实现这个的大致步骤如下
1、程序猿通过xml配置文件、注解、java配置类等方式,对java对象进行定义,就是将一个个的对象标志成spring能够识别的bean
2、spring启动是,IOC容器会自动根据对象的定义,将这些对象创建出来并管理起来
3、当程序需要使用一些对象的时候,就去找IOC容器就可以了(通过ApplicationContext对象的getBean方法),就不需要再通过new对象的方式了。
IOC带来的最大的改变并不是代码层面的,而是思想层面的,发生了对象创建的主动被动的调换,在spring中,程序不再主动去创造对象,而是要用到的时候通过调用ioc容器,然后让ioc给到我们要用的对象
3、依赖注入(DI)
在了解了 IoC 之后,我们还需要了解另外一个非常重要的概念:依赖注入。
依赖注入(Denpendency Injection,简写为 DI),在面向对象中,对象和对象之间是存在一种叫做“依赖”的关系。简单来说,依赖关系就是在一个对象中需要用到另外一个对象,即对象中存在一个属性,该属性是另外一个类的对象。
例如,有一个名为 B 的 Java 类,它的代码如下。
public class A {
private String a;
}
public class B{
private String b;
private A a;
}
从代码可以看出,B 中存在一个 A 类型的对象属性 a,此时我们就可以说 B 的对象依赖于对象 a。而依赖注入就是就是基于这种“依赖关系”而产生的。
我们知道,控制反转核心思想就是由 Spring 负责对象的创建。在对象创建过程中,Spring 会自动根据依赖关系,将它依赖的对象注入到当前对象中,这就是所谓的“依赖注入”。
依赖注入本质上是 bean属性注入的一种,只不过这个属性是一个对象属性而已。
下面的额代码,理解一下依赖注入
首先,我们先要有一个被引用的类
public class Address {
}
然后是实现DI的类,这里我们官方文档的各种属性注入都来一遍
package com.zhang;
import java.util.*;
public class Student {
private String name; //String类型
private Address address; //引用类型
private String[] books; //数组类型
private List<String> hobby; //集合类型
private Map<String,String> card; //map
private Set<String> games; //set
private Properties properties; //prop
private String wife;
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", address=" + address.toString() +
", books=" + Arrays.toString(books) +
", hobby=" + hobby +
", card=" + card +
", games=" + games +
", properties=" + properties +
", wife='" + wife + '\'' +
'}';
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
public String[] getBooks() {
return books;
}
public void setBooks(String[] books) {
this.books = books;
}
public List<String> getHobby() {
return hobby;
}
public void setHobby(List<String> hobby) {
this.hobby = hobby;
}
public Map<String, String> getCard() {
return card;
}
public void setCard(Map<String, String> card) {
this.card = card;
}
public Set<String> getGames() {
return games;
}
public void setGames(Set<String> games) {
this.games = games;
}
public Properties getProperties() {
return properties;
}
public void setProperties(Properties properties) {
this.properties = properties;
}
public String getWife() {
return wife;
}
public void setWife(String wife) {
this.wife = wife;
}
}
下面是applicationContext.xml文件的内容,官方是这么命名文件的,只是一个规范,当然为了简单,写其他文件名也可以,例如beans.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="address" class="com.zhang.Address"/>
<bean id="student" class="com.zhang.Student">
<!--普通值注入-->
<property name="name" value="张三"/>
<!--bean注入-->
<property name="address" ref="address"/>
<!--数组注入-->
<property name="books">
<array>
<value>红楼梦</value>
<value>西游记</value>
<value>三国演义</value>
<value>水浒传</value>
</array>
</property>
<!--集合-->
<property name="hobby">
<list>
<value>sing</value>
<value>read</value>
<value>sleep</value>
</list>
</property>
<!--map-->
<property name="card">
<map>
<entry key="身份证" value="79487321759743978"/>
<entry key="银行卡" value="23783507290894385"/>
</map>
</property>
<!--set-->
<property name="games">
<set>
<value>lol</value>
<value>coc</value>
<value>王者</value>
</set>
</property>
<!--null-->
<property name="wife">
<null></null>
</property>
<!--properties-->
<property name="properties">
<props>
<prop key="学号">2020</prop>
<prop key="班级">8班</prop>
<prop key="姓名">老张</prop>
</props>
</property>
</bean>
</beans>
上面的代码对student类的各个属性都进行了注入,<bean>标签就是我们的对象,里面有id,class
下面这句话就相当于我们new对象,id 相当于对象名,class相当于对象的类
<bean id="address" class="com.zhang.Address"/>
等价于
Address address = new Address();
4、IoC 的工作原理
在 Java 软件开发过程中,系统中的各个对象之间、各个模块之间、软件系统和硬件系统之间,或多或少都存在一定的耦合关系。
若一个系统的耦合度过高,那么就会造成难以维护的问题,但完全没有耦合的代码几乎无法完成任何工作,这是由于几乎所有的功能都需要代码之间相互协作、相互依赖才能完成。因此我们在程序设计时,所秉承的思想一般都是在不影响系统功能的前提下,最大限度的降低耦合度。
IoC 底层通过工厂模式、Java 的反射机制、XML 解析等技术,将代码的耦合度降低到最低限度,其主要步骤如下。
- 在配置文件(官方:applicationContext.xml)中,对各个对象以及它们之间的依赖关系进行配置;
- 我们可以把 IoC 容器当做一个工厂,这个工厂的产品就是 Spring Bean;
- 容器启动时会加载并解析这些配置文件,得到对象的基本信息以及它们之间的依赖关系;
- IoC 利用 Java 的反射机制,根据类名生成相应的对象(即 Spring Bean),并根据依赖关系将这个对象注入到依赖它的对象中。
由于对象的基本信息、对象之间的依赖关系都是在配置文件中定义的,并没有在代码中紧密耦合,因此即使对象发生改变,我们也只需要在配置文件中进行修改即可,而无须对 Java 代码进行修改,这就是 Spring IoC 实现解耦的原理。
5、IOC创建对象的方式
看代码吧,一切尽在不言中
public class User {
private String name;
/* public User() {
System.out.println("无参构造方法");
}*/
public User(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
'}';
}
}
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">
<!-- Ioc创建对象的方式-->
<!-- 1、通过无参构造创建-->
<!-- <bean id="user" class="com.zhang.User">-->
<!-- <property name="name" value="小张"/>-->
<!-- </bean>-->
<!-- 2、下标赋值-->
<!-- <bean id="user" class="com.zhang.User">-->
<!-- <constructor-arg index="0" value="小张"/>-->
<!-- </bean>-->
<!-- 3、通过类型创建(不建议使用)-->
<!-- <bean id="user" class="com.zhang.User">-->
<!-- <constructor-arg type="java.lang.String" value="小张"/>-->
<!-- </bean>-->
<!--4、通过属性名创建bean-->
<bean id="user" class="com.zhang.User" name="user3,user4 user5;user6">
<constructor-arg name="name" value="小张" />
</bean>
<!--别名-->
<alias name="user" alias="user2"/>
<!--
id: bean的唯一标识符,相当于我们的对象名
class: bean对象对应的全限定名 包名+类名
name : 也是别名,可以取多个
-->
</beans>
测试
package com.zhang;
import com.sun.deploy.net.MessageHeader;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class UserTest {
@Test
public void test(){
//Spring容器,把所有bean丢到里面,要用什么通过ApplicationContext 对象去取
//在配置文件被加载的时候,bean就已经被初始化出来了
ApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
User user = classPathXmlApplicationContext.getBean("user2",User.class);
System.out.println(user.toString());
}
}
6、jar包
最后附上spring要导的jar包,使用maven创建项目,这里spring其实要导挺多的,像aop,bean,core等等,导下面这个就可以直接全部导进来了。
<dependencies>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.22</version>
</dependency>
<!-- https://mvnrepository.com/artifact/junit/junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
</dependencies>
完结,撒花~~~~~~~