33458

Can I use Spring @Autowired in multiple classes?

Question:

My question is - <strong>Can I autowire an instance of a class in multiple classes?</strong>

My application uses Spring MVC to communicate between the JSP front end pages and my MongoDB back end. I am using MongoDB for my repository. I have created a service which performs the CRUD methods for MongoDB. This can be seen below with one of the CRUD methods (not all are shown as they are not needed). It uses the Mongo template.

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); }

As can be seen, the MongoTemplate is autowired. In my Dispatcher-Servlet I have the following code for 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 />

I now also have a controller which has autowired the service so that requests from the JSP page can be passed to the controller, and the controller then writes these requests to the database via the autowired service. (One example method shown below as the class is massive). This autowired service doesn't have anything in the dispatcher servlet like the MongoDB autowired previously.

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"); }

The sensorReadingService method runs perfectly, performing CRUD methods on the database. <strong>Now my question is, how do I use this sensorReadingService in another class?</strong> When I add

//mongodb service @Autowired private SensorReadingService sensorReadingService;

into another class, the service doesn't work. No errors are flung, the service just does not add anything to the database. Is it because @Autowired only allows the class to be autowired in one class i.e. there can't be multiple instances? Or is it because I haven't specified anything in my dispatcher-servlet like with the MongoDB?

The reason I need to have it work in another class is that in my class, I am listening for Serial events. When there is data available, I create a new thread to deal with this serial event so that the rest of my program can still function. In the thread, I parse the string I received from Serial, create a new SensorReading object, and then I want to write this SensorReading object to the database. However, as I cannot get sensorReadingService to work in any other classes, I can't perform this write to database.

First I used a class that implements Runnable to perform the parsing and saving. Having @Autowired in this class did not work, so I tried passing the sensorReadingService to the thread from the controller (shown in code below). This did not work either. I then changed my thread to implement Callable so I could return the SensorReading and save it to the database in my original controller class that has the working autowired service. However, this defeats the purpose of creating the thread in the first place as it is the writing to the database I wish to perform in the thread, as it slows down the whole program if not.

@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);

So, I was just wondering if anyone knew what I was doing wrong with @Autowired? <strong>Can I have multiple instances of @Autowired? Do I need to add something in my dispatcher servlet? Or should I just not use @Autowired and try to call my service another way?</strong>

Any suggestions would be much appreciated and if you need me to post anymore code let me know! Thanks in advance!

<strong>EDIT:</strong>

For my RealTimeThread class I have the following code

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; } }

When trying to create a bean either in my XML or using @Component I get a BeanCreationException saying that there is no default empty constructor. I have a constructor but it has inputs into it. <strong>How do I make Spring use this constructor?</strong>

I tried using @Autowired on the constructor I have but I got an error saying that it could not autowire field BufferedInput. Any suggestions? Thanks!

<strong>EDIT:</strong>

I have used @Component on my RealTimeThread class and @Autowired on my constructor. Now the errors I am getting are as follows:

[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: {}

So what I gather from this is that my BufferedReader is not handled by Spring? I am passing my BufferedReader in from my Controller class as follows:

@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);

I thought that because I was passing these variables from the controller, Spring already dealt with them. But I guess not. <strong>How do I make Spring manage these variables?</strong> Do I just use @Autowired above them like I have done with my service?

Answer1:

The problem seems to be that the class in which SensorReadingService is being autowired into, is not a class managed by Spring. For autowiring to work, the class that requires the wiring needs to have it's lifecycle managed by Spring (meaning that you need to have an entry for that class in your Spring XML of Spring Java Config)

You could refactor your code like this:

1) Add another constructor argument to RealTimeThread which is going to be of type SensorReadingService.

2) Create a class RealTimeThreadFactory like so:

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) Add a Java Config class somewhere under the package that is being included in the component scan

@Configuration public class RealTimeThreadConfig { @Autowired private SensorReadingService sensorReadingService; @Bean public RealTimeThreadFactory realTimeThreadFactory() { RealTimeThreadFactory realTimeThreadFactory = new RealTimeThreadFactory(sensorReadingService); return realTimeThreadFactory; } }

4) The class that with your current code creating RealTimeThread needs to now be a Spring bean (using any way you prefer) and be injected with RealTimeThreadFactory. In order to create the RealTimeThread objects , simple call the getObject() method on the factory with the appropriate arguments

Recommend

  • How to import fonts with pure JS? [duplicate]
  • Change colour of TLabel in FireMonkey iOS app, and add items to TStringGrid?
  • Click doesn't work on this Google Translate button?
  • Locking tables in Access 2010
  • Is there a less verbose alternative to Perl's Data::Dumper?
  • pass parameters from servlet to jsp page
  • WPF UI on multiple threads?
  • inter thread comunication using ZeroMQ messages [closed]
  • Override Data Tip Circle
  • why adding a space after `(.+?)` can completely change the result
  • Can TextIO write to prefixes derived from the window maxTimestamp?
  • what -j4 value is used to invoke make?
  • Prevent Emacs from modifying the OS X clipboard?
  • Aspect not executed in Spring
  • Replicating and differentiating portions of a form
  • Read stdin in chunks in Bash pipe
  • Best Datagrid for Bootstrap/jQuery?
  • WPF Run Animation in a separate Thread
  • vi mode to emacs mode while on R
  • css font-size and line-height not matching the baseline
  • WooCommerce hook after order is updated?
  • Return to second to last URL in MVC (return View with previous filter conditions applied)?
  • Graceful pod termination
  • insert a picture into database(sqlite) with java code. what should i do?
  • Getting error java.io.FileNotFoundException (log4j log file) at the time of publish project on cloud
  • Converter from SAT to 3-SAT
  • Zoom in and out of jPanel
  • Firefox Extension - Monitor refresh and change of tab
  • Saving Changes After In-App Purchase Has Been Purchased
  • Sencha Touch 2.0 Controller refs attribute not working?
  • Trying to switch camera back to front but getting exception
  • How can I use Kendo UI with Razor?
  • To display the title for the current loaction in map in iphone
  • bootstrap to use multiple ng-app
  • Error creating VM instance in Google Compute Engine
  • Free memory of cv::Mat loaded using FileStorage API
  • Angular 2 constructor injection vs direct access
  • Programmatically clearing map cache
  • Binding checkboxes to object values in AngularJs
  • How to load view controller without button in storyboard?