Bean的类型
普通bean
工厂bean
普通bean
我们平时在xml中定义的bean,还有通过@Component定义的bean。
<bean class="com.test.bean.Child"/>
@Component
public class Child {
}
@Bean
public Child child() {
return new Child();
}
工厂bean
场景:
Bean的创建需要指定一些策略,或者依赖特殊的场景来分别创建,又或者bean的创建过程太复杂,使用xml或者注解声明比较复杂。
这种情况下,普通bean的创建就搞不定了。
Factorybean:使用工厂方法创建对象。本身就是一个创建对象的工厂接口。如果定义的bean实现了FactoryBean接口,那这个bean本身就不是普通的bean,不会在实际业务逻辑中起作用,而是由**创建的对象来起作用
**。
public class Child {
// 当前的小孩子想玩球
private String wantToy = "ball";
public String getWantToy() {
return wantToy;
}
public abstract class Toy {
private String name;
public Toy(String name) {
this.name = name;
}
public class Ball extends Toy { // 球
public Ball(String name) {
super(name);
}
}
public class Car extends Toy { // 玩具汽车
public Car(String name) {
super(name);
}
}
public interface FactoryBean<T> {
// 返回创建的对象
@Nullable
T getObject() throws Exception;
// 返回创建的对象的类型(即泛型类型)
@Nullable
Class<?> getObjectType();
// 创建的对象是单实例Bean还是原型Bean,默认单实例
default boolean isSingleton() {
return true;
}
}
定义一个工厂bean,玩具工厂根据小孩的爱好生产玩具:
public class ToyFactoryBean implements FactoryBean<Toy> {
private Child child;
@Override
public Toy getObject() throws Exception {
switch (child.getWantToy()) {
case "ball":
return new Ball("ball");
case "car":
return new Car("car");
default:
// SpringFramework2.0开始允许返回null
// 之前的1.x版本是不允许的
return null;
}
}
@Override
public Class<Toy> getObjectType() {
return Toy.class;
}
public void setChild(Child child) {
this.child = child;
}
}
xml方式:
<bean id="child" class="com.linkedbear.spring.bean.a_type.bean.Child"/>
<bean id="toyFactory" class="com.test.bean.ToyFactoryBean">
<property name="child" ref="child"/>
</bean>
注解方式:
@Bean
public Child child() {
return new Child();
}
@Bean
public ToyFactoryBean toyFactory() {
ToyFactoryBean toyFactory = new ToyFactoryBean();
toyFactory.setChild(child());
return toyFactory;
}
FactoryBean本身的加载是伴随IOC容器的初始化时机一起的。
FactoryBean创建bean的时机是延迟生成。
FactoryBean中有一个默认方法isSingleton,默认是true。代表是单例的。FactoryBean默认生成的Bean也是单例的。
注意:如果通过上下文取FactoryBean本体,传入getBean(id),FactoryBean的id前面需要加"&“符号。getBean(”&id")。不加&,取出来的还是生产的Bean。
Bean的作用域
spring框架的内置的作用域
Bean的实例化方式
实例化:通过调用构造方法,创建新的对象。
普通bean实例化
bean标签,@Bean注解都是普通bean的对象。IOC容器初始化时就已经被初始化。
FactoryBean创建bean
注册Bean时,只需要注入FactoryBean,IOC容器会自动识别,并默认在第一次获取时创建对应的Bean并缓存(针对默认的单实例FactoryBean)。
静态工厂创建Bean
public class Cat {
public Car() {}
}
public class CarStaticFactory {
pubic static Car getCar() {
return new Car();
}
}
下面代码中,上面的是普通bean的注册方式,下面的会直接引用静态方法创建对象。
<bean id="car1" class="com.test.bean.Car"/>
<bean id="car2" class="com.test.CarStaticFactory" factory-method="getCar"/>
public class BeanInstantiateXmlApplication {
public static void main(String[] args) throws Exception {
ApplicationContext ctx = new ClassPathXmlApplicationContext("resource/applicationContext.xml");
ctx.getBeansOfType(Car.class).forEach((beanName, car) -> {
System.out.println(beanName + " : " + car);
});
}
}
可以看到创建了两个Car的对象
car1 : com.linkedbear.spring.bean.c_instantiate.bean.Car@3c0ecd4b
car2 : com.linkedbear.spring.bean.c_instantiate.bean.Car@14bf9759
注意:静态工厂类本身不会被注册到IOC容器中。
实例工厂创建bean
public class CarInstanceFactory {
public Car getCar() {
return new Car();
}
}
<bean id="carInstanceFactory" class="com.test.bean.CarInstanceFactory"/>
<bean id="car3" factory-bean="carInstanceFactory" factory-method="getCar"/>
Bean的生命周期-初始化与销毁
创建 / 实例化阶段:此时会调用类的构造方法,产生一个新的对象
初始化阶段:此时对象已经创建好,但还没有被正式使用,可能这里面需要做一些额外的操作(如预初始化数据库的连接池)
运行使用期:此时对象已经完全初始化好,程序正常运行,对象被使用
销毁阶段:此时对象准备被销毁,已不再使用,需要预先的把自身占用的资源等处理好(如关闭、释放数据库连接)
回收阶段:此时对象已经完全没有被引用了,被垃圾回收器回收
spring框架除了回收阶段干预不了,其他阶段都可以干预。
回想一下Servlet,Servlet
里面有两个方法init
和destroy
。这两个方法都被Web容器调用,用来初始化和销毁Servlet的。这种方法的设计思想就是“回调机制”。它都不是给自己设计的,而是由父类/接口定义好的,由第三者(框架,容器等)来调用。
init-method和destroy-method
1.init-method,destroy-method
public class Dog {
private String name;
public void setName(String name) {
this.name = name;
}
public void init() {
System.out.println(name + "被初始化了。。。");
}
public void destroy() {
System.out.println(name + "被销毁了。。。");
}
}
<bean class="com.test.bean.Cat"
init-method="init" destroy-method="destroy">
<property name="name" value="小熊"/>
</bean>
@Bean(initMethod = "init", destroyMethod = "destroy")
public Dog dog() {
Dog dog = new Dog();
dog.setName("小熊");
return dog;
}
方法特征:
方法访问权限无限制要求(通过反射调用)
方法无参数
方法无返回值
可以抛出异常(异常不由自己处理,交予spring框架可以打断Bean的初始化/销毁步骤)
public class InitMethodXmlApplication {
public static void main(String[] args) throws Exception {
System.out.println("准备初始化IOC容器。。。");
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("resuorce/applicationContext.xml");
System.out.println("IOC容器初始化完成。。。");
System.out.println();
System.out.println("准备销毁IOC容器。。。");
ctx.close();
System.out.println("IOC容器销毁完成。。。");
}
}
准备初始化IOC容器。。。
小熊被初始化了。。。
IOC容器初始化完成。。。
准备销毁IOC容器。。。
小熊被销毁了。。。
IOC容器销毁完成。。。
在IOC容器初始化之前,默认情况下Bean已经创建好了,而且完成了初始化动作。容器调用销毁动作时,先销毁所有Bean,最后IOC容器全部销毁完成。
Bean的声明周期中,是先对属性赋值,后执行init-method标记的方法。
2.JSR250规范
对于使用模式注解的Bean,通过使用注解@PostConstruct,@PreDestroy注解,分别对应init-method和destroy-method。
@Component
public class Pen {
private Integer ink;
@PostConstruct
public void addInk() {
System.out.println("钢笔中已加满墨水。。。");
this.ink = 100;
}
@PreDestroy
public void outwellInk() {
System.out.println("钢笔中的墨水都放干净了。。。");
this.ink = 0;
}
@Override
public String toString() {
return "Pen{" + "ink=" + ink + '}';
}
}
3.InitializingBean和DisposableBean
@Component
public class Pen implements InitializingBean, DisposableBean {
private Integer ink;
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("钢笔中已加满墨水。。。");
this.ink = 100;
}
@Override
public void destroy() throws Exception {
System.out.println("钢笔中的墨水都放干净了。。。");
this.ink = 0;
}
@Override
public String toString() {
return "Pen{" + "ink=" + ink + '}';
}
}
总的初始化执行循序:
@PostConstruct → InitializingBean → init-method 。
注意:原型 Bean 在销毁时不处理 destroy-method 标注的方法。