Java开发工程师(Web方向) - 04.Spring框架 - 第2章.IoC容器
第2章.IoC容器
IoC容器概述
abstract: 介绍IoC和bean的用处和使用
IoC容器处于整个Spring框架中比较核心的位置:Core Container: Beans, Core, Context, SpEL
为上层AOP/Aspects/Instrumentation/Messaging提供支持
IoC容器的用途:
创建对象的依赖,最后组装成所需的业务对象
容器通过业务对象和配置(application-context.xml; xxxController.java)-->生成可用对象
在Spring场景下,把对象A/B/C都称作Bean
从代码角度看,一个ApplicationContext (org.springframework.context包下,属于spring-context依赖中)对象就是一个IoC容器
初始化IoC容器:
在web项目的web.xml中,用
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:application-context.xml</param-value>
</context-param>
ApplicationContext context = new ClassPathXmlApplicationContext("application-context.xml");
(文件系统中:ApplicationContext context = new FileSystemXmlApplicationContext("/home/user/conf/application-context.xml");)
来初始化。
定义一个Bean:
在application-context.xml中
<beans>
...
<bean id="screwDriver" class="package_url"></bean>
</beans>
使用Bean:
1. 初始化容器:ApplicationContext context = new ClassPathXmlApplicationContext("application-context.xml");
2. 通过getBean()获取对象:ScrewDriver screwDriver = context.getBean("screwDriver", ScrewDriver.class);
3. 使用对象:screwDriver.use();
实例演练:
1. New Maven Project -> Create a simple project (skip archetype selection)
GroupId: com.netease.nanodegree
ArtifactId: spring-container
2. 将所需依赖加入pom.xml
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.2.1.RELEASE</version>
</dependency>
</dependencies>
发现依赖的jar包自动被加入
3. 在src/main/resources/下新建application-context.xml
初始为:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd"> </beans>
此时,还未定义任何bean,因此还不能从容器中获取任何bean
4. 在src/main/java/下新建类ScrewDriver.java
package: com.netease.nanodegree;
package com.netease.nanodegree; public class ScrewDriver {
public void ues () {
System.out.println("Use screw driver");
}
}
5. 此时有了bean,在application-context.xml中加入相关bean的定义
<bean id = "screwDriver" class = "com.netease.nanodegree.ScrewDriver"></bean>
6. 在src/main/java/com.netease.nanodegree下新建测试类TestContainer.java
7. Run as -> Java Application
Bean的作用域:Bean的有效范围
singleton:与设计模式中的singleton有点像
保证对象在整个容器中最多只能有一个单例,也保证在整个生命周期中只会创建一个实例
上例中bean定义的默认作用域就是singleton
也可以显式定义<bean id="screwDriver" class="com.netease.nanodegree.ScrewDriver" scope="singleton"></bean>
例子Test:
ScrewDriver.java
public class ScrewDriver {
private String color = "red";
public void use () {
//System.out.println("Use screw driver");
System.out.println("Use " + color + " screw driver");
} public void setColor (String color) {
this.color = color;
}
}
TestContainer.java
public class TestContainer {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("application-context.xml"); ScrewDriver screwDriver = context.getBean("screwDriver", ScrewDriver.class);
screwDriver.setColor("green");
screwDriver.use(); ScrewDriver screwDriver2 = context.getBean("screwDriver", ScrewDriver.class);
screwDriver2.use();
}
}
要想测试是不是单例对象,使用getBean()得到两个对象并设置属性为"green",如果是单例,则两个对象的属性均为"green"
应用:大部分场景下使用singleton模式就能满足业务需求,比如Servlet实例,开始时创建实例,结束时销毁实例,在中间时只需这一个实例来处理即可。
prototype:每次引用时都需要创建一个新的对象/实例,比如每次使用时不希望保留上一次的属性值/状态
<bean id=... class=... scope="prototype"></bean>
例子Test:
将bean screwDriver的scope改为prototype;
此时打印出来的结果应该是第一次为green(因为使用setColor()改了),第二次为red(因为初始为red且并未无改动)
在web应用场景下才出现的作用域:
request:在每个request中分别创建和销毁bean
比如在request1开始时创建了若干个beans,结束时这些beans就被销毁了;而另一个request中又是一个request scope,有它自己的bean的创建和销毁
session:在每个session中的作用域
每个用户对应一个session,在session中可能若干个requests,但是sessionBean在这些request之间是可以共享的
而多个session会有各自的session scope
global session:在此不做介绍
application:每个session之间需要共享的信息
Bean的生命周期回调:
有了Bean的作用域后,我们知道/控制bean的创建/销毁,但是我们不能控制创建/销毁时可以做哪些事情(比如申请/释放资源、创建线程池)--生命周期回调的功能可以支持实现这些功能
Spring提供了两种回调:
InitializingBean接口中的afterPropertiesSet()函数:
public interface InitializingBean {
void afterPropertiesSet() throws Exception;
}
当一个bean创建完成后调用
例子:
<bean id=... class=... init-method="init"></bean>
public class ScrewDriver {
public void init() {
Syso("Init screw driver");
}
}
DisposableBean接口中的destroy()函数:
public interface DisposableBean {
void destroy() throws Exception;
}
当一个bean被销毁之后调用
例子:
<bean id=... class=... destroy-method="cleanup"></bean>
public class ScrewDriver {
public void cleanup() {
Syso("Clean up screw driver");
}
}
测试Test结果:
<bean id = "screwDriver" class = "com.netease.nanodegree.ScrewDriver" scope="prototype" init-method="init" destroy-method="cleanup"></bean>
output:
发现没有进行cleanup的回调:因为bean没有被销毁
手动关闭:
((ConfigurableApplicationContext) context).close();
output:还是相同
将bean定义中的scope="prototype"改为默认的singleton后
成功执行cleanup()回调函数
依赖注入
第一章中举的例子:在ScrewDriver中声明变量private Header header = new StraightHeader();
虽然有抽象类header的想法,但是直接用new StraightHeader()创建实例,并没有将抽象的想法实施到位。
如果需要一个带有CrossHeader的screwDriver呢?需要修改ScrewDriver的代码:
private Header header = new CrossHeader();
如果要修改一字刀口螺丝刀的属性,比如颜色等,则需要修改constructor:
private Header header = new StraightHeader("green", 15);
此时:ScrewDriver和StraightHeader的关系是强依赖关系
在第一章中的IoC解决方法是在ScrewDriver的constructor中传入一个Header(抽象接口对象)作为参数
public ScrewDriver(Header header) { this.header = header; }
1. 定义接口:抽象接口Header
2. 实现接口:如StraightHeader和CrossHeader
3. 以接口为基础来进行依赖的注入
基于构造函数的依赖注入方式:如上例中的在constructor中传入接口对象的方法
基于Setter方法的依赖注入方式:和基于构造函数的方式很相近
public class ScrewDriver {
private Header header; public setHeader(Header header) {
this.header = header;
}
}
那...究竟是选择哪一种方法呢?
实际开发中的原则:
如果关系是强依赖的,则基于构造函数(比如ScrewDriver必须有一个Header)
如果关系是可选依赖的,则基于Setter方法(可有可无的依赖)
实际开发中可能注入的是基本类型、集合、Bean,或通过配置文件注入等等
例子(只演示基于构造函数的依赖注入):
定义Header接口:Header.java
package com.netease.nanodegree; public interface Header {
public void doWork();
public String getInfo();
}
定义实现类StraightHeader.java
package com.netease.nanodegree; public class StraightHeader implements Header {
private String color;
private int size; public StraightHeader(String color, int size) {
this.color = color;
this.size = size;
} @Override
public void doWork() {
System.out.println("Do work with straight header");
} @Override
public String getInfo() {
return "Straight Header: " + this.color + ", " + this.size;
}
}
定义实现类CrossHeader.java(相似)
定义StraightHeader bean和CrossHeader bean
<bean id = "header" class = "com.netease.nanodegree.StraightHeader">
<constructor-arg index="0" type="java.lang.String" value="red"></constructor-arg>
<constructor-arg index="1" type="int" value="15"></constructor-arg>
</bean>
可以通过constructor-arg中的index来说明param的顺序;如index="0"; index="1";
可以通过type来说明param的类型;如type="int"; type="java.lang.String";
可以通过name来说明param的对应的变量名;如name="size"; name="color";
(index/type/name都不是必要的)
Test.java
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("application-context.xml"); Header header = context.getBean("header", StraightHeader.class);
System.out.println(header.getInfo());
header.doWork();
}
output:
如何注入集合呢?
通过map的方式注入
假设StraightHeader的构造函数为:
public StraightHeader(Map<String, String> params) {
this.color = params.get("color");
this.size = Integer.parseInt(params.get("size"));
}
此时,定义bean为(使用map的方法)
<bean id = "header" class = "com.netease.nanodegree.StraightHeader">
<constructor-arg>
<map>
<entry key="color" value="red"></entry>
<entry key="size" value="15"></entry>
</map>
</constructor-arg>
</bean>
也可以注入List/Set之类的集合,配置也相似
<bean id = "header" class = "com.netease.nanodegree.StraightHeader">
<constructor-arg>
<list>
<value>red</value>
<value>14</value>
</list>
</constructor-arg>
</bean>
也可以通过配置文件加载一些配置项:比如注入Property的一些属性
<bean id = "header" class = "com.netease.nanodegree.StraightHeader">
<constructor-arg>
<props>
<prop key="color">red</prop>
<prop key="size">14</prop>
</props>
</props>
</bean>
对应的构造函数:
public StraightHeader(Properties props) {
this.color = props.xxx;
this.size = props.xxx;
}
那怎么通过配置文件注入呢?
在src/main/resources/下创建一个配置文件header.properties
color=green
size=16
在application-context.xml中加载该配置文件:
<bean id="headerProperties"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:header.properties"/>
</bean>
property->value为配置文件所在地址
读取配置文件中的信息:
<bean id = "header" class = "com.netease.nanodegree.StraightHeader">
<constructor-arg name="color" value="${color}"></constructor-arg>
<constructor-arg name="size" value="${size}"></constructor-arg>
</bean>
上例注入的都是一些基础类型,如果是ScrewDriver需要注入一个Header类型(bean)的参数呢?
ScrewDriver.java
public class ScrewDriver {
private Header header; public ScrewDriver(Header header) {
this.header = header;
} public void use () {
System.out.println("Use screw driver with header " + header.getInfo());
}
}
注入:
<bean id = "screwDriver" class = "com.netease.nanodegree.ScrewDriver">
<constructor-arg>
<ref bean="header"/>
</constructor-arg>
</bean>
Test.java
ScrewDriver screwDriver = context.getBean("screwDriver", ScrewDriver.class);
screwDriver.use();
输出:
通过Setter实现注入:
需要删除constructor
加上Setter()
public void setColor(String color) {
this.color = color;
} public void setSize(int size) {
this.size = size;
}
<!-- setter方法注入 -->
<bean id = "header" class = "com.netease.nanodegree.StraightHeader">
<property name="color" value="${color}"></property>
<property name="size" value="${size}"></property>
</bean>
<bean id="headerProperties" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:header.properties"/>
</bean>
如果一个bean有很多依赖呢?使用一个依赖添加一个配置的方法吗?
Spring提供了自动装配(Autowiring)的机制(三种)
byName: 根据Bean的名称 (Setter方法)
byType: 根据Bean的类型 (Setter方法)
constructor: 构造函数,根据类型
例子:
<!-- autowiring自动装配的方式 -->
<bean id = "screwDriver" class = "com.netease.nanodegree.ScrewDriver" autowire="byName"></bean>
<bean id = "screwDriver" class = "com.netease.nanodegree.ScrewDriver" autowire="byType"></bean>
在ScrewDriver.java中,不再需要setter,只需在private Header header;前加上@Autowired即可
或使用@Resource(@Autowired和@Resource是存在不同包中的,前者在springframework中,后者在javax.annotation中,相对标准一些)
通过Annotation的方式
@Component:定义Bean
@Value:properties注入
@Autowired (Spring)/ @Resource (Java):自动装配依赖
@PostConstruct & @PreDestroy:生命周期回调
例子:
application-context.xml中:
<context:component-scan base-package="com.netease.nanodegree" />
表示支持通过Annotation来定义bean或其他属性。扫描/搜索com.netease.nanodegree包,使用Annotation的方式进行配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd"> <context:component-scan base-package="com.netease.nanodegree" /> <bean id="headerProperties"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:header.properties"/>
</bean> <bean id = "screwDriver" class = "com.netease.nanodegree.ScrewDriver" autowire="byName"></bean>
</beans>
StraightHeader.java中:
删除setters;
添加Annotation:
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component; @Componentpublic class StraightHeader implements Header {
@Value("${color}")
private String color;
@Value("${size}")
private int size; @Override
public void doWork() {...} @Override
public String getInfo() {...}
}
报错:No bean named 'header' is defined
因为写@Component时,默认name为StraightHeader而不是header,因此需要specify
@Component("header")
与生命周期有关的Annotation:
@PostConstruct
public void init() {
System.out.println("init the header");
} @PreDestroy
public void destroy() {
System.out.println("destroy the header");
}
利弊:
XML文件:繁琐,但是代码独立(代码和配置分离);实际开发中有些东西需要XML文件来配置
Annotation:简洁,但是配置和代码耦合,配置位置分散,不好管理;实际开发中用于controller/service等
IoC容器单元测试
下列关于IoC容器的描述错误的是:
- A.容器必须由应用代码主动创建;2.00/2.00
- B.应用可以从容器中获取所配置的对象;
- C.容器负责所管理对象的生命周期管理;
- D.根据会根据依赖关系组装对象;
如下关于自动装配(Autowiring)说法中,错误的是:
- A.可以根据Bean名称自动注入;
- B.可以根据Bean的类型通过Setter方法注入;
- C.不可以根据Bean的类型通过构建函数注入;2.00/2.00
- D.可以根据Bean的类型自动注入;
关于依赖注入方式,说法错误的是:
- A.可选依赖可以通过构造函数注入;
- B.可以通过构造函数注入;
- C.强依赖必须通过构造函数注入;2.00/2.00
- D.可以通过Setter方法注入;
关于Bean作用域的singleton和prototype说法正确的是:
- A.prototype只在容器中创建一个对象;
- B.prototype在每次通过容器获取实例时创建一个对象;1.50/3.00
- C.singleton在每次通过容器获取实例时创建一个对象;
- D.singleton只在容器中创建一个对象;1.50/3.00
关于Bean的生命周期回调,说明正确的是:
- A.生命周期回调发生在运行时;
- B.可以通过创建(初始化时)回调进行申请资源的操作;0.75/3.00
- C.销毁回调发生在对象回收前
- D.可以通过销毁时回调进行资源回收的操作0.75/3.00
关于Bean的生命周期回调,说明正确的是:
- A.销毁回调发生在对象回收后
- B.可以通过创建(初始化时)回调进行申请资源的操作;1.50/3.00
- C.生命周期回调发生在编译时;
- D.生命周期回调发生在运行时;1.50/3.00
IoC容器在初始化时,必须要加载名称为application-context.xml的配置文件;
- A.√
- B.×2.00/2.00
IoC容器作业
https://my.oschina.net/hava/blog/758277
我们在示例中通过
context.getBean("header", StraightHeader.class);
方式来获取容器管理的对象,查找并学习Spring的API文档,列出getBean函数的其它形式接口,并解释各个接口的差异。
假设我们有个应用(Application),它包含一个写文件的服务(FileWriterService),该服务在创建时会创建并打开一个文件,销毁时关闭该文件,并且该服务包含一个write(String content)方法:会将content写入到文件中,要求:
1. 使用Spring IoC的相关内容组织该程序;
2. 文件路径通过配置文件(properties)指定;
(如果不清楚或者忘记如何打开,关闭以及写入文件,请回顾一个翁老师的 Java基础 课程)
基本要求:必须附加一个项目说明文档,说明每个功能点对应程序的运行结果(截图),项目的接口说明或者关键代码(不要把全部代码贴出来)等可以反映项目结果的内容。提交作业的时候必须有这个项目说明文档,否则会影响最终评分。
Java开发工程师(Web方向) - 04.Spring框架 - 第2章.IoC容器的更多相关文章
- Java开发工程师(Web方向) - 04.Spring框架 - 第1章.Spring概述
第1章.Spring概述 Spring概述 The Spring Framework is a lightweight solution and a potential one-stop-shop f ...
- Java开发工程师(Web方向) - 04.Spring框架 - 第3章.AOP技术
第3章--AOP技术 Spring框架 - AOP概述 笔记https://my.oschina.net/hava/blog/758873Spring框架 - AOP使用 笔记https://my.o ...
- Java开发工程师(Web方向) - 04.Spring框架 - 第5章.Web框架
第5章--Web框架 Web框架概述 Web框架单元测验 本次得分为:13.50/15.00, 本次测试的提交时间为:2017-09-25 1单选(2分) 关于Spring MVC中Dispatche ...
- Java开发工程师(Web方向) - 04.Spring框架 - 第4章.数据访问
第4章--数据访问 Spring JDBC DAO (Data Access Object) 实现数据访问相关接口(接口和实现分离) ORM (Object Relation Mapping) 对象关 ...
- Java开发工程师(Web方向) - 04.Spring框架 - 期末测试
Spring框架客观题 Spring框架编程题 http://blog.csdn.net/sinoacc/article/details/51702458 1 (25分) 假设有如下数据表: crea ...
- Java开发工程师(Web方向) - 02.Servlet技术 - 第1章.Servlet
第1章--Servlet Servlet简介 Servlet应用于? 浏览器发出HTTP请求,服务器接收请求后返回响应给浏览器. 接收请求后到返回响应之间: 服务器将请求对象转交给Servlet容器 ...
- Java开发工程师(Web方向) - 02.Servlet技术 - 第3章.Servlet应用
第3章.Servlet应用 转发与重定向 转发:浏览器发送资源请求到ServletA后,ServletA传递请求给ServletB,ServletB生成响应后返回给浏览器. 请求转发:forward: ...
- Java开发工程师(Web方向) - 02.Servlet技术 - 第4章.JSP
第4章--JSP JSP JSP(Java Server Pages) - 中文名:Java服务器页面 动态网页技术标准 JSP = Html + Java + JSP tags 在服务器端执行,返回 ...
- Java开发工程师(Web方向) - 02.Servlet技术 - 第2章.Cookie与Session
第2章--Cookie与Session Cookie与Session 浏览器输入地址--HTTP请求--Servlet--HTTP响应--浏览器接收 会话(session):打开浏览器,打开一系列页面 ...
随机推荐
- java中prepareStatement与createStatement的区别
首先来看两段代码: 第一个使用createStatement() public void delete( int id){ try { Connection c = DBUtil.getConnect ...
- Spark Streaming编程示例
近期也有开始研究使用spark streaming来实现流式处理.本文以流式计算word count为例,简单描述如何进行spark streaming编程. 1. 依赖的jar包 参考<分别用 ...
- NAT和DHCP
涉及的命令 NAT 动态NAT清除:clear ip nat translation * 进入路由器环回接口:int loo 0 静态NAT清除: (config)#No ip nat inside ...
- Eclipse切换字体颜色
打开window-preferences
- 「PHP」设计模式介绍
引言 最近再看PHP设计模式相关的一些技术文章,网上有关PHP的设计模式范例很少,这里做一些总结仅供参考,不足之处望提出. 参考资料: <大话设计模式>程杰 什么是设计模式 设 ...
- 帝国CMS调用友情链接多种方法
今天织梦模板给大家讲解帝国CMS调用友情链接的常见方法: 1.帝国内置标签:phomelink [phomelink]每行显示数,显示总数,操作类型,分类id,是否显示原链接[/phomelink] ...
- inotify和epoll
参考EventHub.cpp 1.初始化inotify int mINotifyFd = inotify_init(); 2.将要监测的目录添加到inotify int result = inotif ...
- Waltz of love
Waltz of love Love me tenderly Love me softly Close your eyes,fling to the dangcing hall Follow your ...
- 『Python基础-11』集合 (set)
# 『Python基础-11』集合 (set) 目录: 集合的基本知识 集合的创建 访问集合里的值 向集合set增加元素 移除集合中的元素 集合set的运算 1. 集合的基本知识 集合(set)是一个 ...
- “子查询返回的值不止一个。当子查询跟随在 =、!=、<、<=、>、>= 之后,或子查询用作表达式时,这种情况是不允许的。”SQL查询错误解析
为了实现下述代码,首先得有数据库和相应的表格,本文用的是https://blog.csdn.net/qaz13177_58_/article/details/5575711/中的案例,即先用链接中那些 ...