【Spring注解必知必会】全面了解@Scope

简介: 【Spring注解必知必会】全面了解@Scope

概述


其实在开发的过程中大家基本上很少使用这个这个注解,我看了下我公司的项目中,完全没用到。但是没用到不代表,没有用,我们今天就来学习这个注解,了解它的基本作用和使用场景。


注解介绍


@Scope, 英文名是范围的意思,用来表示Spring中Bean的作用域范围, 该注解只能写在类上或者方法上。

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Scope {
  @AliasFor("scopeName")
  String value() default "";
  @AliasFor("value")
  String scopeName() default "";
  ScopedProxyMode proxyMode() default ScopedProxyMode.DEFAULT;
}

注解有3个属性,value和scopeName一样,用来表示注解的作用于范围。proxyMode用来为spring bean设置代理。

作用域(value或者scopeName)属性范围

  • singleton: 默认值,单例模式,在整个Spring IoC容器中,使用singleton定义的Bean将只有一个实例。
  • prototype: 原型模式,每次通过容器的getBean方法获取prototype定义的Bean时,都将产生一个新的Bean实例。
  • request: 对于每次HTTP请求,使用request定义的Bean都将产生一个新实例,即每次HTTP请求将会产生不同的Bean实例。只有在Web应用中使用Spring时,该作用域才有效。
  • session: 对于每次HTTP Session,使用session定义的Bean都将产生一个新实例。同样只有在Web应用中使用Spring时,该作用域才有效。

默认的作用域范围为singleton。

proxyMethod属性:

  • DEFAULT:proxyMode的默认值,一般情况下等同于NO,即不需要动态代理。
  • NO:不需要动态代理,即返回的是Bean的实例对象。
  • INTERFACES:代理的对象是一个接口,即@Scope的作用对象是接口,这种情况是基于jdk实现的动态代理。
  • TARGET_CLASS:代理的对象是一个类,即@Scope的作用对象是一个类,上面例子中的ClassB就可以用这种代理,是以生成目标类扩展的方式创建代理,基于CGLib实现动态代理。

后面通过实例来讲解下我们为什么要有这个属性。


使用注解


前面讲了该注解作用在类上或者方法上,但是其实它前提必须是一个Bean,所以存在下面两种情况:

作用在类上

搭配@Component、@Service注解

@Component
@Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
public class Student {
    private String name;
    private Integer age;
    public Student() {
        System.out.println("实例化学生对象~~~");
    }
}

作用在方法上

搭配@Bean注解使用

@Configuration
public class ScopeConfig {
    @Bean
    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    public Student getStudent() {
        return new Student();
    }
}

通常我们不配置Scope情况下的Bean的作用域都是单例模式singleton,不进行任何代理。


实例演示


我们前面讲解了通过Bean如何控制我们Bean的作用域范围,那我们通过例子演示验证下。


原型模式prototype


原型模式prototype,也叫多例模式,每次通过容器的getBean方法获取prototype定义的Bean时,都将产生一个新的Bean实例。

public class PrototypeBean {
    PrototypeBean() {
        System.out.println("实例化 PrototypeBean");
    }
    public void init() {
        System.out.println("初始化 PrototypeBean");
    }
    public void destroy() {
        System.out.println("销毁 PrototypeBean");
    }
}
@Bean(initMethod = "init", destroyMethod = "destroy")
    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    public PrototypeBean prototypeBean() {
        return new PrototypeBean();
    }

验证代码:

PrototypeBean prototypeBean1 = context.getBean(PrototypeBean.class);
 System.out.println(prototypeBean1);
 PrototypeBean prototypeBean2 = context.getBean(PrototypeBean.class);
 System.out.println(prototypeBean2);
 System.out.println(prototypeBean1 == prototypeBean2);
 context.close();

执行结果:

实例化 PrototypeBean
初始化 PrototypeBean
com.alvinlkk.scope.PrototypeBean@26a94fa5
实例化 PrototypeBean
初始化 PrototypeBean
com.alvinlkk.scope.PrototypeBean@464a4442
false

小结:

  1. prototype多例模式,每次在调用getBean() 获取实例时,都会重新实例化,初始化。
  2. prototype多例模式,它的Bean实例对象则不受IOC容器的管理,最终由GC来销毁。


