I have defined a class MyFrontService in a jar, here is how I want to use it:
import blabla.MyFrontService;
public class Main {
public static void main(String[] args) {
MyFrontService.doThis();
}
}
The FrontService serves as an entry point to access other services.
Here is how it is currently defined (and it works).
MyFrontService.java:
public class MyFrontService {
public static void doThis(){
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("META-INF/springBeans.xml");
MyService1 myService1 = (MyService1) context.getBean("myService1");
myService1.doSomething();
context.close();
}
}
MyService1.java:
package blabla.service;
#Service("myService1")
public class MyService1 {
public void doSomething(){
// some code
}
}
src/main/resources/META-INF/springBeans.xml:
(...)
<context:annotation-config />
<context:component-scan base-package="blabla.service" />
(...)
I would like to replace the code of MyFrontService.java by something like this:
#MagicAnnotation("what_path?/springBeans.xml")
public class MyFrontService {
#Autowired
private static MyService1 myService1;
public static void doThis(){
myService1.doSomething();
}
}
I've read a lot from other questions on this website and others. It is sometimes said that it's impossible, and sometimes annotations like #Configuration, #Import and others are used but I can't make them work. For example, using
#ImportResource("classpath:META-INF/springBeans.xml")
triggers a NullPointerException when calling myService1.doSomething().
If it is possible to do it, what annotation and what path should I use?
Use Spring Boot framework and you will be able to write smth like this
#Controller
#EnableAutoConfiguration
public class SampleController {
#RequestMapping("/")
#ResponseBody
String home() {
return "Hello World!";
}
public static void main(String[] args) throws Exception {
SpringApplication.run(SampleController.class, args);
}
}
if you mark your class MyFrontService with #Component annotation that should save the headache of doing all the stuff you are doing. if you dont want to add the annotation and dont want to have everything under spring config you can define a class as below.
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
public class SpringContextUtil implements ApplicationContextAware {
private static ApplicationContext applicationContext;
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
#Override
public void setApplicationContext(
org.springframework.context.ApplicationContext ctx)
throws BeansException {
applicationContext = ctx;
}
}
And then in any class you can do,
ApplicationContext ctx = SpringContextUtil.getApplicationContext();
BeanClass av = ctx.getBean(BeanClass.class);
Related
I am using jpos Q2 Server with transaction manager from my spring boot application, however when i try to implement DI in my class which is implementing from the Jpos TransactionParticipant interface it is giving me the null pointer exception.
I have tried all the option that may be there in spring boot for IoC as per my understanding. It seems that TransactionParticipant third party library i am not able to register it in Spring IoC/DI module.
package com.fonepay.iso;
#Service("processIsoTxn")
public class ProcessIsoTxn implements TransactionParticipant{
#Autowired
private CbsTxnService cbsTxnService;
#Override
public int prepare(long id, Serializable context) {
Context ctx = (Context) context;
try{
ISOMsg request = (ISOMsg) ctx.get("REQUEST");
//Call local processing Message
//CbsTxnService cbsTxnService = new CbsTxnServiceImpl();
ISOMsg response = cbsTxnService.createFinancialTxn(request);
ctx.put("RESPONSE", response);
return PREPARED;
}catch(Exception ex){
System.out.println("Process Iso Txn | error | "+ex);
}
return 0;
}
}
package com.fonepay.service.impl;
#Service("cbsTxnService")
#Transactional
public class CbsTxnServiceImpl implements CbsTxnService{
public ISOMsg createFinancialTxn(ISOMsg isoMsg) {...}
}
#SpringBootApplication
#ComponentScan("com.fonepay")
#EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class, HibernateJpaAutoConfiguration.class})
public class JposserverApplication {
public static void main(String[] args) {
SpringApplication.run(JposserverApplication.class, args);
}
}
I am constantly getting java.lang.NullPointerException in line
ISOMsg response = cbsTxnService.createFinancialTxn(request);
Try this one , replace #Autowired annotation
Try to use constructor
#Service("processIsoTxn")
public class ProcessIsoTxn implements TransactionParticipant{
private CbsTxnService cbsTxnService;
public ProcessIsoTxn (CbsTxnService cbsTxnService) {
this.cbsTxnService = cbsTxnService;
}
If anyone is interested, this is how i achieved the workaround, please refer : https://confluence.jaytaala.com/display/TKB/Super+simple+approach+to+accessing+Spring+beans+from+non-Spring+managed+classes+and+POJOs
private CbsTxnService getCbsTxnService() {
return SpringContext.getBean(CbsTxnService.class);
}
ISOMsg response = getCbsTxnService().createFinancialTxn(request);
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
#Component
public class SpringContext implements ApplicationContextAware {
private static ApplicationContext context;
/**
* Returns the Spring managed bean instance of the given class type if it exists.
* Returns null otherwise.
* #param beanClass
* #return
*/
public static <T extends Object> T getBean(Class<T> beanClass) {
return context.getBean(beanClass);
}
#Override
public void setApplicationContext(ApplicationContext context) throws BeansException {
// store ApplicationContext reference to access required beans later on
SpringContext.context = context;
}
}
This question already has answers here:
Injecting beans into a class outside the Spring managed context
(8 answers)
Closed 3 years ago.
Suppose I have
#Service
public class myService () {
public void sayHello() {
System.out.println("Hello");
}
}
public class myTestClass() {
#Autowired
private myService thisService;
public void transferHello() {
thisService.sayHello();
}
}
public class Application() {
public static void main(String[] args) {
SpringApplication.run(PilContainerApplication.class, args);
myTestClass thisTest = new myTestClass();
thisTest.transferHello();
}
}
Since myTestClass() is not a bean like service/controller, thisService would have a null reference when I use myTestClass thisTest = new myTestClass();.
I was wondering how to overcome this.. I tried using public static myService thisService and it said cannot use #Autowired on static fields.
Thank you
When a class cannot be changed to be annotated with a Spring stereotype, #Bean is a very good alternative :
#Configuration
public class MyBeansConfiguration {
#Bean
public MyTestClass getMyTestClass(MyService myService) {
return new MyTestClass(myService);
}
}
You can inject it now in your Application class :
public class PilContainerApplication {
#Autowired
MyTestClass myTestClass;
#PostConstruct
public void init(){
myTestClass.transferHello();
}
public static void main(String[] args) {
SpringApplication.run(PilContainerApplication.class, args);
}
}
Note that beans are instances of class and are injected in other instances of class that depend on them. So you don't have access to the injected beans in the static main() method. But you have access to it in an instance method annotated with #PostConstruct that will be executed when the dependencies were injected in the current bean.
Side note : classes have to start with an uppercase. I did it in the provided code.
if you want inject bean B without marking bean A via some annotation, or xml definition, you can use SpringBeanAutowiringSupport
public class A {
#Autowired
private class B b;
public A{
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
}
}
I do have ServiceImpl which looks like this:
#Service
#RequiredArgsConstructor
public class ServiceAImpl implements ServiceA {
private final String fieldA;
#Override
public boolean isFieldA(String text){
return fieldA.equals(text);
}
And I would like to inject a field value to fieldA in an Application.java from application.yml like this:
#EnableSwagger2
#SpringBootApplication
public class Application {
#Value("${fieldA}")
private String fieldA;
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Bean
public ServiceA serviceA() {
return new ServiceAImpl(fieldA);
}
But I receive the following error when running SpringBoot app:
Error creating bean with name 'serviceAImpl' defined in URLNo qualifying bean of type 'java.lang.String' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
Do you have any solution for that?
You annotated your class with #Service and defined it manually as a bean with the #Bean annotation. I do think the second is the way you planned to use it.
The #Service annotation will make this class get picked up by Spring's component scan and additionally create an instance of it.
Of course it tries to resolve the parameters and fails when it tries to find a matching "bean" for the String field because there is no simple String bean (and should not :) ).
Remove the #Service annotation and everything should work as expected.
Try this
#Service
public class ServiceAImpl implements ServiceA {
private final String fieldA;
#Autowire
public ServiceAImpl(#Value("${fieldA}") String fieldA){
this.fieldA = fieldA;
}
#Override
public boolean isFieldA(String text){
return fieldA.equals(text);
}
}
and this
#EnableSwagger2
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
You should not use #Service and #Bean for the same class!
Spring is not so smart :)
You should annotate your bean like:
#RequiredArgsConstructor
public class ServiceAImpl {
#Value("${fieldA}")
private final String something;
...
But I'm not sure it will work with the #RequiredFieldsConstructor, it would be simpler for you write down the constructor annotated with #Autowired and using the #Value annotation for the String parameter:
#Autowired
public ServiceAImpl(#Value("${aProp}") String string) {
You're using two bean declaration mechanisms:
You're registering your bean using #Service
You're registering a bean using #Bean
This means that your service will be created twice. The one defined using #Bean works properly, since it uses the #Value annotation to inject the proper value in your service.
However, the service created due to #Service doesn't know about the #Value annotation and will try to find any bean of type String, which it can't find, and thus it will throw the exception you're seeing.
Now, the solution is to pick either one of these. If you want to keep the #Bean configuration, you should remove the #Service annotation from ServiceAImpl and that will do the trick.
Alternatively, if you want to keep the #Service annotation, you should remove the #Bean declaration, and you should write your own constructor rather than relying on Lombok because this allows you to use the #Value annotation within the constructor:
#Service
public class ServiceAImpl implements ServiceA {
private final String fieldA;
/**
* This constructor works as well
*/
public ServiceAImpl(#Value("${fieldA}") String fieldA) {
this.fieldA = fieldA;
}
#Override
public boolean isFieldA(String text){
return fieldA.equals(text);
}
}
If you want to declare ServiceAImpl as a Spring bean in your Java Configuration file, you should remove the #Service annotation from the class declaration. These annotations doesn't work well together.
ServiceAImpl.java
import org.springframework.beans.factory.annotation.Autowired;
public class ServiceAImpl implements ServiceA {
private final String fieldA;
#Autowired
public ServiceAImpl(String fieldA) {
this.fieldA = fieldA;
}
#Override
public boolean isFieldA(String text) {
return fieldA.equals(text);
}
}
Application.java
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
#SpringBootApplication
public class Application {
#Value("${fieldA}")
private String fieldA;
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Bean
public ServiceA serviceA() {
return new ServiceAImpl(fieldA);
}
}
Your application.properties
fieldA=value
The below implementation works well for me. You have two issues, first you have to choose between #Service and #Bean and the other issue I've seen in your code was the #Value annotation, you have to use only to inject a value from the properties.
#SpringBootApplication
public class TestedValueApplication {
#Autowired
void printServiceInstance(ServiceA service) {
System.out.println("Service instance: " + service);
System.out.println("value==value? " + service.isFieldA("value"));
}
public static void main(String[] args) {
SpringApplication.run(TestedValueApplication.class, args);
}
#Bean
public ServiceA serviceA(#Value("${fieldA}") String fieldA) {
return new ServiceAImpl(fieldA);
}
}
Service:
public class ServiceAImpl implements ServiceA {
private String fieldA;
ServiceAImpl(String fieldA) {
this.fieldA = fieldA;
}
public boolean isFieldA(String text) {
return fieldA.equals(text);
}
}
application.properties:
fieldA=value
I have following repository which extends jpa repositroy and also have an implementation class where i have autowired this.
#Repository
public interface ProjectDAO extends CrudRepository<Project, Integer> {}
#Service
public class ProjectServiceImpl {
#Autowired private ProjectDAO pDAO;
public void save(Project p) { pDAO.save(p); } }
Now i have one Application.java class
Class Application{
public static void main(String..s){
// I need a way to call a method of repository
}
}
configuration file
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories
#PropertySource("file:/Users/abc/Documents/application.properties")
public class PersistenceContext {
#Autowired
Environment environment;
So how do we call this from main in case i dont to use any web based controller?
This is a way:
class Application {
public static void main(String[] s){
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(PersistenceContext.class);
ProjectDAO dao = applicationContext.getBean(ProjectDAO.class);
}
}
Edit:
class Application {
public static void main(String[] s){
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(PersistenceContext.class);
ProjectServiceImpl service = applicationContext.getBean(ProjectServiceImpl.class);
}
}
I have a simple Spring Boot web project, right from a template:
#SpringBootApplication
#RestController
public class HelloWorldRestApplication {
public static void main(String[] args) {
SpringApplication.run(HelloWorldRestApplication.class, args);
Performer p = new Performer();
p.perform();
}
}
I have a test to ensure autowiring works, and in fact it does in this test class (examples come from Spring in Action, 4th):
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes=CDPlayerConfig.class)
public class CDPlayerTest {
#Autowired
private CDPlayer cdp;
#Test
public void cdShouldNotBeNull(){
assertNotNull(cdp);
}
}
and:
public class Performer {
#Autowired
private CDPlayer cdp;
public void perform(){
System.out.println(cdp);
cdp.play();
}
public CDPlayer getCdp() {
return cdp;
}
public void setCdp(CDPlayer cdp) {
this.cdp = cdp;
}
}
and:
#Component
public class CDPlayer{
public void play(){
System.out.println("play");
}
}
config:
#Configuration
#ComponentScan
public class CDPlayerConfig {
}
However, it doesnt work in HelloWorldRestApplication, I get null.
Adding #ContextConfiguration(classes=CDPlayerConfig.class) doesn't help.
What do I miss?
Try enabling #ComponentScan your packages in your main class and get Performer class instance from ApplicationContext as below:
#SpringBootApplication
#RestController
#ComponentScan({“package.name.1”,”package.name.2”})
public class HelloWorldRestApplication {
public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run(HelloWorldRestApplication.class, args);
Performer p = ctx.getBean(Performer.class);//get the bean by type
p.perform();
}
}