【问题标题】:Spring @Autowired on a class new instanceSpring @Autowired 在类新实例上
【发布时间】:2019-02-20 15:13:01
【问题描述】:

我对Spring不是很熟悉,遇到以下情况:

一个存储库类:

@Repository
public class MyRepository {
    // ...
}

使用存储库类的类:

public class MyClass extends AbstractClass {

    @Autowired
    private MyRepository myRepository;

    //...
}

我知道,如果我用@Component 注释我的MyClass 并将它与@Autowired 一起使用,那么@Autowired MyRepository 就可以很好地解决。 问题是我需要用反射创建MyClass 的新实例。所以MyRepository 永远不会被解析并且一直为空。

有没有办法在这种情况下使用@Autowired

更好地解释我的情况: 我有一些AbstractClass 的实现。 在我的应用程序的设置阶段,我创建了这些实现的HashMap。基本上:

{"MyClass", MyClass.class}
//...

然后我有一个通用的Controller 映射到网址/{class}?options=... 使用{class}@PathVariable、上面的HashMap 和反射,我可以根据给定的options 创建一个类的实例(这部分很重要)。你们认为有更好的方法吗?

提前致谢

【问题讨论】:

  • 您是如何通过反射创建MyClass 的新实例的?你能发布代码吗?
  • 嗨 Maruthi,这是一个简单的map.get(className).newInstance()
  • @JoãoMenighin 看了我的回答?对你有用吗?
  • Spring 不能以这种方式工作。 @Autowired 是 Spring 创建的构造函数。你不能自己创建一个类的新实例并期望 Spring 注入它的 @Autowired

标签: java spring dependency-injection autowired


【解决方案1】:

Spring 本身提供了一些在对象中进行自动装配的功能 您由 newnewInstance() 或其他创建的。

要使用它,您需要AutowireCapableBeanFactory 您可以通过 Spring 的正常依赖注入 @Autowired 获得。

@Autowired
private  AutowireCapableBeanFactory autowireCapableBeanFactory;

然后你使用它的autowireBean(Object) 方法 将@Autowired 属性注入到您的 bean 中。

Object myBean = map.get(className).newInstance();
autowireCapableBeanFactory.autowireBean(myBean);

设计说明:

如果您真的需要上述方法,请好好考虑。 AutowireCapableBeanFactory 的 javadoc 建议不要在大多数用例中使用此接口:

BeanFactory 的此子接口不适用于普通应用程序代码:对于典型用例,请坚持使用 BeanFactoryListableBeanFactory

其他框架的集成代码可以利用此接口来连接和填充 Spring 不控制其生命周期的现有 bean 实例。例如,这对于 WebWork Actions 和 Tapestry Page 对象特别有用。

【讨论】:

  • 我不会对你投反对票,但考虑到你对我投了反对票。我将只引用 javadoc 中的一句话。 “BeanFactory 的这个子接口并不打算在普通应用程序代码中使用:对于典型用例,请坚持使用 BeanFactory 或 ListableBeanFactory。其他框架的集成代码可以利用此接口来连接和填充 Spring 不控制其生命周期的现有 bean 实例. 这对于 WebWork Actions 和 Tapestry Page 对象特别有用,例如"
  • @AlexandarPetrov 我同意。这种方法有点臭,如果有更清洁的解决方案,应该避免。
  • 我认为这实际上是我的情况。我有点建立一个迷你框架来生成代码......知道这一点真的很好。另一种对我有用的方法是将我的MyClass 标记为Scope("prototype")(这样它每次都会获得一个新实例)并使用ApplicationContext.getBean() 方法。由于我的问题基本上是“有没有办法在 Spring 生命周期之外自动装配对象?”,我认为你的答案最合适。我会给其他人投票。非常感谢大家!
  • @ThomasFritsch 也许你可以用 Alexandar 的评论来编辑你的答案,这样来这里的人就会知道 java 文档上的这个“警告”。再次感谢!
  • @JoãoMenighin 好点,我已将 Alexandar 的反对意见添加到我的回答中。
