bean的创建方式

尽管spring中声明bean的形式多种多样,但spring容器创建bean主要有两种方式:构造器创建bean和工厂模式创建bean。

参考官方文档相关小节 - 实例化bean

参考练习examples/spring-03-create-bean

构造器创建bean
调用无参构造

在我们通过xml声明bean时,如果我们没有指定构造器参数,则默认会使用无参构造来创建bean。

<bean id="myBook" class="com.xiaoma.spring.example.reading.Book" />

如果我们为上面的bean声明提供这样的Book类,容器启动会报错:

package com.xiaoma.spring.example.reading;

import ...

@Data
//@NoArgsConstructor
@AllArgsConstructor
@Slf4j
public class Book {
	private String name;
	private String type;

//	public Book() {
//		log.info("调用了Book的无参构造");
//	}
}

因为无参的构造器我们没有显式的定义

Error creating bean with name 'myBook' defined in class path resource [application-context.xml]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.xiaoma.spring.example.reading.Book]: No default constructor found; nested exception is java.lang.NoSuchMethodException: com.xiaoma.spring.example.reading.Book.<init>()
...
调用有参构造

我们可以在<bean>标签中指定<constructor-arg>来让spring根据我们指定的实参,找到并调用合适的构造器来完成bean的创建。比如:

<bean id="funnyBook" class="com.xiaoma.spring.example.reading.Book">
    <constructor-arg name="name" value="金瓶梅" />
    <constructor-arg name="type" value="古典文学" />
</bean>
<bean id="dabao" class="com.xiaoma.spring.example.reading.Child">
    <constructor-arg name="name" value="大宝" />
    <!-- 通过ref属性引用其他bean -->
    <constructor-arg name="book" ref="funnyBook" />
</bean>

spring在使用构造器创建bean时,如果构造器参数涉及到bean的引用,会先尝试获取相应的bean作为构造器实参来完成bean创建的成员变量(bean依赖)初始化工作,关于从spring容器获取bean的细节这里我们暂时不作讨论。

创建静态内部类

AuthorBook的静态内部类

public class Book {
   ...

   static class Author {
      public Author() {
         log.info("调用了静态内部类Author的无参构造");
      }
   }
}

在外部手动new一个静态内部类的实例,我们会使用.的形式:

Book.Author author = new Book.Author();

而在xml中声明内部类时采用$的形式:

<bean id="author" class="com.xiaoma.spring.example.reading.Book$Author" />
工厂模式创建bean

包括了用静态工厂、实例工厂(工厂bean)来创建bean。这种方式可以确保创建一个bean之前可以先做一些资源的初始化获取工作,在工厂中封装bean构建的细节,也可以对构建步骤执行日志输出以方便调试、排查问题。

静态工厂
package com.xiaoma.spring.example.reading.factory;

import ...

@Slf4j
public class BookStaticFactory {
   public static Book createBook(String name, String type) {
      log.info("调用静态工厂来创建对象,name: {}, type: {}", name, type);
      return new Book(name, type);
   }
}
<bean id="boringBook" class="com.xiaoma.spring.example.reading.factory.BookStaticFactory" factory-method="createBook">
    <constructor-arg name="name" value="Spring入门" />
    <constructor-arg name="type" value="IT" />
</bean>
实例工厂
package com.xiaoma.spring.example.reading.factory;

import ...

@Slf4j
public class BookFactory {
   public Book create(String name, String type) {
      log.info("调用实例工厂来创建对象,name: {}, type: {}", name, type);
      return new Book(name, type);
   }
}
<bean id="bookFactory" class="com.xiaoma.spring.example.reading.factory.BookFactory" />
<bean id="funnyBook" factory-bean="bookFactory" factory-method="create">
   <constructor-arg name="name" value="金瓶梅" />
   <constructor-arg name="type" value="古典文学" />
</bean>

实例工厂创建bean还有一种简化形式,继承AbstractFactoryBean

package com.xiaoma.spring.example.reading.factory;

import ...

@Slf4j
@Setter
public class BookFactoryBean extends AbstractFactoryBean<Book> {

   private String name;
   private String type;
   
   @Override
   public Class<?> getObjectType() {
      return Book.class;
   }

   @Override
   protected Book createInstance() throws Exception {
      log.info("调用FactoryBean来创建对象,name: {}, type: {}", name, type);
      return new Book(name, type);
   }
}
<bean id="funnyBook" class="com.xiaoma.spring.example.reading.factory.BookFactoryBean">
   <property name="name" value="葵花宝典" />
   <property name="type" value="武功秘籍" />
</bean>

注意获取FactoryBean创建出来的bean和获取FactoryBean本身的区别:

Book funnyBook = context.getBean("funnyBook", Book.class);
BookFactoryBean bookFactoryBean = context.getBean("&funnyBook", BookFactoryBean.class);

轻松一刻

有意思的是在从spring容器获取FactoryBean时,前面可以指定任意多的&符号。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Java小卷

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值