Java Spring declare bean in service class - java

I have Spring Java Services class with a lot of beans:
#Service ("employeeManager")
public class EmployeeManagerImpl implements EmployeeManager
{
#Autowired
EmployeeDAO employeedao;
#Autowired
Workerdao workerdao;
#Autowired
SallaeryDao sallaeryDao
#Autowired
TaxDao taxDao
#Autowired
HouresDao houresDao
#Autowired
*****Dao
public EmployeeDTO createNewEmployee()
{
return dao.createNewEmployee();
}
}
Can I clean the Service class code without a lot of bean declare in my class that I will have only one bean? I have a lot of bean and I don't want to declare them in the class with

Related

JAVA - Can I delete #Autowire directly?

after my teammates merged her changes (implements #EnableFeignClients for our SpringBootApplication and created some feign clients, properties and configs from the main branch), when boot the application it will pop up the The dependencies of some of the beans in the application context from a cycle: xxx
To resolve this issue I deleted some #Autowired from some involved controller/service classes and make them private and then the application can be boot successfully:
before my change:
#Autowired
MyUtil myUtil; //#Component
#Autowired
MyConfig myConfig; //#Component
#Autowired
MyApi myApi; //Interface class
public void myFunction(){
String id = myUtil.getId();
String name = myConfig.getNameById(id);
myApi.sendInfo(id, name);
}
after my change:
private MyUtil myUtil; //#Component
private MyConfig myConfig; //#Component
private MyApi myApi; //#Component
public void myFunction(){
String id = myUtil.getId();
String name = myConfig.getNameById(id);
myApi.sendInfo(id, name);
}
Spring version:
<groupID>org.springframework.boot</groupID>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.11</version>
and here are my concerns:
Will there be any impact after my changes (functional, performance, etc.)?
In my case (or in most scenarios in the future development), should i use #Autowired? When need to? When doesn't need to?
TL DR. You cannot delete #Autowired directly.
#Autowired is used by Spring in order to do dependency injection ( I know this is a fancy word)
#Autowired
ChickenService chickenService
#Autowired is almost equivalent to ChickenService chickenService = new ChickenService();
After your change,private MyConfig myConfig; //#Component myConfig will be null and therefore you introduced a bug.
Suppose that you have an EggService
#Service
public class ChickenService {
#Autowired
private EggService eggService;
}
The chicken holds a reference to the egg
#Service
public class EggService {
#Autowired
private ChickenService;
}
Now you have a chicken and egg problem.
https://www.baeldung.com/circular-dependencies-in-spring
#Autowired help the spring to find where it should inject classes.
The impact is that spring will not create objects of the classes where you removed the annotation.
Additional way to create objects in spring are with setter or constructor injection.
Example of constructor injection will be something like:
public YourClassName(MyUtil myUtil, MyConfig myConfig, MyApi myApi){
this.myUtil = myUtil;
this.myConfig = myConfig;
this.myApi = myApi;
}
I think also good idea as well to check why #Autowired not working. Can you provide full error? Does your class annotated with #Service or something

Spring: Get the list of beans injected into a bean

I'm looking for a way to list the beans that are injected into a particular Spring bean at runtime. For example, given these two classes:
#Controller
public class TestController {
#Autowired
private TestComponent testComponent;
private final TestService testService;
public TestController(TestService testService) {
this.testService = testService;
}
}
and
#Service
public class TestService {
}
and
#Component
public class TestComponent {
}
The list of beans for the TestController class should return:
TestService (injected via constructor)
TestComponent (injected via #Autowired annotation)
Is there an existing Spring helper/utility that can return this information for me?
You can query names of dependent beans from the ConfigurableBeanFactory for a given bean name with the method getDependenciesForBean(). So in your example the code could look like
try (ConfigurableApplicationContext app = SpringApplication.run(MySpringApplication.class)) {
ConfigurableListableBeanFactory beanFactory = app.getBeanFactory();
String[] dependencies = beanFactory.getDependenciesForBean("testController");
System.out.println(Arrays.toString(dependencies)); // [testService, testComponent]
}
The problem hereby is that you only work on names of beans. So to make the code generic for a given bean instance you would have to find out the name of the bean (which can be non-unique) and also when getting the actual injected beans for these names it can be possible that you don't get the same instances (because of #Scope(SCOPE_PROTOTYPE) on the bean definition).

How does Spring #profile work with inheritance?

Currently in a project, I have parent abstract classes called page object classes which are subclassed typically by 2 or 3 child classes, each being used based on a specific configuration (For example: platform as Android, IOS, Web).
#Component
public abstract class MePage {}
#Profile("android")
#Component
public class AndroidMePage extends MePage {}
#Profile("ios")
#Component
public class IOSMePage extends MePage {}
Whenever an instance of one of the subclasses is needed, it's retrieved using
#Autowired
MePage mePage;
Question
How does Spring work in such cases where subclasses are annotated with #profile and the parent class is an abstract class and also a component?
Does Spring automatically assign instance of one of the subclasses to the #Autowired abstract class variable based on the profile configured?
You should not have #Component on top of an abstract class, since abstract classes are not meant to be instantiated (not event by Spring).
On top of that, Spring will inject the right bean based on your profile.
#Autowired
private MePage mePage; // AndroidMePage if android profile is active
#Autowired
private MePage mePage; // IOSMePage if ios profile is active
If your parent class is not abstract, you have to deal with multiple bean definitions as usual.
I think you have at least three options here.
1) Declare one of the beans as #Primary
#Component
public class MePage {}
#Profile("android")
#Component
#Primary
public class AndroidMePage extends MePage {}
#Profile("ios")
#Component
#Primary
public class IOSMePage extends MePage {}
#Autowired
private MePage mePage; // AndroidMePage if android profile is active
#Autowired
private MePage mePage; // IOSMePage if ios profile is active
2) Autowire a List of beans
#Component
public class MePage {}
#Profile("android")
#Component
public class AndroidMePage extends MePage {}
#Profile("ios")
#Component
public class IOSMePage extends MePage {}
#Autowired
private List<MePage> pages; // MePage and one of AndroidMePage or IOSMePage , based on active profile
3) Add #Qualifier to your bean definitions and use that when autowiring
#Component
#Qualifier("default")
public class MePage {}
#Profile("android")
#Component
#Qualifier("android")
public class AndroidMePage extends MePage {}
#Profile("ios")
#Component
#Qualifier("ios")
public class IOSMePage extends MePage {}
#Autowired
#Qualifier("default")
private MePage mePage; // MePage is injected, regardless of active profile
#Autowired
#Qualifier("ios")
private MePage mePage; // IOSMePage if ios profile is active
#Autowired
#Qualifier("android")
private MePage mePage; // AndroidMePage if android profile is active

