【问题标题】:Can I use Spring @Autowired in multiple classes?我可以在多个类中使用 Spring @Autowired 吗?
【发布时间】:2014-04-06 19:03:40
【问题描述】:

我的问题是 - 我可以在多个类中自动装配一个类的实例吗?

我的应用程序使用 Spring MVC 在 JSP 前端页面和我的 MongoDB 后端之间进行通信。我正在为我的存储库使用 MongoDB。我创建了一个为 MongoDB 执行 CRUD 方法的服务。这可以通过其中一种 CRUD 方法在下面看到(并非所有都显示,因为它们不是必需的)。它使用 Mongo 模板。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.stereotype.Repository;

@Repository
public class SensorReadingService {

@Autowired
private MongoTemplate mongoTemplate;

/**
 * Create a unique mongo id and insert the document into
 * the collection specified. If collection doesn't exist, 
 * create it.
 * @param sensor
 */ 
public void addSensorReadingIntoCollection(SensorReading sensor, String collection)  {
    if (!mongoTemplate.collectionExists(SensorReading.class)) {
        mongoTemplate.createCollection(SensorReading.class);
    }

    String id = UUID.randomUUID().toString();

    String[] ids = id.split("-");

    String noHyphens = "";

    for(int i = 0; i<ids.length; i++) {
        noHyphens = noHyphens.concat(ids[i]);
    }

    sensor.setId(noHyphens);
    mongoTemplate.insert(sensor, collection);
}

可以看出,MongoTemplate 是自动装配的。在我的 Dispatcher-Servlet 中,我有以下 MongoDB 代码:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:mvc="http://www.springframework.org/schema/mvc"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns:context="http://www.springframework.org/schema/context"
   xmlns:p="http://www.springframework.org/schema/p"
   xsi:schemaLocation="
   http://www.springframework.org/schema/beans 
   http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
   http://www.springframework.org/schema/context 
   http://www.springframework.org/schema/context/spring-context-3.2.xsd
   http://www.springframework.org/schema/mvc 
   http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">

<context:component-scan base-package="usm" />

<!-- Factory bean that creates the Mongo instance -->
<bean id="mongo" class="org.springframework.data.mongodb.core.MongoFactoryBean">
    <property name="host" value="localhost" />
</bean>

<!-- MongoTemplate for connecting and querying the documents in the database -->
<bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
    <constructor-arg name="mongo" ref="mongo" />
    <constructor-arg name="databaseName" value="USMdb" />
</bean>

<!-- Use this post processor to translate any MongoExceptions thrown in @Repository annotated classes -->
<bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor" />   

<bean id="jspViewResolver"
      class="org.springframework.web.servlet.view.InternalResourceViewResolver"
      p:prefix="/WEB-INF/jsp/"
      p:suffix=".jsp" />    

<mvc:resources mapping="/resources/**" location="/resources/" />

<mvc:annotation-driven />

我现在还有一个控制器,它已经自动连接了服务,因此来自 JSP 页面的请求可以传递给控制器​​,然后控制器通过自动连接的服务将这些请求写入数据库。 (下面显示了一个示例方法,因为该类非常庞大)。这个自动装配的服务在调度程序 servlet 中没有任何东西,如之前自动装配的 MongoDB。

import gnu.io.SerialPortEventListener;
import gnu.io.CommPortIdentifier;
import gnu.io.SerialPort;
import gnu.io.SerialPortEvent;
import gnu.io.SerialPortEventListener;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

@Controller
public class SensorReadingController implements SerialPortEventListener {

//mongodb service
@Autowired
private SensorReadingService sensorReadingService;

