【Spring Framework】spring管理自己new的对象

使用AutowireCapableBeanFactory手动注入

使用.newInstance();创建对象的话,如果其他对象都使用Spring Autowired,还需要手动创建所有依赖的Bean:

private @Autowired AutowireCapableBeanFactory beanFactory;

public void process() {
  MyBean obj = new MyBean();
  beanFactory.autowireBean(obj);
  // obj will now have its dependencies autowired.
}

例如策略工厂中可以

private @Autowired AutowireCapableBeanFactory beanFactory;
    /**
     * 使用策略工厂获取具体策略实现
     * @param code
     * @return
     */
    public  PayStrategy getPayStrategy(String code) {
        String className = PayEnumStrategy.getClassNameByCode(code);
        try {
            PayStrategy str = (PayStrategy) Class.forName(className).getDeclaredConstructor().newInstance();
            beanFactory.autowireBean(str);
            return str;
        } catch (InstantiationException |
                NoSuchMethodException |
                ClassNotFoundException |
                IllegalAccessException |
                InvocationTargetException e) {
            e.printStackTrace();
        }
       return null;
    }

但是这种方式只有对field注入方式才有用。

使用Map<String,?> 自动注入

先附上如下的代码:

public interface TalkService {
    void talk(String content);
}
@Service(value = "withSisterTalkService")
public class WithSisterTalkService implements TalkService {
    @Override
    public void talk(String content) {
        System.out.println(this.getClass().getName() + ":" + content);
    }
}
@Service(value = "withGirlFriendTalkService")
public class WithGirlFriendTalkService implements TalkService {
    @Override
    public void talk(String content) {
        System.out.println(this.getClass().getName() + ":" + content);
    }
}
@Service
public class TalkServiceStrategyContext implements TalkService {

    private Map<String, TalkService> strategyMap = new ConcurrentHashMap<>();
     
    @Autowired
    public TalkServiceStrategyContext(Map<String, TalkService> strategyMap) {
        this.strategyMap.clear();
        this.strategyMap.putAll(strategyMap);
    }
     
    @Override
    public void talk(String content) {
     
    }

}

注意,这里必须是Map<String, TalkService>类型!

@Autowired
private Map<String, TalkService> talkServiceMap;

    @GetMapping(value = "doTest")
    public String doTest() {
        Set<String> strings = talkServiceMap.keySet();
        for (String string : strings) {
            System.out.println(string + ":" + talkServiceMap.get(string).toString());
        }
        return this.getClass().getName();
    }


其访问测试controller后,打印的信息如下:

talkServiceStrategyContext:com.haiyang.onlinejava.complier.service.impl.TalkServiceStrategyContext@2f0b1419
withGirlFriendTalkService:com.haiyang.onlinejava.complier.service.impl.WithGirlFriendTalkService@1cf19a02
withSisterTalkService:com.haiyang.onlinejava.complier.service.impl.WithSisterTalkService@1ef3c76d

看了后感觉很奇怪,在上方只定义了一个map<String,TalkService>的map,居然它就能自动找到实现了TalkService的所有bean,并将service的beanName作为了key,感觉还是牛逼啊,spring的注解居然还能这样用。

然后简单看了下Autowired的源码,其javaDoc文档里也有说明:

