做过.NET的人很多都用过Microsoft Enterprise Library,里面有一个Dependency injection工具Unity,我们可以使用它来实现依赖注入;什么是依赖注入呢?我个人认为依赖注入就是脱藕,当类A一个对象要引用另外一个类B对象才能完成操作时,我们说两个类之间具有依赖关系;如果类A只是通过类B实现的接口来引用类B的对象,我们说这两个类之间是松耦合的;那么我们如何通过一种更灵活的方式把类B的对象赋值给类A对象,使得类A对象根本不需要了解到B这个类的存在,这种方式叫做依赖注入。

在Java中,Spring作为开发利器,其核心就是DI和AOP;我们只需要在xml中配置好类A的对象生成过程,然后调用上下文方法,Spring就会为我们创造出一个类A的对象,至于如何把B类的一个对象创建出来并赋给类A对象的,我们不需要关心,并且类A在编码时都无需知道类B的存在,一切将由Spring自动完成。

那么我们来看看如何构造这个类A对象的创建过程,通常来讲我们把Java中需要用Spring来创建的对象都称之为Bean,而把这个创建的过程叫做装配。

  1. 如何申明Bean

    申明Bean的方式有两种,一种是通过一个或多个xml文件作为配置文件,还有一种是使用Java注解。

    我们现在主要讲前面这种方式:

    <bean id="objA" class="com.company.project.A"></bean>
    <bean id="objB" class="com.company.project.B"></bean>

    我们现在申明了两个Bean,由于类A的对象需要使用到类B的对象,如何讲类B对象告知类A对象?假如类A对象有一个构造函数需要传入类B对象的值:

    public A(B obj)
    {.....
    }

    那么我们可以使用构造函数注入:

    <bean id="objA" class="com.company.project.A"><constructor-arg ref="objB"/>
    </bean>
    <bean id="objB" class="com.company.project.B"></bean>

    如果类B只有一个单列对象,如:

    public class B
    {private B(){}private static class BSingletonHodler{static B instance = new B();}private static B getSingletonInstance(){return BSingletonHodler.instance;}
    }

    那么我们的配置应该是:

    <bean id="objB" class="com.company.project.B" factory-method="getSingletonInstance"></bean>

    注意,所有通过Spring上下文来创建的bean都是单列的,也就是说每一次通过相同的id来得到一个bean时,都得到的是相同的对象,我们可以通过xml中bean元素的scope属性来改变这种行为;

    还有一种情况,我们需要Spring在构造一个bean对象成功之后,或者在销毁一个bean之前执行这个bean的一个方法,应该这样使用:

    <bean id="objA" class="com.company.project.A" init-method="构造完成后执行的方法" destory-method="销毁之前执行的方法"><constructor-arg ref="objB"/>
    </bean>

    如果类A只是通过一个属性引用了类B的对象,而并非构造函数:

    public class A
    {public A(){}private B b;public B getB(){return b;}public void setB(B obj){b = obj;}
    }

    那么我们需要属性注入:

    <bean id="objA" class="com.company.project.A"><property name="b" ref="objB"/>
    </bean>
    <bean id="objB" class="com.company.project.B"></bean>

    或者使用一种内嵌的方式:

    <bean id="objA" class="com.company.project.A"><property name="b"><bean class="com.company.project.B"></bean></property>
    </bean>

    或者:

    <bean id="objA" class="com.company.project.A" p:b-ref="objB">
    </bean>

    采用这种方式时,应该在文件头申明xmlns:p="http://ww.springframework.org/schema/p"这个命名空间,加-ref后缀是用来告知spring应该装配一个bean,而不是一个字面量。

    如果类A不是需要的一个类B的对象,而是一个类B对象的集合,如:

    public class A
    {public A(){}private Collection<B> bList;public void setBList(Collection<B> bList){this.bList = bList;}
    }

    我们可以使用:

    <bean id="objA" class="com.company.project.A"><property name="bList"><list><ref bean="objB"/><ref bean="objB"/><null/><!--插入一个空值--></list></property>
    </bean>
    <bean id="objB" class="com.company.project.B" scope="prototype"></bean>

    如果类A接受一个Map集合:

    public class A
    {public A(){}private Map<string,B> maps;public void setMaps(Map<string,B> maps){this.maps = maps;}
    }
    public class B{...}

    我们应该使用:

    <bean id="objA" class="com.company.project.A"><property name="maps"><map><entry key="b1" value-ref="objB"/><entry key="b2" value-ref="objB"/></map></property>
    </bean>
    <bean id="objB" class="com.company.project.B" scope="prototype"></bean>

    如果类A需要装配一个properties:

    public class A
    {private Properties properties;public void setProperties(Properties properties){this.properties = properties;}public A(){ ... }
    }

    我们可以在Spring配置文件中做如下配置:

    <bean id="objA" class="com.company.project.A"><property name="properties"><props><prop key="JOEL">STRUM</prop><prop key="Cymbal">SRASH</prop><prop key="Harmonica">HUM</prop></props></property>
    </bean>

  2. 使用表达式来提供装配值

    自Spring3提供了Spring表达式语言(即SpEL)以来,我们便可以在配置文件中使用运行时执行的表达式将值装配到Bean的属性或构造器参数中。所有的SpEL都应该放置到以#{}为界定符的标记里面,如提供一个Integer常量表达式:

    <property name="message" value="The value is #{5}"></property>

    字符串常量表达式应该使用单引号或者双引号作为界定符:

    <property name="message" value="#{'This is a message'}"></property>

    Boolean类型的常量表达式:

    <property name="enabled" value="#{true}"></property>

    我们可以在SpEL中通过ID引用其他的bean:

    <property name="b" value="#{objB}"></property>

    或者引用其他Bean的一个属性:

    <property name="message" value="#{objB.message}"/>

    或者其他Bean的一个方法:

    <property name="message" value="#{objB.getMessage()}"/>

    如果上例中message属性只能接收大写字母,但是我们不能确定objB.getMessage()返回null,如果返回null,我们则不需要调用toUpperCase()方法,我们可以利用?.符号:

    <property message="message" value="#{objB.getMessage()?.toUpperCase()}"/>

    利用一个静态属性或方法的返回值对某个属性进行装配:

    <property name="pi" value="#{T(java.lang.Math).PI}"/>

    在表达式中也可以使用算数运算符:

    <property name="amount" value="#{counter.total + 5}"/>

    在表达式中使用比较操作符时,应该使用相应的文本类型,如:==(eq),<(lt),<=(le),>(gt),>=(ge),如:

    <property name="hasCapacity" value="#{counter.total le 100000}"/>

    也可以使用逻辑操作符:and or not

    有时候我们希望在某个条件为true时,SpEL表达式的求值结果为是某个值;当条件为false时,求值结果是另一个值:

    <property name="message" value="#{objB.message != null ? objB.message : 'this is a message'}"/>

    上面也可以简写为:

    <property name="message" value="#{objB.message ?: 'this is a message'}"/>

    在SpEL中使用正则表达式:

    <property name="isValid" value="#{admin.email matches '[a-zA-Z0-9._%+_]'}"/>

    SpEL作用于集合,假设我们有这样一个类:

    package com.thoughtworks.demo.core;public class Student {private String name;public void setName(String name){this.name = name;}
    }

    我们可以在Spring里面利用util:list来构建一个Student对象的List:

    <util:list id="students"><bean class="com.thoughtworks.demo.core.Student" p:name="Josen"></bean><bean class="com.thoughtworks.demo.core.Student" p:name="Cindy"></bean><bean class="com.thoughtworks.demo.core.Student" p:name="Baby"></bean>
    </util:list>

    前提是我们必须在文件头加入xmlns:util="http://www.springframework.org/schema/util"命名空间,并在xsi:schemaLocation加入了http://www.springframework.org/schema/util和http://www.springframework.org/schema/util/spring-util-2.0.xsd

    如果我们要在集合中提取一个成员,我们应该使用:

    <property name="chosenStudent" values="#{students[1]}"/>

    我们也可以使用util:properties来构造一个properties文件的bean,如:

    <util:properties id="settings" location="classpath:settings.properties">
    </util:properties>
    <bean id="man" class="com.thoughtworks.demo.core.Student"><property name="name" value="#{settings['project.name']}"></property>
    </bean>

  3. 自动装配

    自动装配的意思是我们无需指定由哪一个bean来装配,spring会按照我们指定的规则去寻找相应的bean,自动装配有4种类型:

    • byName:如果某个bean的ID与property的名字一样,则这个bean就会自动装配;
    • byType:如果某个bean的类型与property的类型一致,则这个bean会被自动装配;
    • constructor:假设通过构造器注入来装配bean,我们让spring在应用上下文中自动选择与入参类型相同的Bean注入到构造器参数中
    • autodetect:Spring首先尝试constructor自动装配,如果没有发现与构造器相匹配的Bean,Spring会尝试使用byType自动装配。

    注意,前两者是针对要装配的bean的所有property而言的,当然我们也可以为某个property提供独特的装配方案,而constructor则不行,我们不能为某个构造器入参提供独特的装配方案,假设我们有一个类Teacher引用了Student类:

    public class Teacher {private Student student;public void setStudent(Student student){this.student = student;}public Student getStudent(){return this.student;}private String name;public void setName(String name){this.name = name;}
    }

    我们按照byName的方式来完成student这个property的自动装配:

    <bean id="student" class="com.thoughtworks.demo.core.Student"><property name="name" value="Josen"></property>
    </bean>
    <bean id="teacher" class="com.thoughtworks.demo.core.Teacher" autowire="byName"><property name="name" value="Alex"/>
    </bean>

    或者按照byType来自动装配:

    <bean id="student" class="com.thoughtworks.demo.core.Student"><property name="name" value="Josen"></property>
    </bean>
    <bean id="teacher" class="com.thoughtworks.demo.core.Teacher" autowire="byType"><property name="name" value="Alex"/><!-- 注意,这里为name property提供了独特的装配方案 -->
    </bean>

    当Teacher类有一个构造函数的时候:

    public class Teacher {private Student student;public void setStudent(Student student){this.student = student;}public Student getStudent(){return this.student;}private String name;public void setName(String name){this.name = name;}public Teacher(Student stu){this.student = stu;}
    }

    我们使用constructor自动装配:

    <bean id="student" class="com.thoughtworks.demo.core.Student"><property name="name" value="Josen"></property>
    </bean>
    <bean id="teacher" class="com.thoughtworks.demo.core.Teacher" autowire="constructor"><property name="name" value="Alex"/><!-- 注意,这里为name property提供了独特的装配方案 -->
    </bean>

  4. 注解装配

    注解装配属于自动装配的范畴,如果我们为某个属性或者属性的setter方法添加了@Autowired,那么这个属性将由Spring按照byType的方式进行自动装配:

    public class Teacher {private Student student;@Autowired //按照 byType方式自动装配public void setStudent(Student student){this.student = student;}public Student getStudent(){return this.student;}@Value("Cindy") //提供常量值的注解装配private String name;public void setName(String name){this.name = name;}
    }

    注意,Spring默认禁用注解装配,所以在使用注解装配之前,应在配置文件中配置它,首先加入xmlns:context="http://www.springframework.org/schema/context"命名空间,然后在xsi:schemaLocation里面加入http://www.springframework.org/schema/context和http://www.springframework.org/schema/context/spring-context-3.0.xsd,最后在beans下加入

    <context:annotation-config/>

    配置节点。

    我们也可以使用@Autowired来注解构造器,那么Spring将按照constructor的自动注解方式完成bean的装配,假如我们注解了多个构造器,Spring将会从满足条件的构造器中选择参数最多的那个构造器

    这里有一个问题,在视同@Autowired来注解属性的时候,假如Spring找不到类型相同的bean,那么spring会抛出异常;这时我们可以使用@Autowired(required=false)方式来注解属性,假如Spring找不到类型相同的bean,则会装配一个null值

    我们也可以使用@Qualifier注解来把@Autowired的byType自动装配转化为byName自动装配,但是@Qualifier必须和@Autowired一起使用:

    public class Teacher {private Student student;@Autowired@Qualifier("student")public void setStudent(Student student){this.student = student;}public Student getStudent(){return this.student;}@Value("Cindy")private String name;public void setName(String name){this.name = name;}
    }

