Autowire a Bean in spring-boot-starter library which is not created yet | TransformerSupplier Kafka Streams - spring

I am creating a spring-boot-starter kafka streams library.
In that I am defining a configuration class which will have a list of storeBuilders.
class CustomConfiguration {
private String A;
private String B;
private Set<StoreBuilder<?>> transformationStoreBuilders;
private Set<StoreBuilder<?>> processorStoreBuilders;
// Constructor and Getter
}
The client which will import this spring-boot-starter library will have to create a bean of the above configuration class.
Now I am creating a custom TransformerSupplier and I want to auto-set the stores by autowiring stores from CustomConfiguration.
I am doing something like this:
public abstract class CustomTransformerSupplier<A, B, C, D> implements TransformerSupplier<A, B, KeyValue<C, D>> {
#Autowired
private CustomConfiguration configuration;
public abstract CustomTransformer<A, B, KeyValue<C, D>> get();
public Set<StoreBuilder<?>> stores() {
return configuration.getTransformationStoreBuilders();
}
}
However the CustomTransformerSupplier bean will be created by the client and we cannot Autowire bean in a non Bean class.
How do I auto set the store builders in the transformer supplier?
Why am I following the approach mentioned is because:
The client can have n store builders
There is going to be a ProcessorSupplier and a TransformerSupplier. They can share common state stores.
I want the client to just create the state stores and not worry about injecting the state stores in the TransformerSupplier and the processorSupplier.
Thanks in advance!

See if #Configurable may answer to your question: https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#aop-atconfigurable

Related

How to create a bean from a fully-qualified class name specified in external configuration? (Spring)

I'm using Spring Boot and have an external configuration file called application.yml.
In this file, suppose I have a property foo that takes a fully-qualified class name as value.
Using Java configuration, what is the typical way to create a bean of the type specified by the foo property?
I suppose that foo is implementing some known interface. Otherwise, it is kind of pointless to create a bean of an unknown Type.
Something like:
#Configuration
public class FooConfiguration {
#Value("foo.class-name") // ref to the key used in you application.yml
private String fooClassName;
#Bean
public FooInterface fooBean(){
FooInterface fooImpl = Class.forName(fooClassName).newInstance(); // concreet implemetation
return fooImpl;
}
}

mock autowired field in#Import class

I am doning an unit test which require some objects which injected by spring so I use:
#RunWith(SpringRunner.class)
#Import({BConfig.class})
public class ATest {
private A a;
#Autowired
private B b;
#Before
public void init() {
a = new A(b);
}
}
However, the BConfig class has an autowired field c which I don' need in this test:
class BConfig {
#Autowired
C c;
#Bean
public getB() {
return new B();
}
// other method code will use field c
}
The autowired c field will get data from redis in #PostConstruct which don't exist in unit test. If I don't omit that, the unit test will report error due to redis data is not exist.
I have a solution to make C to 2 subclasses CProduction and CUnitTest both of them implements interface C, then active profile to use CUnitTest in unit test. However this is some kind of invasive because if don't do the unit test, the interface of C is useless.
Is there a better way to do this?
Consider using:
#RunWith(SpringRunner.class)
#ContextConfiguration({BConfig.class})
class ATest {
#MockBean // will place a mock implementation of C to the context instead of real one that tries to connect with Redis.
C c;
...
}
Note that it will work seamlessly if C is an Interface. Otherwise it will try to create a proxy by inheritance (something that extends from C) and if C has some redis-related code in constructor it might still attempt to call it.
Update 1
Based on OP's comment in an attempt to clarify the answer:
When spring application context starts it basically resolves all the beans and injects what's needed.
First it resolves the bean definitions (metadata) - the real class, scope, etc.
And then it creates beans in the order that will allow injection. For example, it class A has a field of class B (both are beans) then spring must create B first, then create a and inject B into A.
Now, #MockBean is a hook relevant for tests. It tells spring application context used in test that instead of a regular bean definition that spring is able to parse out of #Configuration classes or find the component due to #Component annotation placed on it, it should use the Mock - something that is generated in runtime with frameworks like Mockito.
This mock implementation can later be used to specify the expectations (see Mockito basic tutorial, there are plenty of those on internet), but what's more important for your case, it wont connect to redis because this mock implementation doesn't have any redis related code.
Update 2
The configuration should be rewritten as follows:
#Configuration
public class BConfig {
#Bean
public getB() {
return new B();
}
#Bean
public C c() {
return new C();
}
// other method code will use field c:
// if for example D uses C: then you can:
#Bean
public D d(C c) {
return new D(c);
}
}

