成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

有多少人用過Spring的@Lookup注解?

開發(fā) 前端
Spring的@Lookup注解提供了一種靈活的機制,用于在運行時動態(tài)地創(chuàng)建和初始化beans。通過@Lookup,開發(fā)者可以在配置中指定一個方法,該方法會在運行時被調用以獲取相應的bean實例。這使得在某些特定條件下或在運行時配置變更時,能夠動態(tài)地選擇和創(chuàng)建不同的bean實現。

環(huán)境:Spring5.3.23

1. 簡介

Lookup方法注入能夠根據@Lookup注解的value屬性值或被注解該方法的返回值,從容器中查找bean作為方法的返回值對象使用。Spring容器會通過CGLIB生成當前類的代理,然后重寫被@Lookup注解的方法。

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Lookup {


  /**
   * 根據你設置的value值,從容器中查找對應的bean,如果沒有指定value值
   * 那么會根據被注解方法的返回值類型從容器中查找相應的bean
   */
  String value() default "";


}

2. 應用案例

準備基礎類

static interface HttpSecurity {
  void http() ;
}
static class HtmlHttpSecurity implements HttpSecurity {
  @Override
  public void http() {
    System.out.println("Html HttpSecurity...") ;
  }
}

定義一個抽象類

該類中有一個抽象方法被@Lookup注解標注

static abstract class SecurityManager {
  public void execute() {
    HttpSecurity httpSecurity = httpSecurity() ;
    System.out.println(httpSecurity.getClass()) ;
    httpSecurity.http() ; 
  }
  @Lookup("html")
  protected abstract HttpSecurity httpSecurity() ;
}

注冊Bean

將上面的類注冊到容器中

public static void main(String[] args) {
  try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext()) {
    context.registerBean("html", HtmlHttpSecurity.class) ;
    context.register(SecurityManager.class) ;
    context.refresh() ;
    SecurityManager sm = context.getBean(SecurityManager.class);
    sm.execute() ;
    System.out.println(sm.getClass()) ;
  }
}

執(zhí)行結果

class com.pack.main.lookup.MethodLookupInjectMain2$HtmlHttpSecurity
Html HttpSecurity...
class com.pack.main.lookup.MethodLookupInjectMain2$SecurityManager$$EnhancerBySpringCGLIB$$ae697832

SecurityManager通過CGLIB被創(chuàng)建為了代理類。同時execute方法中HttpSecurity對象就是HtmlHttpSecurity類,也就是容器通過查找注入的。

去掉@Lookup注解的value屬性

@Lookup
protected abstract HttpSecurity httpSecurity() ;

繼續(xù)執(zhí)行上面的測試代碼,程序也能正常的輸出。

在添加一個HttpSecurity的實現

static class CssHttpSecurity implements HttpSecurity {
  @Override
  public void http() {
    System.out.println("Css HttpSecurity...") ;
  }
}

將上面的類也注冊到容器中,如果這時候你的@Lookup注解沒有value屬性將會報錯

Exception in thread "main" org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.pack.main.lookup.MethodLookupInjectMain2$HttpSecurity' available: expected single matching bean but found 2: html,css
  at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveNamedBean(DefaultListableBeanFactory.java:1273)

expected single matching bean but found 2: html,css;錯誤提示你有2個bean,期望的是一個,所以這里我們必須添加value屬性,指明要查找的bean對象。

繼續(xù)測試,被@Lookup注解的方法是不是必須是抽象的?

修改代碼如下:

@Lookup("html")
protected HttpSecurity httpSecurity() {
  return null ;
};

方法返回null,執(zhí)行結果

class com.pack.main.lookup.MethodLookupInjectMain2$HtmlHttpSecurity
Html HttpSecurity...
class com.pack.main.lookup.MethodLookupInjectMain2$SecurityManager$$EnhancerBySpringCGLIB$$ae697832

程序正常,沒有問題。這也說明了,這里和具體的返回值是沒有關系的。

3. 實現原理

容器在創(chuàng)建一個Bean對象時會執(zhí)行如下步驟