转载于:https://www.cnblogs.com/cdutedu/p/3636974.html

Java Spring DI之旅相关推荐

  1. Java Spring IOC用法

    Java Spring IOC用法 Spring IoC 转载于:http://www.cnblogs.com/flowwind/p/4772375.html 在前两篇文章中,我们讲了java web ...

  2. 一步一步手绘Spring DI运行时序图(Spring 自动装配之依赖注入)

    相关内容: 架构师系列内容:架构师学习笔记(持续更新) 一步一步手绘Spring IOC运行时序图一(Spring 核心容器 IOC初始化过程) 一步一步手绘Spring IOC运行时序图二(基于XM ...

  3. java毕业设计——基于java+Spring+JSP的宠物网站设计与实现(毕业论文+程序源码)——宠物网站

    基于java+Spring+JSP的宠物网站设计与实现(毕业论文+程序源码) 大家好,今天给大家介绍基于java+Spring+JSP的宠物网站设计与实现,文章末尾附有本毕业设计的论文和源码下载地址哦 ...

  4. 手写Spring DI依赖注入,嘿,你的益达!

    手写DI 提前实例化单例Bean DI分析 DI的实现 构造参数依赖 一:定义分析 二:定义一个类BeanReference 三:BeanDefinition接口及其实现类 四:DefaultBean ...

  5. Java Spring框架入门详解教程【多测师_何sir】

    Spring框架入门详解教程 spring概述 spring结构 spring IOC spring DI spring概述 Spring是一个非常活跃的开源框架, 它是一个基于IOC和AOP来构架多 ...

  6. 开启你的Spring Boot之旅 | VS Code | Maven | Spring

    开启你的Spring Boot之旅 | VS Code | Maven | Spring 如果你像我一样,受够了 Eclipse 那令人眼花的白,那你来对地方了!如果你是个学习Java或Spring的 ...

  7. java spring详解

    1.什么是java spring Spring是一个开放源代码的设计层面框架,他解决的是业务逻辑层和其他各层的松耦合问题,因此它将面向接口的编程思想贯穿整个系统应用."耦合"一般指 ...

  8. Java Spring @Scheduled 定时任务crontab表达式设置

    Java Spring @Scheduled 定时任务crontab表达式设置 1. Cron详解 2. 例子 参考 1. Cron详解 Cron表达式是一个字符串,字符串以5或6个空格隔开,分为6或 ...

  9. java spring框架 注解_史上最全的java spring注解

    史上最全的java spring注解,没有之一 注解是个好东西,但好东西我们也是看见过,整理过,理解过,用过才知道好.不求我们每个都记住,但求保有印象,在需要的时候能提取出来再查找相关资料,平时工作就 ...

最新文章

  1. 在数据库SQl中拼串
  2. .net core mvc部署到IIS导出Word 提示80070005拒绝访问
  3. Linux-rmdir命令
  4. 机器学习的核心工作流程
  5. Java程序员通用的几个框架分享
  6. boost::graph模块实现bellman的测试程序
  7. 关于虚拟机ubuntu多虚拟硬盘(VDMK)的使用方法
  8. 浅谈AJAX并实现使用pagehelper-5.1.10.jar分页插件实现异步从数据库中获取数据分页显示
  9. EOS 帐户权限操作--你找不到的干货 (原创) 续集-EOS 3.0
  10. LiveVideoStack线上交流分享 (十四) —— 深度学习在视频分析处理的实践
  11. 二分查找递归与非递归的时间比较_我们说一说Python的查找算法!
  12. Hadoop的安装与配置——搭建完全分布式集群
  13. Python检验某个字符(串)是否属于另一个字符串
  14. 多个notification引发的问题
  15. sis新地址_“这是什么梗?”,互联网上的新梗老梗如何影响你?
  16. win7计算机摄像头怎么打开,告诉你win7如何打开摄像头
  17. 微软ios服务器,iOS 客户端入门
  18. 使用mergeAssets对Android的assets文件在构建的时候进行修改处理
  19. python 泰森多边形边界_geotools中泰森多边形的生成
  20. 扫盲:集线器、网桥、交换机、路由器、网关大解析

热门文章

  1. LeetCode Algorithm 剑指 Offer 25. 合并两个排序的链表
  2. 深入理解Python生成器(Generator)
  3. 1114:白细胞计数
  4. Dsp BootLoader的学习
  5. 【机器视觉】 convert_vector_to_tuple算子
  6. 【Qt】第一个Qt程序
  7. 【Linux】一步一步学Linux——ssh命令(176)
  8. 【Linux】一步一步学Linux——Linux内核版本和发行版本(03)
  9. 【Ubuntu】 Ubuntu16.04快速搭建环境
  10. python实时数据流设计_Python读取实时数据流示例