    /**
 * When a post method is achieved via save, check if reading already
 * exists and update. Else, create new sensor. Redirect back to post race
 * page.
 * @param sensorReading
 * @param model
 * @return post race page
 */
@RequestMapping(value = "/postRace-save", method = RequestMethod.POST)
public View createSensorReading(@ModelAttribute SensorReading sensor, ModelMap model,@RequestParam(value="colName", required=true)String name) {
    if (StringUtils.hasText(sensor.getId())) {
        sensorReadingService.updateSensorReading(sensor, name);
    }
    else {
        sensorReadingService.addSensorReading(sensor);
    }
    return new RedirectView("/USM-Analysis-Application/postRace");
}

sensorReadingService 方法运行完美,对数据库执行 CRUD 方法。 现在我的问题是,如何在另一个类中使用这个 sensorReadingService? 当我添加时

//mongodb service
@Autowired
private SensorReadingService sensorReadingService;

进入另一个类,服务不起作用。没有错误被抛出,该服务只是没有向数据库添加任何内容。是因为@Autowired 只允许类在一个类中自动装配,即不能有多个实例?还是因为我没有像 MongoDB 那样在 dispatcher-servlet 中指定任何内容?

我需要让它在另一个班级工作的原因是在我的班级里,我正在监听串行事件。当有可用数据时,我创建一个新线程来处理这个串行事件,以便我的程序的其余部分仍然可以运行。在线程中,我解析从 Serial 收到的字符串,创建一个新的 SensorReading 对象,然后我想将这个 SensorReading 对象写入数据库。但是,由于我无法让 sensorReadingService 在任何其他类中工作,我无法执行此对数据库的写入。

首先我使用了一个实现 Runnable 的类来执行解析和保存。在此类中使用 @Autowired 不起作用,因此我尝试将 sensorReadingService 从控制器传递给线程(如下面的代码所示)。这也不起作用。然后,我更改了线程以实现 Callable,因此我可以返回 SensorReading 并将其保存到具有工作的自动连接服务的原始控制器类中的数据库中。但是,这首先破坏了创建线程的目的,因为它是写入我希望在线程中执行的数据库,否则会减慢整个程序的速度。

