Spring的原型Bean(Prototype)声明和注入方式

Spring的原型Bean(Prototype)声明和注入方式

目录一、了解单例和原型Bean1.1 什么是单例Bean?什么是原型Bean?1.2 如何去定义一个原型Bean二、注入原型Bean的方法2.1 使用ApplicationContext的getBean每次进行获取2.2 使用@Lookup注解三、使用场景四、参考

一、了解单例和原型Bean

1.1 什么是单例Bean?什么是原型Bean?

单例Bean,相信各位朋友都不陌生,Spring当中的Bean默认就是单例的,也就是无论从什么地方去使用@Autowired或者@Resource等方式去进行注入,拿到的都是同一个对象,这个对象维护在Spring容器当中,每次使用都是直接从Spring容器当中直接进行获取。

原型Bean,也就是说你每次使用到该Bean,都是Spring框架它去重新帮你去进行创建的,也就是说你任意的两次获取该Bean,永远不可能获取到相同的对象。

1.2 如何去定义一个原型Bean

使用@Component、@Bean、@Configuration等注解往容器中注册的Bean,都是单例Bean,要想实现原型Bean,可以通过@Scope注解等方式去配置为Bean的作用域为prototype

比如:

@Component

@Scope(value = BeanDefinition.SCOPE_PROTOTYPE)

public class User {

private int id;

public void setId(int id) {

this.id = id;

}

public int getId() {

return id;

}

}

二、注入原型Bean的方法

因为使用@Autowire注入时,只会在初始化时注入一次,所以每次请求获取的user都是同一个实例。

@Configuration

class UserConfiguration {

// 声明原型bean

@Scope(BeanDefinition.SCOPE_PROTOTYPE)

@Bean

fun user(): UserDO {

return UserDO(0, "root", Date(), Date())

}

}

@RestController

class UserController {

@Autowired

lateinit var user: UserDO

@GetMapping("/user")

fun getUser(): ResultDTO {

// 拿到的user都是同一个实例

println(System.identityHashCode(user))

return ResultDTO.ok(user)

}

}

2.1 使用ApplicationContext的getBean每次进行获取

@RestController

class UserController {

@Autowired

lateinit var applicationContext: ApplicationContext

@GetMapping("/user")

fun getUser(): ResultDTO {

val user = applicationContext.getBean(UserDO::class.java)

println(System.identityHashCode(user))

return ResultDTO.ok(user)

}

}

2.2 使用@Lookup注解

@Lookup注解需要配置在返回类型为具体类型的方法上,spring会实现或覆盖该方法,改为从Ioc容器中获取对象。该方法需要满足以下语法要求

[abstract] theMethodName(no-arguments);

public|protected要求方法必须是可以被子类重写和调用的

abstract可选,如果是抽象方法,CGLIB的动态代理类就会实现这个方法,如果不是抽象方法,就会覆盖这个方法

return-type是非单例的类型

no-arguments不允许有参数

修改后:

@RestController

class UserController {

@GetMapping("/user")

fun getUser(): ResultDTO {

val user = findUser()

println(System.identityHashCode(user))

return ResultDTO.ok(user)

}

@Lookup

fun findUser(): UserDO? {

return null

}

}

如果不配置@Lookup注解的value属性,那么默认是按照方法的返回类型去返回对象

如果配置了@Lookup注解的value属性,那么将会按照beanName去返回对象

Spring会通过CGLIB创建代理对象,然后实现或覆盖@Lookup标注的方法,因此@Lookup标注的方法内部是什么逻辑不重要,直接 return null 就行

三、使用场景

原型bean适用于每次需要一个新对象的时候。比如说通过builder模式创建很多对象,但每个对象又有很多相同的配置,那么可以把builder声明为原型bean。每个对象创建时,通过使用注入的builder进行创建,这样既保留了公共的配置,又能进行自定义配置

spring的JacksonAutoConfiguration里就使用了该方式创建ObjectMapper

@Configuration(proxyBeanMethods = false)

@ConditionalOnClass(Jackson2ObjectMapperBuilder.class)

static class JacksonObjectMapperConfiguration {

@Bean

@Primary

@ConditionalOnMissingBean

ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) {

return builder.createXmlMapper(false).build();

}

}

@Configuration(proxyBeanMethods = false)

@ConditionalOnClass(Jackson2ObjectMapperBuilder.class)

static class JacksonObjectMapperBuilderConfiguration {

@Bean

@Scope("prototype")

@ConditionalOnMissingBean

Jackson2ObjectMapperBuilder jacksonObjectMapperBuilder(ApplicationContext applicationContext,

List customizers) {

Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();

builder.applicationContext(applicationContext);

customize(builder, customizers);

return builder;

}

private void customize(Jackson2ObjectMapperBuilder builder,

List customizers) {

for (Jackson2ObjectMapperBuilderCustomizer customizer : customizers) {

customizer.customize(builder);

}

}

}

通过Jackson2ObjectMapperBuilder,可以创建多个ObjectMapper,这些ObjectMapper可以通过builder做相同的配置

四、参考

https://www.jianshu.com/p/b894edef2966

https://www.cnblogs.com/XiaoZhengYu/p/15732023.html

相关文章

价值与理念-京东方官网

价值与理念-京东方官网

2025-07-25 阅读 2053
网络电台软件哪个最好用 十款常用网络电台软件精选
带有肝字的成语有哪些

带有肝字的成语有哪些

2025-07-11 阅读 7352