基于Spring,属性注入动态对象

15 篇文章 0 订阅
2 篇文章 0 订阅


前言

今天遇到前同事在群里咨询一个问题,虽然他不是问我的。我就大概想了下思路。
问题如下:
有什么框架可以动态取值么? 比如有一个对象 A 里面有B,B对象有值 c。 通过b.c 来取值

一、思路

其实如果看过我这篇博客(Spring如何实现注入request)的,应该都会有思路。正好我之前在读源码的时候也做了类似的小例子,此处把思路分享下。我写的是一个简单的A调用B,B返回的对象里面有个字段是随机数。

二、使用步骤

1.注入对象类型必须是接口

/**
 * 注入的对象类型
 */
public interface WangObjInterface {

	long getTime();
}

同时我们需要定义一个实际实现接口的对象实体,使得真正注入时调用,类似于request里的我们从Tomcat处拿到的封装好的HttpServletRequest的对象

/**
 * 实际注入的对象
 */
public class WangObj implements WangObjInterface {

	private long time;

	public WangObj() {
		long randomValue = new Random().nextLong();
		randomValue = Math.abs(randomValue);
		this.time = Long.MAX_VALUE - randomValue;
	}

	@Override
	public long getTime() {
		return time;
	}
}

2. 注入的实际对象必须是一个ObjectFactory

/**
 * 参照Request的原理,让注入的对象可以不停变化:此处我们想要注入的对象是WangObjInterface类型的
 */
public class WangFactory implements ObjectFactory<WangObjInterface>, Serializable {

	private static final long serialVersionUID = -1L;

	@Override
	public WangObjInterface getObject() throws BeansException {
		return new WangObj();
	}

	@Override
	public String toString() {
		return "my object factory";
	}
}

3. 注入的对象属性必须在DefaultListableBeanFactory#resolvableDependencies里

此处,咱们通过自定义BeanFactoryPostProcessor实现

@Component
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
	@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
		beanFactory.registerResolvableDependency(WangObjInterface.class, new WangFactory());
	}
}

4. 测试实现

咱们自定义一个WangObjectService,里面注入一个WangObjInterface,而我们希望真正注入的是动态的WangObj对象,咱们用来标志是否是不同的WangObj对象的方式是通过调用它的getTime方法,getTime返回了随机数和时间戳的差值。

@Service
public class WangObjectService {

	@Autowired
	private WangObjInterface wangObjInterface;

	public long getTime() {
		return wangObjInterface.getTime();
	}

	public WangObjInterface getWangObjInterface() {
		return wangObjInterface;
	}

	public void setWangObjInterface(WangObjInterface wangObjInterface) {
		this.wangObjInterface = wangObjInterface;
	}
}

好了,来看测试逻辑代码

	WangObjectService wangObjectService = ac.getBean(WangObjectService.class);
	System.out.println(wangObjectService.getWangObjInterface());
	for (int i = 0; i < 5; i++) {
		System.out.println("time:" + wangObjectService.getTime());
	}

返回值打印:

my object factory
time:7309532888751391840
time:509858204091199019
time:1410838547326061649
time:8688668706783773577
time:1226783376940146402

可以看到打印结果,my object factory 代表注入的就是我们的WangFactory对象,然后通过getTime打印,发现每次都会重新调用MyFactory#getObject方法,获取新的MyObj对象。

三、ObjectFactory 和 FactoryBean

大家应该发现了ObjectFactory接口和FactoryBean接口有点像

3.1 ObjectFactory

@FunctionalInterface
public interface ObjectFactory<T> {

	/**
	 * Return an instance (possibly shared or independent)
	 * of the object managed by this factory.
	 * @return the resulting instance
	 * @throws BeansException in case of creation errors
	 */
	T getObject() throws BeansException;

}

3.2 FactoryBean

public interface FactoryBean<T> {

	/**
	 * Return an instance (possibly shared or independent) of the object
	 * managed by this factory.
	 * <p>As with a {@link BeanFactory}, this allows support for both the
	 * Singleton and Prototype design pattern.
	 * <p>If this FactoryBean is not fully initialized yet at the time of
	 * the call (for example because it is involved in a circular reference),
	 * throw a corresponding {@link FactoryBeanNotInitializedException}.
	 * <p>As of Spring 2.0, FactoryBeans are allowed to return {@code null}
	 * objects. The factory will consider this as normal value to be used; it
	 * will not throw a FactoryBeanNotInitializedException in this case anymore.
	 * FactoryBean implementations are encouraged to throw
	 * FactoryBeanNotInitializedException themselves now, as appropriate.
	 * @return an instance of the bean (can be {@code null})
	 * @throws Exception in case of creation errors
	 * @see FactoryBeanNotInitializedException
	 */
	@Nullable
	T getObject() throws Exception;

	@Nullable
	Class<?> getObjectType();

	default boolean isSingleton() {
		return true;
	}
}

哪怕自己把WangObj的的scope设置为“prototype”,或者定义FactoryBean将isSingleton重写为false,对于WangObjectService来说,它注入的WangObj永远是同一个。不过想想我们上一篇文章(@Lazy的实现原理),我又有了一个新方案。

四、一个新方案(prototype + @Lazy)

咱们在上一篇文章(@Lazy的实现原理)中说明了,当注入的属性添加了@Lazy注解的时候,其实注入的是一个proxy代理对象,每次使用的时候,会重新通过调用getTarget间接调用getBean方法。

4.1 将WangObj的作用域scope改为prototype

@Component
@Scope("prototype")
public class WangObj implements WangObjInterface {

	private long time;

	public WangObj() {
		long randomValue = new Random().nextLong();
		randomValue = Math.abs(randomValue);
		this.time = Long.MAX_VALUE - randomValue;
	}

	@Override
	public long getTime() {
		return time;
	}
}

4.2 将WangObjectService里的注入加上@Lazy注解

@Service
public class WangObjectService {

	@Autowired
	@Lazy
	private WangObjInterface wangObjInterface;

	public long getTime() {
		return wangObjInterface.getTime();
	}

	public WangObjInterface getWangObjInterface() {
		return wangObjInterface;
	}

	public void setWangObjInterface(WangObjInterface wangObjInterface) {
		this.wangObjInterface = wangObjInterface;
	}
}

4.3 测试结果

接口打印:

com.wang.object.obj.WangObj@202b0582
time:2945488805692724694
time:4901411098222725520
time:9100103087754096939
time:6215070990098523572
time:5841306683236090197

可以看到,我们虽然注入的对象同样是不停变化的

总结

本文主要讲了两种方法,通过spring注入不停变化的对象

  1. 通过ObjectFactory
  2. 通过@Lazy和修改@Scope
    第一种方案更适用于知道自己需要注入的对象是什么;而第二种方案适用于在单例的对象中获取不停变化的对象而已。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值