做过.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注解。

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

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

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

      1. public A(B obj)
      2. {
      3. .....
      4. }

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

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

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

      1. public class B
      2. {
      3. private B(){}
      4.  
      5. private static class BSingletonHodler
      6. {
      7. static B instance = new B();
      8. }
      9.  
      10. private static B getSingletonInstance()
      11. {
      12. return BSingletonHodler.instance;
      13. }
      14. }

      那么我们的配置应该是:

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

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

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

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

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

      1. public class A
      2. {
      3. public A(){}
      4. private B b;
      5. public B getB()
      6. {
      7. return b;
      8. }
      9. public void setB(B obj)
      10. {
      11. b = obj;
      12. }
      13. }

      那么我们需要属性注入:

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

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

      1. <bean id="objA" class="com.company.project.A">
      2. <property name="b">
      3. <bean class="com.company.project.B"></bean>
      4. </property>
      5. </bean>

      或者:

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

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

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

      1. public class A
      2. {
      3. public A(){}
      4. private Collection<B> bList;
      5. public void setBList(Collection<B> bList)
      6. {
      7. this.bList = bList;
      8. }
      9. }

      我们可以使用:

      1. <bean id="objA" class="com.company.project.A">
      2. <property name="bList">
      3. <list>
      4. <ref bean="objB"/>
      5. <ref bean="objB"/>
      6. <null/><!--插入一个空值-->
      7. </list>
      8. </property>
      9. </bean>
      10. <bean id="objB" class="com.company.project.B" scope="prototype"></bean>

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

      1. public class A
      2. {
      3. public A(){}
      4. private Map<string,B> maps;
      5. public void setMaps(Map<string,B> maps)
      6. {
      7. this.maps = maps;
      8. }
      9. }
      10. public class B{...}

      我们应该使用:

      1. <bean id="objA" class="com.company.project.A">
      2. <property name="maps">
      3. <map>
      4. <entry key="b1" value-ref="objB"/>
      5. <entry key="b2" value-ref="objB"/>
      6. </map>
      7. </property>
      8. </bean>
      9. <bean id="objB" class="com.company.project.B" scope="prototype"></bean>

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

      1. public class A
      2. {
      3. private Properties properties;
      4. public void setProperties(Properties properties)
      5. {
      6. this.properties = properties;
      7. }
      8. public A(){ ... }
      9. }

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

      1. <bean id="objA" class="com.company.project.A">
      2. <property name="properties">
      3. <props>
      4. <prop key="JOEL">STRUM</prop>
      5. <prop key="Cymbal">SRASH</prop>
      6. <prop key="Harmonica">HUM</prop>
      7. </props>
      8. </property>
      9. </bean>
    2. 使用表达式来提供装配值

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

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

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

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

      Boolean类型的常量表达式:

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

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

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

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

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

      或者其他Bean的一个方法:

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

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

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

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

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

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

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

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

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

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

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

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

      上面也可以简写为:

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

      在SpEL中使用正则表达式:

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

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

      1. package com.thoughtworks.demo.core;
      2.  
      3. public class Student {
      4. private String name;
      5. public void setName(String name)
      6. {
      7. this.name = name;
      8. }
      9. }

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

      1. <util:list id="students">
      2. <bean class="com.thoughtworks.demo.core.Student" p:name="Josen"></bean>
      3. <bean class="com.thoughtworks.demo.core.Student" p:name="Cindy"></bean>
      4. <bean class="com.thoughtworks.demo.core.Student" p:name="Baby"></bean>
      5. </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

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

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

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

      1. <util:properties id="settings" location="classpath:settings.properties">
      2. </util:properties>
      3. <bean id="man" class="com.thoughtworks.demo.core.Student">
      4. <property name="name" value="#{settings['project.name']}"></property>
      5. </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类:

      1. public class Teacher {
      2. private Student student;
      3. public void setStudent(Student student)
      4. {
      5. this.student = student;
      6. }
      7. public Student getStudent()
      8. {
      9. return this.student;
      10. }
      11.  
      12. private String name;
      13. public void setName(String name)
      14. {
      15. this.name = name;
      16. }
      17. }

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

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

      或者按照byType来自动装配:

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

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

      1. public class Teacher {
      2. private Student student;
      3. public void setStudent(Student student)
      4. {
      5. this.student = student;
      6. }
      7. public Student getStudent()
      8. {
      9. return this.student;
      10. }
      11.  
      12. private String name;
      13. public void setName(String name)
      14. {
      15. this.name = name;
      16. }
      17.  
      18. public Teacher(Student stu)
      19. {
      20. this.student = stu;
      21. }
      22. }

      我们使用constructor自动装配:

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

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

      1. public class Teacher {
      2. private Student student;
      3. @Autowired //按照 byType方式自动装配
      4. public void setStudent(Student student)
      5. {
      6. this.student = student;
      7. }
      8. public Student getStudent()
      9. {
      10. return this.student;
      11. }
      12.  
      13. @Value("Cindy") //提供常量值的注解装配
      14. private String name;
      15. public void setName(String name)
      16. {
      17. this.name = name;
      18. }
      19. }

      注意,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下加入

      1. <context:annotation-config/>

      配置节点。

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

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

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

      1. public class Teacher {
      2. private Student student;
      3. @Autowired
      4. @Qualifier("student")
      5. public void setStudent(Student student)
      6. {
      7. this.student = student;
      8. }
      9. public Student getStudent()
      10. {
      11. return this.student;
      12. }
      13.  
      14. @Value("Cindy")
      15. private String name;
      16. public void setName(String name)
      17. {
      18. this.name = name;
      19. }
      20. }

