In Spring Boot I have a class that I want to instantiate by passing parameters in the the constructor in runtime. I am able to do this but all of the AutoWire properties are null and PostConstruct doesn't get called.
Constructor<KafkaController> constructorsA[] = (Constructor<KafkaController>[]) KafkaController.class.getConstructors();
KafkaController kafkaObject = constructorsA[0].newInstance(new Object[] { "1", "2" });
This is the class in question
#Component
public class KafkaController {
private KafkaConsumer<String, String> consumer;
#Autowired
private Util sentinelUtil;
final String subscriberID;
final String interactionID;
#Autowired
public KafkaController(#Value("") String subscriberID, #Value("") String interactionID) {
this.subscriberID = subscriberID;
this.interactionID = interactionID;
}
#PostConstruct
private void initKafka() {
}
}
Do I have to instantiate the class using a different method?
Related
I am a student studying java. I want to make a simple membership registration using Spring boot mongoDB, but an error occurs and I ask a question.
my source code
MemberDTO
public class MemberDTO {
#Id
private String memberId;
private String memberPwd;
private String memberName;
private String memberEName;
private String memberCompany;
private String memberPhone;
private String memberZipCode;
private String memberAddress;
private String memberAddressDetail;
private String memberAuth;
#CreatedDate
private Date createdDate;
}
MemberController
#Controller
#RequestMapping("/member")
public class MemberController {
#Autowired
private MemberSv memberSv;
#RequestMapping(value = "/join", method = RequestMethod.POST)
public String membersave(MemberDTO memberDTO)
{
memberSv.membersave(memberDTO);
return "join_form";
}
}
MemberSv
#Repository
public interface MemberSv extends MongoRepository<MemberDTO, String> {
MemberDTO membersave(MemberDTO memberDTO);
}
MemberSvImp
#Service
#Transactional
public class MemberSvImp {
#Autowired
private MemberSv memberSv;
public MemberDTO membersave(MemberDTO memberDTO) {
return memberSv.save(memberDTO);
}
}
Spring boot main method
#EnableMongoRepositories(basePackageClasses =com.example.accountproject.models.interfaces.MemberSv.class)
#SpringBootApplication
public class AccountProjectApplication {
public static void main(String[] args) {
SpringApplication.run(AccountProjectApplication.class, args);
}
}
Error
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'memberController':
Unsatisfied dependency expressed through field 'memberSv'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'memberSv' defined in com.example.accountproject.models.interfaces.MemberSv defined in #EnableMongoRepositories declared on AccountProjectApplication: Invocation of init method failed;
nested exception is org.springframework.data.repository.query.QueryCreationException: Could not create query for public abstract com.example.accountproject.documents.MemberDTO com.example.accountproject.models.interfaces.MemberSv.membersave(com.example.accountproje ct.documents.MemberDTO)! Reason: No property membersave found for type MemberDTO! Did you mean 'memberName'?; nested exception is org.springframework.data.mapping.PropertyReferenceException: No property membersave found for type MemberDTO! Did you mean 'memberName'?
The first thing is you cannot save the MemberDTO as it is through MongoRepository. You need to persist the entity object rather than a POJO.
You need to add a MongoDB configuration By annotating the configuration class with #EnableMongoRepositories
And finally, you need to make sure that you have created a collection (the MongoDB equivalent of a table in SQL) in MongoDB to persists the Member Document (the MongoDB equivalent of a record in a table in SQL)
MongoDB configuration class.
#Configuration
#EnableTransactionManagement
#EnableMongoRepositories(basePackages = {"com.example.accountproject.models"})
#PropertySource({"classpath:application.properties"})
public class MongoDBConfiguration{
#Value("${spring.data.mongodb.host}") // set host in application.properties file
private String mongoHost;
#Value("${spring.data.mongodb.port}") // set port in application.properties file
private int mongoPort;
#Value("${spring.data.mongodb.database}") // set DB name in application.properties file
private String mongoDatabase;
#Value("${spring.data.mongodb.user}") // set user in application.properties file
private String mongoUser;
#Value("${spring.data.mongodb.password}") // set password in application.properties file
private String mongoPassword;
#Bean
public MongoDbFactory mongoDbFactory() {
MongoCredential mongoCredential = MongoCredential.createCredential(mongoUser, mongoDatabase, mongoPassword.toCharArray());
ServerAddress serverAddress = new ServerAddress(mongoHost, mongoPort);
MongoClient mongoClient = new MongoClient(serverAddress, Arrays.asList(mongoCredential));
return new SimpleMongoDbFactory(mongoClient, mongoDatabase);
}
#Bean
public MongoTemplate mongoTemplate(#Autowired MongoDbFactory mongoDbFactory) {
MongoTemplate mongoTemplate = new MongoTemplate(mongoDbFactory);
return mongoTemplate;
}
}
And remove the #EnableMongoRepositories Annotation in the AccountProjectApplication class
introduce a new Model class for the Entity Member(Let's say the collection name is 'register')
#Document(collection = 'register')
public class Member{
#Id
private String memberId;
private String memberPwd;
private String memberName;
private String memberEName;
private String memberCompany;
private String memberPhone;
private String memberZipCode;
private String memberAddress;
private String memberAddressDetail;
private String memberAuth;
#CreatedDate
private Date createdDate;
//getters and setters
}
MemberController
#Controller
#RequestMapping("/member")
public class MemberController {
#Autowired
private MemberSvImp memberService;
#RequestMapping(value = "/join", method = RequestMethod.POST)
public String membersave(MemberDTO memberDTO)
{
memberService.membersave(memberDTO);
return "join_form";
}
}
Service layer
#Service
#Transactional
public class MemberSvImp {
#Autowired
private MemberSv memberSv;
public Member membersave(MemberDTO memberDTO){
Member member = new Member();
//set member attributes through memberDTO
//ex : member.setMemberName=memeberDTO.getMemeberName();
return memberSv.save(member);
}
}
Repository Class's Base Entity should be changed to Member
#Repository
public interface MemberSv extends MongoRepository<Member, String> {
//you can call save(Entity) or saveAndFlush(Entity) of JPARespository directly without defining any custom methods to save new member.
}
SUGGESTION : Better if you can rename the repository and service class to show their purpose
MemberSv -> MemberRespository
MemberSvImp - > MemberService
where is your configuration file for setting up credentials??
i have a question here, please give some ideas.
I have two beans. FaceComparisonServerImpl depends on FaceServer.
When i want to test. I want to change the String in my 'FaceServer' bean.
#Service
public class FaceComparisonServerImpl implements FaceComparisonServer {
#Autowired
private FaceServer faceServer;
#Override
public FaceComparsionInfo getServerInfo() {
String serverInfo = faceServer.getServerInfo();
...
}
}
#Component
public class FaceServer {
#Autowired
private RestTemplate restTemplate;
//Not final, just to test.
private String version = "1.0";
private static final String CODE = "code";
private static final String MESSAGE = "message";
//Final
private static final String SERVER_URL = "http://127.0.0.1:8066/api/ZKComparison";
}
Bellow is my test code.
#ExtendWith(SpringExtension.class)
#ContextConfiguration(classes = TestConfig.class)
public class FaceServerTestByTyler {
#Autowired
private FaceComparisonServer faceComparisonServer;
#Test
public void getServerInfo(){
//How can i modify the value of SERVER_URL in faceServer?
FaceComparsionInfo serverInfo = faceComparisonServer.getServerInfo();
System.out.println(serverInfo);
}
}
My question is:
How can i modified the value of 'version' and 'SERVER_URL' in #Bean(faceServer)?
Thanks you!
You need create FaceServer mock bean for test configuration.
And override required methods
#Configuration
Class TestConfig{
#Bean
#Primary
public FaceServer faceServer() {
return new FaceServer() {
#override
public String getServerInfo(){
return "required info";
}
};
}
}
The easiest way to customize the values is to make them Spring properties:
#Component
public class FaceServer {
#Value("${faceServer.version}")
private String version;
#Value("${faceServer.url}")
private String serverUrl;
// ...
}
You can either have default values for the #Value annotations or use some default property values in application.yml.
Now just override those properties in your test with the values you want:
#ExtendWith(SpringExtension.class)
#ContextConfiguration(classes = TestConfig.class)
#TestPropertySource(properties = {
"faceServer.version=1.0",
"faceServer.url=http://127.0.0.1:8066/api/ZKComparison"
})
public class FaceServerTestByTyler {
#Autowired
private FaceComparisonServer faceComparisonServer;
// ...
}
However...
The second option is to make your classes more unit-testable. Prefer construction injection over field injection, and you can test your classes more independently.
#Service
public class FaceComparisonServerImpl implements FaceComparisonServer {
private final FaceServer faceServer;
public FaceComparisonServerImpl(FaceServer faceServer) {
this.faceServer = faceServer;
}
#Override
public FaceComparsionInfo getServerInfo() {
String serverInfo = faceServer.getServerInfo();
// ...
}
}
This now becomes unit-testable:
public class FaceServerTestByTyler {
private FaceComparisonServer faceComparisonServer;
private FaceServer faceServer;
#BeforeEach
public setup() {
faceServer = mock(FaceServer.class);
faceComparisonServer = new FaceComparisonServer(faceServer);
}
#Test
public void getServerInfo() {
when(faceServer.getServerInfo()).thenReturn(xxx);
// ...
}
}
The second option ends up with a test that runs much faster than any solutions that suggest to create a mock bean through a test configuration.
I want to implement FusionAuth in a microservices enviroment with spring boot, so i want to make a singleton of the FusionAuth Client for java. But i get error for using the variables apiKey, baseUrl in a static context.
#Configuration
public class FusionAuthClientConfig {
private static FusionAuthClient INSTANCE = null;
#Value("${fusionAuth.apiKey}")
private String apiKey;
#Value("${fusionAuth.baseUrl}")
private String baseUrl;
public static FusionAuthClient getInstance() {
if(INSTANCE == null)
INSTANCE = new FusionAuthClient(apiKey, baseUrl);
return INSTANCE;
}
}
Anyway, is this scope of singleton class used? I mean for concurrency enviroments and performance, should i use a client for each request to fusion auth?
Your private static FusionAuthClient INSTANCE = null is unnecessary. By default, beans are scoped as singletons. #see: Bean Scopes
Since you're using #Configuration, all you need to do is change your FusionAuthClientConfigto the following and you will be able to reference it elsewhere in your application as an #Autowired property.
#Configuration
public class FusionAuthClientConfig {
#Value("${fusionAuth.apiKey}")
private String apiKey;
#Value("${fusionAuth.baseUrl}")
private String baseUrl;
#Bean
public FusionAuthClient fusionAuthClient() {
return new FusionAuthClient(apiKey, baseUrl);
}
}
Now your fusionAuthClient bean can be referenced elsewhere like this:
#Service
//or #Component etc
public class MyService {
private final FusionAuthClient fusionAuthClient;
public MyService(FusionAuthClient fusionAuthClient) {
this.fusionAuthClient = fusionAuthClient;
}
public void doTheThing() {
// use this.fusionAuthClient
}
}
I have a classes CosmosConnection and SupplierGetResponseFeed
I am calling the method of CosmosConnection from SupplierGetResponseFeed
The method of SupplierGetResponseFeed from which I am calling CosmosConnection method is static
Example : public static SupplierResponseDataEntity prepareSupplierAzureData(Map<String, Object> row, String[] columnNames) {
So , When I create the object of CosmosConnection in SupplierGetResponseFeed I can not use #Autowired as a reason I am not able to pick the value from bootstrap.yml file in CosmosConnection
Though I create the object using #Autowired in SupplierGetResponseFeed I am not able to pick the values from bootstrap
#Autowired
static CosmosConnection cosmos;
Below is the code for SupplierGetResponseFeed
public class SupplierGetResponseFeed {
static CosmosConnection cosmos= new CosmosConnection(); //creating object
public static SupplierResponseDataEntity prepareSupplierAzureData(Map<String, Object> row, String[] columnNames) {
//Some code
cosmos.connectToDB(); //calling the method of CosmosConnection class
}
The code for is CosmosConnection
#Configuration
#ComponentScan
public class CosmosConnection {
#Value("${cosmos.connectionuri}") private String uri;
#Value("${cosmos.primarykey}") private String primarykey;
public String connectToDB() throws DocumentClientException, IOException, ParseException {
System.out.println("URI is " + uri); //getting this as null
What changes I need to do for picking the values from bootstrap.yml ??
A annotation called PostConstruct in package javax.annotation with spring framework can be used to solve the problem. As described in the source code:
The PostConstruct annotation is used on a method that needs to be executed
after dependency injection is done to perform any initialization
And code below is a example:
#Configuration
public class ComosConfig {
#Value("${cosmos.connectionuri}") private String uri;
#Value("${cosmos.primarykey}") private String primarykey;
//get and set methods here
}
public class CosmosConnection {
private String uri;
private String primaryKey;
public CosmosConnection(String uri, String primaryKey) {
this.uri = uri;
this.primaryKey = primaryKey;
}
public String connectToDB() {
//do something here
}
}
#Component
public class SupplierGetResponseFeed {
private static CosmosConnection cosmos;
private CosmosConfig config;
public SupplierGetResponseFeed(CosmosConfig config) {
this.config = config;
}
#PostConstruct
public void init() {
String uri = config.getUri();
String primaryKey = config.getprimaryKey();
cosmos = new cosmos(uri, primaryKey);
}
public static SupplierResponseDataEntity prepareSupplierAzureData() {
cosmos.connectToDB(); //calling the method of CosmosConnection class
}
}
After all, it's not recommended in view of code analysis utils to write to static from instance method, so you may need suppress-warning-annotation used with init method to eliminate warnings.
I have a Rest Controller in which I initialise a service like this :
class Config {
#Value(${"number.of.books"})
private final static String numberOfBooks;
}
class MyController {
private final Service myService = new ServiceImplementation(Config.numberOfBooks)
public ResponseEntity methodA() { ... }
}
The numberOfBooks field has a initialisation value but when it's passed in the ServiceImplementation constructor it comes null.
I'm thinking I'm missing something obvious over here.
What is the mistake and which would be the best practice to inject a value from a property file into a constructor?
I recommend you to directly inject numberOfBooks in your ServiceImplementation, as follows:
public class ServiceImplementation implements Service {
#Value("${number.of.books}")
private String numberOfBooks;
}
Otherwise use setter injection for static variables, as follows:
#Component
class Config {
public static String numberOfBooks;
#Value("${number.of.books}")
public void setNumberOfBooks(String numberOfBooks) {
numberOfBooks = numberOfBooks;
}
}
After studying a little I've found out that the dependency injection happens after the constructor has been called. This being said the approach used was to use Autowired on my services constructor.
class ServiceImplementation implements Service {
private final String numberOfBooks;
#Autowired
private ServiceImplementation(Config config) {
this.numberOfBooks = config.getNumberOfBooks();
}
}
In this way Spring creates the dependency tree and makes sure that Config is not null when injected.