首页 > 其他分享 >Spring的原型Bean(Prototype)声明和注入方式

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

时间:2023-05-17 21:36:36浏览次数:38  
标签:return Spring builder Bean 原型 user Prototype class

目录

一、了解单例和原型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<UserDO> {
        // 拿到的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<UserDO> {
        val user = applicationContext.getBean(UserDO::class.java)
        println(System.identityHashCode(user))
        return ResultDTO.ok(user)
    }
}

2.2 使用@Lookup注解

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

<public|protected> [abstract] <return-type> theMethodName(no-arguments);
  • public|protected要求方法必须是可以被子类重写和调用的
  • abstract可选,如果是抽象方法,CGLIB的动态代理类就会实现这个方法,如果不是抽象方法,就会覆盖这个方法
  • return-type是非单例的类型
  • no-arguments不允许有参数

修改后:

@RestController
class UserController {

    @GetMapping("/user")
    fun getUser(): ResultDTO<UserDO> {
        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<Jackson2ObjectMapperBuilderCustomizer> customizers) {
		Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
		builder.applicationContext(applicationContext);
		customize(builder, customizers);
		return builder;
	}

	private void customize(Jackson2ObjectMapperBuilder builder,
			List<Jackson2ObjectMapperBuilderCustomizer> 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

标签:return,Spring,builder,Bean,原型,user,Prototype,class
From: https://www.cnblogs.com/wusanga/p/17410332.html

相关文章

  • 28、说说Java Bean的命名规范
    JavaBean类必须是一个公共类,并将其访问属性设置为publicJavaBean类必须有一个空的构造函数:类中必须有一个不带参数的公用构造器,此构造器也应该通过调用各个特性的设置方法来设置特性的缺省值。一个javaBean类不应有公共实例变量,类变量都为private持有值应该通过一组存取方法(g......
  • spring validation 分组手动校验
    定义两个分组接口GroupDefaultOne、GroupDefaultTwo,接口可以继承javax.validation.groups.Default类,也可以不继承。如果继承那么当校验分组的时候,会自动校验没有带分组的参数注解,如@NotNull(message="xxxx不能为Null")如果不继承那么当校验分组的时候,只会校验带了对应......
  • 【渗透测试】关闭springboot中actuator监控
    正常情况下,为了安全,可以通过如下配置关闭监控点management:endpoints:enabled-by-default:false之后访问如health,info等端点,就会报404了,但是如果单纯的访问/actuator路径,还是会有一些信息返回的,这样至少说明了/actuator路径是可以访问的,此时如果有严格的安全渗透......
  • springCloud Alibaba服务的注册与发现之eureka客户端注册
    1、在客户端maven项目中添加eureka客户端依赖<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId></dependency>2、在工程application.yml文件中......
  • SpringBoot基于注解来动态切换数据源
    前言我们在日常开发中,经常会用到多数据源,实现的方式有很多种,我这里分享一种通过动态数据源的方式来实现多数据源。通过自定义一个注解DS加上AOP来动态切换数据源。我们的注解可以作用于类、方法、接口、接口方法上。优先级为:类方法>类>接口方法>接口SpringBoot的动态数据源,实际......
  • 【Jmeter】BeanShell 脚本
    一、BeanShellBeanShell是由java编写的,是一个轻量级的脚本语言,也相当于一个小巧免费的JAVA源码解释器,支持对象式的脚本语言特性,亦可嵌入到JAVA源代码中,能动态执行JAVA源代码并为其扩展了脚本语言的一些特性BeanShell是一种完全符合Java语法规范的脚本语言,并且又拥有自己......
  • Spring Boot整合Mybatis Plus逆向工程
    1.导入依赖主要导入Mybatis-Plus-generator版本需要与Mybatis-Plus版本一致<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-generator</artifactId><version>3.4.1</versio......
  • SpringBoot项目预加载数据——ApplicationRunner、CommandLineRunner、InitializingBe
    0、参考、业务需求参考:https://www.cnblogs.com/java-chen-hao/p/11835120.html#_label1https://zhuanlan.zhihu.com/p/541268993业务需求:缓存数据字典数据、初始化线程池、提前加载好加密证书1、方式实现ApplicationRunner接口实现CommandLineRunner接口实现In......
  • SpringBootWeb案例 —— ①
     ......
  • 【小小demo】SpringBoot+Layui登录
    easy-login基于layui注册、登录简单实现,并他通过拦截器拦截未登录请求。项目地址文章末尾登录拦截器SystemInterceptorpreHandle在Controller之前执行,因此拦截器的功能主要就是在这个部分实现:检查session中是否有user对象存在;如果存在,就返回true,那么Controller就......