单例模式singleton


默认情况下,Spring Bean都是单例模式,在容器启动的时候,Bean就会创建。

public class SingletonBean {
    SingletonBean() {
        System.out.println("实例化 SingletonBean");
    }
    public void init() {
        System.out.println("初始化 SingletonBean");
    }
    public void destroy() {
        System.out.println("销毁 SingletonBean");
    }
}
@Bean(initMethod = "init", destroyMethod = "destroy")
    @Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
    public SingletonBean singletonBean() {
        return new SingletonBean();
    }

验证代码:

SingletonBean singletonBean1 = context.getBean(SingletonBean.class);
System.out.println(singletonBean1);
SingletonBean singletonBean2 = context.getBean(SingletonBean.class);
System.out.println(singletonBean1 == singletonBean1);

执行结果:

实例化 SingletonBean
初始化 SingletonBean
执行 SingletonBean 测试:
com.alvinlkk.scope.SingletonBean@e8fadb0
com.alvinlkk.scope.SingletonBean@e8fadb0
true
销毁 SingletonBean

小结:

  1. singleton单实例模式下,多次getBean()取到的对象是一样的。
  2. 针对单实例bean的话,容器启动的时候,bean的对象就创建了,而且容器销毁的时候,也会调用Bean的销毁方法。


单实例Bean注入多实例Bean


那么如果单实例中注入了多实例的bean,会是什么样的情况呢?

定义多实例Bean

@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class PrototypeBean1 {
    /**
     * 打印自己这个对象
     */
    public void printCurrentObj() {
        System.out.println(this);
    }
}

定义单实例Bean:

@Component
@Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
public class SingletonBean1 {
    @Autowired
    private PrototypeBean1 prototypeBean;
    public void callProtypeBeanPrint() {
        prototypeBean.printCurrentObj();
    }
}

验证:

SingletonBean1 singletonBean1 = context.getBean(SingletonBean1.class);
for (int i = 0; i < 5; i++) {
    singletonBean1.callProtypeBeanPrint();
}

执行结果:

1671160965039.jpg

在单实例对象Bean中注入多实例对象,最终都是同一个对象,因为在Bean创建的那个时刻被注入了,那有什么棒法变成真正的多实例吗?这时候代理proxyMethod属性派上用场了。

1671160972564.jpg

重新运行测试,查看结果如下:

1671160980549.jpg

发现每个对象都不一样了,本质上是通过代理对象调用方法printCurrentObj时,会重新从容器getBean,获取真实的Bean,这时候会重新创建对象,具体可以查看。blog.csdn.net/geng2568/ar…


总结


几乎90%以上的业务使用 singleton单例就可以,所以 Spring 默认的类型也是singleton,singleton虽然保证了全局是一个实例,对性能有所提高,但是如果实例中有非静态变量时,会导致线程安全问题,共享资源的竞争。

当设置为prototype多例时:每次连接请求,都会生成一个bean实例,也会导致一个问题,当请求数越多,性能会降低,因为创建的实例,导致GC频繁,GC时长增加。

