1. Introduction
1.介绍
In this quick tutorial, we’ll take a look at Spring’s method-level dependency injection support, via the @Lookup annotation.
在这个快速教程中,我们将通过@Lookup注解来看看Spring的方法级依赖注入支持。
2. Why @Lookup?
2.为什么@Lookup?
A method annotated with @Lookup tells Spring to return an instance of the method’s return type when we invoke it.
用@Lookup注解的方法告诉Spring在我们调用该方法时返回该方法的返回类型的实例。
Essentially, Spring will override our annotated method and use our method’s return type and parameters as arguments to BeanFactory#getBean.
本质上,Spring将覆盖我们的注解方法,并使用我们方法的返回类型和参数作为BeanFactory#getBean.的参数。
@Lookup is useful for:
@Lookup在以下方面很有用。
- Injecting a prototype-scoped bean into a singleton bean (similar to Provider)
- Injecting dependencies procedurally
Note also that @Lookup is the Java equivalent of the XML element lookup-method.
还要注意,@Lookup是XML元素lookup-method的Java等价物。
3. Using @Lookup
3.使用@Lookup
3.1. Injecting prototype-scoped Bean Into a Singleton Bean
3.1.将原型范围的Bean注入到Singleton Bean中
If we happen to decide to have a prototype Spring bean, then we are almost immediately faced with the problem of how will our singleton Spring beans access these prototype Spring beans?
如果我们碰巧决定有一个原型Spring Bean,那么我们几乎立刻就会面临这样的问题:我们的单子Spring Bean将如何访问这些原型Spring Bean?
Now, Provider is certainly one way, though @Lookup is more versatile in some respects.
现在,Provider当然是一种方式,尽管@Lookup在某些方面更通用。
First, let’s create a prototype bean that we will later inject into a singleton bean:
首先,让我们创建一个原型Bean,以后我们将把它注入到一个单子Bean中。
@Component
@Scope("prototype")
public class SchoolNotification {
// ... prototype-scoped state
}
And if we create a singleton bean that uses @Lookup:
而如果我们创建一个使用@Lookup的单子Bean。
@Component
public class StudentServices {
// ... member variables, etc.
@Lookup
public SchoolNotification getNotification() {
return null;
}
// ... getters and setters
}
Using @Lookup, we can get an instance of SchoolNotification through our singleton bean:
使用@Lookup,我们可以通过我们的单子Bean获得SchoolNotification的一个实例。
@Test
public void whenLookupMethodCalled_thenNewInstanceReturned() {
// ... initialize context
StudentServices first = this.context.getBean(StudentServices.class);
StudentServices second = this.context.getBean(StudentServices.class);
assertEquals(first, second);
assertNotEquals(first.getNotification(), second.getNotification());
}
Note that in StudentServices, we left the getNotification method as a stub.
请注意,在StudentServices中,我们将getNotification方法作为一个存根。
This is because Spring overrides the method with a call to beanFactory.getBean(StudentNotification.class), so we can leave it empty.
这是因为Spring通过调用beanFactory.getBean(StudentNotification.class)重写了该方法,所以我们可以让它留空。
3.2. Injecting Dependencies Procedurally
3.2.程序性地注入依赖关系
Still more powerful, though, is that @Lookup allows us to inject a dependency procedurally, something that we cannot do with Provider.
但更强大的是,@Lookup允许我们程序性地注入一个依赖关系,这是我们无法用Provider做到的。
Let’s enhance StudentNotification with some state:
让我们用一些状态来增强StudentNotification。
@Component
@Scope("prototype")
public class SchoolNotification {
@Autowired Grader grader;
private String name;
private Collection<Integer> marks;
public SchoolNotification(String name) {
// ... set fields
}
// ... getters and setters
public String addMark(Integer mark) {
this.marks.add(mark);
return this.grader.grade(this.marks);
}
}
Now, it is dependent on some Spring context and also additional context that we will provide procedurally.
现在,它依赖于一些Spring的上下文,也依赖于我们将按程序提供的额外上下文。
We can then add a method to StudentServices that takes student data and persists it:
然后我们可以给StudentServices添加一个方法,获取学生数据并将其持久化。
public abstract class StudentServices {
private Map<String, SchoolNotification> notes = new HashMap<>();
@Lookup
protected abstract SchoolNotification getNotification(String name);
public String appendMark(String name, Integer mark) {
SchoolNotification notification
= notes.computeIfAbsent(name, exists -> getNotification(name)));
return notification.addMark(mark);
}
}
At runtime, Spring will implement the method in the same way, with a couple of additional tricks.
在运行时,Spring将以同样的方式实现该方法,但有一些额外的技巧。
First, note that it can call a complex constructor as well as inject other Spring beans, allowing us to treat SchoolNotification a bit more like a Spring-aware method.
首先,请注意,它可以调用复杂的构造函数以及注入其他Spring Bean,使我们可以把SchoolNotification处理得更像一个Spring感知的方法。
It does this by implementing getSchoolNotification with a call to beanFactory.getBean(SchoolNotification.class, name).
它通过调用beanFactory.getBean(SchoolNotification.class, name)来实现getSchoolNotification。
Second, we can sometimes make the @Lookup-annotated method abstract, like the above example.
其次,我们有时可以让@Lookup-注释的方法变得抽象,就像上面的例子。
Using abstract is a bit nicer-looking than a stub, but we can only use it when we don’t component-scan or @Bean-manage the surrounding bean:
使用abstract比存根好看一些,但我们只能在不component-scan或@Bean管理周围的bean时使用它。
@Test
public void whenAbstractGetterMethodInjects_thenNewInstanceReturned() {
// ... initialize context
StudentServices services = context.getBean(StudentServices.class);
assertEquals("PASS", services.appendMark("Alex", 89));
assertEquals("FAIL", services.appendMark("Bethany", 78));
assertEquals("PASS", services.appendMark("Claire", 96));
}
With this setup, we can add Spring dependencies as well as method dependencies to SchoolNotification.
通过这样的设置,我们可以向SchoolNotification添加Spring依赖性以及方法依赖性。
4. Limitations
4.局限性
Despite @Lookup‘s versatility, there are a few notable limitations:
尽管@Lookup具有多功能性,但也有一些明显的限制。
- @Lookup-annotated methods, like getNotification, must be concrete when the surrounding class, like Student, is component-scanned. This is because component scanning skips abstract beans.
- @Lookup-annotated methods won’t work at all when the surrounding class is @Bean-managed.
In those circumstances, if we need to inject a prototype bean into a singleton, we can look to Provider as an alternative.
在这些情况下,如果我们需要将一个原型Bean注入到一个单子中,我们可以将Provider作为一种选择。
5. Conclusion
5.结论
In this quick article, we learned how and when to use Spring’s @Lookup annotation, including how to use it to inject prototype-scoped beans into singleton beans and how to use it to inject dependencies procedurally.
在这篇快速文章中,我们学习了如何以及何时使用Spring的@Lookup注解,包括如何使用它将原型范围的Bean注入到单子Bean中,以及如何使用它来程序性地注入依赖关系。
All the code used for this tutorial can be found over on Github.
本教程所使用的所有代码都可以在Github上找到over。