 @Override
public void serialEvent(SerialPortEvent oEvent) {
    if (oEvent.getEventType() == SerialPortEvent.DATA_AVAILABLE) {          
        //use a thread in the thread pool
       //tried passing sensorReadingService to thread but did not work
        //threadPool.execute(new RealTimeThread(input, sensorReadingService, getRealTimeIdsAndNames()));

        //tried return sensor reading. This works but slows down the program.
        Callable<List<SensorReading>> callable = new RealTimeThread(input, getRealTimeIdsAndNames(), offset, sensitivity);
        Future<List<SensorReading>> future = threadPool.submit(callable);

所以,我只是想知道是否有人知道我在使用 @Autowired 时做错了什么? 我可以有多个@Autowired 实例吗?我需要在调度程序 servlet 中添加一些东西吗?还是我应该不使用@Autowired 并尝试以另一种方式调用我的服务?

任何建议都将不胜感激,如果您需要我发布更多代码,请告诉我!提前致谢!

编辑:

对于我的 RealTimeThread 类,我有以下代码

import org.springframework.beans.factory.annotation.Autowired;
import usm.service.SensorReadingService;

 public class RealTimeThread implements Callable<List<SensorReading>> {

//mongodb service
@Autowired
private SensorReadingService sensorReadingService;

//fed by an InputStreamReader to convert bytes to strings
BufferedReader input;

//variables for getting data from real time
ArrayList<Byte> values = new ArrayList<Byte>();
//mappings for sensors
double[] offset;
double[] sensitivity;

Map<String, String> realTimeIDs;

public RealTimeThread(BufferedReader input, Map<String, String> rt, double[] offset, double[] sens) {
    this.input = input;
    realTimeIDs = rt;
    this.offset = offset;
    this.sensitivity = sens;
}

//Split up the line, parse it and add to database
@Override
public List<SensorReading> call() throws Exception {
    List<SensorReading> toReturn = new ArrayList<SensorReading>();

        String inputLine;
        if((inputLine = input.readLine()) != null) {

            //pass to the scanner
            Scanner scan = new Scanner(inputLine); 

            //get everything after the starting pattern 
            Pattern pStart = Pattern.compile("\\x02\\x02\\x02\\x02\\x02(.*)"); 
            Matcher mStart = pStart.matcher(inputLine);
            if ( mStart.find() ) {
               inputLine = mStart.group(1);

               //get everything before ending pattern
               Pattern pEnd = Pattern.compile("(.*)\\x04\\x04\\x04\\x04\\x04"); 
               Matcher mEnd = pEnd.matcher(inputLine);
               if ( mEnd.find() ) {
                   inputLine = mEnd.group(1); // " that is awesome"

                   //split up this string
                   scan = new Scanner(inputLine);

                   //set the delimiter to unit separator
                   Pattern delim = Pattern.compile("\\x1F"); 
                   scan.useDelimiter(delim); 

                   while(scan.hasNext()) {
                       //get the next string
                       String s = scan.next();

                       //change it to an integer and make it a byte
                       int val = Integer.parseInt(s);
                       byte b = (byte) val;

                       //add to the arraylist
                       values.add(b);
                   } 

                   //parse the values
                   toReturn = parser(values);

                  // System.out.println("RETURN 0 " + toReturn.get(1).getRawValue());

                   //reset the arraylist
                   values = new ArrayList<Byte>();
                }                  
            }
        }

    return toReturn;
}

//Parser to split up line, create a new sensor reading and add to database
private List<SensorReading> parser(ArrayList<Byte> data) {

    //arraylist for data after transformation
    ArrayList<Short> convertedData = new ArrayList<Short>();

    //get all data in big endian 
    for (int i = 0; i<46; i=i+2) {

    ...
...
        convertedData.add(dataChunk);
    }

    //get the time now
    double myTime = System.currentTimeMillis();

    ArrayList<SensorReading> toReturn = new ArrayList<SensorReading>();

//add to the database
    for(int i = 0; i<convertedData.size(); i++) {
        //create new sensor reading
        SensorReading sr = new SensorReading();
        sr.setSensorId(keys[i].toString());
        sr.setName(sens.get(keys[i]));
        sr.setRawValue(convertedData.get(i));
        sr.setTime(myTime);

        //add to database - this is not working 
        sensorReadingService.addSensorReadingIntoCollection(sr, "realTime");
        System.out.println("added");

        toReturn.add(sr);
    }

    return toReturn;
}
}

当尝试在我的 XML 中或使用 @Component 创建 bean 时,我得到一个 BeanCreationException 说没有默认的空构造函数。我有一个构造函数,但它有输入。 如何让 Spring 使用这个构造函数?

我尝试在我拥有的构造函数上使用@Autowired,但我收到一条错误消息,指出它无法自动装配字段 BufferedInput。有什么建议么?谢谢!

编辑:

我在我的 RealTimeThread 类上使用了@Component,在我的构造函数上使用了@Autowired。现在我得到的错误如下:

[localhost-startStop-1] ERROR org.springframework.web.context.ContextLoader - Context
initialization failed
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'realTimeThread' defined in file [C:\Users\Lauren\Dropbox\MEng Project\1. Off-board Software\Lauren\.metadata\.plugins\org.eclipse.wst.server.core\tmp1\wtpwebapps\USM-Analysis-Application\WEB-INF\classes\usm\model\RealTimeThread.class]: Unsatisfied dependency expressed through constructor argument with index 0 of type [java.io.BufferedReader]: : No qualifying bean of type [java.io.BufferedReader] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [java.io.BufferedReader] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}

所以我从中收集到的是我的 BufferedReader 不是由 Spring 处理的?我正在从我的 Controller 类中传递我的 BufferedReader,如下所示:

@Controller
public class SensorReadingController implements SerialPortEventListener {

//mongodb service
@Autowired
private SensorReadingService sensorReadingService;

private BufferedReader input;
double[] offset;
double[] sensitivity;

...

@Override
public void serialEvent(SerialPortEvent oEvent) {
    if (oEvent.getEventType() == SerialPortEvent.DATA_AVAILABLE) {          
        //use a thread in the thread pool
        //threadPool.execute(new RealTimeThread(input, sensorReadingService, getRealTimeIdsAndNames()));

        //input.readLine();

        Callable<List<SensorReading>> callable = new RealTimeThread(input, getRealTimeIdsAndNames(), offset, sensitivity);
        Future<List<SensorReading>> future = threadPool.submit(callable);

我认为因为我是从控制器传递这些变量,所以 Spring 已经处理了它们。但我想不会。 我如何让 Spring 管理这些变量?我是否只在它们上方使用 @Autowired,就像我对我的服务所做的那样?

【问题讨论】:

  • 您缺少&lt;context:annotation-config /&gt;,这将支持通过注释自动连接。
  • 你想将你的服务@Autowire 加入什么样的类?
  • @Bart 不是 在做什么吗?我所有的课程都在一个 usm 包中,例如usm.controller, usm.model 所以我认为这将启用自动装配。看看stackoverflow.com/questions/7414794/…,听起来两者的工作基本相同。但我会尝试使用
  • @geoand public class RealTimeThread implements Callable> 是我试图将我的服务自动连接到的类,它将在串行事件中调用。目前,该服务已自动连接到(AT 符号)Controller 公共类 SensorReadingController 实现 SerialPortEventListener 并且工作正常。不确定这是否能回答您的问题。
  • @Lauren 感谢您的回答! RealTimeThread 是 Spring bean 吗?

标签: java mongodb spring-mvc autowired spring-annotations


【解决方案1】:

问题似乎是 SensorReadingService 被自动装配到的类不是 Spring 管理的类。为了使自动装配工作,需要装配的类需要由 Spring 管理它的生命周期(这意味着您需要在 Spring Java Config 的 Spring XML 中有该类的条目)

你可以这样重构你的代码:

1) 向RealTimeThread 添加另一个构造函数参数,其类型为SensorReadingService

2) 像这样创建一个类RealTimeThreadFactory

public class RealTimeThreadFactory {

    private final SensorReadingService sensorReadingService;

    public RealTimeThreadFactory(SensorReadingService sensorReadingService) {
        this.sensorReadingService = sensorReadingService;
    }

    public RealTimeThread getObject(BufferedReader input, Map<String, String> rt, double[] offset, double[] sens) {
          return new RealTimeThread(input, rt, offset, sens, sensorReadingService);
    }
}

3) 在组件扫描中包含的包下的某处添加一个 Java Config 类

@Configuration
public class RealTimeThreadConfig {

    @Autowired
    private SensorReadingService sensorReadingService;

    @Bean
    public RealTimeThreadFactory realTimeThreadFactory() {
        RealTimeThreadFactory realTimeThreadFactory = new RealTimeThreadFactory(sensorReadingService);
        return realTimeThreadFactory;
    }

}

4) 使用当前代码创建 RealTimeThread 的类现在需要成为 Spring bean(使用您喜欢的任何方式)并注入 RealTimeThreadFactory。为了创建 RealTimeThread 对象,只需使用适当的参数调用工厂的 getObject() 方法

【讨论】:

