我注意到我的原型范围的Spring bean的@PreDestroy
挂钩没有被执行。
我已经读过here这实际上是设计的。 Spring容器将销毁单例bean,但不会销毁原型bean。我不清楚为什么。如果Spring容器将创建我的原型bean并执行其@PostConstruct
挂钩,为什么它不会在容器关闭时销毁我的bean?一旦我的Spring容器关闭,继续使用它的任何bean都有意义吗?我看不到你想要在完成bean之前关闭容器的场景。是否可以在容器关闭后继续使用原型Spring bean?
上面描述了我的主要问题的令人费解的背景:如果Spring容器没有破坏原型bean,那是否意味着可能发生内存泄漏?或者原型bean会在某个时刻被垃圾收集吗?
春季文件指出:
客户端代码必须清理原型范围的对象并释放 原型bean持有的昂贵资源。得到的 Spring容器释放由原型范围的bean持有的资源, 尝试使用自定义bean后处理器,它包含一个引用 需要清理的豆子。
这是什么意思?该文本告诉我,我作为程序员负责明确(手动)销毁我的原型bean。它是否正确?如果是这样,我该怎么做?
答案 0 :(得分:11)
为了他人的利益,我将在下面列出我从调查中收集到的内容:
只要原型bean本身不包含对另一个资源(如数据库连接或会话对象)的引用,只要删除了对该对象的所有引用或者对象都没有被删除,它就会被垃圾收集。范围。因此,通常没有必要明确销毁原型bean。
但是,在如上所述可能发生内存泄漏的情况下,可以通过创建单一bean后处理器来销毁原型bean,其后处理方法显式调用原型bean的销毁挂钩。因为后处理器本身是单例范围的,所以它的破坏钩子将由Spring调用:
创建一个bean post处理器来处理所有原型bean的销毁。这是必要的,因为Spring不会破坏原型bean,因此代码中的任何@PreDestroy挂钩永远不会被容器调用。
实现以下接口:
1的了BeanFactoryAware 强>
该接口提供了一个接收Beanfactory对象的回调方法。此BeanFactory对象在后处理器类中用于通过其BeanFactory.isPrototype(String beanName)方法标识所有原型bean。
2。的 DisposableBean的强>
此接口提供Spring容器调用的Destroy()回调方法。我们将在此方法中调用所有原型bean的Destroy()方法。
3。的的BeanPostProcessor 强>
实现此接口提供对进程内回调的访问,我们在其中准备内部List<> Spring容器实例化的所有原型对象。我们稍后将遍历此List<>摧毁我们的每一个原型豆。
3.最后在每个原型bean中实现DisposableBean接口,提供此合同所需的Destroy()方法。
为了说明这个逻辑,我提供了以下代码article:
/**
* Bean PostProcessor that handles destruction of prototype beans
*/
@Component
public class DestroyPrototypeBeansPostProcessor implements BeanPostProcessor, BeanFactoryAware, DisposableBean {
private BeanFactory beanFactory;
private final List<Object> prototypeBeans = new LinkedList<>();
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (beanFactory.isPrototype(beanName)) {
synchronized (prototypeBeans) {
prototypeBeans.add(bean);
}
}
return bean;
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
@Override
public void destroy() throws Exception {
synchronized (prototypeBeans) {
for (Object bean : prototypeBeans) {
if (bean instanceof DisposableBean) {
DisposableBean disposable = (DisposableBean)bean;
try {
disposable.destroy();
} catch (Exception e) {
e.printStackTrace();
}
}
}
prototypeBeans.clear();
}
}
}
答案 1 :(得分:0)
您的回答很好。我还想分享有关替代解决方案的一些说明,该解决方案允许原型成员通过使用内部bean由Spring IoC容器生命周期本地管理。
我最近在一个有关内部bean的问题上写了answer。通过将bean属性值分配为BeanDefinition
对象来创建内部bean。 Bean定义属性值会自动解析为其定义的(inner)
个实例(作为托管的Singleton Bean)。
以下XML上下文配置元素可用于为将要管理的每个引用创建不同的可自动连接的ForkJoinPool
bean({@PreDestroy
将在上下文关闭时调用):
<!-- Prototype-scoped bean for creating distinct FJPs within the application -->
<bean id="forkJoinPool" class="org.springframework.beans.factory.support.GenericBeanDefinition" scope="prototype">
<property name="beanClass" value="org.springframework.scheduling.concurrent.ForkJoinPoolFactoryBean" />
</bean>
但是,此行为取决于引用被分配为bean定义的属性值。这意味着@Autowired
和构造函数注入默认情况下不起作用,因为这些自动装配方法会立即解析值,而不是在AbstractAutowireCapableBeanFactory#applyPropertyValues
中使用属性值解析。按类型自动装配也将不起作用,因为类型解析不会通过BeanDefinition
的bean传播以查找产生的类型。
仅当以下两个条件之一成立时,此方法才有效:
AutowireCapableBeanFactory#AUTOWIRE_BY_NAME
<!-- Setting bean references through XML -->
<beans ...>
<bean id="myOtherBean" class="com.example.demo.ForkJoinPoolContainer">
<property name="forkJoinPool" ref="forkJoinPool" />
</bean>
</beans>
<!-- Or setting the default autowire mode -->
<beans default-autowire="byName" ...>
...
</beans>
可能还要进行两项其他更改以启用构造函数注入和@Autowired
注入。
构造函数注入:
bean工厂为构造函数注入分配了AutowireCandidateResolver
。可以重写默认值(ContextAnnotationAutowireCandidateResolver
来应用(DefaultListableBeanFactory#setAutowireCandidateResolver
)来应用候选解析器,该解析器将寻找类型为BeanDefinition
的合格bean进行注入。
@Autowired
-注入:
AutowiredAnnotationBeanPostProcessor
bean后处理器直接设置bean值,而无需解析BeanDefinition
内部bean。该后处理器可以被覆盖,或者可以创建一个单独的bean后处理器来处理托管原型bean的自定义注释(例如@AutowiredManagedPrototype
)。