I'm new to spring boot, and I need to know how to use #autowired in an attribute that needs parameters to be instantiated.
Please bear in mind the following illustrative situation. It would be something like this:
public class MyClassA{
public SpecificClass myMethod(){
//some logic
}
}
public class MyClassB extends MyClassA{
#Autowired
MyComponent myComponent (myMethod()); //here is my doubt, because my component needs a parameter to be built
}
#Component
public class MyComponent{
public MyComponent(SpecificClass foo){
this.foo=foo;
}
That's not really proper design if your intention is to work with dependency injection. There shouldn't be a direct dependency to the superclass' method like that. Injecting the dependencies indirectly as you're supposed to do would result in something like the following
public class MyClassB extends MyClassA {
#Autowired
private MyComponent myComponent;
}
#Configuration
public class SomeConfig {
#Bean
#Autowired
public MyComponent createComponent(SpecificClass foo) {
// SpecificClass is also injected, providing another layer of indirection
return new MyComponent(foo);
}
}
#Autowired only tells Spring to inject a component into a constructor, field, or method parameter. The injected component is instantiated by the bean container before that. I assume what you are looking for is a way to create MyComponent in such a way that it also receives a Spring Bean.
In your example you could achieve this with the following
#Configuration
public class MyClassA{
#Bean //the bean would have the name 'myMethod', so maybe change that
public SpecificClass myMethod(){
//some logic
}
}
//this needs to be a component, service, ...
#Component
public class MyClassB {
#Autowired
MyComponent myComponent;
}
#Component
public class MyComponent{
#Autowired //Spring wires the Bean 'myMethod' in here, autowired is not needed in the latest Spring Versions
public MyComponent(SpecificClass foo){
this.foo=foo;
}
}
This is a basic Spring question, and not specific to Spring Boot. To better understand wiring you can take a look at the Spring 4 Framework Reference Documentation.
Related
I am learning Spring while I like the idea of using #Component and #Autowired to let Spring manage the dependent bean. For example, I have a Service class and Builder Class I can do with
// SomeService.java
#Service
public class SomeService {
// SomeBuilder is a #Component class
#Autowired
SomeBuilder someBuilder;
}
// SomeController.java
#Component
public class SomeController {
#Autowired
SomeService someSerivce;
}
Spring would take care of the creation of from SomeController to SomeService to SomeBuilder with the usage of #Autowired. However, now my SomeService class needs a private field which is NOT a Component class, just a plain context object, for example
// SomeService.java
#Service
public class SomeService {
#Autowired
SomeBuilder someBuilder;
private SomeContext someContext;
// Plan A: Using constructor to initiate the private field. However, I cannot use #Autowired to initiate SomeService in SomeController anymore as it requires a parameterless constructor
// Plan B: using #Autowired on constructor level, I cannot use this because SomeContext is not a #Component class
//public SomeService(SomeContext someContext) {
//this.someContext = someContext;
//}
// Plan C: This seems work but I kinda feel it's not the right way, as usually private field are initiated through constructor
//public void init(SomeContext someContext) {
// this.someContext = someContext;
//}
// demo usage of someContext
public someAnswer realMethod() {
System.out.println(someContext.getName());
}
}
Now I have no idea how to inject the someContext now, I used
plan A: Assign the private field using class constructor
plan B: Using #Autowired on constructor level
plan C: Using a wired method to assign the private field.
but I am not satisfied and don't have a clear way of doing the right approach.
First lets take a look at your plans and bust some myths/misunderstandings.
Plan A: Using constructor to initiate the private field. However, I cannot use #Autowired to initiate SomeService in SomeController anymore as it requires a parameterless constructor
Great plan, and the way to go. #Autowired doesn't depend on having a default constructor. It only indicates that you want the field to be injected with an object of that type. How that object comes to live (default constructor, constructor with arguments) doesn't matter for #Autowired. So that part of your understanding is just wrong.
using #Autowired on constructor level, I cannot use this because SomeContext is not a #Component class
If there is just a single constructor in a bean Spring will automatically use that to satisfy the dependencies. So in this case you don't need #Autowired. A bean doesn't have to be an #Component, a bean is just an instance of a class available to the application context. One way of achieving that is by marking it as an #Component but there are other ways as well. Like defining an #Bean method in in an #Configuration class to construct the bean.
#Configuration
#ComponentScan("your.base.package")
public class YourConfiguration {
#Bean
public SomeContext someContext() {
return new SomeContext();
}
}
Something along those lines. It will detect the #Component annotated classes through the #ComponentScan and will create a bean of type SomeContext for use as a bean.
Plan C: This seems work but I kinda feel it's not the right way, as usually private field are initiated through constructor
All your fields should be private not just the ones initialized in a constructor, so also the #Autowired ones. You don't want those fields to be, easily, accessible from the outside so they can be modified. So make them private.
That all being said, go with constructor injection over field injection or setters/methods for injection. It is clearer and less hidden than field injection and the way to go for mandatory dependencies (for optional dependencies you can use a setter/method).
So using the above config and below classes, it should "just work (tm)".
// SomeService.java
#Service
public class SomeService {
// SomeBuilder is a #Component class
private final SomeBuilder someBuilder;
private final SomeContext someContext;
public SomeService(SomeBuilder someBuilder, SomeContext someContext) {
this.someBuilder=someBuilder;
this.someContext=someContext;
}
}
// SomeController.java
#Component
public class SomeController {
private final SomeService someSerivce;
public SomeController(SomeService someService) {
this.someService=someService;
}
}
I have a requirement where I want to call a parameterised constructor of a class annotated with #Component inside another class which is annotated with #Service
feel free if you didn't get my question.
#Service
Class ServiceClass{
//here I want to create ComponentClass instance by Spring.
Result result=new ComponentClass(sending data to get result);
}
#Component
Class ComponentClass {
Component(received data){
}
}
I think you should try "Autowired" keyword. It says "Hey Spring Framework try to initialize the variable for me".
#Autowired
Result result=new ComponentClass(sending data to get result);
Whenever you define a #Servive or a #Component a bean of that type will be created (keep in mind that all beans are singletons).
A bean can be injected into any other spring managed bean by making use of this annotation:
#Service
Class ServiceClass{
#Autowired
Result result;
}
Using beans (components, services etc.) is not always needed and especially in the case, you need an non-singleton class, things can get tricky because of two reasons:
You won't be able to use annotations on that class
A non-annotated (spring managed bean) class do not support injection.
To inject a bean inside a class that is not annotated you will need to define a spring context:
#Component
public class SpringContext implements ApplicationContextAware {
private static ApplicationContext context
public static <T extends Object> T getBean(Class<T> beanClass) {
return context.getBean(beanClass);
}
#Override
public void setApplicationContext(ApplicationContext context) throws BeansException {
SpringContext.context = context;
}
}
and then into any class:
Class ServiceClass{
Result result = SpringContext.getBean(Result.class);
}
where result is a either component or service.
I have a project structure similar to the one linked here: https://stackoverflow.com/a/29583882/1243462 . I have a util library containing a Service class in one JAR, meant to be consumed from another Java library/Maven project. However, my Service class itself uses Constructor Injection. So, where the original question had:
#Service
public class PermissionsService { ... }
I have
#Service
public class PermissionsService {
public PermissionsService(#Autowired PermissionsDao dao) {
//assign private dao field to autowired dao
}
}
And, like the original post, I want to create an instance of PermissionsService and inject it into my client/consumer application. I'm not sure of how to create a Configuration class.
#Configuration
public class PersistenceConfig {
public PermissionsService getPermissionsServiceBean() {
//What goes here?
}
}
For now, I have a workaround where I replaced the #Autowired PermissionsDao constructor argument with a field injection, and having a no-args constructor. This allows me to:
#Configuration
public class PersistenceConfig {
public PermissionsService getPermissionsServiceBean() {
return new PermissionsService();
}
}
But, since Field injection is discouraged, what is the right way to structure this code?
In your main module
#Configuration
#Import(PersistenceConfig.class)
public class ServiceConfig() {
}
In your utils module
#Configuration
#ComponentScan(basePackages = {"path-to-persistence-service-and-any-dependencies"})
public class PersistenceConfig {
}
The fact that you use constructor injection for PermissionsDao should not matter if you get the configuration right.
I'm relatively new to Spring Boot and dependency injection overall, so please forgive any noob things going on here. I'm building an API and am having trouble when injecting dependencies into a POJO resource (DTO).
When I call the method in the POJO this.numComments = commentSvc.getAllForPhoto(this.getId()); I am getting a NullPointerException. However, when I do this from another spring-managed bean and pass the values into the constructor, it works fine.
After reading around, it looks like I need to do something with aspectJ and Load Time Weaving, but I'm not sure what that would look like in my code.
In essence, my approach looks something like this:
PhotoResource.java (POJO)
public class PhotoResource extends BaseRepresentable {
#Autowired
CommentService commentSvc;
private Long id;
private Integer numComments;
PhotoResource(PhotoEntity entity){
super(entity);
this.setId(entity.getId);
this.numComments = commentSvc.getAllForPhoto(this.getId());
}
}
CommentService.java
#Service
public class CommentService{
public List<CommentResource> getAllForPhoto(Long photoId) {
// code to get all comments for photo
}
}
Application.java
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class);
}
}
Spring won't inject the dependency unless you ask the Spring container to manage the bean. In order for commentSvc to be injected into PhotoResource class, you need to annotate it with #Component or #Bean or #Service, e.g.:
#Component
public class PhotoResource extends BaseRepresentable {
#Autowired
CommentService commentSvc;
}
And make sure the package of this class is included into #ComponentScan packages.
Also, the following won't compile:
#Service
public class CommentService(){
You don't need paranthesis to declare a class, it should be:
#Service
public class CommentService{
I have a class that is annotated #Component that was then #Autowired into another class. However, I need to remove this #Component annotation and instead, create it with an #Bean annotated method in the class where its was previously autowired.
Where previously the classes looked like:
#Component
public class MyClass implements IMyClass
{
// Stuff
}
#Configuration
public class MyUsingClass
{
#Autowired
private IMyClass myClass;
private void methodUsingMyClass()
{
myClass.doStuff();
}
}
So now I have removed the #Component annotation and written a #Bean annotated method like this:
public class MyClass implements IMyClass
{
// Stuff
}
#Configuration
public class MyUsingClass
{
#Bean
public IMyClass getMyClass()
{
return new MyClass();
}
....
}
My question is around replacing the previous call of myClass.doStuff() to use the new bean. Do I now pass in a parameter of type MyClass to the private method:
private void methodUsingMyClass(final MyClass myClass)
{
myClass.doStuff();
}
... or do I call this method directly (doesn't seem the correct way to me):
private void methodUsingMyClass()
{
getMyClass().doStuff();
}
... or are neither of these correct?
I think you misunderstand the #Bean annotation. It can be used to create a Bean. So basically spring will scan all classes, will find your #Bean and create a Bean, not more. You can now use this bean, like if you would use one created with <bean></bean>. To actually use the bean you need to either get it from ApplicationContext or #Autowire it. Of course you can still use that function like any other function in your code, to create a new instance of that object, but that would contradict to what you want to achieve with beans
Using Annotations that solutions
public class MyClass implements IMyClass{
private OtherClassInjection otherClassInjection;
private OtherClassInjection2 otherClassInjection2;
MyClass(OtherClassInjection otherClassInjection, OtherClassInjection2 otherClassInjection2){
this.otherClassInjection=otherClassInjection;
this.otherClassInjection2=otherClassInjection2;
}
public void useObject(){
otherClassInjection.user();
}
}
#Bean(name = "myClass")
#Autowired
#Scope("prototype") //Define scope as needed
public MyClass getMyClass(#Qualifier("otherClassInjection") OtherClassInjection otherClassInjection,
OtherClassInjection2 otherClassInjection2) throws Exception
{
return new MyClass(otherClassInjection, otherClassInjection2);
}
that logical, it's work injection #Autowired when create a Bean if context are know that bean, that you will to want inject.
I'm use that way.