前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Spring IOC原理

Spring IOC原理

作者头像
chenchenchen
发布2021-09-06 14:29:08
3530
发布2021-09-06 14:29:08
举报
文章被收录于专栏:chenchenchenchenchenchen
  • 回顾以前对象依赖
  • Spring依赖注入
  • IOC控制反转

回顾以前对象依赖

传统的实现,需要自己new对象,然后给对象属性赋值,一旦有改动,特别麻烦。

代码语言:javascript
复制
class  UserService{
    UserDao userDao = new UserDao();
}

后来,使用DaoFactory,通过字符串创建对应的对象。

代码语言:javascript
复制
    private UserDao userDao = DaoFactory.getInstance().createDao("zhongfucheng.dao.impl.UserDaoImpl", UserDao.class);
    

public class DaoFactory {

    private static final DaoFactory factory = new DaoFactory();
    private DaoFactory(){}

    public static DaoFactory getInstance(){
        return factory;
    }

    public <T> T createDao(String className,Class<T> clazz){
        try{
            T t = (T) Class.forName(className).newInstance();
            return t;
        }catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

}

再后来,我们在DaoFactory中读取配置文件,根据配置文件来创建对象

代码语言:javascript
复制
    UserDao dao = DaoFactory.getInstance().createUserDao();
    
    
public class DaoFactory {

    private  UserDao userdao = null;

    private DaoFactory(){
        try{
            InputStream in = DaoFactory.class.getClassLoader().getResourceAsStream("dao.properties");
            Properties prop = new Properties();
            prop.load(in);

            String daoClassName = prop.getProperty("userdao");
            userdao = (UserDao)Class.forName(daoClassName).newInstance();

        }catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private static final DaoFactory instance = new DaoFactory();

    public static DaoFactory getInstance(){
        return instance;
    }


    public UserDao createUserDao(){
        return userdao;
    }

}

Spring依赖注入

对象之间的依赖关系,其实就是给对象上的属性赋值!因为对象上有其他对象的变量,因此存在了依赖。

Spring提供了好几种的方式来给属性赋值

  • 构造函数注入,实现强制依赖。
  • 属性注入setter()方法,实现可选依赖。
  • 自动装配(xml/注解)。
  • 工厂方法接口注入,遗留方法。

构造函数给属性赋值

在serice中加入一个构造函数,参数就是userDao

代码语言:javascript
复制
public class UserService {

    private UserDao userDao; 

    public UserService(UserDao userDao) {
        this.userDao = userDao;
    }
}

applicationContext.xml配置文件

代码语言:javascript
复制
    <!--创建userDao对象-->
    <bean id="userDao" class="UserDao"/>

    <!--创建userService对象-->
    <bean id="userService" class="UserService">
        <!--要想在userService层中能够引用到userDao,就必须先创建userDao对象-->
        <constructor-arg index="0" name="userDao" type="UserDao" ref="userDao"></constructor-arg>
    </bean>

通过set方法给属性注入值

在service层通过set方法来把userDao注入到UserService中

代码语言:javascript
复制
public class UserService {

    private UserDao userDao; 

    public void setUserDao(String userId,String userName) {
    	UserDao userDao = new UserDao();
    	userDao.setId(userId);
    	userDao.setName(userName);
        this.userDao = userDao;
    }
}

applicationContext.xml配置文件:通过property节点来给属性赋值,引用类型使用ref属性,基本类型使用value属性。

代码语言:javascript
复制
    <!--创建userDao对象-->
    <bean id="userDao" class="UserDao"/>

    <!--创建userService对象-->
    <bean id="userService" class="UserService">
        <property name="userDao" ref="userDao"/>
    </bean>

内部Bean

先创建userService,发现userService需要userDao的属性,再创建userDao。缺点是如果Bean要多次被使用的话,就要多次配置。

applicationContext.xml配置文件:property节点内置bean节点

代码语言:javascript
复制
    <!--
        1.创建userService,看到有userDao这个属性
        2.而userDao这个属性又是一个对象
        3.在property属性下又内置了一个bean
        4.创建userDao
    -->
    <bean id="userService" class="UserService">
        <property name="userDao">
            <bean id="userDao" class="UserDao"/>
        </property>
    </bean>

p 名称空间注入属性值

applicationContext.xml配置文件:使用p名称空间

代码语言:javascript
复制
    <bean id="userDao" class="UserDao"/>

    <!--不用写property节点了,直接使用p名称空间-->
    <bean id="userService" class="UserService" p:userDao-ref="userDao"/>

自动装配

自动装载默认是不打开的,修改applicationContext.xml配置文件使其生效。

byName:通过参数名自动装配,装配和该bean的属性具有相同名字的bean。

代码语言:javascript
复制
    <bean id="userDao" class="UserDao"/>

