说说Spring中为何要引入Lookup注解

前言

我们先探一探官方文档关于Method Injection的章节是怎么说的:

In most application scenarios, most beans in the container are singletons. When a singleton bean needs to collaborate with another singleton bean or a non-singleton bean needs to collaborate with another non-singleton bean, you typically handle the dependency by defining one bean as a property of the other. A problem arises when the bean lifecycles are different. Suppose singleton bean A needs to use non-singleton (prototype) bean B, perhaps on each method invocation on A. The container creates the singleton bean A only once, and thus only gets one opportunity to set the properties. The container cannot provide bean A with a new instance of bean B every time one is needed.

用一句话概括就是 一个单例Bean A每次获取另外一个Bean B的时候怎么保证这个Bean B是一个新的实例?

正文

ApplicationContextAware接口

官方文档首先也提到了一个解决方案就是把A弄成容器的Aware( make bean A aware of the container),也就是实现ApplicationContextAware接口。

A solution is to forego some inversion of control. You can make bean A aware of the container by implementing the ApplicationContextAware interface, and by making a getBean("B") call to the container ask for (a typically new) bean B instance every time bean A needs it. The following example shows this approach

文档里随后就提供了一个示例来说明这个解决方案如何做。

// a class that uses a stateful Command-style class to perform some processing
package fiona.apple;
// Spring-API imports
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

public class CommandManager implements ApplicationContextAware {
 private ApplicationContext applicationContext;
 public Object process(Map commandState) {
  // grab a new instance of the appropriate Command
  Command command = createCommand();
  // set the state on the (hopefully brand new) Command instance
  command.setState(commandState);
  return command.execute();
 }
 protected Command createCommand() {
  // notice the Spring API dependency!
  return this.applicationContext.getBean("command", Command.class);
 }
  public void setApplicationContext(
   ApplicationContext applicationContext) throws BeansException {
  this.applicationContext = applicationContext;
 }
 }

虽然解决了一开始提出的问题,但是Spring随后就说到:

The preceding is not desirable, because the business code is aware of and coupled to the Spring Framework. Method Injection, a somewhat advanced feature of the Spring IoC container, lets you handle this use case cleanly.

也就是说 前面的方法是不可取的,因为业务代码知道并耦合到Spring框架。那怎么降低这个耦合度呢?后半句就给出了答案:方法注入是Spring IOC容器的一个稍微高级的特性,它允许您干净地处理这个用例。

Lookup Method方法注入

首先再次引入官方文档中的阐述:

Lookup method injection is the ability of the container to override methods on container-managed beans and return the lookup result for another named bean in the container. The lookup typically involves a prototype bean, as in the scenario described in the preceding section. The Spring Framework implements this method injection by using bytecode generation from the CGLIB library to dynamically generate a subclass that overrides the method.

简要概括下这段话有三个意思:

  • Lookup Method注入可以让容器重写容器中bean上的方法并返回容器中另一个bean的查找结果。
  • Lookup通常会是一个原型bean,如文章开头所说的。
  • Spring框架通过使用CGLIB库中的字节码生成来动态生成覆盖该方法的子类,从而实现这种方法注入。

使用Lookup方式需要注意以下几点:

  • For this dynamic subclassing to work, the class that the Spring bean container subclasses cannot be final, and the method to be overridden cannot be final, either.
  • Unit-testing a class that has an abstract method requires you to subclass the class yourself and to supply a stub implementation of the abstract method.
  • Concrete methods are also necessary for component scanning, which requires concrete classes to pick up.
  • A further key limitation is that lookup methods do not work with factory methods and in particular not with @Bean methods in configuration classes, since, in that case, the container is not in charge of creating the instance and therefore cannot create a runtime-generated subclass on the fly.

