Spring控制反转(IOC)与依赖注入(DI)

2 篇文章 0 订阅


前言

  1. Spring是一个以IOC(Inversion of Control,控制反转)和AOP(Aspect Oriented Programming,面向切面编程)为内核的框架。
  2. IOC是Spring的基础,Ioc实现的是一种控制,简单来说以前是调用new构造方法来创建对象,现在变成了使用Spring来创建对象。
  3. DI(Dependency Inject,依赖注入)就是对象的属性,已经被注入好相关值,直接使用即可。

一、控制反转(IOC)

A、B类之间需要调用到对方方法的时候,每次都需要new一个对象,这样代码就已经固定了,以后需要更改业务的时候,就需要修改代码,这样做的耦合度比较高。
解决方案:使用对象时,在程序中不要主动new产生对象,转换为由外部提供对象。
Spring提供了一个容器,称为IoC容器,用来充当IoC思想中的外部,IOC容器负责对象的创建、初始化等一系列工作,被创建或被管理的对象在IOC容器中统称为Bean。

所谓的控制反转也可以说是权限反转,把我们new对象的权利交给外部(Spring的IOC容器)去做。

2.1 XML方式

下面就举例子说明下以XML方式让IOC容器帮我们创建对象。
1.maven项目中,pom.xml文件导入Spring坐标

<!--spring-context 5.2.10-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.2.10.RELEASE</version>
</dependency>

2.定义BookDao接口

public interface BookDao {
    void save();
}

3.BookDao接口的实现类

public class BookDaoImpl implements BookDao {
    @Override
    public void save() {
        System.out.println("BookDao实现类");
    }
}

4.定义BookService接口

public interface BookService {
    void save();
}

5.定义BookService实现类

public class BookServiceImpl implements BookService {
    private BookDao bookDao = new BookDaoImpl();
    @Override
    public void save() {
        bookDao.save();
    }
}

6.再resource目录中创建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">
    <!-- 让Ioc容器创建 service 对象
     语法 <bean id="对象别名" class="类全名(不能是接口)"> Spring的Ioc容器会根据这个配置穿件对象加入容器中存储下来
    -->
    <bean id="bookService" class="com.hntou.service.impl.BookServiceImpl">
    </bean>
</beans>

5.测试类

public class BookServiceTest {
    @Test
    public void test(){
        //目标: 创建Ioc容器并获取里面的 id = "bookService" 对象
        //1.根据配置文件 applicationContext.xml 创建Ioc容器对象 ClassPathXMLApplicationContext
        //1.1 会先创建Ioc容器对象
        //1.2 自动解析类路径下的applicationContext.xml 里面的数据
        //1.3 根据<bean> 配置对象加入Ioc容器中存储
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        //2.获取id = "bookService" 对象
        BookService bookService =(BookService) context.getBean("bookService");
        //3.调用对象方法
        bookService.save();
        //4.一般不用关闭容器对象
        context.close();
    }
}

在这里插入图片描述
这里为了对比,BookDao的实现类对象用手动new的方式实现。BookService的实现类对象由IOC容器帮我们创建。
1.传统做法就是当我们业务层(Service层)需要调用数据访问层的方法的时候,就需要手动去new数据访问层(Dao/Mapper层)的对象。为什么说这样做会造成代码耦合呢,因为一个接口可以有多个实现类,当此时的业务需要用到BooDaoImpl2的时候我们就需要更改Service业务层的代码,这样做消耗是很大的,因为一旦修改代码,就需要重新编译。
2.当我们在配置文件定义Bean对象之后,Spring容器就可以帮我们创建该对象,可以看见测试类中我们并没有new BookServiceImpl的对象,而是直接从IOC容器中去获取。这样就算以后需要更换业务也只需要修改xml配置文件,而xml文件是不需要重新编译的,同时也不需要修改原来的代码,这样做降低了耦合。

2.2 注解方式

1.使用@Component定义bean