How to #Autowired a concrete implementation of a service?

I have the following situation:
public interface ServiceAura extends Serializable { }
#Service
public class ServiceA implements ServiceAura {
....
}
#Service
public class ServiceB implements ServiceAura {
....
}
Now from the controller I need to call both of them by separate:
#Path("")
#Controller
public class ServiciosAuraPortalRESTfulService {
#Autowired
private ServiceAura srvA;
#Autowired
private ServiceAura srvB;
}
I have read about #Qualified, is this the only way? How can I archive this?
You're right. You can use #Qualifier("ServiceA") to specify which implementation you want to autowire.
#Path("")
#Controller
public class ServiciosAuraPortalRESTfulService {
#Autowired
#Qualifier("ServiceA")
private ServiceAura srvA;
#Autowired
#Qualifier("ServiceB")
private ServiceAura srvB;
}
On the service itself, you can use the annotation #Primary to specify which one is the default implementation that you want.
Alternatively, you can use the application context to retrieve a specific bean. You'll need to autowire the ApplicationContext class and then retrieve it with ServiceAura srvA = context.getBean(ServiceA.class);
There are two ways to do this.
The first way is using #Qualifier annotation as you've stated.
#Path("")
#Controller
public class ServiciosAuraPortalRESTfulService {
#Autowired
#Qualifier("serviceA")
private ServiceAura srvA;
#Autowired
#Qualifier("serviceB")
private ServiceAura srvB;
}
Your services should be defined like this:
#Service
#Qualifier("serviceA")
public class ServiceA implements ServiceAura {
....
}
#Service
#Qualifier("serviceB")
public class ServiceB implements ServiceAura {
....
}
Another way is to create interfaces that extend interface ServiceAura
interface ServiceAInterface extends ServiceAura {
}
class ServiceA implements ServiceAInterface {}
.... // the same for service B
And then in code:
public class ServiciosAuraPortalRESTfulService {
#Autowired
ServiceAInterface serviceA;
}

How to autowire Spring service with Class name?

I have multiple services and I want to autowire this services dynamically with using their class names. I have a method named "runCustomService" and this methods takes service's class names as input parameter (like "Service1" or "Service2"). I want to autowire these services and call its run method. Is there any way to do this?
#Service
public class Service1 extends BaseService{
#Autowired
private AnotherService anotherService;
public void run(){ .... }
}
#Service
public class Service2 extends BaseService{
#Autowired
private AnotherService anotherService;
public void run(){ .... }
}
public void runCustomService(String serviceClassName){
BaseService baseService = //Create baseService object from serviceClassName
baseService.run();
}
You could use qualifiers on your two services and get the correct bean based on the qualifier name from the ApplicationContext.
#Service
#Qualifier("Service1")
public class Service1 extends BaseService{
#Service
#Qualifier("Service2")
public class Service2 extends BaseService{
#Autowired
ApplicationContext applicationContext;
public void runCustomService(String serviceClassName){
BaseService baseService = applicationContext.getBean(serviceClassName);
baseService.run();
}
Get an instance of ApplicationContext and get bean by a class name:
#Autowired
ApplicationContext ctx;
Use the method getBean(...):
BaseService baseService = ctx.getBean(Service1.class.getName());
However, as the other answer says, I recommend you to use #Qualifier(...) to inject a certain named conditionally.

Categories

Resources