Spring Ioc依赖注入原理以及简单使用

64 篇文章 1 订阅
39 篇文章 1 订阅



一、定义

控制反转(Inversion of Control ,Ioc)

也称为依赖注入(Dependency Injection,DI)

是面向对象编程中的一种设计理念,用来降低程序代码之间的耦合度。

依赖一般指通过局部变量、方法参数、返回值等简历的对于其他对象的调用关系。

例如在A类方法中,实例化了B类的对象并调用其方法来完成特定的功能,那么我们就说A类依赖于B类

几乎所有的应用都由两个或更多的类通过合作来实现完整的功能。类与类之间的依赖关系增加了程序开发的复杂程度,我们在开发一个类的时候,还要考虑对正在使用该类的其他类的影响。例如,常见的业务调用数据访问层以实现持久化操作




二、使用场景

定义一种常见的业务层低矮用数据访问层以实现持久化操作:

/* 用户DAO接口 定义了所需的持久化方法*/
public interface UserDao{
	public void save(User user);
}

/* 用户DAO实现类,实现对User类的持久化操作*/
public class UserDaoImpl implements UserDao{
    public void save(User user){
        // 保存信息到数据库
    }
}

/* 用户业务类 实现对User功能的业务管理 */
public class UserServiceImpl implements UserService{
    // 实例化所依赖的UserDao对象
    private UserDao dao = new UserDaoImpl();
    public void addNewUser(User user);
    //调用UserDao的方法保存用户信息
    dao.save(user);
}

如上代码所示,UserServiceImp对UserDaoImpl存在依赖关系。

这样的代码很常见,但是存在一个严重的问题,即UserServiceImpl和UserDaoImpl高度耦合,如果因为需求变化需要替换UserDao的实现类,将导致UserServiceImpl中的代码随之发生修改。如此,程序将不具备优良的可扩展性和可维护性,甚至在开发中难以测试。




三、使用工厂模式简单体现控制反转

/* 增加用户DAO工厂类。负责用户DAO实例的创建工作 */
public class UserDaoFactory{
    public static UserDao getInstance(){
    // 具体实现过程略
	}
}

/* 用户业务类,实现对User功能的业务管理 */
public class UserServiceImpl implements  UserService{
    // 通过工厂类获取所依赖的用户DAO对象
    private UserDao dao = UserDaoFactory.getInstance();
    public void addNewUser(User user){
        // 调用用户DAO的方法保存用户信息
        dao.save(user);
    }
}

此处示例代码中的用户DAO工厂类UserDaoFactory体现了“控制反转”的思想。

UserServiceImpl不再依赖自身的代码去获得所依赖的具体DAO对象,而是把这一工作转交给“第三方”UserDaoFactory,从而避免了和具体UserDao实现类之间的耦合。由此可见,在如何获取所依赖的对象上,“控制权”发生了“反转”,即从UserServiceImpl转移到了UserDaoFactory,这就是“控制反转”。




四、使用Spring实现控制反转

在Spring配置文件中,使用元素来定义Bean(也可以成为组件)的实例。

Bean元素有两个常用属性:一个是id,表示定义的Bean实例的名称;另一个是class,表示定义的Bean实例的类型

IOC原理

BeanFactory源码接口:

package org.springframework.beans.factory; 
import org.springframework.beans.BeansException; 
import org.springframework.core.ResolvableType; 
public interface BeanFactory {      
    // 前缀
    String FACTORY_BEAN_PREFIX = "&"; 
    // 多个getBean方法 
    Object getBean(String name) throws BeansException;
    Object <T> T getBean(String name, Class<T> requiredType) throws BeansException; 
    <T> T getBean(Class<T> requiredType) throws BeansException; 
    Object getBean(String name, Object... args) throws BeansException; 
    <T> T getBean(Class<T> requiredType, Object... args) throws BeansException;
    // 是否包含Bean 
    boolean containsBean(String name); 
    // Bean是否单例
    boolean isSingleton(String name) throws NoSuchBeanDefinitionException; 
    // Bean是否原型 
    boolean isPrototype(String name) throws NoSuchBeanDefinitionException; 
    // 是否类型匹配 
    boolean isTypeMatch(String name, ResolvableType typeToMatch)  throws NoSuchBeanDefinitionException; 
    boolean isTypeMatch(String name, Class<?> typeToMatch)  throws NoSuchBeanDefinitionException; 
    // 获取Bean的类型 
    Class<?> getType(String name) throws NoSuchBeanDefinitionException;
    // 获取Bean的别名 
    String[] getAliases(String name); 
}

首先我们看到了多个getBean方法,这也是IoC容器最重要的方法之一,它的意义是从IoC容器中获取Bean。而从多个getBean方法中可以看到有按类型(by type)获取Bean的,也有按名称(by name)获取Bean的,这就意味着在Spring IoC容器中,允许我们按类型或者名称获取Bean

isSingleton方法则判断Bean是否在Spring IoC中为单例。这里需要记住的是在Spring IoC容器中,默认的情况下,Bean都是以单例存在的,也就是使用getBean方法返回的都是同一个对象。与isSingleton方法相反的是isPrototype方法,如果它返回的是true,那么当我们使用getBean方法获取Bean的时候,Spring IoC容器就会创建一个新的Bean返回给调用者

由于BeanFactory的功能还不够强大,因此Spring在BeanFactory的基础上,还设计了一个更为高级的接口ApplicationContext。它是BeanFactory的子接口之一,在Spring的体系中BeanFactory和ApplicationContext是最为重要的接口设计

在现实中我们使用的大部分Spring IoC容器是ApplicationContext接口的实现类,它们的关系如图所示。
在这里插入图片描述在图中可以看到,ApplicationContext接口通过继承上级接口,进而继承BeanFactory接口,但是在BeanFactory的基础上,扩展了消息国际化接口(MessageSource)、环境可配置接口(EnvironmentCapable)、应用事件发布接口(ApplicationEventPublisher)和资源模式解析接口(ResourcePatternResolver),所以它的功能会更为强大。



Java类-HelloSpring类

/* 第一个Spring,输出Hello,Spring */
public class HelloSpring{
    // 定义who属性,该属性的值将通过Spring框架进行设置
    private String who = null;
    
    public void pring(){
        System.out.println("Hello,"+this.getWho()+"!");
    }
    
    // 设置who
    public void setWho(String who){
        this.who = who ;
    }
}


Spring配置文件-application.xml

<!-- 省略 -->

<!-- 通过bean元素声明需要Spring创建的实例。该实例的类型通过class属性指定 -->
<bean class="cn.springdemo.HelloSpring" id="helloSpring">
	<!-- properties元素用来为实例的属性赋值 -->
    <!-- 此处调用了setWho()-->
    <property name="who">
    	<!-- 此处将字符串“Spring” 赋值给who属性 -->
        <value>String</value>
    </property>
</bean>

<!-- 省略 -->

经验:

1.使用<bean>元素定义一个组件时,通常需要使用id属性为其指定一个用来访问的唯一名称。如果想为Bean指定更多的别名,可以通过name属性指定,名称之间使用逗号、分号或空格进行分隔。

2.在本例中,Spring为Bean的属性赋值是通过调用属性的setter方法实现的,这种做法称为“设值注入”,而非直接为属性赋值。若属性名为who.setter方法名为setSomebody(),Spring配置文件中应写name="somebody"


使用Spring Ioc发现

实例的属性值将不再由程序中的代码来主动创建和管理,改为被动接受Spring的注入,使得组件之间以配置文件而不是硬编码的方式组织在一起




五、参考资料

书籍:SSM轻量级框架应用实战
书籍:SpringBoot2.X

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值