Java Spring DI之旅的更多相关文章

  1. Java Spring IOC用法

    Java Spring IOC用法 Spring IoC 在前两篇文章中,我们讲了java web环境搭建 和 java web项目搭建,现在看下spring ioc在java中的运用,开发工具为In ...

  2. Java Spring Boot VS .NetCore (二)实现一个过滤器Filter

    Java Spring Boot VS .NetCore (一)来一个简单的 Hello World Java Spring Boot VS .NetCore (二)实现一个过滤器Filter Jav ...

  3. Java Spring Boot VS .NetCore (三)Ioc容器处理

    Java Spring Boot VS .NetCore (一)来一个简单的 Hello World Java Spring Boot VS .NetCore (二)实现一个过滤器Filter Jav ...

  4. Java Spring Boot VS .NetCore (十一)自定义标签 Java Tag Freemarker VS .NetCore Tag TagHelper

    Java Spring Boot VS .NetCore (一)来一个简单的 Hello World Java Spring Boot VS .NetCore (二)实现一个过滤器Filter Jav ...

  5. Spring学习之旅(八)Spring 基于AspectJ注解配置的AOP编程工作原理初探

    由小编的上篇博文可以一窥基于AspectJ注解配置的AOP编程实现. 本文一下未贴出的相关代码示例请关注小编的上篇博文<Spring学习之旅(七)基于XML配置与基于AspectJ注解配置的AO ...

  6. Spring DI

    一.   Spring DI 依赖注入 利用spring IOC实例化了对象,而DI将实例化的对象注入到需要对象的地方,完成初始化任务. 对象由spring创建,之后再由spring给属性赋值 spr ...

  7. 4.spring di

    spring di,即依赖注入,从应用的浅显意义来讲就是对属性赋值 1.用setter赋值,在spring的applicationContext.xml配置文件的bean下的property标签 属性 ...

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

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

  9. [Java] Spring 使用

    背景 JavaEE 应用框架 基于IOC和AOP的结构J2EE系统的框架 IOC(反转控制):即创建对象由以前的程序员自己new 构造方法来调用,变成了交由Spring创建对象,是Spring的基础 ...

随机推荐

  1. 继承(JAVA)

    继承是面向对象最显著的一个特性.继承是从已有的类中派生出新的类,新的类能吸收已有类的数据属性和行为,并能扩展新的能力. 一.继承的基本语法: 在Java语言中,用extends关键字表示一个类继承了另 ...

  2. winfrom 无边框窗体移动和阴影

    无边框窗体移动: //窗体移动API [DllImport("user32.dll")] public static extern bool ReleaseCapture(); [ ...

  3. bzoj1832: [AHOI2008]聚会--LCA

    本来觉得这是一道挺水的题目,后来觉得出题人挺变态的= = 半个小时敲完后,内存超限它给我看TLE,还是0ms,后来才发现内存限制64m 然后卡了一个小时后AC了.. 题目大意是在一棵树上找三点的最短路 ...

  4. Python开发问题和解决方案汇集

    1.Sublime Text中用Tab批量替换空格Whitespace缩进:Ctrl+A全选代码,Ctrl+Shift+P打开下拉框,输入indent,找到Convert indentation to ...

  5. C# Datatable排序

    在C#中要对Datatable排序,可使用DefaultView的Sort方法.先获取Datatable的DefaultView,然后设置 得到的Dataview的sort属性,最后用视图的ToTab ...

  6. poj3292-Semi-prime H-numbers(筛法打表)

    一,题意:  一个H-number是所有的模四余一的数.(x=4*k+1)  如果一个H-number是H-primes 当且仅当它的因数只有1和它本身(除1外). 一个H-number是H-semi ...

  7. ios-WKWebView 拨打电话

    -(void)webView:(WKWebView* )webView didStartProvisionalNavigation:(WKNavigation* )navigation { NSStr ...

  8. smarty模板原理和增删改查例题

    Smarty模板:(前后端分离)原理:核心是一个类,先修改配置文件,在使用的时候引入配置文件即可,(init.inc.php)$smarty->assign("ceshi", ...

  9. Objective-C 编码建议

    Objective-C 是 C 语言的扩展,增加了动态类型和面对对象的特性.它被设计成具有易读易用的,支持复杂的面向对象设计的编程语言.它是 Mac OS X 以及 iPhone 的主要开发语言. C ...

  10. ERROR 1010 (HY000): Error dropping database (can't rmdir '.\qpweb', errno: 41) 删库失败问题的解决

    Win8 下,MySQL5.5,root 用户登录 MySQL 5.5 Command Line Client,删除 qpweb 数据,执行命令 drop database qpweb;报错信息:ER ...