怎样解决在单例对象中注入原生对象失效

前言

使用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类代码:

@Component
public 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对象。修改后代码如下:

@Component
public 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来对象为:

@Component
public 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抽象类代码如下:

@Component
public abstract class ServiceImpl  {
//	@Autowired
	User user;

	@Lookup
	public abstract User getPrototypUser();

	public void query() {
		getPrototypUser ().query ();
	}
}

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

@Component
public class ServiceImpl  {
//	@Autowired
	User user;

	@Lookup
	public User getPrototypUser(){
		return null;
	}


	public void query() {
		getPrototypUser ().query ();
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值