【解决方案2】:

你可以在这里使用Factory Design Pattern

这在开始时可能看起来有点复杂,但我相信您在实施后会喜欢它。

步骤:

  1. 在 AbstractClass 的所有实现上添加 @Component。
  2. 创建一个工厂类:

    @Component
    public class MyFactory {
    
        private final Map<String, AbstractClass> impletationMap = new HashMap<>();
    
        @Autowired
        ApplicationContext context;
    
        @PostConstruct
        public void initialize() {
            populateDataMapperMap(context.getBeansOfType(AbstractClass.class).values().iterator());
        }
    
        private void populateDataMapperMap(final Iterator<AbstractClass> classIterator) {
            while (classIterator.hasNext()) {
                AbstractClass abstractClassImpl = (AbstractClass) classIterator.next();
                impletationMap.put(abstractClassImpl.getClass().getName(), abstractClassImpl);
    
            }
        }
    }
    

当这个 MyFactory 类的 Bean 被初始化时,它会查找所有 AbstractClass 类型的 bean 并将它们放入 HashMap(implementationMap) 中。

现在,您可以从这个工厂获取 HashMap,然后在需要时获取实现。当您添加 AbstractClass 的新实现时,这将非常容易,因为工厂会处理它。

【讨论】:

    【解决方案3】:

    一种解决方法是将 MyClass 绑定到 Hashmap 以绑定 Factory 类。我的类工厂。通过这种方式,您可以将构造委托给具体工厂,该工厂将完成实例化正确类并初始化正确存储库的工作。

    这是一个例子:

    {"MyClass", MyClassFactory.class}
    

    工厂也可以是Component,那么你需要将hashmap绑定到工厂实例而不是工厂类。但可以说它不是一个组件:

    //@Component   this is optional
        public MyClassFactory {
            //@Autowired optional
            ApplicationContext ctx;
    
    
           public MyClass createInstance() {
                MyRepository repo = ctx.getBean("")
                MyClass myclass = new MyClass(repo)
                return myclass;
           }
        }
    

    如果你将它标记为组件,你也可以使用 ApplicationContextAware 接口,如果你要自动装配 ApplicationContext。

    【讨论】:

      【解决方案4】:

      一种方法是在MyClass 之上声明@Component

      然后,在设置阶段,您可以在 HashMap 中传递MyClass实例,而不是MyClass.class 本身。不需要通过反射创建实例。

      注意:您可以在设置阶段从您的ApplicationContext 获取MyClass 的实例。

      【讨论】:

      • 感谢 Pankaj 的回答,问题是我不能拥有这个家伙的单身人士。每个实例可能取决于 URL 中给出的options...有什么想法吗?嗯
      • 然后你可以传递一个工厂类的实例,它可以在运行时根据你可以通过它的构造函数传递的选项来获取你的bean。但是,实例必须通过spring生成,否则将无法工作。
      • @JoãoMenighin 参考这个例子来实现工厂设计模式 - howtodoinjava.com/spring-core/…
      【解决方案5】:

      试试这个

      @Component    
      public class SomeClass extends AbstractClass {
      
        private static ApplicationContext applicationContext;
      
        public MyClass getMyClass(){
            // Now @Autowired MyRepository will work
            return applicationContext.getBean(MyClass.class);
        }
      
      }
      

      【讨论】:

        【解决方案6】:

        是的,您可以使用 @Component 注释所有 AbstractClass 实现 bean 并使用下一个声明

        @Autowired
        private List<AbstractClass> beans;
        

        然后您可以在 @PostConstruct 方法中将其转换为 Map。

        如果您自动装配列表,Spring 不会抱怨重复定义。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2020-05-22
          • 2016-02-17
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-06-24
          相关资源
          最近更新 更多