Spring框架:IoC和AOP是扩展的核心

单例Bean中出现OOM内存溢出问题
### 架构师写的基类
@Slf4j
public abstract class SayService {
    List<String> data = new ArrayList<>();

    public void say() {
        data.add(IntStream.rangeClosed(1, 1000000)
                .mapToObj(__ -> "a")
                .collect(Collectors.joining("")) + UUID.randomUUID().toString());
        log.info("I'm {} size:{}", this, data.size());
    }
  ###开发人员进行实现,然后每次都进行调用父类的say方法
  
@Service
@Slf4j
public class SayHello extends SayService {
    @Override
    public void say() {
        super.say();
        log.info("hello");
    }
}

@Service
@Slf4j
public class SayBye extends SayService {
    @Override
    public void say() {
        super.say();
        log.info("bye");
    }
}
}

出现问题:一直想data中List 一直加数据,会出现OOM内存溢出问题

这一个点非常容易忽略。开发基类的架构师将基类设计为有状态的,但并不知道子类是怎么使用基类的;而开发子类的同学,没多想就直接标记了 @Service,让类成为了 Bean,通过 @Autowired 注解来注入这个服务。但这样设置后,有状态的基类就可能产生内存泄露或线程安全问题。

解决方法:让Bean不再是单例,而是多例Bean

正确的方式是,在为类标记上 @Service 注解把类型交由容器管理前,首先评估一下类是否有状态,然后为 Bean 设置合适的 Scope。好在上线前,架构师发现了这个内存泄露问题,开发同学也做了修改,为 SayHello 和 SayBye 两个类都标记了 @Scope 注解,设置了 PROTOTYPE 的生命周期,也就是多例:


@Scope(value=ConfigurableBeanFactory.SCOPE_PROTOTYPE)这个是说在每次注入的时候回自动创建一个新的bean实例
@Scope(value=ConfigurableBeanFactory.SCOPE_SINGLETON)单例模式,在整个应用中只能创建一个实例
@Scope(value=WebApplicationContext.SCOPE_GLOBAL_SESSION)全局session中的一般不常用
@Scope(value=WebApplicationContext.SCOPE_APPLICATION)在一个web应用中只创建一个实例
@Scope(value=WebApplicationContext.SCOPE_REQUEST)在一个请求中创建一个实例
@Scope(value=WebApplicationContext.SCOPE_SESSION)每次创建一个会话中创建一个实例
里面还有个属性
proxyMode=ScopedProxyMode.INTERFACES创建一个JDK代理模式
proxyMode=ScopedProxyMode.TARGET_CLASS基于类的代理模式
proxyMode=ScopedProxyMode.NO(默认)不进行代理

但是上线之后有出现问题:还是会出现OOM异常

!!!!这里的意思有点不懂需要进一步去思考理解
这就引出了单例的 Bean 如何注入 Prototype 的 Bean 这个问题。Controller 标记了 @RestController 注解,而 @RestController 注解 =@Controller 注解 +@ResponseBody 注解,又因为 @Controller 标记了 @Component 元注解,所以 @RestController 注解其实也是一个 Spring Bean:


//@RestController注解=@Controller注解+@ResponseBody注解@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller
@ResponseBody
public @interface RestController {}

//@Controller又标记了@Component元注解
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Controller {}

解决方法:

Bean 默认是单例的,所以单例的 Controller 注入的 Service 也是一次性创建的,即使 Service 本身标识了 prototype 的范围也没用。
修复方式是,让 Service 以代理方式注入。这样虽然 Controller 本身是单例的,但每次都能从代理获取 Service。这样一来,prototype 范围的配置才能真正生效:
加上代理类:@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE, proxyMode = ScopedProxyMode.TARGET_CLASS)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值