When I try to inject the packagePropertiesList Bean in Operation.class, I get values from a properties file. But when I use operation.removeStudentFromList(), I get only null values. Do you see any problem here?
values while injecting beans
#SpringBootApplication
public class LearningCenterApplication {
public static void main(String[] args) {
SpringApplication.run(LearningCenterApplication.class, args);
ApplicationContext context =
new AnnotationConfigApplicationContext(Config.class, Operations.class, PackageProperties.class);
Operations operations = context.getBean(Operations.class);
operations.removeStudentFromList();
}
#Bean
List<PackageProperties> packagePropertiesList(List<PackageProperties> packageProperties) {
System.out.println(packageProperties);
return packageProperties;
}
#Bean
#ConfigurationProperties(prefix = "remove")
public PackageProperties removeMethod() {
return new PackageProperties();
}
#Bean
#ConfigurationProperties(prefix = "add")
public PackageProperties addMethod() {
return new PackageProperties();
}
}
#Component
public class Operations {
private List<PackageProperties> packagePropertiesList;
#Autowired
public Operations(List<PackageProperties> packagePropertiesList) {
this.packagePropertiesList = packagePropertiesList;
}
public void removeStudentFromList() {
System.out.println(packagePropertiesList);
}
}
public class PackageProperties {
private String packageName;
private String className;
private String methodName;
public String getPackageName() {
return packageName;
}
public void setPackageName(String packageName) {
this.packageName = packageName;
}
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
public String getMethodName() {
return methodName;
}
public void setMethodName(String methodName) {
this.methodName = methodName;
}
#Override
public String toString() {
return packageName + "." + className + "." + methodName;
}
}
application.properties
remove.packageName = org.epam.operations
remove.className = Operations
remove.methodName = removeStudentFromList()
add.packageName = org.epam.operations
add.className = Operations
add.methodName = addStudent()
[null.null.null] — output when operations.removeStudentFromList() is invoked
You are creating an additional ApplicationContext next to the one that's already created by Spring Boot (with new AnnotationConfigApplicationContext(). Remove that and use the existing context to get a bean, or rather have a look at the ApplicationRunner interface, if you want to run code automatically after the application has started.
The whole property handling will only work for the application context, that's created and managed by Spring Boot, not for the one that you created yourself.
Related
I'm dwelling with an autoWired service class which is null in a Spring Boot application.. Every object is instantiated by Spring, so I don't know why it happens.
The situation is:
I have a Rele.java class which is the following:
#Component
public class Rele {
private Pin pin;
private GpioController gpio;
private GpioPinDigitalOutput relePin;
private static final Logger logger = Logger.getLogger(Rele.class);
private Interruttore interruttore;
#Autowired AccensioneService accensioneService;
public Rele(){
}
// Costruttore
public Rele(Pin pin, Interruttore interruttore) {
this.pin = pin;
this.gpio = GpioFactory.getInstance();
this.relePin = gpio.provisionDigitalOutputPin(pin, "MyRele", PinState.LOW);
this.interruttore = interruttore;
}
public void lightOn() {
try {
if (relePin.isLow()) {
relePin.high();
updateAccensione(interruttore, true);
logger.debug("Rele acceso");
}
} catch (NullPointerException e) {
logger.debug("relepin è:" +relePin);
logger.debug("gpio è:"+gpio);
}
}
public void lightOff() {
if (relePin.isHigh()) {
relePin.low();
updateAccensione(interruttore, false);
logger.debug("Rele spento");
}
}
public void updateAccensione(Interruttore interruttore, boolean acceso) {
Date lastDateAccensione = new Date();
try {
logger.debug("accensioneService is"+accensioneService);
lastDateAccensione = accensioneService.findLastDate(interruttore);
} catch(NullPointerException npe){
logger.debug("accensioneService is: "+accensioneService);
logger.error("Error is:", npe);
lastDateAccensione = new Timestamp(lastDateAccensione.getTime());
}
Accensione accensione = new Accensione();
Date date = new Date();
logger.debug("lastDate:" + lastDateAccensione);
accensione.setDateTime(new Timestamp(date.getTime()));
accensione.setInterruttore(interruttore);
accensione.setIsLit(acceso);
accensione.setLastDateTime(lastDateAccensione);
logger.debug("Accensione è:"+accensione.toString());
accensioneService.saveAccensione(accensione);
}
public Pin getPin() {
return pin;
}
public void setPin(Pin pin) {
this.pin = pin;
}
public Interruttore getInterruttore() {
return interruttore;
}
public void setInterruttore(Interruttore interruttore) {
this.interruttore = interruttore;
}
public GpioPinDigitalOutput getRelePin() {
return relePin;
}
public void setRelePin(GpioPinDigitalOutput relePin) {
this.relePin = relePin;
}
public GpioController getGpio() {
return gpio;
}
public void setGpio(GpioController gpio) {
this.gpio = gpio;
}
}
When trying to call for updateAccensione, this is null.
Rele is created from a Controller, by this method
#RequestMapping(value="/illuminazione")
public ResponseEntity<Illuminazione> findIlluminazione(#RequestParam(value="idLuce") int idLuce,
#RequestParam(value="lit") boolean lit,
#RequestParam(value="suServer") boolean suServer) {
Illuminazione illuminazione = new Illuminazione();
Date lastDate = illuminazioneService.findLastDate(idLuce);
illuminazione.setLastDateTime(lastDate);
illuminazione.setIdLuce(idLuce);
illuminazione.setIsLit(lit);
Date date = new Date();
illuminazione.setDateTime(new Timestamp(date.getTime()));
illuminazioneService.saveIlluminazione(illuminazione);
logger.debug("Aggiornata luce " + idLuce + " accesa: "+lit);
//managing rele
if(suServer){
//check if status has changed
Luce luce = luceService.findById(idLuce);
int idInterruttore = luce.getInterruttore().getIdInterruttore();
Interruttore interruttore = interruttoreService.findById(idInterruttore);
Rele rele = releService.findByInterruttore(interruttore);
logger.debug("rele="+rele.toString());
if(lit){
rele.lightOn();
} else {
rele.lightOff();
}
}
return new ResponseEntity<Illuminazione>(illuminazione,HttpStatus.OK);
}
Rele is created, i find it in my logs.
AccensioneService is an interface, it's concrete implementation is AccensioneServiceImpl:
#Service("accensioneService")
#Transactional
public class AccensioneServiceImpl implements AccensioneService{
#Autowired AccensioneDao dao;
#Override
public void saveAccensione(Accensione accensione) {
dao.saveAccensione(accensione);
}
#Override
public Accensione findById(int id) {
return dao.findById(id);
}
#Override
public Date findLastDate(Interruttore interruttore) {
return dao.findLastDate(interruttore);
}
#Override
public boolean findLastStatus(int id) {
return dao.findLastStatus(id);
}
#Override
public void updateAccensione(Interruttore interruttore) {
}
}
I don't know if anything else is needed. AccensioneService is also called in other methods and controller, and it works... only when called inside Rele gives me this error...
Edited to add
You must be calling new Rele() or the other Rele(Pin, Interruttore ) constructor? If you are calling these in your code, the accensioneService will be null because Spring needs to create the bean, you cannot create it with its constructor if you want beans Autowired into it or for it to be Autowired. If you want it to behave like this, Spring has to know about it, so it has to be in (and come from) the Spring context.
Put a log statement in each constructor and find out who is calling them, and fix that so that instead of calling the constructor, you get the bean from Spring.
Old answer below
You need to post this method to be sure:
Rele rele = releService.findByInterruttore(interruttore);
I'll bet you are creating rele somewhere by calling new Rele(), which is not correct. You need to let Spring create it for you.
You did not post enough code to give further suggestions.
Also, you say this is null. What this are you talking about?
Say I have this dependency in a Spring #Configuration:
#Bean
public SomeClass someClass(SomeClass1 someClass1, SomeClass2 someClass2, ...) {
return new SomeClass(someClass1, someClass2, ...);
}
Say I want do do something in #PostConstruct that includes someClass dependency:
#PostConstruct
public void init() {
someClass.doSomething();
}
This cannot be injected:
#PostConstruct
public void init(SomeClass someClass) {
someClass.doSomething();
}
causes:
Caused by: java.lang.IllegalStateException: Lifecycle method annotation requires a no-arg method: ...
This cannot be autowired in the same config like this:
#Autowire
private SomeClass someClass;
#Bean
public SomeClass someClass(SomeClass1 someClass1, SomeClass2 someClass2, ...) {
return new SomeClass(someClass1, someClass2, ...);
}
as that leads to:
Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'globalBus': Requested bean is currently in creation: Is there an unresolvable circular reference?
A config can be split (so #Bean goes to the other config) and #Import-ed by this one and it works OK. Probably other solutoins exist - e.g. creating a separate initialization bean or so.
Is there a way to do this within one #Configuration?
Edit
As requested by #SotiriosDelimanolis, a sscce for the exception when using #Autowired:
public class ConfigPostConstructDependenciesPrb {
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Config.class);
ctx.getBean(Service.class);
ctx.close();
}
public static class Service {
private final Dependency dependency;
public Service(Dependency dependency) {
this.dependency = dependency;
}
public void work() {
System.out.println(dependency.getNum());
}
#Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("Service [dependency=");
sb.append(dependency);
sb.append("]");
return sb.toString();
}
}
public static class Dependency {
private final int num;
public Dependency(int num) {
this.num = num;
}
public int getNum() {
return this.num;
}
#Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("SomeClass1 [num=");
sb.append(num);
sb.append("]");
return sb.toString();
}
}
#Configuration
public static class BaseConfig {
#Autowired
private Service service;
#Bean
public Dependency dependency() {
return new Dependency(42);
}
#Bean
public Service service(Dependency dependency) {
return new Service(dependency);
}
#PostConstruct
public void init() {
service.work();
}
}
#Configuration
#Import(BaseConfig.class)
public static class Config {
#Autowired
private Service service;
}
}
(Tested in Spring 4.3.6)
Create a nested class inside your #Configuration and put there declarations of #Autowired service and #PostConstruct init():
#Configuration
public static class BaseConfig {
//...
#Bean
public Service service(Dependency dependency) {
return new Service(dependency);
}
#Configuration
public static class Setup {
#Autowired
private Service service;
#PostConstruct
public void init() {
service.work();
}
}
}
Below is your full example updated accordingly.
Notice that you don't have to add explicit reference to BaseConfig.Setup (look at the #Import annotation before Config class - it only refers to BaseConfig itself).
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.*;
import javax.annotation.PostConstruct;
public class ConfigPostConstructDependenciesPrb {
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Config.class);
ctx.getBean(Service.class);
ctx.close();
}
public static class Service {
private final Dependency dependency;
public Service(Dependency dependency) {
this.dependency = dependency;
}
public void work() {
System.out.println(dependency.getNum());
}
#Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("Service [dependency=");
sb.append(dependency);
sb.append("]");
return sb.toString();
}
}
public static class Dependency {
private final int num;
public Dependency(int num) {
this.num = num;
}
public int getNum() {
return this.num;
}
#Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("SomeClass1 [num=");
sb.append(num);
sb.append("]");
return sb.toString();
}
}
#Configuration
public static class BaseConfig {
#Bean
public Dependency dependency() {
return new Dependency(42);
}
#Bean
public Service service(Dependency dependency) {
return new Service(dependency);
}
#Configuration
public static class Setup {
#Autowired
private Service service;
#PostConstruct
public void init() {
service.work();
}
}
}
#Configuration
#Import(BaseConfig.class)
public static class Config {
#Autowired
private Service service;
}
}
Try this way:
public class ConfigPostConstructDependenciesPrb {
public static void main(String[] args) {
try {
AnnotationConfigApplicationContext ctx =
new AnnotationConfigApplicationContext(BaseConfig.class);
ctx.registerShutdownHook();
ctx.getBean(Service.class);
ctx.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
#Configuration
class BaseConfig {
#Autowired
private Service service;
#Bean
public Dependency dependency() {
return new Dependency(42);
}
#Bean
public Service service(Dependency dependency) {
return new Service(dependency);
}
#PostConstruct
public void init() {
this.service.work();
}
}
class Dependency {
private int num;
public Dependency() {
}
public Dependency(int num) {
this.num = num;
}
public int getNum() {
return this.num;
}
#Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("SomeClass1 [num=");
sb.append(num);
sb.append("]");
return sb.toString();
}
}
class Service {
private Dependency dependency;
public Service() {
}
public Service(Dependency dependency) {
this.dependency = dependency;
}
public void work() {
System.out.println(dependency.getNum());
}
#Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("Service [dependency=");
sb.append(dependency);
sb.append("]");
return sb.toString();
}
}
I am trying to update code from Jmockit 1.1 to 1.9 to Access Real Instance, But it seems to be unsuccessful as below:
java.lang.IllegalArgumentException: Matching real methods not found for the following mocks:
CopyOfAccessRealInstanceTest$1#getRealInstanceName(String m, mockit.Invocation inv)
at CopyOfAccessRealInstanceTest$1.<init>(CopyOfAccessRealInstanceTest.java:28)
at CopyOfAccessRealInstanceTest.mockConstructor(CopyOfAccessRealInstanceTest.java:28)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
I think it is due to mock method:
#Mock(invocations = 1)
public String getRealInstanceName(String m,Invocation inv)
-------------------------Codes 1.9---------------------------------
public class CopyOfAccessRealInstanceTest {
private Constructor constructor = new Constructor("");
#Test
public void mockConstructor() {
// Mockit.setUpMock(Constructor.class, new MockedConstructor());
MockUp<Constructor> mockup = new MockUp<Constructor>() {
//public Constructor it;
#Mock(invocations = 1)
public String getRealInstanceName(String m,Invocation inv)
{
if ("real".equals(m)) {
return inv.proceed(inv.getInvokedArguments());
// return it.getRealInstanceName(m);
} else {
return "mock";
}
}
};
Assert.assertEquals("mock",
constructor.getRealInstanceName(""));
Assert.assertEquals("real_m_real",
constructor.getRealInstanceName("real"));
}
}
-----------------------Code of Jmock 1.1--------------------------------
public class AccessRealInstanceTest {
private Constructor constructor = new Constructor("");
#Test
public void mockConstructor() {
Mockit.setUpMock(Constructor.class, new MockedConstructor());
Assert.assertEquals("real_m_real",
constructor.getRealInstanceName("real"));
}
public static class MockedConstructor {
public Constructor it;
#Mock(reentrant = true)//reentrant allow to access real instance
public String getRealInstanceName(String m) {
if ("real".equals(m)) {
return it.getRealInstanceName(m);
} else {
return "mock";
}
}
}
}
Class to be Mocked:
public class Constructor {
private String memberId;
public Constructor(String memberId) {
this.memberId = memberId;
}
public String getRealName() {
return "real_" + this.memberId;
}
public String getRealInstanceName(String m) {
return "real_m_" + m;
}
}
It is resolved. Thank you for Rogério and Sean!
Note the API documentation says that an Invocation parameter must be the first parameter in the mock method. – Rogério
Using Guice, one can do the following:
interface Leg {}
_
class LeftLeg implements Leg {
public String toString() {
return "LeftLeg";
}
}
_
class RightLeg implements Leg {
public String toString() {
return "RightLeg";
}
}
_
class Robot {
final Leg leftLeg_;
final Leg rightLeg_;
#Inject
Robot(#Named("left") Leg leftLeg, #Named("right") Leg rightLeg) {
leftLeg_ = leftLeg;
rightLeg_ = rightLeg;
}
public String toString() {
return "leftLeg_=" + leftLeg_ + ", rightLeg_=" + rightLeg_;
}
}
_
class RobotTest {
#Test
public void t1() throws Exception {
Injector inj = Guice.createInjector(new AnGuiceModule());
Robot r = inj.getInstance(Robot.class);
assertEquals(r.toString(), "leftLeg_=LeftLeg, rightLeg_=RightLeg");
}
}
_
class AnGuiceModule extends AbstractModule {
protected void configure() {
bind(Leg.class).annotatedWith(Names.named("left")).to(LeftLeg.class);
bind(Leg.class).annotatedWith(Names.named("right")).to(RightLeg.class);
}
}
How can i achieve the same thing with Spring 3.x (3.1.x or 3.2) using JSR-330 (optional) annotations and JavaConfig without using XML configuration?
interface Leg {}
_
#Component
class LeftLeg implements Leg {
public String toString() {
return "LeftLeg";
}
}
_
#Component
class RightLeg implements Leg {
public String toString() {
return "RightLeg";
}
}
_
class Robot {
#Autowired
Leg leftLeg_;
#Autowired
Leg rightLeg_;
public String toString() {
return "leftLeg_=" + leftLeg_ + ", rightLeg_=" + rightLeg_;
}
}
_
#RunWith(SpringJUnit4ClassRunner.class)
class RobotTest {
#Autowired
Robot r;
#Test
public void t1() throws Exception {
System.out.println(r);
}
}
You can do it like this; although this one uses Spring annotations, #Qualifier and #Autowired, though I don't see any reason for it not to work with #Named and #Inject as well, you should try:
public class MovieRecommender {
private MovieCatalog movieCatalog;
private CustomerPreferenceDao customerPreferenceDao;
#Autowired
public void prepare(#Qualifier("main") MovieCatalog movieCatalog,
CustomerPreferenceDao customerPreferenceDao) {
this.movieCatalog = movieCatalog;
this.customerPreferenceDao = customerPreferenceDao;
}
// ...
}
Example taken from the reference.
The closest i could find is following (The definition of the Robot and Leg* classes does not change):
public class RobotTest {
#Test
public void t1() throws Exception {
ApplicationContext ctx = new
AnnotationConfigApplicationContext(RobotConfig.class, Robot.class);
Robot r = ctx.getBean(Robot.class);
assertEquals("leftLeg_=LeftLeg, rightLeg_=RightLeg", r.toString());
}
}
#Configuration
class RobotConfig {
#Bean
public Leg leftLeg() {
return new LeftLeg();
}
#Bean
public Leg rightLeg() {
return new RightLeg();
}
}
Alternative would be:
public class RobotTest {
#Test public void t1() throws Exception {
ApplicationContext ctx = new
AnnotationConfigApplicationContext(RobotConfig.class);
Robot r = ctx.getBean(Robot.class);
assertEquals("leftLeg_=LeftLeg, rightLeg_=RightLeg", r.toString());
}
}
#Configuration
class RobotConfig {
#Bean #Scope("prototype") public Robot robot() {
return new Robot(leftLeg(), rightLeg());
}
#Bean #Scope("prototype") public Leg leftLeg() {
return new LeftLeg();
}
#Bean #Scope("prototype") public Leg rightLeg() {
return new RightLeg();
}
}
There is an interesting approach described on spring forum.
You need to obtain a reference to the child context somehow, I don't like the approach presented there, but I there should be other ways.
Usage:
<bean name="someBean" class="playground.spring.BeanImportFactoryBean">
<property name="applicationContext" ref="privateCtx"/>
<property name="importBeanName" value="importBean"/>
</bean>
FactoryBean code:
public class BeanImportFactoryBean implements FactoryBean, BeanNameAware {
transient private final Log log = LogFactory.getLog(this.getClass());
private String beanName;
private ApplicationContext applicationContext;
private String importBeanName;
public BeanImportFactoryBean() {
}
public void setBeanName(String beanName) {
this.beanName = beanName;
}
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
public void setImportBeanName(String importBeanName) {
this.importBeanName = importBeanName;
}
protected String getUsedBeanName() {
String returnName;
if (importBeanName == null) {
returnName = beanName;
} else {
returnName = importBeanName;
}
return returnName;
}
public Object getObject() throws Exception {
return this.applicationContext.getBean(getUsedBeanName());
}
public Class getObjectType() {
return this.applicationContext.getType(getUsedBeanName());
}
public boolean isSingleton() {
return this.applicationContext.isSingleton(getUsedBeanName());
}
}
I have a spring controller/POJO like this:
#RequestMapping("/foo");
public class MyController {
#RequestMapping("/bar")
public String MyAction() { return someSharedFunc(false); }
#RequestMapping("/debug/ping");
public String MyDebugPing() { return someSharedFunc(true); }
private String someSharedFunc(boolean debug) {
if(debug) return "FooBar"; else return "Debug!";
}
}
In this scenario, the URL for MyDebugPing is /foo/debug/ping. However, I want it to be /debug/ping, effectively ignoring the RequestMapping on the class.
Is that possible?
Just remove the #RequestMapping annotation from the class and use full paths per individual methods. E.g.
public class MyController {
#RequestMapping("/foo/bar")
public String MyAction() { return someSharedFunc(false); }
#RequestMapping("/debug/ping");
public String MyDebugPing() { return someSharedFunc(true); }
private String someSharedFunc(boolean debug) {
if(debug) return "FooBar"; else return "Debug!";
}
}
If there is a lot of methods then you can simply move out the method to another controller.