Im geting the error Parameter 0 of constructor required a single bean, but 2 were found but one of the beans "found" has been deleted by me

Im trying to set up a h2 database using jpa/jdbc, after creating an implemntation for a query interface using jpa as opposed to jdbc i am now getting the error:
Parameter 0 of constructor in com.nsa.charitystarter.service.CharityQueries required a single bean, but 2 were found:
- charityRepoJDBC: defined in file [C:\Users\V La Roche\Desktop\assessment-1-starter\out\production\classes\com\nsa\charitystarter\data\CharityRepoJDBC.class]
- charityRepoJPA: defined in null
Im unsure as to what has gone wrong and am not really sure where to go from here, i havent been able to find many people with a similar issue to me online.
My implementation using jdbc
#Repository
public class CharityRepoJDBC implements CharityRepository {
private JdbcTemplate jdbc;
private RowMapper<Charity> charityMapper;
#Autowired
public CharityRepoJDBC(JdbcTemplate aTemplate) {
jdbc = aTemplate;
charityMapper = (rs, i) -> new Charity(
rs.getLong("id"),
rs.getString("name"),
rs.getString("registration_id"),
rs.getString("acronym"),
rs.getString("purpose")
);
}
#Override
public List<Charity> findCharityBySearch(String searchTerm) {
String likeSearch = "%" + searchTerm + "%";
return jdbc.query(
"select id, acronym, name, purpose, logo_file_name, registration_id " +
"from charity " +
"where concat(name, acronym, purpose, registration_id) like ?",
new Object[]{likeSearch},
charityMapper);
}
#Override
public Optional<Charity> findById(Long id) {
return Optional.of(
jdbc.queryForObject(
"select id, acronym, name, purpose, logo_file_name, registration_id from charity where id=?",
new Object[]{id},
charityMapper)
);
}
}
Charity finder implementation:
#Service
public class CharityQueries implements CharityFinder {
private CharityRepository charityRepository;
public CharityQueries(CharityRepository repo) {
charityRepository = repo;
}
public Optional<Charity> findCharityByIndex(Integer index) {
return charityRepository.findById(index.longValue());
}
public List<Charity> findCharityBySearch(String searchTerm) {
return charityRepository.findCharityBySearch(searchTerm);
}
}
CharityFinder interface
public interface CharityFinder {
public Optional<Charity> findCharityByIndex(Integer index);
public List<Charity> findCharityBySearch(String searchTerm);
}
error log :
***************************
APPLICATION FAILED TO START
***************************
Description:
Parameter 0 of constructor in com.nsa.charitystarter.service.CharityQueries required a single bean, but 2 were found:
- charityRepoJDBC: defined in file [C:\Users\V La Roche\Desktop\assessment-1-starter\out\production\classes\com\nsa\charitystarter\data\CharityRepoJDBC.class]
- charityRepoJPA: defined in null
Action:
Consider marking one of the beans as #Primary, updating the consumer to accept multiple beans, or using #Qualifier to identify the bean that should be consumed
Process finished with exit code 0
You have following definition currently,
#Repository
public class CharityRepoJDBC implements CharityRepository {
And you are injecting CharityRepository in your service layer CharityQueries
#Service
public class CharityQueries implements CharityFinder {
private CharityRepository charityRepository;
Hence when you deploy your application the container is confused which bean you are trying to autowire into the service.
By default spring autowires by type and hence by that there are 2 beans which are qualified to be injected by spring container.
CharityRepository itself and other
CharityRepoJDBC
So you need to either explicitly tell container which bean you are trying to autowire in this case.
So you can try adding qualifiers as below to solve the issue.
#Service
public class CharityQueries implements CharityFinder {
#Qualifier("CharityRepoJDBC")
private CharityRepository charityRepository;
and at the same time modify your CharityRepoJDBC to be,
#Repository(value = "CharityRepoJDBC")
public class CharityRepoJDBC implements CharityRepository {
You seem to have the Spring Data JDBC starter on the classpath and the Spring Data JPA starter.
Spring Data JDBC has a bug which causes it to produce implementation for repository interfaces even if it shouldn't, thus you end up with one implementation from JPA and another one from JDBC.
If you really want to use Spring Data JDBC and Spring Data JPA you can limit the #EnableJdbcRepositories and #EnableJpaRepositories annotations using the include and exclude filters.
But from your code and the tags you used I suspect you might be not at all interested in Spring Data Jdbc but only in Spring Jdbc.
If this is the case look for a dependency spring-boot-starter-data-jdbc and change it to spring-boot-starter-jdbc.
In case all this Spring (Data) JDBC/JPA confuse you this question and its answers might help: Spring Data JDBC / Spring Data JPA vs Hibernate
I solved it putting #Primary annotation in the repository interface.
In your case it would be the following:
#Primary
public interface CharityFinder {
public Optional<Charity> findCharityByIndex(Integer index);
public List<Charity> findCharityBySearch(String searchTerm);
}

How to use #Autowired in an class annotated with #Entity?

I have an entity called TimeBooking. When I request this entity and return to the client I want to get a list of ActivityTimeBookings from a repository. But when the function get called the repo is null.
So I tried to #Autowired the repo and marked it as transient and also said Spring that there is a dependency which should be injected.
#Configurable(preConstruction = true)
#Entity
public class TimeBooking extends BaseEntity{
#Autowired
private transient ActivityTimeBookingRepository activityTimeBookingRepository;
...
#JsonProperty("activityTimeBookings")
private List<ActivityTimeBooking> activityTimeBookings() {
return this.activityTimeBookingRepository.findByDate(this.timeFrom);
}
}
Any suggestions?
Using #Autowired in a class annotated with #Entity is a bad practice.
The solution is given below :
1. Create a service interface :
public interface TimeBookingService {
public List<ActivityTimeBooking> activityTimeBookings();
}
2. Create an implementation of the service interface :
#Service
public class TimeBookingServiceImpl implements TimeBookingService {
#Autowired
private ActivityTimeBookingRepository activityTimeBookingRepository;
public List<ActivityTimeBooking> activityTimeBookings() {
return this.activityTimeBookingRepository.findByDate(this.timeFrom);
}
}
Usually its indeed a bad practice to inject something into JPA entities.
These are usually created by JPA implementation (like Hibernate) and spring as a DI framework doesn't really participate in this process.
Note, that there can be many instances of this class created as a result of query, so if you later use this for serialization of the list of this object you might end up running N queries to the database given N entities like this were retrieved.
Answering your question about "getting access to the repo" I believe you should consider refactoring:
In the service class (assuming you have a "regular" contoller, service and dao):
you can:
class MyService {
SomeResult.. doSomething() {
List<TimeBooking> allTimeBookings = dao.getAllTimeBooking();
LocalDateTime timeFrom = calculateTimeFrom(allTimeBookings);
List<ActivityTimeBooking> allActivityTimeBookings = dao.findByDate(timeFrom);
return calculateResults(allTimeBookings, allActivityTimeBooking);
}
}
class MyDao {
List<ActivityTimeBooking> findByDate(LocalDateTime timeFrom) {...}
List<TimeBooking> getAllTimeBookings() {...}
}
Regarding the service implementation, I've assumed this use case can't be covered by usual "JOIN between two tables" so that that creating an association between TimeBooking and ActivityTimeBooking is not an option.
Note 2, I've used one repository (dao) for brevity, in real application you might want to inject two different repositories into the service.

ejb references passed in helper class or create ejb instead

I was wondering which one of the two solutions hereunder is the best practice?
Problem : I have a stateless session bean VehicleBean, I implemented a method with business logic and persistence of a Car with its wheels , their tires, etc.
#Stateless
class VehicleBean{
#PersistenceContext
private EntityManager em;
#EJB
Inspector inspectorBean;
#EJB
DocumentBean documentBean;
public void persistCar(){
Car car = new Car();
car.setMake("Ford");
em.persist(car);
documentBean.persistDocument(new Document(car));
for (int i = 0; i < 4; i++){
createWheel(car);
}
private void createWheel(Car car){
// some business logic
inspectorBean.inspect();
Wheel wheel = new Wheel();
wheel.setCar(car);
em.persist(wheel);
...
}
...
}
I have different private methods that are called after each other (1) create car, 2) create wheels, etc) and I would like to regroup all these methods in one helper class.
Solutions:
1) Make use of a helper class CarBuilder which contains all the private methods from my VehicleBean. As I can't use ejb injection, I want to pass it by reference to the constructor as following :
public void persistCar(){
new CarBuilder(this, document, inspectorBean);
}
class CarBuilder{
private VehiculeBean vehiculeBean;
...
CarBuilder(VehiculeBean,....){
this.vehiculeBean = vehiculeBean;
}
}
2) create CarBuilder as a stateless session bean and inject the bean in the VehiculeBean.
Do you see any advantage using the 2)? So having a lot of different beans like CarBuilder, MotorcyleBuilder, etc managed by the container.
Passing references to container managed objects in constructors will decrease readability of the code (IMO). Code becomes a lot clearer if you create container managed resources with dependency injection.
Let the helper classes work with the entities instead and create a new EJB when you want to break out bussiness logic in another class.

Resources