目录
相关文章
|
6天前
|
Java 开发者 Spring
【SpringBoot 异步魔法】@Async 注解:揭秘 SpringBoot 中异步方法的终极奥秘!
【8月更文挑战第25天】异步编程对于提升软件应用的性能至关重要,尤其是在高并发环境下。Spring Boot 通过 `@Async` 注解简化了异步方法的实现。本文详细介绍了 `@Async` 的基本用法及配置步骤,并提供了示例代码展示如何在 Spring Boot 项目中创建与管理异步任务,包括自定义线程池、使用 `CompletableFuture` 处理结果及异常情况,帮助开发者更好地理解和运用这一关键特性。
51 1
|
15天前
|
XML Java 测试技术
Spring5入门到实战------17、Spring5新功能 --Nullable注解和函数式注册对象。整合JUnit5单元测试框架
这篇文章介绍了Spring5框架的三个新特性:支持@Nullable注解以明确方法返回、参数和属性值可以为空;引入函数式风格的GenericApplicationContext进行对象注册和管理;以及如何整合JUnit5进行单元测试,同时讨论了JUnit4与JUnit5的整合方法,并提出了关于配置文件加载的疑问。
Spring5入门到实战------17、Spring5新功能 --Nullable注解和函数式注册对象。整合JUnit5单元测试框架
|
3天前
|
缓存 Java 数据库连接
Spring Boot奇迹时刻:@PostConstruct注解如何成为应用初始化的关键先生?
【8月更文挑战第29天】作为一名Java开发工程师,我一直对Spring Boot的便捷性和灵活性着迷。本文将深入探讨@PostConstruct注解在Spring Boot中的应用场景,展示其在资源加载、数据初始化及第三方库初始化等方面的作用。
13 0
|
15天前
|
Java 数据安全/隐私保护 Spring
揭秘Spring Boot自定义注解的魔法:三个实用场景让你的代码更加优雅高效
揭秘Spring Boot自定义注解的魔法:三个实用场景让你的代码更加优雅高效
|
15天前
|
XML Java 数据库
Spring5入门到实战------15、事务操作---概念--场景---声明式事务管理---事务参数--注解方式---xml方式
这篇文章是Spring5框架的实战教程,详细介绍了事务的概念、ACID特性、事务操作的场景,并通过实际的银行转账示例,演示了Spring框架中声明式事务管理的实现,包括使用注解和XML配置两种方式,以及如何配置事务参数来控制事务的行为。
Spring5入门到实战------15、事务操作---概念--场景---声明式事务管理---事务参数--注解方式---xml方式
|
15天前
|
XML 数据库 数据格式
Spring5入门到实战------14、完全注解开发形式 ----JdbcTemplate操作数据库(增删改查、批量增删改)。具体代码+讲解 【终结篇】
这篇文章是Spring5框架的实战教程的终结篇,介绍了如何使用注解而非XML配置文件来实现JdbcTemplate的数据库操作,包括增删改查和批量操作,通过创建配置类来注入数据库连接池和JdbcTemplate对象,并展示了完全注解开发形式的项目结构和代码实现。
Spring5入门到实战------14、完全注解开发形式 ----JdbcTemplate操作数据库(增删改查、批量增删改)。具体代码+讲解 【终结篇】
|
15天前
|
XML Java 数据格式
Spring5入门到实战------8、IOC容器-Bean管理注解方式
这篇文章详细介绍了Spring5框架中使用注解进行Bean管理的方法,包括创建Bean的注解、自动装配和属性注入的注解,以及如何用配置类替代XML配置文件实现完全注解开发。
Spring5入门到实战------8、IOC容器-Bean管理注解方式
|
3天前
|
监控 安全 Java
【开发者必备】Spring Boot中自定义注解与处理器的神奇魔力:一键解锁代码新高度!
【8月更文挑战第29天】本文介绍如何在Spring Boot中利用自定义注解与处理器增强应用功能。通过定义如`@CustomProcessor`注解并结合`BeanPostProcessor`实现特定逻辑处理,如业务逻辑封装、配置管理及元数据分析等,从而提升代码整洁度与可维护性。文章详细展示了从注解定义、处理器编写到实际应用的具体步骤,并提供了实战案例,帮助开发者更好地理解和运用这一强大特性,以实现代码的高效组织与优化。
|
15天前
|
设计模式 Java 测试技术
公司为何禁止在SpringBoot中使用@Autowired注解?
【8月更文挑战第15天】在Spring Boot的广泛应用中,@Autowired注解作为依赖注入的核心机制之一,极大地简化了Bean之间的装配过程。然而,在某些企业环境下,我们可能会遇到公司政策明确禁止或限制使用@Autowired注解的情况。这一决策背后,往往蕴含着对代码质量、可维护性、测试便利性以及团队开发效率等多方面的考量。以下将从几个方面深入探讨这一决定的合理性及替代方案。
24 0
|
15天前
|
XML Java 数据库
Spring5入门到实战------10、操作术语解释--Aspectj注解开发实例。AOP切面编程的实际应用
这篇文章是Spring5框架的实战教程,详细解释了AOP的关键术语,包括连接点、切入点、通知、切面,并展示了如何使用AspectJ注解来开发AOP实例,包括切入点表达式的编写、增强方法的配置、代理对象的创建和优先级设置,以及如何通过注解方式实现完全的AOP配置。
下一篇
云函数