I have a Rest Controller in which I initialise a service like this :
class Config {
#Value(${"number.of.books"})
private final static String numberOfBooks;
}
class MyController {
private final Service myService = new ServiceImplementation(Config.numberOfBooks)
public ResponseEntity methodA() { ... }
}
The numberOfBooks field has a initialisation value but when it's passed in the ServiceImplementation constructor it comes null.
I'm thinking I'm missing something obvious over here.
What is the mistake and which would be the best practice to inject a value from a property file into a constructor?
I recommend you to directly inject numberOfBooks in your ServiceImplementation, as follows:
public class ServiceImplementation implements Service {
#Value("${number.of.books}")
private String numberOfBooks;
}
Otherwise use setter injection for static variables, as follows:
#Component
class Config {
public static String numberOfBooks;
#Value("${number.of.books}")
public void setNumberOfBooks(String numberOfBooks) {
numberOfBooks = numberOfBooks;
}
}
After studying a little I've found out that the dependency injection happens after the constructor has been called. This being said the approach used was to use Autowired on my services constructor.
class ServiceImplementation implements Service {
private final String numberOfBooks;
#Autowired
private ServiceImplementation(Config config) {
this.numberOfBooks = config.getNumberOfBooks();
}
}
In this way Spring creates the dependency tree and makes sure that Config is not null when injected.
Related
I'm creating telegram bot with Spring-Boot. I have AscractState class:
public abstract class AbstractState {
boolean isInputIndeed = Boolean.FALSE;
public abstract void handleInput(BotContext context);
//another parts
}
And there is extend which is
#Slf4j
public class AgeInputState extends AbstractState {
#Autowired
ClientService clientService;
public AgeInputState(boolean isInputIndeed) {
super(isInputIndeed, State.AGE_INPUT);
}
#Override
public void handleInput(BotContext context) {
context.getClient().setAge(Integer.parseInt(context.getInput()));
clientService.updateUser(context.getClient());
}
}
But i have touble with ClientService. Which annotations on class i need to add for autowiring this fiels?
Since this class has a constructor which only accepts a boolean, I assume you're needing to make lots of them.
Spring won't know you're wanting to load these as spring beans if you call this constructor directly. So creating these through a factory of some sort would be one way to go. Something like:
#Configuration
public class AgeInputStateFactory {
private #Autowired ClientService clientService;
#Bean
#Scope("prototype") // Makes a new one each time...
public AgeInputState create(final boolean isInputIndeed) {
return new AgeInputState(this.clientService, isInputIndeed);
}
}
Along with a newly designed AgeInputState constructor which also takes the ClientService field.
public class AgeInputState extends AbstractState {
private final ClientService clientService;
// Package private constructor so that no one outside
// of this package will call it. This means you can
// (try your best to) limit the construction to the
// factory class.
AgeInputState(final ClientService clientService,
final boolean isInputIndeed) {
super(isInputIndeed, State.AGE_INPUT);
this.clientService = clientService;
}
}
And then all you would do is wherever you need to create these AgeInputState Objects, you would #Autowire the AgeInputStateFactory instance, and call the create method whenever you need one.
In a basic Spring Boot app I have this component:
#Component
public class TheComponent {
public String getKey() {return "value";}
}
used by a service. If I design my service like this:
#Service
public class TheService {
#Autowired
private TheComponent theComponent;
private final String keyValue = theComponent.getKey();
private TheService() {}
}
then Spring Boot doesn't build because theComponent triggers a NullPointerException.
If I design it like this:
#Service
public class TheService {
private String keyValue;
private TheService(TheComponent theComponent) {
keyValue = theComponent.getKey();
}
}
then SonarLint tells me that I should Remove this unused private "TheService" constructor.
Is there a solution that would suit both Spring and SonarLint, using a private constructor ?
Annotate your constructor with:
#SuppressWarnings("unused")
You can also use it on class level to suppress all warnings.
I have a controller class which is below. I have a TagRepository interface which extends JPA repository which I am using to save TagReader instance to my DB and it works fine when I use it in my controller class. However, when I try to use tagRepository in another class and try to save my TagReader object from there it throws a null pointer exception.
The following logic works fine.
#RestController
public class Controller {
#Autowired
TagRepository tagRepository;
#Autowired
Rfid6204Connection rfid6204Connection;
#RequestMapping(value = "/test")
public void testRepoController(){
String tagid = "0x3504ACE6E0040E5147D516A6";
String serial ="00333478";
String departure ="2017-12-22T12:16:58.857";
String type = "ISOC";
TagReader tagReader = new TagReader(tagid,serial,departure,type,"5");
tagRepository.save(tagReader);
}
}
The following logic throws a null pointer exception.
#component
public class Rfid6204Connection{
#Autowired
static TagRepository tagRepository;
public static void test(TagReader tag){
tagRepository.save(tag);
}
}
Can someone please tell me what the issue is?
I think you are using Rfid6204Connection.test as a static method. Spring doesn't work with Static methods. It works with Objects instantiated by the Spring Container. So change your Rfid6204Connection as below;
#Component
public class Rfid6204Connection{
#Autowired
private TagRepository tagRepository;
public void test(TagReader tag){
tagRepository.save(tag);
}
}
And use it wherever you want as below;
#Autowired
Rfid6204Connection rfid6204Connection;
// Within a method or constructor
rfid6204Connection.test(tag);
You made the Autowired field static and when the class loader loads the static values, the Spring context is not yet loaded and your object is not correctly initialized; remove the static keyword:
#Autowired
private TagRepository tagRepository;
you couldn't autowired static variables directly
then, you have some options.
first, autowired instance of TagRepository and after dependency injection
set a instance to static variable
#Component
public class Rfid6204Connection {
private static TagRepository sTagRepository;
#Autowired
private TagRepository tagRepository;
#PostConstruct
public void init() {
Rfid6204Connection.sTagRepository = tagRepository;
}
}
second prepare setter method of TagRepository and put a autowired
public class Rfid6204Connection {
private static TagRepository tagRepository;
#Autowired
public void setTagRepository(TagRepository tagRepository) {
Rfid6204Connection.tagRepository = tagRepository;
}
}
but originally ... you shoudn't autowire to static variables.
I have an object reads configuration properties like this:
#ApplicationScoped
public class Configuration {
#Inject
#Config(value = "endpoint.base", defaultValue = "http://localhost:52885/consumers")
private String base;
public String getBase() { return base; }
}
this object is injected to a service object like this:
public class LoyaltyService {
final Sender sender;
final Configuration config;
#Inject
public LoyaltyService(Sender sender, Configuration config) {
this.sender = sender;
this.config = config;
}
}
I am now testing this service object with Mockito. I want to mock the Sender object, but I don't want to mock the configuration, or at least I just want to use the default value defined inside the object.
How can I do that in a Test object?
For example, I tried the following:
public class LoyaltyServiceTest {
#Mock
private Sender sender;
#Inject
private Configuration config;
private LoyaltyService target;
#Before
public void init() {
MockitoAnnotations.initMocks(this);
when (sender.post(anyString(), anyString())).thenReturn("Post Success");
target =new LoyaltyService(sender, config);
}
}
It doesn't seem CDI will register the Config object at all. How does this work? Thanks!
It doesn't seem CDI will register the Config object at all.
The CDI beans are not initialised when running the test, only the mocked objects are.
MockitoAnnotations.initMocks only initializes
objects annotated with Mockito annotations for given testClass: #Mock, #Spy, #Captor, #InjectMocks.
You need to use a CDI test framework like cdi-unit or Pax Exam in your test class to create the non-mocked beans for you.
I am newbie to Spring Framework.I have tried following example in spring.
#Path("/XZY")
#Service
#Transactional
public class XZY {
#Autowired
SampleDAO sampleDao;
#Autowired
TestDAO testDao;
#Autowired
XZYinterface xzyinterface;
#POST
#Produces("text/plain")
#Path("/checkservice")
public Response XZYservice(#FormParam("Code") String Code,
#FormParam("source") String source,
#FormParam("value") String value) {
//return xzyinterface.checkXYZService(Code,sourceName,source);
XZYinterface xyz = ServiceFactory.getXZY(999);
return xyz.checkXYZService(Code,sourceName,source);
}
}
The following code will use to create singleton object
public class Singleton {
private static sampleA sampleClassA=null;
private static SampleB sampleClassB=null;
public static XZYAbstract getXZY(long id){
if(id == 999){
if(sampleClass == null){
sampleClassA = new sampleA();
}
return sampleClass;
}
if(id == 9999){
sampleClassB = new sampleA();
}
return sampleClassB;
}
}
Interface
public interface XZYinterface {
Response XZYservice(String Code, String source,String value)
}
Abstract class and implements Interface
public class XZYAbstract implements XZYinterface {
public XZYAbstract(){
super();
}
#Autowired
SampleDAO sampleDao;
#Autowired
TestDAO testDao;
public Response checkXYZService(String Code,String source,String value){
String sample = sampleDao.getValue(code);
//..source code
}
}
The following class extends abstract class.
public class sampleA extends XZYAbstract {
//some methods.
}
If i run the application it throws following errors
SEVERE [com.sun.jersey.spi.container.ContainerResponse] The RuntimeException could not be mapped to a response, re-throwing to the HTTP container: java.lang.NullPointerException
at com.test.xyz.XZYAbstract.checkXYZService(XZYAbstract.java:112) [:]
at com.test.XYZ.XZYservice(XZY.java:140) [:]
If i call directly without singleton object, values are initialized properly using Auto wired (//return xzyinterface.checkXYZService(Code,sourceName,source);) and it's working fine.
Throw from singleton object, values(sampleDAo,testDao) are not initialized properly.
How to resolve this error?
The reason is quite trivial: it's because Spring is just a library, and not a change to the Java language. Spring doesn't instrument nor enhance constructors, so the only way to get initialized Spring bean is to get it from the Spring context.
If you call new Bean(), you becomes Bean instance untouched by Spring.
For the question how to use singleton bean: do nothing. Spring beans are Singletons by default. You can specify other scope via #org.springframework.beans.factory.config.Scope annotation. See for example #Scope("prototype") bean scope not creating new bean, how it works.