博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
怎样解决在单例对象中注入原生对象失效
阅读量:2348 次
发布时间:2019-05-10

本文共 4201 字,大约阅读时间需要 14 分钟。

前言

使用spring框架时,当在单例对象中注入一个原生对象是,注入的原生会变成单例对象,本文就这一问题介绍几种 解决方案。

在Spring Framework官方文档的第一章第四节方法注入中,最后一个点方法注入,详细描绘了这个问题,也为此提出了解决方案。截图如下:

官网叙述

1、还原问题

1.1 创建原生对象User

User类代码:

@Component@Scope("prototype")public class User {
public User() {
System.out.println ("Constrctor of user"); } public void query(){
System.out.println ("useer `s hashCode"+this.hashCode ()); }}

把User类交给spring IOC容器管理,将其设置为原生对象,创建无参构造方法,打印一行文字,再添加一个普通的方法,打印当前对象的哈希值。

1.2 测试原生对象

测试类代码:

public class MainTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext (AppConfig.class); annotationConfigApplicationContext.getBean (User.class).query (); annotationConfigApplicationContext.getBean (User.class).query (); annotationConfigApplicationContext.getBean (User.class).query (); annotationConfigApplicationContext.getBean (User.class).query (); }}

测试结果:

原生对象测试结果
通过运行结果可以看出,user的构造方法执行了五次,第一次是spring IoC容器创建时调用的,其他四次是在测试类中获取user对象时调用的。user对象的哈希值打印了四次,四次打印的值都不同,所以可以证明四次实行的对象不是同一个对象,在测试类中使用annotationConfigApplicationContext对象的getBean方法获取一次对象spring IOC容器就会创建一个user对象。

1.3 创建一个单例类,引入原生对象

创建一个ServiceImpl类,注入User类,添加一个方法,调用user类的query方法。

ServiceImpl类代码:

@Componentpublic class ServiceImpl {
@Autowired User user; public void query() {
user.query (); }}

1.4 测试单例类

测试类代码:

public class MainTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext (AppConfig.class); annotationConfigApplicationContext.getBean (ServiceImpl.class).query (); annotationConfigApplicationContext.getBean (ServiceImpl.class).query (); annotationConfigApplicationContext.getBean (ServiceImpl.class).query (); annotationConfigApplicationContext.getBean (ServiceImpl.class).query (); }}

测试结果:

单例类测试结果
通过测试结果可以看出,user类的构造方法值执行了一次,通过serviceImpl对象打印四次user对象的哈希值都相同,证明四次调用的对象都是同一对象。

结果分析:以为ServiceImpl类是单例的,所以在Spring IoC容器创建该对象时只创建了一个ServiceImpl实例对象,同时创建了一个user实例对象,只后将user实例对象注册到ServiceImpl实例对象中。因为ServiceImpl类是单例的,所以在测试方法中使用getBean方法获取ServiceImpl对象时,annotationConfigApplicationContext对象直接从Spring IoC容器中获取,得到的ServiceImpl对象由于已经在初始化过程中已注入了user对象,所以可直接使用,整个过程中没有通过annotationConfigApplicationContext对象获取user对象,因此ServiceImpl对象中user的原生属性失效。

2、解决方案

2.1 实现 ApplicationContextAware 接口直接在Spring 容器中获取原生对象

在spring framework官方文档中,1.4.6的方法注入中给了我们关于这个问题的解决方案。如图:

spring framework官网实现接口解决方案

可以是单例对对象的类实现 ApplicationContextAware接口,该接口提供了setApplicationContext方法,传入了applicationContext对象,及Spring上下文对象,程序员可以通过该对象直接获取原型类对象,来解决上述问题。

具体解决方法为,在ServiceImpl中引入一个ApplicationContext对象,继承ApplicationContextAware接口,重写setApplicationContext方法,将该方法传入的ApplicationContext对象赋值给ServiceImpl类中的ApplicationContext对象。最后在query方法中使用ApplicationContext对象获取user对象。修改后代码如下:

@Componentpublic class ServiceImpl implements ApplicationContextAware {
ApplicationContext applicationContext;// @Autowired User user; public void query() {
applicationContext.getBean (User.class).query (); } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext; }}

运行测试类结果为:

2.1测试结果

可以看到已经解决了上述问题。这种解决方案的弊端在于必须实现ApplicationContextAware接口,spring framework的侵入性较强。

这种方法的核心在于使用applicationContext对象重新获取原生对象,因为applicationContext对象本身就在Spring IoC容器中,所以可以直接将applicationContext对象注入到单例对象中。修改后的ServiceImpl来对象为:

@Componentpublic class ServiceImpl  {
@Autowired ApplicationContext applicationContext;// @Autowired User user; public void query() {
applicationContext.getBean (User.class).query (); }}

2.2 Lookup方法注入

将单例对象类修改成抽象了类,加入一个返回值类型为原型类的抽象方法,将该方法加上一个Lookup注解,这样spring framework就会利用CGLIB代理方法实现这个类,返回一个原生对象。spring官网中的截图如下:

Lookup方法注入官网截图

修改后的ServiceImpl抽象类代码如下:

@Componentpublic abstract class ServiceImpl  {
// @Autowired User user; @Lookup public abstract User getPrototypUser(); public void query() {
getPrototypUser ().query (); }}

因为spring framework是用代理的方法实现这个类,程序员也可以将上述的抽象方法改成返回值为空的方法,也是可以同样的效果。这种方法在官网上没有提到,但博主经过试验也可是可以,希望大家谨慎采用。更改后的ServiceImpl的代码如下:

@Componentpublic class ServiceImpl  {
// @Autowired User user; @Lookup public User getPrototypUser(){
return null; } public void query() {
getPrototypUser ().query (); }}

转载地址:http://wntvb.baihongyu.com/

你可能感兴趣的文章
Java 编程中异常处理的最佳实践
查看>>
Java异常处理机制
查看>>
Java:回调机制
查看>>
axis2创建web service
查看>>
Axis,axis2,Xfire以及cxf对比
查看>>
近几年,产品经理这个岗位越来越火(2012-2013)
查看>>
准确率99.80%!腾讯优图再次刷新人脸识别世界纪录
查看>>
【工具】人脸识别比对开放平台汇总
查看>>
基于DirectUI技术开发的发卡系统
查看>>
STM32 HAL库、标准外设库、LL库(STM32 Embedded Software)
查看>>
51和AVR单片机
查看>>
DSP开发板
查看>>
stm32标准外设库和芯片资料下载地址
查看>>
ARM Keil MDK开发STM32工程模板
查看>>
NoSQL分类及常用软件
查看>>
ubuntu 16.04安装nVidia显卡驱动和cuda/cudnn踩坑过程
查看>>
谷歌TPU优化新进展:数据吞吐提升15倍、每瓦特性能猛增
查看>>
基于STM32CubeMX创建STM32L496ZGTx的工程
查看>>
如何通过OpenFace实现人脸识别框架
查看>>
人脸检测与识别年度进展概述
查看>>