I have a spring bean class with a constructor with multiple parameters and #Inject annotation.
Is there a way to use spring Java configuration class to create a bean for the class without actually writing code for creating the object? Something like using #Bean on a field?
#Bean(MyClassName.class) private MyInterfaceName myBean;
Or maybe by making the configuration class abstract and the bean method abstract, like:
#Bean(MyClassName.class) abstract MyInterfaceName myBean();
It's quite annoying (and pointless) to write each time the whole method that only creates a new object if you know that you only have 1 implementation of the class and you want to use auto wiring and constructor injection.
You can use #Component annotation. According to Spring documentation:
#Component indicates that an annotated class is a "component". Such classes are
considered as candidates for auto-detection when using
annotation-based configuration and classpath scanning.
Use the #Component annotation.
Indicates that an annotated class is a "component". Such classes are
considered as candidates for auto-detection when using
annotation-based configuration and classpath scanning. Other
class-level annotations may be considered as identifying a component
as well, typically a special kind of component: e.g. the #Repository
annotation or AspectJ's #Aspect annotation.
Here is an example:
import org.springframework.stereotype.Component;
#Component
public class CustomerDAO
{
#Override
public String toString() {
return "Hello , This is CustomerDAO";
}
}
A DAO class:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
#Component
public class CustomerService
{
#Autowired
CustomerDAO customerDAO;
#Override
public String toString() {
return "CustomerService [customerDAO=" + customerDAO + "]";
}
}
And a runner class:
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App
{
public static void main( String[] args )
{
ApplicationContext context =
new ClassPathXmlApplicationContext(new String[] {"Spring-AutoScan.xml"});
CustomerService cust = (CustomerService)context.getBean("customerService");
System.out.println(cust);
}
}
And your output:
CustomerService [customerDAO=Hello , This is CustomerDAO]
Related
I am working on an application which uses some third java libraries which are build on top of core spring framework.
Now when my application uses these libraries I am getting ConflictingBeanDefinitionException because two libraries have the same bean name.
Now as these libraries are external I cannot change the bean name. Is there a way by which in my application I can use both the beans in same container?
#Component
class ApplicationLogic {
#Autowire FetcherAndResolver fetchFromLibraryA;
#Autowire FetcherAndResolver fetchFromLibraryB; //Because both bean names are same here comes the exception.
}
I think this would require a bit of a hack.
First of all, structure your packages in a way so that external beans are not added automatically.
So, if external class is located in package a.b. Then you have to move your own classes in either a.c or a.b.c. THis will ensure that you are in control of how beans are initialized.
Once this is done, you can add a #Configuration class where you can create Beans of both type:
#Configuration
public class ExternalBeanConfiguration {
#Bean("internal-resolver" )
public FetcherAndResolver internalResolver() {
return new FetcherAndResolver();
}
#Bean("external-resolver" )
public a.b.c.FetcherAndResolver externalResolver() {
return new a.b.c.FetcherAndResolver();
}
}
I am assuming that FetcherAndResolver is a class rather than an interface. If it is an interface, it is easier to do it as you won't have to use fully-qualified name for classes.
Then you can simply autowire with qualifiers.
#Component
public class SomeComponent {
#Qualifier( "internal-resolver" )
FetcherAndResolver internalResolver;
#Qualifier( "external-resolver" )
FetcherAndResolver externalResolver;
}
First component from third java libraries:
package com.example.component1;
import org.springframework.stereotype.Component;
#Component
public class MyComponent {
public String makeSomeWork() {
return "Component 1";
}
}
Second component from third java libraries:
package com.example.component2;
import org.springframework.stereotype.Component;
#Component
public class MyComponent {
public String makeSomeWork() {
return "Component 2";
}
}
Controller:
package com.example.controller;
import com.example.component1.MyComponent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
#RestController
public class MyController {
#Autowired
private MyComponent myComponent;
#Autowired
private com.example.component2.MyComponent myComponent2;
#RequestMapping("/components")
public String getComponents() {
return myComponent.makeSomeWork() + "_" + myComponent2.makeSomeWork();
}
}
I'm trying to use Spring's autowire annotation in my test class in order to inject an instance of a class.
package com.mycom.mycust.processing.tasks.references;
public class ReferenceIdentifierTest {
#Autowired
private FormsDB formsDB;
#PostConstruct
#Test
public void testCreateTopLevelReferencesFrom() throws Exception {
ReferenceIdentifier referenceIdentifier = new ReferenceIdentifier(formsDB);
}
}
This is the FormsDB class:
package com.mycom.mycust.mysql;
import org.springframework.stereotype.Component;
import java.sql.SQLException;
#Component
public class FormsDB extends KeyedDBTable<Form> {
public FormsDB(ConnectionFactory factory) throws SQLException {
super(factory.from("former", new FormsObjectMapper()));
}
}
And here is the SpringBootApplication class:
package com.mycom.mycust.processing;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
#SpringBootApplication
#ComponentScan("com.mycom.mycust")
public class Processing implements CommandLineRunner {
// Code
}
When I run my test, formsDB is null. Since I've used the PostConstruct annotation on the test function I think that FormsDB could not be autowired due to the class not being found. There is also an IntelliJ warning on the Autowired annotation in test class: Autowired members must be defined in valid Spring bean (#Component|#Service...). But I have put the Component annotation above the FormsDB class and I've also put the path com.mycom.mycust in the ComponentScan annotation of the SpringBootApplication. So I can't see why it can't find the class.
What is wrong here?
Your test calls is missing some important annotations to make autowiring work:
#SpringBootTest
#RunWith(SpringRunner.class)
public class ReferenceIdentifierTest {
#Autowired
private FormsDB formsDB;
#Test
public void testCreateTopLevelReferencesFrom() throws Exception {
ReferenceIdentifier referenceIdentifier = new ReferenceIdentifier(formsDB);
}
}
also you can remove #PostConstruct that does not make sense in a test.
I am building a Spring-boot application where I am using Spring data jpa feature.
Please find below my dao layer code
package com.adv.dao;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
#Repository
public interface CustomerDao extends JpaRepository<Customer, String> {
}
I am using a DaoProvider class as follows:
package com.adv.dao;
import java.io.Serializable;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
#Repository
public class DaoProvider implements Serializable {
private static final long serialVersionUID = 1L;
#Autowired
private CustomerDao customerDao;
public CustomerDao getCustomerDao() {
return customerDao;
}
}
My spring boot main class is defined as follows:
#SpringBootApplication
#ComponentScan(basePackages="com.adv")
public class AdvMain extends SpringBootServletInitializer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(AdvMain.class);
}
public static void main(String[] args) {
SpringApplication.run(AdvMain.class, args);
}
}
Now during runtime I am getting following exception:
Field customerDao in com.adv.dao.DaoProvider required a bean of type 'com.adv.dao.CustomerDao' that could not be found.
I guess that #Repository annotation on interface CustomerDao is not working.
But I am unable to figure out the issue.Can anyone figure out the problem?
Try to add #EnableJpaRepositories("com.adv.dao") on AdvMain as suggested by #hang321 on Can't Autowire #Repository annotated interface in Spring Boot
Ask
Remove the annotation #Repository from the dao interface. That annotation must be put only on implented classes.
Be careful also to implement both the empty constructor than the all args constructor of the Customer class.
Just remove the #ComponentScan annotation altogether.The #SpringBootApplication annotation already includes the component scan as specified here.
I would like for Spring Boot to throw an exception if any of my beans are not fully configured during initialization. I thought that the correct way to do that would be to annotate the relevance bean methods with #Required, but it does not behave as I expect.
application.yml:
my_field: 100
Simple bean class:
package com.example.demo;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.stereotype.Component;
#Component
public class MyProperties {
private int myField;
public MyProperties(){}
#Required
public void setMyField(int myField) {
this.myField = myField;
}
#Override
public String toString() {
return "{myField=" + myField + '}';
}
}
My application class:
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import javax.annotation.PostConstruct;
#SpringBootApplication
public class DemoApplication {
#Bean
#ConfigurationProperties
public MyProperties getMyProperties() {
return new MyProperties();
}
#PostConstruct
public void init() {
MyProperties myProperties = getMyProperties();
System.out.println(myProperties);
}
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
In the init method of DemoApplication I am printing the resulting bean object. Without the #Required annotation it is loaded correctly and prints {myField=100}. However, when I add the annotation it throws this exception:
org.springframework.beans.factory.BeanInitializationException: Property 'myField' is required for bean 'myProperties'
This is despite the fact that the config file contains the required value.
What is the correct to tell Spring that a field is required?
From the docs
Spring Boot will attempt to validate #ConfigurationProperties classes whenever they are annotated with Spring’s #Validated annotation. You can use JSR-303 javax.validation constraint annotations directly on your configuration class. Simply ensure that a compliant JSR-303 implementation is on your classpath, then add constraint annotations to your fields
You should declare myField as follows:
#NonNull
private int myField;
I have a utils class that use #Autowired to inject a repository using spring-boot-starter-data-jpa. But when I used this repository to access the database, it said the repository is null. I used the same method in my controller and it works well. And here is my Utils.class
package com.example.controller;
import java.util.HashMap;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RestController;
import com.example.dao.RuleRepository;
import com.example.model.Project;
import com.example.model.Rule;
public class Judge {
#Autowired
RuleRepository ruleRepository;
public boolean ageJudge(Project project) {
try {
if (ruleRepository == null)
{
System.out.println("yes");
}else {
System.out.println("false");
}
return false;
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
return false;
}
}
}
Here is my Application.java
package com.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
#SpringBootApplication
#ComponentScan(basePackages = {"com.example"})
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
This is the RuleRepository.java
package com.example.dao;
import org.springframework.data.jpa.repository.JpaRepository;
import com.example.model.Project;
import com.example.model.Rule;
public interface RuleRepository extends JpaRepository<Rule, Integer>{
Rule findById(Integer id);
Rule findByRuleName(String ruleName);
}
It is the directory.
The RuleRepository works well in controller. So, what is the problem?
Your util class Judge is a plain POJO not a Spring bean and you can only inject Spring beans inside another Spring beans not Plain POJOs.
If you wish to use your ruleRepository bean inside Judge then make it a Spring component using #Component annotation:
#Component
public class Judge {
#Autowired
RuleRepository ruleRepository;
..............................
}
User #Service annotation of Judge class is acting as business logic implementation class.
Your Judge should be annotated #Component
#Component
public class Judge{
// ...
}
so that Spring will instantiate a Judge bean and it will be available for injection. You can then use that judge bean in any managed bean (e.g: a controller)
// SomeController
#Autowired
Judge judge;
But if you instantiate judge object your self, like this:
Judge judge2 = new Judge();
your repository will be null, be cause Spring have nothing to do with judge2 object, it is not managed by Spring.
You need to make your Judge class at least a #Component of your project, which will make your class managed by Spring, therefore your RuleRepository will be instantiated.
If it doesn't work on first try, you will have to add your com.example.controller package in the list of packages to scan, in the #ComponentScan annotation
First as everyone mention your class Judge does not have #Component annotations.
The other thing is, maybe my Spring is getting little bit rusty.
But as far as I remember, I think your repository also require to have #Component or #Repository annotation