@Lookup Annotation in Spring – 在Spring中的@Lookup注释

最后修改: 2018年 4月 13日

中文/混合/英文(键盘快捷键:t)

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