/**
 * 在指定包下类的上面使用IOC注解创建对象并加入IOC容器
 * @Compoent
 * 作用:创建对象加入IOC容器
 * 功能类似于 <bean id = "别名" class = "类全名">
 *     用法1(推荐使用): 默认别名 @Component 默认当前类名的小驼峰名字作为别名 bookDaoImpl
 *     用法2(自定义别名) @Component("自定义别名")
 */
@Component
public class BookDaoImpl implements BookDao {
    @Override
    public void save() {
        System.out.println("BookDao save....");
    }
}

@Component
public class BookServiceImpl implements BookService {

}

2.核心配置文件中通过组件扫描注解加载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"
       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 https://www.springframework.org/schema/context/spring-context.xsd">
    <!-- 开启组建扫描注解加载Bean -->
    <context:component-scan base-package="com.hntou" />
</beans>

步骤:
1.开启IOC注解扫描。
2.在指定的包下创建IOC注解创建对象加入IOC容器。
测试类

@Test
    public void test01(){
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        //根据默认的别名获取Bean
        //BookDao bookDao =(BookDao) applicationContext.getBean("bookDaoImpl");
        //根据类型获取对象,条件是IOC容器中只有一个这样类型的对象  BookDao.class 和   BookDaoImpl.class 都可以,层与层之间的调用推荐使用接口隔离
        //如果写死实现类会导致耦合高,获取接口,只需要给需要的实现类@Comonponet降低耦合,如果有多个实现类用别名区分
        BookDao bookDao = applicationContext.getBean(BookDao.class);
        bookDao.save();
    }

在这里插入图片描述

我们用xml方式去管理的Bean的时候,可以看到每个Bean都需要一个id还要指定类全名,可以看出还是比较麻烦。所以Spring中@Component注解加在需要容器创建对象的类上面,在xml文件中开启包扫描,Spring就会扫描包下所有的类如果有该注解就会帮我们创建对象并且加入IOC容器,就不用我们自己去定义id和指定类全名,默认就是当前类的小驼峰命名为id。

二、依赖注入(DI)

DI(Dependency Injection)依赖注入,在容器中建立bean与bean之间的依赖关系的整个过程,称为依赖注入。简单来说就是IOC容器在创建对象的过程中,给对象的成员变量赋值。

2.1 XML方式

从上面IOC的xml方式实现中我们可以看见,BookServiceImpl中有我们手动去new BookDaoImpl 并且赋值给了BookServiceImpl的成员变量bookDao。这种做法还是去手动new了对象赋值给成员变量,非常的耦合,所以就需要DI来解决。
依然是复用刚才(IOC_XML方式实现)的代码:
1.删除BookServiceImpl创建BookDaoImpl对象的方法。
2.提供依赖对象对应的setter方法

public class BookServiceImpl implements BookService {
    private BookDao bookDao;
   	public void setBookDao(BookDao bookDao){
		this.bookDao = bookDao;
	}
    @Override
    public void save() {
        bookDao.save();
    }
}

3.核心配置文件

<?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.hntou.dao.impl.BookDaoImpl"></bean>
    <bean id="bookService" class="com.hntou.service.impl.BookServiceImpl" >
        <property name="bookDao" ref="bookDao" />
    </bean>

在这里插入图片描述
创建IOC容器的时候,读取xml配置文件,首先就根据Bean找到创建id维bookService的BookServiceImpl对象,根据property标签属性name就会去找bookDao方法(setBookDao去掉set首字母小写),发现需要BookDao的参数,于是又根据ref找id为bookDao的Bean创建其对象,通过setter的方式给bookServiceImpl的属性bookDao注入值(赋值)。

2.2 注解方式

可以看见xml方式注入非常的麻烦,需要提供setter或者构造方法才能给属性赋值。所以Spring提供了@Autowired注解赋值。
1.使用@Autowired注解开启自动装配模式(按类型)
2.使用@Autowired注解+@Qualifier(name = “别名”)注解(按别名)
注意:@Qualifier注解无法单独使用,必须配合@Autowired注解使用