  • 所以我需要在我的调度程序 servlet xml 文件中有类似 的东西吗?或者有没有办法可以在该类中使用注释使其由 Spring 管理?我尝试使用@Component,但由于我没有默认构造函数,它正在抛出一个 Bean 创建异常。我将编辑我的问题以包含我的 RealTimeThread 类,以向您展示我正在使用的构造函数
  • 是的,您可以使用 xml 或 @Component。但是,您的类必须具有默认构造函数,否则您也必须在构造函数上使用 Autowired,并且构造函数的所有参数也由 Spring 管理。可以发一下RealTimeThread的代码吗?
  • 我已经发布了我的 RealTimeThread 的代码。我使用过 Component 和 Autowired 但就像你说的我的构造函数的参数也需要由 Spring 管理。我认为它们是因为它们在 Controller 类中,但显然不是。我如何让这些变量由 Spring 管理?非常感谢您给我的所有帮助,真的很有帮助!
  • @Lauren 我稍后会更新我的答案。敬请期待
  • 我认为在 RealTimeThreadFactory 中您的意思是返回新的 RealTimeThread。感谢这个解决方案有效——我现在可以在这个其他类的数据库中添加东西了! :) 但是它似乎并没有在多线程中工作,它挂在串行事件侦听器中。但现在这是另一个问题哈哈非常感谢您的帮助和耐心!
猜你喜欢
  • 2020-02-09
  • 2019-05-29
  • 1970-01-01
  • 2012-05-15
  • 2018-11-11
  • 2020-01-14
  • 2015-04-25
  • 1970-01-01
  • 2012-07-16
相关资源
最近更新 更多