package org.springframework.beans.factory.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**

 * Marks a constructor, field, setter method or config method as to be

 * autowired by Spring's dependency injection facilities.
   *

 * <p>Only one constructor (at max) of any given bean class may carry this

 * annotation, indicating the constructor to autowire when used as a Spring

 * bean. Such a constructor does not have to be public.
   *

 * <p>Fields are injected right after construction of a bean, before any

 * config methods are invoked. Such a config field does not have to be public.
   *

 * <p>Config methods may have an arbitrary name and any number of arguments;

 * each of those arguments will be autowired with a matching bean in the

 * Spring container. Bean property setter methods are effectively just

 * a special case of such a general config method. Such config methods

 * do not have to be public.
   *

 * <p>In the case of multiple argument methods, the 'required' parameter is

 * applicable for all arguments.
   *

 * <p>In case of a {@link java.util.Collection} or {@link java.util.Map}

 * dependency type, the container will autowire all beans matching the

 * declared value type. In case of a Map, the keys must be declared as

 * type String and will be resolved to the corresponding bean names.
   *

 * <p>Note that actual injection is performed through a

 * {@link org.springframework.beans.factory.config.BeanPostProcessor

 * BeanPostProcessor} which in turn means that you <em>cannot</em>

 * use {@code @Autowired} to inject references into

 * {@link org.springframework.beans.factory.config.BeanPostProcessor

 * BeanPostProcessor} or

 * {@link org.springframework.beans.factory.config.BeanFactoryPostProcessor BeanFactoryPostProcessor}

 * types. Please consult the javadoc for the {@link AutowiredAnnotationBeanPostProcessor}

 * class (which, by default, checks for the presence of this annotation).
   *

 * @author Juergen Hoeller

 * @author Mark Fisher

 * @since 2.5

 * @see AutowiredAnnotationBeanPostProcessor

 * @see Qualifier

 * @see Value
   */
   @Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
   @Retention(RetentionPolicy.RUNTIME)
   @Documented
   public @interface Autowired {

   /**

    * Declares whether the annotated dependency is required.

    * <p>Defaults to {@code true}.
      */
      boolean required() default true;

}

关注这句:

In case of a java.util.Collection or java.util.Map dependency type, the container will autowire all beans matching the declared value type. In case of a Map, the keys must be declared as type String and will be resolved to the corresponding bean names.

它大致是说Autowired当使用在Collection里时,会将所申明类的所有实现类都放在那个指定的Collection里;

如果Autowired和map使用的话呢,它将它bean的名称作为key,所有的bean作为value.

使用Set<?>自动注入

如果不想使用bean的名字作为map的Key的话,我们可以自定义寻址方式,自动注入时候使用Set<?>:

public interface Strategy {
    void doStuff();
    StrategyName getStrategyName();
}
public enum StrategyName {
  StrategyA,
  StrategyB,
  StrategyC
}
@Component
public class StrategyA  implements Strategy{
  @Override
  public void doStuff() {
      //implement algorithm A here 
  }
  @Override 
  public StrategyName getStrategyName() {
    return StrategyName.StrategyA;
  }
}
@Component
public class StrategyB  implements Strategy{
  @Override
  public void doStuff() {
     //implement algorithm B here
   }
  @Override
  public StrategyName getStrategyName() {
     return StrategyName.StrategyB;
  }
}
@Component
public class StrategyC  implements Strategy{
  @Override
  public void doStuff() {
  }
  @Override
  public StrategyName getStrategyName() {
    return StrategyName.StrategyC;
  }
}
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import org.springframework.stereotype.Component;
@Component
public class StrategyFactory {
  private Map<StrategyName, Strategy> strategies;
  
  @Autowired
  public StrategyFactory(Set<Strategy> strategySet) {
     createStrategy(strategySet);
  }
  
  public Strategy findStrategy(StrategyName strategyName) {
     return strategies.get(strategyName);
  }
  private void createStrategy(Set<Strategy> strategySet) {
      strategies = new HashMap<StrategyName, Strategy>();
      strategySet.forEach( 
   strategy ->strategies.put(strategy.getStrategyName(), strategy));
}
}

Now we can inject StrategyFactory using @Autowired annotation. Here is the sample code using our StrategyFactory.

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class SomeService {
  @Autowired
  private StrategyFactory strategyFactory;
public void findSome(){  
 
 // Now get the strategy by passing the name 
 Strategy strategy = strategyFactory.findStrategy(StrategyName.StrategyA);
   // you can now call the methods defined in strategy.
    strategy.doStuff();
  }
}
posted @ 2021-06-09 20:34  satire  阅读(336)  评论(0编辑  收藏  举报