Spring要把xml配置中bean的属性实例化为具体的bean,"依赖注入"是关卡。所谓的"依赖注入",就是把应用程序对bean的属性依赖都注入到spring容器中,由spring容器实例化bean然后交给程序员。spring的依赖注入有属性注入、构造函数注入、工厂方法注入等多种方式,下面用几个简单的栗子来一一道来。
一、首先是属性注入:
代码001 1 <?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="car" class="com.mesopotamia.AttrInject.Car" lazy-init="default">
<property name="brand">
<value>红旗CA72</value>
</property>
</bean>
</beans>
代码001表示配置了汽车的brand属性。对应的bean:
代码002
1 package com.mesopotamia.AttrInject; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; public class Car {
public String brand;
private Log log=LogFactory.getLog(Car.class); public Car(){
log.info("加载Car构造函数。。"); }
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
} }
main:
//代码003 public class Main {
private static Log log=LogFactory.getLog(Main.class);
public static void main(String args[]){
ApplicationContext ctx = new ClassPathXmlApplicationContext("com/mesopotamia/AttrInject/*.xml");
Car car=ctx.getBean("car",Car.class);
log.info("初始化Car,brand="+car.brand);
}
}
执行结果:
<!--代码004-->
-- ::, INFO [main] (AbstractApplicationContext.java:) - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@1ff5ea7: startup date [Mon Nov :: CST ]; root of context hierarchy
-- ::, INFO [main] (XmlBeanDefinitionReader.java:) - Loading XML bean definitions from file [C:\MySoftware\workspace\SpringTest\WebRoot\WEB-INF\classes\com\mesopotamia\AttrInject\beans.xml]
-- ::, INFO [main] (DefaultListableBeanFactory.java:) - Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@d19bc8: defining beans [car]; root of factory hierarchy
-- ::, INFO [main] (Car.java:) - 加载Car构造函数。。
-- ::, INFO [main] (Main.java:) - 初始化Car,brand=红旗CA72
代码001中的property是属性,多个属性往后接着添加就行。由于我妈喊我回家吃饭了,就捡要紧的讲一下:
- 代码001中,name与value分别对应什么,结合代码002一目了然,我不语。
- name书写有一定的规范,比如你写个cARBrand可能会出问题。前两个字母,要么全部大写,要么全部小写。
- value中不能掺杂xml的特殊符号,如:& < > “ ‘ 如果必须要写这些字符,要用<![CDATA[XXX]]>来转义。
- value里面如果要设置null值,不能什么都不写,代码001的7-9行要变为:<property name="brand"><null/></property>
- 假如bean类是Vehicle,Vehicle类有一个属性是Car,那么你想直接在Vehicle的配置文件中定义Car的brand属性,就酱紫:<property name="car.brand" value="xxx"/>
- 如果是集合类型,配置里面要这样写:
<property name="favorites">
<set>
<value>抽烟</value>
<value>喝酒</value>
<value>烫头</value>
</set>
</property>下雨天这样的写法跟bean中的set属性这么配,你应该能看懂吧?(自己换成list再测测)
- 如果是哈希马噗属性呢,是HashMap,不好意思。那么你就这么干:
<property name="weekends">
<map>
<entry >
<key>
<value>Saturday</value>
</key>
<value>约妹子</value>
</entry>
<entry>
<key>
<value>Sunday</value>
</key>
<value>发呆</value>
</entry>
</map>
</property>你应该写过不少java代码了,对于map的entry、key、value之类的东西应该信手拈来了吧?(稍微注释一下吧,就是说java代码中如果bean的一个属性是map类型的,那么这个属性在xml中配置的时候就按上面的格式,entry、key、value是什么你应该清楚了吧?)
- 还有一种类型叫Properties,虾米?它是一种特殊的Map,键值都是String类型。遇到它,又有花样:
<property name="mails">
<props>
<prop key="jobMail">john-office@baobaotao.com</prop>
<prop key="lifeMail">john-life@baobaotao.com</prop>
</props>
</property>哎呦,脱了马甲就不认识啦?
二、OK,属性注入大概就酱紫,下面介绍构造函数注入:
在代码002中的第11行,我如果直接打印brand,打印出来必然是null。因为实例化不等于初始化,这个时候,属性都还没有赋值(实例化只是跑了个构造函数,除非你在bean的构造函数中就给属性附了值,否则属性就都没初始化。)。构造函数的注入可以让属性在实例化的过程中就有了值(其实就相当于java在bean的构造函数中给属性赋值,只不过现在是在xml配置中实现)。来,走你:
代码005 1 <?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="car3" class="com.mesopotamia.AttrInject.Car3">
<constructor-arg type="java.lang.String">
<value>红旗CA72</value>
</constructor-arg>
<constructor-arg type="double">
<value>20000</value>
</constructor-arg>
</bean>
</beans>
//代码006
package com.mesopotamia.AttrInject; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; public class Car3 {
private String brand;
private double price;
private Log log=LogFactory.getLog(Car3.class); public Car3(String brand,double price){
this.brand=brand;
this.price=price;
log.info("加载Car3构造函数,加载后,brand="+brand+" & price="+price); }
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public void setPrice(double price) {
this.price = price;
}
public double getPrice() {
return price;
} }
//代码007
package com.mesopotamia.AttrInject; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext; public class Main {
private static Log log=LogFactory.getLog(Main.class);
public static void main(String args[]){
ApplicationContext ctx = new ClassPathXmlApplicationContext("com/mesopotamia/AttrInject/*.xml");
Car3 car=ctx.getBean("car3",Car3.class);
log.info("初始化Car,brand="+car.getBrand());
}
}
还是老模式,代码005是配置,代码006是bean,代码007跑起来(这三大块基本就是spring运转的三座擎天柱了)。观察代码005的7-12行,对比代码006的构造函数,应该可以对号入座吧?在代码002的第11行如果你打印brand属性值,是空的,null的。但是在代码006的第15行,你会发现它是有值的,这就是构造函数的注入起到的效果。来看打印的结果:
代码008
5 2015-11-09 22:52:18,573 INFO [main] (XmlBeanDefinitionReader.java:315) - Loading XML bean definitions from file [C:\MySoftware\workspace\SpringTest\WebRoot\WEB-INF\classes\com\mesopotamia\AttrInject\beans3.xml]
2015-11-09 22:52:18,624 INFO [main] (DefaultListableBeanFactory.java:555) - Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@1e8a1f6: defining beans [car,car3]; root of factory hierarchy
2015-11-09 22:52:18,705 INFO [main] (Car3.java:14) - 加载Car3构造函数,加载后,brand=红旗CA72 & price=20000.0
2015-11-09 22:52:18,708 INFO [main] (Main.java:14) - 初始化Car,brand=红旗CA72
我只把有用的日志抠出来了。注意代码008的第7行的结果。
村民们请注意了,从代码005可以看出,spring是通过入参(形参)的类型来作为区分的(一个是java.lang.String,一个是double),那么,如果某bean的构造函数两个入参的类型相同肿么办?换做你,自然而然的就能想到来个index就完事儿了。木有错,这个时候,给代码005的两个constructor-arg分别加属性index="0",index="1"就好了。在有些情况下,可能要两种方式结合起来使用,双管齐下才能药到病除,自己举个栗子玩玩。
三、下面开始介绍工厂方法的注入:
工厂类负责创建一个或者多个bean实例,调用工厂方法即可获取该实例。就是说,我们想要使用某个Bean类,这个Bean类在工厂中就已经实例化好了,只需要调用工厂把实例拿出来就行了。再换言之,一旦在XML中注册了某工厂,那么调用工厂的过程中已经把该Bean实例化了。算了,说不清,道不明,上干货:
代码009 1 <?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="car1" factory-bean="carFactory" factory-method="createHongQiCar"/>
<bean id="carFactory" class="com.mesopotamia.AttrInject.CarFactory" />
</beans>
看6、7行,聪明的你一定会看出第7行是Factory的Bean配置(也就是说,CarFactory这个java写的工厂类本身也是一个bean ),而第6行的factory-bean指向第7行的id,第6行的factory-method就是Factory要调用的方法。看factory类:
package com.mesopotamia.AttrInject; public class CarFactory {
public Car createHongQiCar(){
Car car = new Car();
car.setBrand("红旗CA72");
return car;
} }
直接在main中调用createHongQiCar就可以获取car的实例。(当然,在这之前你必须先像代码007第13、14行那样获取CarFactory实例。) 代码009举的是非静态方法的栗子,如果工厂内的方法是静态的,在配置文件中就不需要6-7两行了,直接像下面这样:
<bean id="car2" class="com.mesopotamia.AttrInject.CarFactory"
factory-method="createCar"></bean>
class直接指定工厂类,然后调用factory-method就可以了。
三、XML配置的简化:
像这样:
<bean id="car" class="com.mesopotamia.AttrInject.Car2" lazy-init="default">
<property name="brand">
<value>红旗CA72</value>
</property>
</bean>
麻烦。简单点:
<bean id="car" class="com.mesopotamia.AttrInject.Car2" lazy-init="default">
<property name="brand" value="红旗CA72"/>
</bean>
你注意观察,它直接把property的内置标签value变成proterty的属性来用了。(请注意我这样的描述,有助于你深入理解)
忘记说个东西,配置文件中的lazy-init="default"或者="false",意思是,该bean在spring容器启动时就实例化。而lazy-init="true"表示该bean在spring容器启动时不会实例化,而在需要这个bean时才实例化。(皇帝是一开始就让某位大将拥有兵权还是在准备打仗的时候才把兵权交给他)。
再来看个栗子:
<property name="jobs">
<map>
<entry >
<key>
<value>AM</value>
</key>
<value>会见客户</value>
</entry>
<entry>
<key>
<value>PM</value>
</key>
<value>公司内部会议</value>
</entry>
</map>
</property>
麻烦。简单点:
<property name="jobs">
<map>
<entry key="AM" value="会见客户"/>
<entry key="PM" value="公司内部会议"/>
</map>
</property>
把entry的两个子标签变成了entry的属性。
但是这还不够简单,spring使用p命名空间进一步作了简化。看这个,原始版:
<bean id="car" class="com.baobaotao.attr.Car">
<property name="brand" value="吉利CT5" />
<property name="maxSpeed" value="100" />
<property name="price" value="1000.00" />
</bean>
再看P命名空间的:
<bean id="car" class="com.baobaotao.ditype.Car"
p:brand="红旗CA72"
p:maxSpeed="200"
p:price="20000.00"/>
这个简化有点多,是遵循p规则的。p+':'+property的每个属性变成了bean的属性,消退bean的子标签。(每一种变化都尽显哲学之美。)
介绍不详尽之处还请读者自己翻书再看。 文中有些细节叙述较为概括,后续章节会有专题介绍,比如实例化的详细过程,比如工厂方法,比如方法注入等,敬请期待。本文旨在让读者大体了解这三种注入在XML中的格式。 靡不有初,鲜克有终。
--《诗经》
(spring-第3回【IoC基础篇】)spring的依赖注入-属性、构造函数、工厂方法等的注入(基于XML)的更多相关文章
-
(spring-第12回【IoC基础篇】)JavaBean的属性编辑器
在spring实例化bean的最后阶段,spring利用属性编辑器将配置文件中的文本配置值转换为bean属性的对应值,例如: 代码0011 <bean id="car" cl ...
-
Spring+SpringMVC+MyBatis+easyUI整合基础篇(六)maven整合SSM
写在前面的话 承接前文<Spring+SpringMVC+MyBatis+easyUI整合基础篇(五)讲一下maven>,本篇所讲述的是如何使用maven与原ssm项目整合,使得一个普 ...
-
Spring+SpringMVC+MyBatis+easyUI整合基础篇(八)mysql中文查询bug修复
写在前面的话 在测试搜索时出现的问题,mysql通过中文查询条件搜索不出数据,但是英文和数字可以搜索到记录,中文无返回记录.本文就是写一下发现问题的过程及解决方法.此bug在第一个项目中点这里还存在, ...
-
Spring+SpringMVC+MyBatis+easyUI整合基础篇(十一)SVN服务器进阶
日常啰嗦 上一篇文章<Spring+SpringMVC+MyBatis+easyUI整合基础篇(十)SVN搭建>简单的讲了一下SVN服务器的搭建,并没有详细的介绍配置文件及一些复杂的功能, ...
-
Spring+SpringMVC+MyBatis+easyUI整合基础篇(十二)阶段总结
不知不觉,已经到了基础篇的收尾阶段了,看着前面的十几篇文章,真的有点不敢相信,自己竟然真的坚持了下来,虽然过程中也有过懒散和焦虑,不过结果还是自己所希望的,克服了很多的问题,将自己的作品展现出来,也发 ...
-
Spring+SpringMVC+MyBatis+easyUI整合基础篇
基础篇 Spring+SpringMVC+MyBatis+easyUI整合基础篇(一)项目简介 Spring+SpringMVC+MyBatis+easyUI整合基础篇(二)牛刀小试 Spring+S ...
-
Spring cloud系列教程第十篇- Spring cloud整合Eureka总结篇
Spring cloud系列教程第十篇- Spring cloud整合Eureka总结篇 本文主要内容: 1:spring cloud整合Eureka总结 本文是由凯哥(凯哥Java:kagejava ...
-
(spring-第2回【IoC基础篇】)Spring的Schema,基于XML的配置
要深入了解Spring机制,首先需要知道Spring是怎样在IoC容器中装配Bean的.而了解这一点的前提是,要搞清楚Spring基于Schema的Xml配置方案. 在深入了解之前,必须要先明白几个标 ...
-
(spring-第4回【IoC基础篇】)spring基于注解的配置
基于XML的bean属性配置:bean的定义信息与bean的实现类是分离的. 基于注解的配置:bean的定义信息是通过在bean实现类上标注注解实现. 也就是说,加了注解,相当于在XML中配置了,一样 ...
-
(spring-第7回【IoC基础篇】)BeanDefinition的载入与解析&;&;spring.schemas、spring.handlers的使用
报错信息:Configuration problem: Unable to locate Spring NamespaceHandler for XML schema namespace [http: ...
随机推荐
-
AWS CLI使用s3
aws CLI是什么东西,暂且先不去了解,目前的需求是s3. 我在Jenkins上创建一个bucket,然后申请access_key,然后就可以使用s3来存储数据了.也就是说,s3就是一个网盘. 1. ...
-
css书写规范及特殊样式
1.CSS书写顺序: (1)位置:position.top.right.z-index.display.float (2)大小:width.height.padding.margin (3)文字系列: ...
-
window下安装wamp环境
Wamp就是Windos Apache Mysql PHP集成安装环境,即在window下的apache.php和mysql的服务器软件.其中php环境配置是至关重要的一部分,本文就针对php在本地的 ...
-
android 提示用户是否退出应用程序 提升用户体验
首先明确一点,用户的一直点击的返回键,之后就会退出到桌面.那么,如何提示呢?很简单,在用户一顿返回键回到程序入口处(即程序的第一个Activity)给用户一个提示,您再按可就退出程序啦?那么如何在程序 ...
-
安全系列之二:OAuth2.0 开放授权协议
本文提取出OAuth2.0规范RFC6749的主要内容,部分内容从文档复制出来,给大家讲讲第三方授权背后的故事. 先是举个知乎的QQ登录授权的例子,然后讲四种授权方式,两种令牌,接着是看看协议流程,分 ...
-
swift内存管理中的引用计数
在swift中,每一个对象都有生命周期,当生命周期结束会调用deinit()函数进行释放内存空间. 观察这一段代码: class Person{ var name: String var pet: P ...
-
mysql常用命令行操作(一):登陆、退出、查看端口、修改密码、刷新
一.登陆和退出mysql mysql -u root -p # 登陆exit # 退出 二.查看当前mysql的端口号 show global variables like 'port'; 三.查看用 ...
-
echarts网络拓扑图
option = { title: { text: '' }, tooltip: {}, animationDurationUpdate: 1500, animationEasingUpdate: ' ...
-
c# 动态数组-----“动态”数组
其实在大多数工作中我们能通过前处理来确定我们的数组有多大,这样我们就可以声明相应大小的数组了.我感觉这种“动态”数组就够我用了.比如我要处理excel中数据,数据有m行*n列,这样我就可以通过读取ex ...
-
[转]ASP.NET Core 指定环境发布(hosting environment)
本文转自:https://www.cnblogs.com/xishuai/p/asp-net-core-set-hosting-environment-with-publish.html ASP.NE ...