这段话也可以概括为以下几点:

  1. 使这个动态子类可以用,这个类不能是final,要重写的方法也不能是final。
  2. 单元测试具有抽象方法的类需要您自己对该类进行子类化,并提供抽象方法的存根实现。
  3. 具体的方法对于组件扫描也是必要的,这需要具体的类来获取。
  4. 一个关键限制是Lookup方法不适用于工厂方法,尤其是配置类中的@Bean方法,因为在这种情况下,容器不负责创建实例,因此不能动态创建运行时生成的子类。

接下来Spring就拿上面本来基于ApplicationContextAware的方法来说,就可以用Lookup来替换了。对于前面代码段中的CommandManager类,Spring容器动态重写createCommand()方法的实现。CommandManager类没有任何Spring依赖项,如修改后的示例所示

In the case of the CommandManager class in the previous code snippet, the Spring container dynamically overrides the implementation of the createCommand() method. The CommandManager class does not have any Spring dependencies, as the reworked example shows.

Xml配置lookup-method

 public abstract class CommandManager {
  public Object process(Object commandState) {
   // grab a new instance of the appropriate Command interface
   Command command = createCommand();
   // set the state on the (hopefully brand new) Command instance
   command.setState(commandState);
   return command.execute();

 }
 // okay... but where is the implementation of this method?
  protected abstract Command createCommand();
 }

在包含要注入的方法的CommandManager类中,要注入的方法需要以下形式的签名:

<public|protected> [abstract] <return-type> theMethodName(no-arguments);

如果方法是抽象的,则动态生成的子类将实现该方法。否则,动态生成的子类将重写在原始类中定义的具体方法。

我们来看下Bean的配置:

当需要myCommand这个Bean的新实例时,被标识为commandManager的bean就会调用自己的createCommand()方法。如果需要的话,必须小心地将myCommand 这个bean部署为原型。如果是单例,则每次都返回相同的myCommand bean实例.

@Lookup注解

在基于注解的组件模型中,可以通过@lookup注释声明查找方法,如下例所示

 public abstract class CommandManager {
  public Object process(Object commandState) {
   Command command = createCommand();
   command.setState(commandState);
   return command.execute();
  }
  @Lookup("myCommand")
  protected abstract Command createCommand();
 }

或者,你也可以依靠目标bean根据lookup方法的声明返回类型进行解析

public abstract class CommandManager {
  public Object process(Object commandState) {
   MyCommand command = createCommand();
   command.setState(commandState);
   return command.execute();
  }
  @Lookup
  protected abstract MyCommand createCommand();
 }

需要注意的是你通常应该用一个具体的存根实现声明这种带注解的查找方法,以便它们与Spring的组件扫描规则兼容,默认情况下抽象类会被忽略。此限制不适用于显式注册或显式导入的bean类。也就是说如果用组件扫描Bean的话因为抽象类默认是被忽略的,但是你加上这个Lookup注解后就不会呗忽略。

Spring在最后也提供了其他两种解决思路:

Another way of accessing differently scoped target beans is an ObjectFactory/ Provider injection point. See Scoped Beans as Dependencies。You may also find the ServiceLocatorFactoryBean (in the org.springframework.beans.factory.config package) to be useful.

  1. 通过ObjectFactory或者ObjectProvider.
  2. 通过ServiceLocatorFactoryBean.

这两个方案我们之后会单独写文章来探讨,下篇文章我打算来具体的使用下这个Lookup 方法注入并且从源码角度来看下Spring如何巧妙地实现它的。

总结