public abstract class AbstractAutowireCapableBeanFactory {
  protected Object doCreateBean() {
    BeanWrapper instanceWrapper = null;
    // ...
    if (instanceWrapper == null) {
      // 創(chuàng)建實例
      instanceWrapper = createBeanInstance(beanName, mbd, args);
    }
    // ...
  }
  protected BeanWrapper createBeanInstance() {
    // 查找構造方法
    Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
    // 實例化bean對象;上面一步對@Lookup注解的方法進行了初始化查找
    return instantiateBean(beanName, mbd);
  }
  protected Constructor<?>[] determineConstructorsFromBeanPostProcessors(@Nullable Class<?> beanClass, String beanName)
      throws BeansException {


    if (beanClass != null && hasInstantiationAwareBeanPostProcessors()) {
      // 調用BeanPostProcessor
      for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) {
        // 這里通過了AutowiredAnnotationBeanPostProcessor處理器
        // 這里其實最終返回了null,關鍵就是執(zhí)行了下面的動作,查找了@Lookup注解的方法進行解析
        // 保存到BeanDefinition中
        Constructor<?>[] ctors = bp.determineCandidateConstructors(beanClass, beanName);
        if (ctors != null) {
          return ctors;
        }
      }
    }
    return null;
  }
}

AutowiredAnnotationBeanPostProcessor處理器

public class AutowiredAnnotationBeanPostProcessor {
  public Constructor<?>[] determineCandidateConstructors() {
    do {
      // 遍歷當前類的所有方法
      ReflectionUtils.doWithLocalMethods(targetClass, method -> {
        // 獲取方法是的@Lookup注解
        Lookup lookup = method.getAnnotation(Lookup.class);
        if (lookup != null) {
          // 如果存在則構造LookupOverride對象,將當前的Method及注解的value值傳入
          LookupOverride override = new LookupOverride(method, lookup.value());
          try {
            RootBeanDefinition mbd = (RootBeanDefinition)this.beanFactory.getMergedBeanDefinition(beanName);
            // 最后將其保存到BeanDefinition中
            mbd.getMethodOverrides().addOverride(override);
          }
        }
      });
      targetClass = targetClass.getSuperclass();
    }
    while (targetClass != null && targetClass != Object.class);
  }
}

繼續(xù)上面執(zhí)行到instantiateBean方法

public abstract class AbstractAutowireCapableBeanFactory {
  protected BeanWrapper instantiateBean() {
    // getInstancetiationStrategy方法返回了CglibSubclassingInstantiationStrategy
    beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, this);
  }
}

進入SimpleInstantiationStrategy#instanceiate方法

public class SimpleInstantiationStrategy implements InstantiationStrategy {
  public Object instantiate(RootBeanDefinition bd) {
    // 判斷當前的BeanDefinition中是否有LookupOverride
    // 上面查找到后已經將其保存到了BeanDefinition中。
    if (!bd.hasMethodOverrides()) {
      // ...
    }
    else {
      // 存在則通過CGLIB進行創(chuàng)建代理
      // 進入CglibSubclassingInstantiationStrategy
      return instantiateWithMethodInjection(bd, beanName, owner);
    }
  }
}

CglibSubclassingInstantiationStrategy

public class CglibSubclassingInstantiationStrategy extends SimpleInstantiationStrategy {
  protected Object instantiateWithMethodInjection(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {
    return instantiateWithMethodInjection(bd, beanName, owner, null);
  }
  protected Object instantiateWithMethodInjection(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner,
      @Nullable Constructor<?> ctor, Object... args)
    return new CglibSubclassCreator(bd, owner).instantiate(ctor, args);
  }
}

CglibSubclassCreator

private static class CglibSubclassCreator {
  private static final Class<?>[] CALLBACK_TYPES = new Class<?>[]
        {NoOp.class, LookupOverrideMethodInterceptor.class, ReplaceOverrideMethodInterceptor.class};
  public Object instantiate(@Nullable Constructor<?> ctor, Object... args) {
    // 創(chuàng)建代理,當前類的子類
    Class<?> subclass = createEnhancedSubclass(this.beanDefinition);
    // ...
  }
  private Class<?> createEnhancedSubclass(RootBeanDefinition beanDefinition) {
    Enhancer enhancer = new Enhancer();
    enhancer.setSuperclass(beanDefinition.getBeanClass());
    enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
    if (this.owner instanceof ConfigurableBeanFactory) {
      ClassLoader cl = ((ConfigurableBeanFactory) this.owner).getBeanClassLoader();
      enhancer.setStrategy(new ClassLoaderAwareGeneratorStrategy(cl));
    }
    
    enhancer.setCallbackFilter(new MethodOverrideCallbackFilter(beanDefinition));
    // 我們主要關注這里,這些都是CGLIB相關的知識了。
    // 關注LookupOverrideMethodInterceptor攔截器
    enhancer.setCallbackTypes(CALLBACK_TYPES);
    return enhancer.createClass();
  }
}

