Java Spring DI之旅
做过.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,而把这个创建的过程叫做装配。
- 如何申明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>
- <bean id="objA" class="com.company.project.A"></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种类型:
- 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>
- 注解装配
注解装配属于自动装配的范畴,如果我们为某个属性或者属性的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;
- }
- }
- public class Teacher {
Java Spring DI之旅的更多相关文章
- Java Spring IOC用法
Java Spring IOC用法 Spring IoC 在前两篇文章中,我们讲了java web环境搭建 和 java web项目搭建,现在看下spring ioc在java中的运用,开发工具为In ...
- Java Spring Boot VS .NetCore (二)实现一个过滤器Filter
Java Spring Boot VS .NetCore (一)来一个简单的 Hello World Java Spring Boot VS .NetCore (二)实现一个过滤器Filter Jav ...
- Java Spring Boot VS .NetCore (三)Ioc容器处理
Java Spring Boot VS .NetCore (一)来一个简单的 Hello World Java Spring Boot VS .NetCore (二)实现一个过滤器Filter Jav ...
- 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 ...
- Spring学习之旅(八)Spring 基于AspectJ注解配置的AOP编程工作原理初探
由小编的上篇博文可以一窥基于AspectJ注解配置的AOP编程实现. 本文一下未贴出的相关代码示例请关注小编的上篇博文<Spring学习之旅(七)基于XML配置与基于AspectJ注解配置的AO ...
- Spring DI
一. Spring DI 依赖注入 利用spring IOC实例化了对象,而DI将实例化的对象注入到需要对象的地方,完成初始化任务. 对象由spring创建,之后再由spring给属性赋值 spr ...
- 4.spring di
spring di,即依赖注入,从应用的浅显意义来讲就是对属性赋值 1.用setter赋值,在spring的applicationContext.xml配置文件的bean下的property标签 属性 ...
- 手写Spring DI依赖注入,嘿,你的益达!
目录 提前实例化单例Bean DI分析 DI的实现 构造参数依赖 一:定义分析 二:定义一个类BeanReference 三:BeanDefinition接口及其实现类 四:DefaultBeanFa ...
- [Java] Spring 使用
背景 JavaEE 应用框架 基于IOC和AOP的结构J2EE系统的框架 IOC(反转控制):即创建对象由以前的程序员自己new 构造方法来调用,变成了交由Spring创建对象,是Spring的基础 ...
随机推荐
- 继承(JAVA)
继承是面向对象最显著的一个特性.继承是从已有的类中派生出新的类,新的类能吸收已有类的数据属性和行为,并能扩展新的能力. 一.继承的基本语法: 在Java语言中,用extends关键字表示一个类继承了另 ...
- winfrom 无边框窗体移动和阴影
无边框窗体移动: //窗体移动API [DllImport("user32.dll")] public static extern bool ReleaseCapture(); [ ...
- bzoj1832: [AHOI2008]聚会--LCA
本来觉得这是一道挺水的题目,后来觉得出题人挺变态的= = 半个小时敲完后,内存超限它给我看TLE,还是0ms,后来才发现内存限制64m 然后卡了一个小时后AC了.. 题目大意是在一棵树上找三点的最短路 ...
- Python开发问题和解决方案汇集
1.Sublime Text中用Tab批量替换空格Whitespace缩进:Ctrl+A全选代码,Ctrl+Shift+P打开下拉框,输入indent,找到Convert indentation to ...
- C# Datatable排序
在C#中要对Datatable排序,可使用DefaultView的Sort方法.先获取Datatable的DefaultView,然后设置 得到的Dataview的sort属性,最后用视图的ToTab ...
- poj3292-Semi-prime H-numbers(筛法打表)
一,题意: 一个H-number是所有的模四余一的数.(x=4*k+1) 如果一个H-number是H-primes 当且仅当它的因数只有1和它本身(除1外). 一个H-number是H-semi ...
- ios-WKWebView 拨打电话
-(void)webView:(WKWebView* )webView didStartProvisionalNavigation:(WKNavigation* )navigation { NSStr ...
- smarty模板原理和增删改查例题
Smarty模板:(前后端分离)原理:核心是一个类,先修改配置文件,在使用的时候引入配置文件即可,(init.inc.php)$smarty->assign("ceshi", ...
- Objective-C 编码建议
Objective-C 是 C 语言的扩展,增加了动态类型和面对对象的特性.它被设计成具有易读易用的,支持复杂的面向对象设计的编程语言.它是 Mac OS X 以及 iPhone 的主要开发语言. C ...
- 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 ...