    <!--
        1.通过名字来自动装配
        2.发现userService中有个叫userDao的属性
        3.看看IOC容器中没有叫userDao的对象
        4.如果有,就装配进去
    -->
    <bean id="userService" class="UserService" autowire="byName"/>

byType:通过参数类型自动装配,装配和该bean的属性具有相同类型的bean。如果有多个bean符合条件,则抛出错误。

代码语言:javascript
复制
    <bean id="userDao" class="UserDao"/>

    <!--
        1.通过名字来自动装配
        2.发现userService中有个叫userDao的属性
        3.看看IOC容器UserDao类型的对象
        4.如果有,就装配进去
    -->
    <bean id="userService" class="UserService" autowire="byType"/>

@Resource默认通过反射按名称装配,找不到才按类型装配。 使用name属性指定byName策略,使用type属性指定byType策略。

@Autowired注解来实现自动装配。 默认按类型装配。结合@Qualifier(“name”)按照名称装配。required属性为false时允许null值。

  • 写在属性上,先根据属性的类型(byType)去Spring容器找对应类型的bean,找到多个再根据属性的名字确定,再通过反射的方式赋值给这个属性。
  • 写在setter方法上,会根据setter方法的参数类型去找bean, 找到多个再根据属性的名字筛选,再调用set方法进行赋值。
代码语言:javascript
复制
@Component
public class UserService {

    @Autowired
    private UserDao userDao ;

//    @Autowired
//    public void setUserDao(UserDao userDao) {
//        this.userDao = userDao;
//    }
}

IOC控制反转

**Spring使用依赖注入来实现对象之间的依赖关系 ** DI dependency injection。

Spring将对象的创建交给外部容器完成称为控制反转 IOC Inversion of Control。

这个外部容器就是IOC容器

使用IOC的优点:

  1. 实现代码之间的解耦,提高程序的灵活性和可维护性。
  2. IOC容器支持加载服务时的饿汉式初始化和懒加载。
  3. 对象不用自己组装,拿来就用。
  4. 享受单例的好处,效率高,不浪费空间。
  5. 统一配置,便于修改。
  6. 便于单元测试,方便切换mock组件。
  7. 便于进行AOP操作,对于使用者是透明的。

缺点:对象生成因为是使用反射编程,在效率上有损耗

IOC容器的原理

IOC容器是个Bean工厂,管理我们所有的对象以及依赖关系。

  • 通过Java的反射技术获取类的所有信息(成员变量、类名等)
  • 通过配置文件(xml)或者注解来描述类与类之间的关系
  • 最后构建出对应的对象和依赖关系

Spring IOC容器实现对象的创建和依赖的步骤:

  1. 根据Bean配置信息在容器内部创建Bean定义注册表
  2. 根据注册表加载、实例化bean、建立Bean与Bean之间的依赖关系
  3. 将这些准备就绪的Bean放到Map缓存池中,等待应用程序调用,getBean获得实例

IOC容器装配Bean方法

IOC容器是工厂模式的一个实现,提供了控制反转功能,用来把应用的配置和依赖从真正的应用代码中分离。可简单分成两种:

  • BeanFactory
    • 这是最基础、面向Spring的
  • ApplicationContext
    • 建立在 BeanFactory 基础之上,提供抽象的面向应用的服务。
      • MessageSource,提供国际化的消息服务,扩展类 MessageResource 接口
      • 资源访问,如 URL 和文件
      • 事件传播
      • 载入多个(有继承关系)上下文,使得每一个上下文都专注于一个特定的层次,比如应用的 web 层。

区别:

  • BeanFactory:是延迟加载,如果 Bean 的某一个属性没有注入,BeanFactory 加载后,直至第一次使用调用 getBean 方法才会抛出异常
  • ApplicationContext:则在初始化自身时检验,这样有利于检查所依赖属性是否注入;所以通常情况下使用 ApplicationContext。

参考:

Spring入门这一篇就够了:https://mp.weixin.qq.com/s/gGzgLaiTzpGemPw_h_r-Hw

Spring【依赖注入】就是这么简单:https://mp.weixin.qq.com/s/7eAnJw10VHdt6N5ZteTMhg

Spring IOC知识点一网打尽:https://mp.weixin.qq.com/s/TYftOpe6J1Y4EDNDxKKqXg

Spring思维导图(ioc篇):https://segmentfault.com/a/1190000017348726

Spring的IOC,属性注入,bean的理解:https://blog.csdn.net/weixin_41737632/article/details/109615913

SpringIOC和AOP原理 设计模式:https://www.jianshu.com/p/40d0a6ad21d2

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2021-06-13 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 回顾以前对象依赖
  • Spring依赖注入
    • 构造函数给属性赋值
      • 通过set方法给属性注入值
        • 自动装配
        • IOC控制反转
          • IOC容器的原理
          相关产品与服务
          容器服务
          腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档