LookupOverrideMethodInterceptor攔截器

private static class LookupOverrideMethodInterceptor extends CglibIdentitySupport implements MethodInterceptor {
  public Object intercept(Object obj, Method method, Object[] args, MethodProxy mp) throws Throwable {
      LookupOverride lo = (LookupOverride) getBeanDefinition().getMethodOverrides().getOverride(method);
      Object[] argsToUse = (args.length > 0 ? args : null);  // if no-arg, don't insist on args at all
      // 判斷是否設置了@Lookup value值
      if (StringUtils.hasText(lo.getBeanName())) {
        // 根據value指定的值查找bean對象
        Object bean = (argsToUse != null ? this.owner.getBean(lo.getBeanName(), argsToUse) :
            this.owner.getBean(lo.getBeanName()));
        return (bean.equals(null) ? null : bean);
      }
      // 如果沒有設置value,那么會根據方法的返回值類型在容器中查找bean
      else {
        ResolvableType genericReturnType = ResolvableType.forMethodReturnType(method);
        return (argsToUse != null ? this.owner.getBeanProvider(genericReturnType).getObject(argsToUse) :
            this.owner.getBeanProvider(genericReturnType).getObject());
      }
    }
}

以上就是@Lookup注解的原理

總結:Spring的@Lookup注解提供了一種靈活的機制,用于在運行時動態(tài)地創(chuàng)建和初始化beans。通過@Lookup,開發(fā)者可以在配置中指定一個方法,該方法會在運行時被調用以獲取相應的bean實例。這使得在某些特定條件下或在運行時配置變更時,能夠動態(tài)地選擇和創(chuàng)建不同的bean實現。

完畢!!!

責任編輯:武曉燕 來源: Spring全家桶實戰(zhàn)案例源碼
相關推薦

2013-08-20 15:27:59

Linux操作系統

2019-08-08 16:27:36

自動駕駛無人駕駛駕駛

2020-04-02 14:33:42

MySQLBUG解決方案

2020-07-20 09:40:49

MySQLBUG數據庫

2010-07-01 14:35:57

Windows 7

2022-11-12 12:32:39

.NET微軟

2023-04-09 15:23:58

Python編程開發(fā)

2021-07-29 06:28:13

網絡網工網絡中斷

2022-10-11 16:28:42

比特幣加密貨幣資產

2018-07-23 14:47:09

人工智能AI機器

2021-04-15 11:07:01

比特幣貨幣加密貨幣

2018-06-21 07:40:23

無線充電無線供電無線輸電

2020-11-13 10:25:41

人臉數據

2010-08-09 16:39:42

職場

2021-08-29 23:20:09

5G4G技術

2019-01-23 11:08:13

Windows微軟操作系統

2012-04-16 15:08:33

2020-10-20 11:16:04

人工智能

2012-11-08 09:25:12

Win 8

2018-10-31 09:16:00

人工智能機器學習技術
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 日韩精品免费 | 中文字幕一区二区三区四区 | 成人视屏在线观看 | 台湾佬伊人 | h视频在线播放 | 精品国产一级 | 亚洲综合天堂 | 国产欧美在线视频 | 日韩高清一区 | 精品在线看 | 久久久九九九九 | 亚洲日本一区二区三区四区 | 玖玖视频免费 | 国产精品美女久久久久aⅴ国产馆 | 人人爽人人爽人人片av | av中文在线 | 韩国理论电影在线 | 国产伦精品一区二区三毛 | 日韩国产免费观看 | 丝袜 亚洲 欧美 日韩 综合 | 一区日韩 | av色站| 美女黄18岁以下禁止观看 | 久久一二三区 | 久久国产高清 | 欧美中文字幕一区二区 | 欧美一区二区三区视频 | 国产永久免费 | 欧美成人自拍视频 | 成人免费在线网 | 一区二区av | 日韩一区二区三区av | 婷婷丁香激情 | 一区二区三区四区国产精品 | 国久久 | 精品乱码一区二区三四区 | 日韩视频中文字幕 | 免费a级毛片在线播放 | 99精品电影 | 视频1区2区 | 精品在线观看一区 |