I'm creating telegram bot with Spring-Boot. I have AscractState class:
public abstract class AbstractState {
boolean isInputIndeed = Boolean.FALSE;
public abstract void handleInput(BotContext context);
//another parts
}
And there is extend which is
#Slf4j
public class AgeInputState extends AbstractState {
#Autowired
ClientService clientService;
public AgeInputState(boolean isInputIndeed) {
super(isInputIndeed, State.AGE_INPUT);
}
#Override
public void handleInput(BotContext context) {
context.getClient().setAge(Integer.parseInt(context.getInput()));
clientService.updateUser(context.getClient());
}
}
But i have touble with ClientService. Which annotations on class i need to add for autowiring this fiels?
Since this class has a constructor which only accepts a boolean, I assume you're needing to make lots of them.
Spring won't know you're wanting to load these as spring beans if you call this constructor directly. So creating these through a factory of some sort would be one way to go. Something like:
#Configuration
public class AgeInputStateFactory {
private #Autowired ClientService clientService;
#Bean
#Scope("prototype") // Makes a new one each time...
public AgeInputState create(final boolean isInputIndeed) {
return new AgeInputState(this.clientService, isInputIndeed);
}
}
Along with a newly designed AgeInputState constructor which also takes the ClientService field.
public class AgeInputState extends AbstractState {
private final ClientService clientService;
// Package private constructor so that no one outside
// of this package will call it. This means you can
// (try your best to) limit the construction to the
// factory class.
AgeInputState(final ClientService clientService,
final boolean isInputIndeed) {
super(isInputIndeed, State.AGE_INPUT);
this.clientService = clientService;
}
}
And then all you would do is wherever you need to create these AgeInputState Objects, you would #Autowire the AgeInputStateFactory instance, and call the create method whenever you need one.
Related
I have a service Impl class which has the following 2 fields autowired:
#Service
public class OServiceImpl implements OService {
#Autowired
private MessageSender<EntityA> messageBrokerEventProducerA;
#Autowired
private MessageSender<EntityB> messageBrokerEventProducerB;
I want to write junits where I can mock implementation of above 2 interfaces using jmockit.
public class TestClass {
#Autowired
OService oService;
private static class MockMessageBrokerEventProducerA implements MessageSender<EntityA> {
#Override
public void sendMessage(EntityA message) {
System.out.println("mock A called");
}
}
private static class MockMessageBrokerEventProducerB implements MessageSender<EntityB>{
#Override
public void sendMessage(EntityB message) {
System.out.println("mock B called");
}
}
private MessageSender<A> mockMessageBrokerEventProducerA;
private MessageSender<B> mockMessageBrokerEventProducerB;
#BeforeEach
public void mockSetuUp() {
mockMessageBrokerEventProducerB = new MockMessageBrokerEventProducerB();
mockMessageBrokerEventProducerA = new MockMessageBrokerEventProducerA();
Deencapsulation.setField(oService, mockMessageBrokerEventProducerA);
Deencapsulation.setField(oService, mockMessageBrokerEventProducerB);
}
The above set up does not work, it throws an error :
java.lang.IllegalArgumentException: More than one instance field to which a value of type ....can be assigned exists in the class.
It works well whenever there is only one interface autowired in impl class and mocking that one. Above error is thrown whenever there is autowiring of more than 1 interface (same interface with generics) in impl class. How should I solve this ?
I solved it with following:
Deencapsulation.setField(oService, "messageBrokerEventProducerA",
mockMessageBrokerEventProducerA);
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.
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.
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
My application have the follow layer:
- Facade
- Business Object
- Repository (Spring JPA Data)
Let's suppose the follow classes:
#Component
public class MessageFacade implements MessageService {
#Autowired
private GarageBO garageBO;
#Autowired
private MessageBO messageBO;
public void createFeedbackMessage(...) {
messageBO.createFeedbackMessage(...);
garageBO.createFeedback(...);
}
}
#Component
public class ServiceOrderFacade implements ServiceOrderService {
#Autowired
private ServiceOrderBO serviceOrderBO;
#Autowired
private MessageBO messageBO;
#Autowired
private GarageBO garageBO;
public void createServiceOrder(...) {
serviceOrderBO.createServiceOrder(...);
messageBO.createFeedbackMessage(...);
garageBO.createFeedback(...);
}
}
Observing the createFeedbackMessage method in MessageFacade i have:
1) The createFeedbackMessage method in MessageBO it's responsible to create an email with the feedback survey LINK;
2) The createFeedbackMessage method in garageBO creates the Feedback ENTITY with the questions and responses;
On createServiceOrder method in ServiceOrderFacade i need to call an method of ServiceOrderBO and after i need to have the same behavior of createFeedbackMessage method in MessageFacade.
Is it a bad idea to create a dependency between ServiceOrderFacade -> MessageFacade ?
The code would be:
#Component
public class ServiceOrderFacade implements ServiceOrderService {
#Autowired
private ServiceOrderBO serviceOrderBO;
#Autowired
private MessageBO messageBO;
#Autowired
private GarageBO garageBO;
public void createServiceOrder(...) {
serviceOrderBO.createServiceOrder(...);
getMessageService().createFeedbackMessage(...);
}
}
If you think in DRY(don't repeat yourself) you have an problem. You can create another layer like a common that is dependency of Business Object layer and will be created once and will be used in your entire BO if U want