到此这篇关于Spring中为何要引入Lookup注解的文章就介绍到这了,更多相关Spring为何引入Lookup内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 深入理解Spring中的Lookup(方法注入)

    前言 本文主要给大家介绍了关于Spring中Lookup(方法注入)的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍: 在使用Spring时,可能会遇到这种情况:一个单例的Bean依赖另一个非单例的Bean.如果简单的使用自动装配来注入依赖,就可能会出现一些问题,如下所示: 单例的Class A @Component public class ClassA { @Autowired private ClassB classB; public void printClass

  • 说说Spring中为何要引入Lookup注解

    前言 我们先探一探官方文档关于Method Injection的章节是怎么说的: In most application scenarios, most beans in the container are singletons. When a singleton bean needs to collaborate with another singleton bean or a non-singleton bean needs to collaborate with another non-s

  • 在Spring 中使用@Aspect 控制自定义注解的操作

    Spring 中使用@Aspect 控制自定义注解 看这篇介绍@Aspect 1.定义系统日志注解类 @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface SysLog { String value() default ""; } 2.定义切面处理类 package com.kxs.common.aspect; import com.google.gso

  • Spring中的事务操作、注解及XML配置详解

    事务 事务全称叫数据库事务,是数据库并发控制时的基本单位,它是一个操作集合,这些操作要么不执行,要么都执行,不可分割.例如我们的转账这个业务,就需要进行数据库事务的处理. 转账中至少会涉及到两条 SQL 语句: update Acoount set balance = balance - money where id = 'A'; update Acoount set balance = balance + money where id = 'B' 上面这两条 SQL 就可以要看成是一个事务,必

  • 浅谈一下Spring中的createBean

    目录 找到BeanClass并且加载类 实例化前 实例化 Supplier创建对象 工厂方法创建对象 推断构造方法 BeanDefionition 的后置处理 实例化后 属性填充 Aware回调 初始化前 初始化 初始化后 总结BeanPostProcessor 找到BeanClass并且加载类 protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws

  • Spring中IOC和AOP的深入讲解

    前言 Spring是一个开源框架,Spring是于2003 年兴起的一个轻量级的Java 开发框架,由Rod Johnson 在其著作Expert One-On-One J2EE Development and Design中阐述的部分理念和原型衍生而来.它是为了解决企业应用开发的复杂性而创建的.Spring使用基本的JavaBean来完成以前只可能由EJB完成的事情.然而,Spring的用途不仅限于服务器端的开发.从简单性.可测试性和松耦合的角度而言,任何Java应用都可以从Spring中受益

  • 基于Spring中的事务@Transactional细节与易错点、幻读

    目录 为什么要使用事务? 如何使用事务? 事务的传播带来的几种结果 两个特例 事务传播属性propagation 数据库隔离级别 1.未提交读(会有脏读的现象) 2.已提交读 3.可重复读 (有可能覆盖掉其他事务的操作) 4.串行化(没有并发操作) Spring事务隔离级别比数据库事务隔离级别多一个default ACID,事务内的一组操作具有 原子性 .一致性.隔离性.持久性. Atomicity(原子性):一个事务(transaction)中的所有操作,要么全部完成,要么全部不完成,不会结束

  • 详解Spring中Lookup注解的使用

    我们知道在spring容器中单独的一个抽象类是不能成为一个bean的,那么有没有办法呢?这个时候我们可以使用Lookup注解,我们可以看下spring的扫描bean部分逻辑.我们知道在spring中要想成为一个bean,必须先生成BeanDefinition对象,如果一个抽象类中没有含有Lookup注解的方法,在spring扫描时就会被排除掉. /** * 1.判断是不是独立的类,非静态内部类则无法生成bean, * 2.判断是不是接口或者抽象类(有一种特殊情况),是则无法生成 * 3.判断如果

  • Spring中常用注解的详细介绍

    spring中使用注解时配置文件的写法: <?xml version="1.0" encoding="UTF-8"?> <span style="font-size:18px;"><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-in

  • 快速理解spring中的各种注解

    Spring中的注解大概可以分为两大类: 1)spring的bean容器相关的注解,或者说bean工厂相关的注解: 2)springmvc相关的注解. spring的bean容器相关的注解,先后有:@Required, @Autowired, @PostConstruct, @PreDestory,还有Spring3.0开始支持的JSR-330标准javax.inject.*中的注解(@Inject, @Named, @Qualifier, @Provider, @Scope, @Singlet

随机推荐