@Repository
public class BookDaoImpl implements BookDao {
    @Override
    public void save() {
        System.out.println("BookDao save....");
    }
}
@Repository
public class BookDaoImpl2 implements BookDao {
    @Override
    public void save() {
        System.out.println("BookDao2 save...");
    }
}
@Service
public class BookServiceImpl implements BookService {
    /**
     * @Autowired 自动装配注入,默认优先执行byType(会根据setter方法)自动装配,如果本类中没有提供setter方法,那么底层通过暴力反射,直接给字段赋值
     * 就是根据属性的类型到IOC容器中获取对象注入
     * 1.@Autowired 如果在容器中找不到对应类型对象注入会报错
     * 解决方案,如果找不到就注入一个null
     *  required = false 如果找不到就赋值为null
     * 2.@Autowired 如果在容器中找到多个,会根据属性名作为别名选择一个,如果匹配不上也会报错
     * @Qualifier(自定义别名)注入
     * @Resource("bookDaoImpl") //默认自动装配 byName 默认根据属性名作为别名去IOC容器中找对象进行注入
     * @Resource(type=BookDao.class) 根据自定义类型注入
     * @Resource("自定义别名")  == @Autowired + @Qualifier("自定义别名")
     */
    @Autowired(required = false)
    @Qualifier("bookDaoImpl")
    private BookDao bookDao;

    @Resource(name = "bookDaoImpl2")
    private BookDao bookdao2;

    @Override
    public String toString() {
        return "BookServiceImpl{" +
                "bookDao=" + bookDao +
                ", bookdao2=" + bookdao2 +
                '}';
    }
}
public class annotationsTest {
    @Test
    public void test01(){
        //根据配置类SpringConfig创建IOC容器
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);
        BookService bookService = applicationContext.getBean(BookService.class);
        System.out.println(bookService);
    }
}

在这里插入图片描述
Spring提供@Component注解的三个衍生注解,功能都是一样的,创建Bean对象加入IOC容器。

@Controller:用于表现层bean定义:放在web层/controoler层用于给控制层的类创建对象加入IOC容器
@Service:用于业务层bean定义:放在service业务层用于给业务层的类创建对象加入IOC容器
@Repository:用于数据层bean定义:资源、仓库、放在数据访问层(持久层)用于给数据访问层的类对象创建并加入IOC容器。

需要在配置文件加包扫描
<context:component-scan base-package=“com.hntou” />

这里我们可以看见数据访问层@Respository注解会帮我们创建其对象加入IOC容器,业务层@Service注解会帮我们创建其对象加入IOC容器。BookDaoServiceImpl的BookDao属性@Autowired注解会帮我们在容器创建Bean对象的时候自动注入值。
两个注解就可以解决之前Xml方式一大堆Bean配置,便捷了开发。

三、纯注解开发

@Component及其衍生注解还有@Autowired注解可以让IOC容器帮我们创建对象并且赋值,但是还是需要核心配置文件去配置一个context:component-scan去帮我们扫描包下的注解。所以Spring3.0开启了纯注解开发,使用Java类代替了核心配置文件。

@Configuration注解用于设定当前类为配置类
@ComponentScan注解用于设定扫描路径,此注解只能添加一次,多个数据请用数组格式

@ComponentScan(basePackages = “com.hntou”) 或者 @ComponentScan({com.htnou.service",“com.hntou.dao”,“…”})

此时就不再需要我们创建applicationContext.xml了,只需要提供一个SpringConfig的配置类加上@Configuration注解告诉Spirng这是个配置类从而代替xml。

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

/**
 * @Configuration注解用于设定当前类为配置类
 * @ComponentScan注解用于设定扫描路径,此注解只能添加一次,多个数据请用数组格式
 */
@Configuration
@ComponentScan(basePackages = "com.hntou")
public class SpringConfig {
}
public class annotationsTest {
    @Test
    public void test01(){
        //根据配置类SpringConfig创建IOC容器
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);
        BookDao bookDao = applicationContext.getBean(BookDao.class);
        bookDao.save();
    }
}

1.SpringConfig类代替applicationContext.xml文件

2.读取Spring核心配置文件初始化容器对象切换为读取Java配置类初始化容器对象

在这里插入图片描述

四、总结

1.IOC做的事情是帮我们创建Bean对象。
2.DI做的事情是为对象的属性赋值。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值