Java Bean
类的定义,符合如下规则的Java对象称为Java Bean
- 必须有包
- 必须有无参数构造器
- 必须实现 序列化接口
- 所有属性为private
- 有getXXX setXXX 方法声明的"Bean属性".
Spring Bean
通常来说,Java Bean配置元数据,被 Spring IOC容器所管理,就成为Spring beans
配置元数据通常以XML,注解或Java 代码的形式表示。
初始化Spring 容器
接口 org.springframework.context.ApplicationContext
表示Spring IoC容器,负责实例化,配置和注入 bean
//初始化Spring 容器,applicationContext.xml为配置元数据的XML文件
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
创建Spring Bean
Spring 帮助创建JavaBean对象, Spring 支持3种对象创建方式
- 利用构造器创建Java Bean,在启动Spring容器时就会创建bean对象
- 利用静态"工厂方法"创建对象,spring只负责调用静态工厂方法,方法内部由我们自己实现
- 利用动态工厂方法创建对象
案例:
<!-- 利用静态工厂方法创建Java Bean对象 -->
<bean id="cal" class="java.util.Calendar" factory-method="getInstance"/>
<!-- 利用bean(实例)的工厂方法创建对象-->
<!-- Spring会调用cal对象的getTime方法创建对象 -->
<bean id="time" factory-bean="cal" factory-method="getTime"/>
测试:
@Test
public void testCal(){
//初始化 Spring 容器
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
//获取Java Bean, 进行测试
Calendar cal = ctx.getBean("cal", Calendar.class);
System.out.println(cal);
}
@Test
public void testTime(){
//初始化Spring 容器
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
//检查是否成功创建了 time 对象
Date time=ctx.getBean("time", Date.class);
System.out.println(time);
}
初始化Spring Bean
1.将@PostConstruct注解标记在方法上
@PostConstruct
public void init() {
System.out.println("init...");
}
2.实现InitializingBean接口,在afterPropertiesSet方法内完成实例化
public interface InitializingBean {
void afterPropertiesSet() throws Exception;
}
3.设置init-method属性, 则在创建对象以后调用初始化方法.
public class TestInit {
public void init() {
System.out.println("init...");
}
}
@Configuration
public class AppConfig {
@Bean(initMethod = "init")
public TestInit testInit() {
return new TestInit();
}
}
或者
<bean id="testInit" class="com.demo.TestInit " init-method="init"/>
顺序依次为:1>2>3
获取Spring Bean
Beanfactory 和 ApplicationContext 可以通过getBean(String name, Class<T> requiredType)
方法从 Spring 容器中获取bean
1.BeanFactory 接口:使用懒加载的方式,只有通过 getBean() 方法直接调用Spring Beans时才进行实例化
2.ApplicationContext 接口:继承BeanFactory 接口,包含 BeanFactory 的所有特性。使用预加载的方式,Spring Beans都在 ApplicationContext 启动之后实例化
销毁Spring Bean
1.@PreDestroy注解标记在方法上
@PreDestroy
public void destroy() {
System.out.println("destroy...");
}
2.实现DisposableBean接口,在destory方法内完成销毁工作
public interface DisposableBean {
void destroy() throws Exception;
}
3.destroy-method
public class TestDestory{
public void destory() {
System.out.println("destroy...");
}
}
@Configuration
public class AppConfig {
@Bean(destroyMethod = "destroy")
public TestDestory testDestory() {
return new TestDestory();
}
}
或者
<bean id="testDestory" class="com.demo.TestDestory" destory-method="destory"/>
Bean 的生命周期管理
Spring 在默认情况下, Bean都是单例的。设置 scope="prototype" 以后就是多例的对象了
Spring Bean对象, 由Spring控制对象的创建和销毁, 这个过程称为Spring中Bean对象的生命周期管理.
-
单例对象 singleton(单例)
- 创建: 在Spring容器初始化时候, 创建单例对象, 如果设置了init-method属性, 则在创建对象以后调用初始化方法.
- 使用: 每次调用getBean时候, 返回的都是同一个对象
- 销毁: 在Spring容器关闭时候,Spring会自动销毁单例对象, 如果指定了destroy-method属性, 则会在销毁之前执行 销毁 方法.
-
多例对象 prototype(原型)
- 创建: 在调用getBean方法时候, 创建对象, 如果设置了init-method属性, 则在创建对象以后调用初始化方法.
- 使用: 每次调用getBean时候, 返回的都是新对象
- 销毁: Spring 不管,也不会调用 destroy-method
Spring提供了以下4种方式来管理Bean的生命周期事件:
(1)InitializingBean和DisposableBean回调接口。
(2)Aware接口。
(3)init-method和destroy-method属性
(4)@PostConstruct和@PreDestroy注解。
Bean 延迟实例化
问题:
单例对象默认情况下在容器启动时立即初始化, 如果这些对象内存耗用高, 则启动会很慢.
解决办法:
对于内存占用高, 使用少的对象, 可以设置延迟实例化, 在bean标签上使用懒惰属性设置即可。
设置了懒惰属性为true:lazy-init="true"
- 容器启动时候, bean不实例化
- 在第一次getBean时候实例化
- 是单例bean对象.
- 多例对象没有延迟初始化问题.
Spring Bean作用域
Spring容器中的Bean可以分为5个作用域
在Spring早期版本中,只有两个作用域:
(1)singleton:每个容器中只有一个Bean实例
(2)prototype:每一个Bean请求提供一个实例
Spring2.X中针对WebApplicationContext新增三个作用域:
(3)request:每次 HTTP 请求都会创建一个新的 Bean
(4)Session:同一个 HttpSession 共享同一 个 Bean,不同的 HttpSession 使用不同的 Bean,在Session过期后,Bean会随之失效。
(5)global-session:同一个全局Session 共享一个 Bean
Spring IOC/DI
Spring 容器负责实例化,配置和装配 Spring beans。主要功能为IOC和DI
IOC:
- IoC全称是Inversion of Control,被译为控制反转;
- IoC是指程序中对象的获取方式发生反转,由最初的new方式创建,转变为由第三方框架创建、注入(DI),它降低了对象之间的耦合度。
- Spring容器是采用DI方式实现了IOC控制,IOC是Spring框架的基础和核心;
DI:
- DI全称是Dependency Injection ,被译为依赖注入;
- DI的基本原理就是将一起工作具有关系的对象,通过构造方法参数或方法参数传入建立关联,因此容器的工作就是创建bean时注入那些依赖关系。
- DI主要有两种注入方式,即Setter注入和构造器注入(接口注入方式在Spring中没有使用)
Setter注入(Bean属性注入,property标签)
实现步骤:
-
创建接口 Tools
public interface Tools { String getName(); }
-
创建 Saw
/** * 电锯 */ public class Saw implements Tools, Serializable { private String name; public Saw() { name = "电锯"; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "Saw [name=" + name + "]"; } }
-
斧子 Axe
/** * 斧子 */ public class Axe implements Tools, Serializable{ private String name; public Axe() { name = "斧子"; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "Axe [name=" + name + "]"; } }
-
创建类型 Man
public class Man implements Serializable { private Tools tool; private String name; public Man() {} public void work(){ System.out.println(name+"使用"+tool.getName()+"砍树!"); } public Tools getTool() { return tool; } public void setTool(Tools tool) { this.tool = tool; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "Man [tool=" + tool + ", name=" + name + "]"; } }
-
编写配置文件 applicationContext.xml:
<bean id="axe" class="spring.Axe"></bean> <bean id="saw" class="spring.Saw"></bean> <!-- ref="axe" 表示将id为axe的bean对象注入到 bean属性tool 中。ref 属性用于注入bean对象。value 属性用于注入基本值, 包括字符串--> <bean id="qiang" class="spring.Man"> <property name="tool" ref="saw"></property> <property name="name" value="光头强"></property> </bean>
-
测试:
@Test public void testQiang(){ Man qiang = ctx.getBean("qiang",Man.class); qiang.work(); }
优点: 更换光头强的工具只需要修改配置文件即可,不需要改变任何Java代码
构造器参数注入(constructor-arg 标签)
实现步骤:
-
给Man增加有参数构造器:
public Man(String name) { this.name = name; }
-
利用配置文件调用有参数构造器
<!-- 构造器参数注入 --> <bean id="miniQiang" class="spring.Man"> <!-- constructor-arg 标签用于实现构造器参数注入, index 用与指定构造器参数序号 --> <constructor-arg index="0" value="小强"/> <property name="tool" ref="saw"/> </bean>
-
测试:
@Test public void testMiniQiang(){ //测试: 构造器参数注入 Man qiang = ctx.getBean("miniQiang", Man.class); qiang.work(); }
自动注入(装配)
Spring 为了简化注入,提供了自动注入功能:
案例:
当指定 autowire=byName时候, Spring会按照set方法的名字找到一致的id名一致的Bean, 并实现自动装配.
实现步骤:
-
定义id为tool的bean
<bean id="tool" class="spring.Saw"/>
-
在自动装配的bean上使用 autowire="byName"
<!-- 当指定了 autowire="byName" 属性时候,Spring会根据 setTool 名字查找 id为tool的bean , 然后自动的注入(装配) 执行setTool方法--> <bean id="man" class="spring.Man" autowire="byName"> <constructor-arg index="0" value="男人"/> </bean>
-
测试:
@Test public void testMan(){ //测试: 按照名字自动参数注入 Man man = ctx.getBean("man", Man.class); man.work(); }
共有5种自动装配模式:
(1)no:默认设置,不进行自动装配,手动设置 Bean 的依赖关系。
(2)byName:根据 Bean 的名字进行自动装配。
(3)byType:根据 Bean 的类型进行自动装配。
(4)constructor:和byType模式类似,但是仅适用于有与构造器相同参数类型的Bean,如果在容器中没有找到与构造器参数类型一致的Bean,那么将会抛出异常。
(5)autodetect:该模式自动探测使用constructor自动装配或者byType自动装配。首先会尝试找合适的带参数的构造器,如果找到就是用构造器自动装配,如果在Bean内部没有找到相应的构造器或者构造器是无参构造器,容器就会自动选择byType模式。
自动装配有如下局限性:
• 需要使用< property>设置指明依赖,这意味着总要重写自动装配。
• 不能自动装配简单属性,如原生类型、字符串。
• 自动装配总是没有自定义装配精确
Spring支持各种类型的参数注入
- 注入基本值---value
- 注入Bean对象---ref
- 注入集合---list / set / map / props
- 注入空值---null
在配置文件中注入属性:
<!-- 多种类型的参数注入 -->
<bean id="demo" class="spring.DemoBean">
<!-- 注入基本值 -->
<property name="times" value="56"/>
<property name="name" value="Hello"/>
<property name="price" value="5.8"/>
<!-- 注入一个Bean对象 -->
<property name="date" ref="dt"/>
<!-- 注入集合 -->
<property name="collection">
<list>
<value>Tom</value>
<value>Jerry</value>
</list>
</property>
<!-- 注入空值 -->
<property name="phone">
<null/>
</property>
</bean>
<bean id="dt" class="java.util.Date"/>
Spring 注解
Spring在2.5版本以后开始支持用注解的方式配置依赖注入。可以用注解的方式来替代XML方式
编写声明组件扫描
<!-- 设置注解组件扫描 -->
<context:component-scan base-package="package1[,pagkage2,…,pagkageN]"/>
/*
* Spring 会主动扫描 package 包,将标注了
* @Component的类Xxx自动实例化为Java bean, 并且绑定id为 "xxx"
*/
注册Bean组件:@Component 与 @Repository/@Service/@Controller功能一样.
自定义bean组件ID:@Component("id")
属性注入:@Resource(javax.annotation),@Autowired(Spring)可以注入Bean组件 / @Value 可以注入基本值
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
</dependency>