作过.NET的人不少都用过Microsoft Enterprise Library,里面有一个Dependency injection工具Unity,咱们可使用它来实现依赖注入;什么是依赖注入呢?我我的认为依赖注入就是脱藕,当类A一个对象要引用另一个类B对象才能完成操做时,咱们说两个类之间具备依赖关系;若是类A只是经过类B实现的接口来引用类B的对象,咱们说这两个类之间是松耦合的;那么咱们如何经过一种更灵活的方式把类B的对象赋值给类A对象,使得类A对象根本不须要了解到B这个类的存在,这种方式叫作依赖注入。java
在Java中,Spring做为开发利器,其核心就是DI和AOP;咱们只须要在xml中配置好类A的对象生成过程,而后调用上下文方法,Spring就会为咱们创造出一个类A的对象,至于如何把B类的一个对象建立出来并赋给类A对象的,咱们不须要关心,而且类A在编码时都无需知道类B的存在,一切将由Spring自动完成。正则表达式
那么咱们来看看如何构造这个类A对象的建立过程,一般来说咱们把Java中须要用Spring来建立的对象都称之为Bean,而把这个建立的过程叫作装配。spring
申明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对象的值:ui
public A(B obj) { ..... }
那么咱们可使用构造函数注入:this
<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; } }
那么咱们的配置应该是:spa
<bean id="objB" class="com.company.project.B" factory-method="getSingletonInstance"></bean>
注意,全部经过Spring上下文来建立的bean都是单列的,也就是说每一次经过相同的id来获得一个bean时,都获得的是相同的对象,咱们能够经过xml中bean元素的scope属性来改变这种行为;prototype
还有一种状况,咱们须要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>
自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>
自动装配的意思是咱们无需指定由哪个bean来装配,spring会按照咱们指定的规则去寻找相应的bean,自动装配有4种类型:
注意,前二者是针对要装配的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>
注解装配属于自动装配的范畴,若是咱们为某个属性或者属性的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; } }