Spring中的Lookup(方法注入)
在使用Spring时,可能会遇到这种情况:一个单例的Bean依赖另一个非单例的Bean。如果简单的使用自动装配来注入依赖,就可能会出现一些问题,如下所示:
单例的Class A
@Component
public class ClassA {
@Autowired
private ClassB classB;
public void printClass() {
System.out.println("This is Class A: " + this);
classB.printClass();
}
}
非单例的Class B
@Component
@Scope(value = SCOPE_PROTOTYPE)
public class ClassB {
public void printClass() {
System.out.println("This is Class B: " + this);
}
}
这里Class A采用了默认的单例scope,并依赖于Class B, 而Class B的scope是prototype,因此不是单例的,这时候跑个测试就看出这样写的问题:
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = {ClassA.class, ClassB.class})
public class MyTest {
@Autowired
private ClassA classA;
@Test
public void simpleTest() {
for (int i = 0; i < 3; i++) {
classA.printClass();
}
}
}
输出的结果是:
This is Class A: ClassA@282003e1
This is Class B: ClassB@7fad8c79
This is Class A: ClassA@282003e1
This is Class B: ClassB@7fad8c79
This is Class A: ClassA@282003e1
This is Class B: ClassB@7fad8c79
可以看到,两个类的Hash Code在三次输出中都是一样。Class A的值不变是可以理解的,因为它是单例的,但是Class B的scope是prototype却也保持Hash Code不变,似乎也成了单例?
产生这种的情况的原因是,Class A的scope是默认的singleton,因此Context只会创建Class A的bean一次,所以也就只有一次注入依赖的机会,容器也就无法每次给Class A提供一个新的Class B。
不那么好的解决方案
要解决上述问题,可以对Class A做一些修改,让它实现ApplicationContextAware。
@Component
public class ClassA implements ApplicationContextAware {
private ApplicationContext applicationContext;
public void printClass() {
System.out.println("This is Class A: " + this);
getClassB().printClass();
}
public ClassB getClassB() {
return applicationContext.getBean(ClassB.class);
}
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
这样就能够在每次需要到Class B的时候手动去Context里找到新的bean。再跑一次测试后得到了以下输出:
This is Class A: com.devhao.ClassA@4df828d7
This is Class B: com.devhao.ClassB@31206beb
This is Class A: com.devhao.ClassA@4df828d7
This is Class B: com.devhao.ClassB@3e77a1ed
This is Class A: com.devhao.ClassA@4df828d7
This is Class B: com.devhao.ClassB@3ffcd140
可以看到Class A的Hash Code在三次输出中保持不变,而Class B的却每次都不同,说明问题得到了解决,每次调用时用到的都是新的实例。
但是这样的写法就和Spring强耦合在一起了,Spring提供了另外两种方法来降低侵入性。
@Lookup
Spring提供了一个名为@Lookup的注解,这是一个作用在方法上的注解,被其标注的方法会被重写,然后根据其返回值的类型,容器调用BeanFactory的getBean()方法来返回一个bean。
@Component
public class ClassA {
public void printClass() {
System.out.println("This is Class A: " + this);
getClassB().printClass();
}
@Lookup
public ClassB getClassB() {
return null;
}
}
可以发现简洁了很多,而且不再和Spring强耦合,再次运行测试依然可以得到正确的输出。
被标注的方法的返回值不再重要,因为容器会动态生成一个子类然后将这个被注解的方法重写/实现,最终调用的是子类的方法。
使用的@Lookup的方法需要符合如下的签名:
<public|protected> [abstract] <return-type> theMethodName(no-arguments);
作用域代理
Spring还提供了另外一种方法来解决这个问题。简单来说就是如果一个bean A对另外一个作用域更短的bean B有依赖,那么在实例化bean A并注入依赖时,注入的不是bean B本身,而是一个AOP代理,这个代理可以找到实际的bean。
@Component
public class ClassA {
@Autowired
private ClassB classB;
public void printClass() {
System.out.println("This is Class A: " + this);
classB.printClass();
}
}
@Component
@Scope(value = SCOPE_PROTOTYPE, proxyMode = ScopedProxyMode.TARGET_CLASS)
public class ClassB {
public void printClass() {
System.out.println("This is Class B: " + this);
}
}
可以看出,使用这种方法的好处是仅需对bean B进行简单的配置,并且bean A根本不用意识到代理的存在,将bean B当做一个正常的bean来装载就好。
Spring中的Lookup(方法注入)的更多相关文章
- Spring应用教程-2 方法注入
作者:禅楼望月(http://www.cnblogs.com/yaoyinglong) 我们通常使用lookup方法注入,它可使Spring替换一个Bean的抽象或具体方法,返回查找容器中,其他Bea ...
- [置顶] Spring中DI设置器注入
Java的反射机制可以说是在Spring中发挥的淋漓尽致,下面要看的代码就是通过反射机制来实现向一个类注入其实际依赖的类型,这个过程的实现会交由Spring容器来帮我们完成. JavaBean中针对属 ...
- 【坑】Spring中抽象父类属性注入,子类调用父类方法使用父类注入属性
运行环境 idea 2017.1.1 spring 3.2.9.RELEASE 需求背景 需要实现一个功能,该功能有2个场景A.B,大同小异 抽象一个抽象基类Base,实现了基本相同的方法BaseMe ...
- 关于spring中无法将service注入到servlet中的问题
首先,servlet是动态网页项目区别于普通的java项目的,是动态网页项目中web.xml主要配置文件管理的,而spring只能管理普通的pojo,而没办法直接注入,尽管你的注入方式和配置方式都没有 ...
- Spring中Ioc容器的注入方式
1 通过setter方法注入 bean类: package com.test; public class UserServiceImplement implements IUserService { ...
- spring中bean配置和注入场景分析
bean与spring容器的关系 Bean配置信息定义了Bean的实现及依赖关系,Spring容器根据各种形式的Bean配置信息在容器内部建立Bean定义注册表,然后根据注册表加载.实例化Bean,并 ...
- Spring 自动装配;方法注入
通过配置defalut—autowire属性,Spring IOC容器可以自动为程序注入Bean:默认是no(不启用自动装配). default—autowire的类型有: byName:通过名称自动 ...
- Spring中的destroy-method方法
1. Bean标签的destroy-method方法 配置数据源的时候,会有一个destroy-method方法 <bean id = "dataSource" class ...
- Spring中如何向 Bean注入系统属性或环境变量
[转自] http://unmi.cc/spring-injection-system-properties-env/ 在 Spring 中为 javabean 注入属性文件中的属性值一般人都知道的, ...
随机推荐
- PS 滤镜算法原理——拼贴
%%%% Tile %%%%% 实现拼贴效果 %%%%% 将原图像进行分块,然后让图像块在 %%%%% 新图像范围内进行随机移动,确定移动后的边界 %%%%% 将移动后的图像块填入新图像内 clc; ...
- Nginx的内部(进程)模型
nginx是以多进程的方式来工作的,当然nginx也是支持多线程的方式的,只是我们主流的方式还是多进程的方式,也是nginx的默认方式.nginx采用多进程的方式有诸多好处. (1)nginx在启动后 ...
- Android 客户端与服务器交互
在android中有时候我们不需要用到本机的SQLite数据库提供数据,更多的时候是从网络上获取数据,那么Android怎么从服务器端获取数据呢?有很多种,归纳起来有 一:基于Http协议获取数据方法 ...
- 访问PHP文件时出现An error occurred.
服务器配置ubuntu16,nginx,php5.6-fpm 我的问题是用户组没有权限导致出现访问PHP文件时An error occurred.,访问Html页面正常. 查看Nginx日志报错(路径 ...
- Qt Creator 更改默认构建目录到工程目录下
Qt Creator 更改默认构建目录到工程目录下 步骤 工具->选项->构建和运行->概要->Default build directory->去掉第一个". ...
- Kubernetes如何支持有状态服务的部署?
作者:Jack47 转载请保留作者和原文出处 PS:如果喜欢我写的文章,欢迎关注我的微信公众账号程序员杰克,两边的文章会同步,也可以添加我的RSS订阅源. Kubernetes对无状态服务有完善的支持 ...
- 返回空的list集合*彻底删除删除集合*只是清空集合
---------- 要求返回空的List集合----------- List<String> allList = Collections.emptyList();// 返回空的List集 ...
- access窗体主体居中
Private Sub Form_Load()DoCmd.Echo False Dim x, y As IntegerDoCmd.Maximizex = Me.WindowWidthy = Me.Wi ...
- COSO企业风险管理框架2017版发布!看看有哪些变化?
近期,COSO发布了新版(2017版)的企业风险管理框架:<企业风险管理—与战略和业绩的整合>.相较于2004年发布的上一版框架<企业风险管理—整合框架>,新框架强调了制定战略 ...
- Spring MVC “404 Not Found”错误的解决
一般这个问题发生的原因不会出在Web.xml配置文件的Servlet URL映射部分,因为这个URL映射做JavaWeb的人经常配置不会发生错误,而且此处的这个映射自由度很大,可以有目录字符串,也可以 ...