How inject a service class into a class not managed by spring - java

I have this situation:
public class Other {
public void test() {
new ClassA().process();
}
}
public class ClassA {
#Autowired
private ClassB classB;
public void process() {
classB.executeSomething(); //--> NUllPOinter because classA was not created by spring.
}
}
#Service
public class ClassB {
public void executeSomething() {
// execute something
}
}
I tried use ApplicationContext but the problem continued.
Someone, have a idea what i should do ?
Thanks.

This is always the right way to declare a class dependency (always for annotated ioc spring bean):
public class ClassA {
private final ClassB objectB;
public ClassA(final ClassB objectB) {
this.objectB = objectB;
}
public void process() {
objectB.executeSomething();
}
}
In Other class, we should retrieve the singleton ClassB instance. Then it must be a bean component.
#Component
public class Other {
private final ApplicationContext applicationContext;
#Autowired
public Other(final ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
public void test() {
new ClassA(applicationContext.getBean(ClassB.class)).process();
}
}
If Other class can't be a spring component, you can't retrieve the application context and then you can't inject the ClassB instance.
You can clearly autowire directly the ClassB object instance into Other component, but this example underline that you can handle bean fetching with an ApplicationContext ref.

Related

Getting Bean instance as null using Strategy Design Pattern and #Autowired

Hi I am trying to use Strategy Design pattern. I am getting ReEncryptionOperation bean as null in my TestServiceImpl class.
this is my interface
public interface ReEncryptionOperation {
void performOperation (String name);
}
These are my implementation classes
public class Test1 implements ReEncryptionOperation {
#Override
public void performOperation(String name){
return ....;
}
}
public class Test2 implements ReEncryptionOperation {
#Override
public void performOperation(String name) {
return ....;
}
}
This is my configuration class where I am defining as a bean
#Configuration
#Slf4j
public class TestConfiguration
{
#Bean("reEncryptionOperation")
public ReEncryptionOperation getReEncryptionOperation () throws ReEncryptionException {
if (annotationSupport) {
return new Test1();
}
return new Test2();
}
}
this is my service class where i am trying to use ReEncryptionOperation using #Autowired. But I am getting null.
#Component
#Slf4j
public class TestServiceImpl
{
#Autowired
private ReEncryptionOperation reEncryptionOperation;
public ReEncryptionResponse submitJob (
final ReEncryptionRequest reEncryptionRequest) throws ReEncryptionException
{
reEncryptionOperation.performOperation(test);
}
}
Your configuration seems ok.
Check that TestConfiguration is located in a package scanned by spring.
To be sure your bean is created on runtime, place a breakpoint in the method getReEncryptionOperation

Inject spring beans into a non-managed class

I have this non-managed class that I want to inject spring beans (that I don't known a-priory what they are). How can I do that?
For example, let's say I have the following class:
public class NonManagedClass extends APIClass {
#Resource
private Service1 service;
#Resource
private Service2 service2;
// here i can declare many different dependencies
#Resource
private ServiceN serviceN;
#Override
public void executeBusinessStuffs() {
// business logics
}
}
I need in someway to let spring inject these dependencies in my class. I have access to these objects after created, so it's easy to me call any method that can accomplish this functionality. For example:
#Service
public void SomeAPIService {
#Resource
private BeanInjector beanInjector; // I'm looking for some funcionality of spring like this
public void someProcessingFunction(Class<? extends APIClass> clazz) throws Exception {
APIClass instance = clazz.getConstructor().newInstance();
beanInjector.injectBeans(instance);
instance.executeBusinessStuffs();
}
}
Does Spring have such functionality to inject beans based on fields annotation for a non-managed class?
Replace BeanInjector with ApplicationContext and you are almost there. From there you can get the AutowireCapableBeanFactory which provides some handy methods like createBean and autowireBean.
#Service
public void SomeAPIService {
#Resource
private ApplicationContext ctx;
public void someProcessingFunction(Class<? extends APIClass> clazz) throws Exception {
APIClass instance = ctx.createBean(clazz);
instance.executeBusinessStuffs();
}
}
or if you really like to construct stuff yourself instead of using the container:
#Service
public void SomeAPIService {
#Resource
private ApplicationContext ctx;
public void someProcessingFunction(Class<? extends APIClass> clazz) throws Exception {
APIClass instance = clazz.getConstructor().newInstance();
ctx.getAutowireCapableBeanFactory().autowireBean(instance);
instance.executeBusinessStuffs();
}
}

How to use #Autowired on field variable when class is not a Bean? [duplicate]

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

Configuration properties not autowired when used in request body?

I'm using a particular class (ClassA) in my controller as the request body, but within that class, my autowired ConfigurationProperties is null.
Controller:
#RestController
#RequestMapping(value = "/rest/v1/")
public class XyzController {
#Autowired
ServiceXyz serviceXyz;
#PostMapping(value = "/route")
public void route(#RequestBody ClassA classA) {
serviceXyz.methodAbc(classA);
}
}
ServiceXYZ:
#Service
public class ServiceXyz {
public boolean methodAbc(ClassA classA) {
return classA.methodA() && otherStuff();
}
}
ClassA.java:
#Component
public class ClassA {
#Autowired
ApplicationProperties applicationProperties;
public boolean methodA() {
return fieldA.equals(applicationProperties.someProperty());
}
}
ApplicationProperties.java:
#Component
#ConfigurationProperties(prefix="stuff")
public class ApplicationProperties {
// etc.
}
It's within ClassA.methodA that applicationProperties is null, even though everybody is marked with the correct annotations, and autowiring is working throughout the rest of the application.
Is it possible that this just doesn't work?
Autowiring works for objects from Spring context. In your request object of ClassA is parsed from JSON I think and is not taken from Spring context.
You'd better change your code to make ClassA as simple DTO and inject ApplicationProperties into your service class.
You can change your ClassA to this
public class ClassA {
public boolean methodA(ApplicationProperties applicationProperties) {
return fieldA.equals(applicationProperties.someProperty());
}
}
And your service to this:
#Service
public class ServiceXyz {
#Autowired
private ApplicationProperties applicationProperties;
public boolean methodAbc(ClassA classA) {
return classA.methodA(applicationProperties) && otherStuff();
}
}

How to make autowire object initialized in ApplicationListener?

I want to read data in ApplicationListener, but my object is not initialized. Below is my code:
AppContextListener.java
#Component
public class AppContextListener implements ApplicationListener<ContextRefreshedEvent> {
#Override
public void onApplicationEvent(ContextRefreshedEvent event) {
AppContext.getInstance();
}
}
AppContext.java
public class AppContext {
private static AppContext instance;
#Autowired
MyElasticsearchRepository repository;
public AppContext(){
InitData();
}
public static synchronized AppContext getInstance() {
if (instance == null) {
instance = new AppContext();
}
return instance;
}
private void InitData(){
List<MyEntity> dataList = repository.findAllEntities();//repository is null here
//.......
}
}
MyElasticsearchRepository.java
public interface MyElasticsearchRepository extends ElasticsearchRepository<MyEntity,String>
{ }
problem
As you can see in my code, at InitData(), repository is null. I don't
know why #Autowired MyElasticsearchRepository repository; does not
worked here.
Please show me how to fix this. Thank you very much.
There are a couple of things wrong with your code.
First you are using the singleton pattern which I would say is an anti-pattern especially when combined with auto wiring.
Second in your getInstance() method you are creating a new instance of AppContext yourself. This instance isn't managed by Spring so #Autowired is pretty much useless here, Spring is only able to inject dependencies into beans it knows about.
Instead make your AppContext a component (or service what ever you like). Remove the getInstance method and use constructor injection instead.
#Component
public class AppContext {
private final MyElasticsearchRepository repository;
#Autowired
public AppContext(MyElasticsearchRepository repository){
this.repository=repository;
}
...
}
Thirdly you are trying to use the #Autowired instance from the constructor (you are doing method call which expects it to be there), however auto wiring can only be done on an instance of a bean. So at that moment the auto wiring hasn't taken place and your variable will always be null. Instead of calling the method from the constructor either, use constructor inject or annotate the InitData method with #PostConstruct.
#PostConstruct
private void InitData(){
List<MyEntity> dataList = repository.findAllEntities();
...
}
Now that your AppContext is a component it will be detect by spring and you can simply inject it into your ApplicationListener.
#Component
public class AppContextListener implements ApplicationListener<ContextRefreshedEvent> {
private final AppContext appContext;
#Autowired
public AppContextListener(AppContext appContext) {
this.appContext=appContext;
}
#Override
public void onApplicationEvent(ContextRefreshedEvent event) {
// Do your thing with appContext
}
}
Note: I prefer constructor injection for required fields and setter injection for optional fields. You should avoid field injection (i.e. #Autowired on instance fields) as that is considered a bad practice. See here why field injection is evil and should be avoided.
#Autowired will only work if bean is marked with Stereotype annotation (What's the difference between #Component, #Repository & #Service annotations in Spring?) or you explicitly define it in spring configuration.
AppContextListener.java
#Component // AFAIR not needed. Spring will create this bean when it will see that class implements `ApplicationListener` interface.
public class AppContextListener implements ApplicationListener<ContextRefreshedEvent> {
#Autowired
private AppContext appContext;
#Override
public void onApplicationEvent(ContextRefreshedEvent event) {
appContext.initData();
}
}
AppContext.java
#Component
public class AppContext {
#Autowired
MyElasticsearchRepository repository;
public void initData(){
List<MyEntity> dataList = repository.findAllEntities();//repository is null here
//.......
}
}
#Autowired will work only after AppContext object constructed. Since you try to access #Autowired element inside constructor, it doesn't exist.
Can't you just do this?
#Component
public class AppContextListener implements ApplicationListener<ContextRefreshedEvent> {
#Override
public void onApplicationEvent(ContextRefreshedEvent event) {
ApplicantContext context = event.getApplicationContext();
MyElasticsearchRepository repository = context.getBean(MyElasticSearchRepository.class);
//do stuff
}
}
http://docs.spring.io/autorepo/docs/spring/4.1.4.RELEASE/javadoc-api/org/springframework/context/event/ContextRefreshedEvent.html

Categories

Resources