Create GenericService Bean with FactoryBean - java

I create Beans for my Generic Service with FactoryBean.
Service:
public abstract class SimpleServiceImpl<T extends IdEntity>
implements SimpleService<T> {
#Autowired
SimpleRepository<T> entitiesRepository;
...
Factory:
#Component
public class ServiceFactoryBean<E extends IdEntity> extends ServiceAbstractFactoryBean<E> {
#Override
protected SimpleServiceImpl<E> doCreateInstance() {
return new SimpleServiceImpl<E>() { };
}
#Override
public Class<?> getObjectType() {
return SimpleService.class;
}
}
if I create bean with this factory then SimpleRepository<T> entitiesRepository; eqally SimpleRepostirory<IdEntity> not T class, what I doing wrong?
Example use Factory:
#RestController
#RequestMapping("/users")
public class UserController extends SimpleController<User> {
#Autowired
public UserController(ServiceFactoryBean<User> serviceFactoryBean) throws Exception {
super(serviceFactoryBean.getObject());
}
}
If I replace T to User in ServiceFactoryBean , it work
#Component
public class ServiceFactoryBean<> extends ServiceAbstractFactoryBean<User> {
#Override
protected SimpleServiceImpl<User> doCreateInstance() {
return new SimpleServiceImpl<User>() { };
}
#Override
public Class<?> getObjectType() {
return SimpleService.class;
}
}

Related

How to get the class of the genereric parameter of a Interface from the interface

I have this Interface:
public interface Test<T> {
default Class<?> getT() {
return T.getClass(); < --error
}
}
next i have a class that implements it:
static class ItemService implements Test<Item>{
}
And i want to get the 'Item' class from the 'ItemService' class
static ItemService service = new ItemService();
private static void k() {
System.out.println(service.getT());
}
Now one way to do it is this:
public interface Test<T> {
default Class<?> getT() {
return Type.type;
}
class Type {
public static Class<?> type;
}
}
Service:
static class ItemService implements Test<Item> {
public ItemService() {
Type.type = Item.class;
}
}
And it works fine but there is a problem,
When another class implement the interface:
static class OrderService implements Test<Order> {
public OrderService() {
Type.type = Order.class;
}
}
And i try:
static ItemService service = new ItemService();
static OrderService orderservice = new OrderService();
private static void k() {
System.out.println(service.getT());
}
I get the Order class and not the Item class
How can i make it work?
Classes inside interfaces are static, You can remove the default from the function and every class will need to implement this. example:
public interface Test<T> {
public Class<T> getT();
}
static class ItemService implements Test<Item> {
public Class<Item> getT() {return Item.class;}
}
static class OrderService implements Test<Order>{
public Class<Order> getT() {return Order.class;}
}
An alternative could be an abstract class.
public interface Test<T> {
public Class<T> getT();
}
abstract class AbstractTest<T> implements Test<T> {
private final Class<T> type;
AbstractItemService(Class<T> type) { this.type = type }
public Class<T> getT() {return type;}
}
class ItemService extends AbstractTest<Item> {
ItemService() { super(Item.class); }
// implement other things
}
class OrderService extends AbstractTest<Order>{
OrderService() { super(Order.class); }
// implement other things
}
Here is another option, if your implementation has an instance of T.
interface Test<T>{
T getT();
default Class<?> getClassOfT(){
return getT().getClass();
}
}

What is the difference between #Bean and #Component in Spring when using #Conditional?

I want to register a Boss when there is a Car in Spring container.
My code is below
#Configuration
#ComponentScan(value ={"org.example.springframework.condition","org.example.springframework.bean"})
public class ConditionConfig {
}
#Component
public class Car {
}
#Conditional(value = BossCondition.class)
#Component
public class Boss {
}
public class BossCondition implements Condition {
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return context.getRegistry().containsBeanDefinition("car");
}
}
But it doesn't work, I can't discover the car in Condition.
Then, I change my code below, it can work well.
#Configuration
public class ConditionConfig {
#Bean
public Car car() {
return new Car();
}
#Bean
#Conditional(value = BossCondition.class)
public Boss boss() {
return new Boss();
}
}
public class Car {
}
public class Boss {
}
public class BossCondition implements Condition {
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return context.getRegistry().containsBeanDefinition("car");
}
}
So, what is the difference between #Bean and #Component?

Put bean in to the map to get bean from factory by name

I need to get the bean from the factory by name.
I wonder if there is a more elegant way to deal with this problem?
My working code now looks like this. This is my interface service and "factory"
public interface GreetingService {
void getGreeting(String name);
}
public interface GreetingServiceFactory {
GreetingService getGreetingService(String region);
}
Implementation greetingService:
#Service
public class EnglishGreetingServiceImpl implements GreetingService {
#Override
public void getGreeting(String name) {
System.out.println("Hello " + name);
}
}
#Service
public class GermanGreetingServiceImpl implements GreetingService {
#Override
public void getGreeting(String name) {
System.out.println("Willkommen " + name);
}
}
Implementation factory:
#Service
public class GreetingServiceFactoryImpl implements GreetingServiceFactory {
private Map<String, GreetingService> greetingBeanMap;
#Autowired
#Qualifier("germanGreetingServiceImpl")
private GreetingService germanGreetingService;
#Autowired
#Qualifier("englishGreetingServiceImpl")
private GreetingService englishGreetingService;
#PostConstruct
public void init () {
greetingBeanMap = new HashMap<>();
greetingBeanMap.put("en", englishGreetingService);
greetingBeanMap.put("de", germanGreetingService);
}
#Override
public GreetingService getGreetingService(String region) {
return greetingBeanMap.get(region);
}
}
Main class with example code where I receive bean after some name
#SpringBootApplication
public class SpringFactoryApplication implements CommandLineRunner {
#Autowired
private GreetingServiceFactory greetingServiceFactory;
public static void main(String[] args) {
SpringApplication.run(SpringFactoryApplication.class, args);
}
#Override
public void run(String... args) throws Exception {
String config1 = "en";
GreetingService english = greetingServiceFactory.getGreetingService(config1);
english.getGreeting("John");
String config2 = "de";
GreetingService deutsch = greetingServiceFactory.getGreetingService(config2);
deutsch.getGreeting("Hans");
}
}
In your above code, this piece of code is completely redundant
#Autowired
#Qualifier("germanGreetingServiceImpl")
private GreetingService germanGreetingService;
#Autowired
#Qualifier("englishGreetingServiceImpl")
private GreetingService englishGreetingService;
#PostConstruct
public void init () {
greetingBeanMap = new HashMap<>();
greetingBeanMap.put("en", englishGreetingService);
greetingBeanMap.put("de", germanGreetingService);
}
this piece of code can be replaced by
#Autowired
private Map<String, GreetingService> greetingBeanMap;
When you declare like this, spring will search for all implementations of GreetingService interface and inject into your map, with the key as the bean name. i.e. the greetingBeanMap will have key's as germanGreetingServiceImpl and englishGreetingServiceImpl and value as the bean's itself.
If you want to make the key's as "en" and "de" instead of bean names, then you can name the beans as "en" and "de". Like this
#Service("en")
public class EnglishGreetingServiceImpl implements GreetingService {
......
}
#Service("de")
public class GermanGreetingServiceImpl implements GreetingService {
......
}

Spring autowire generic type

Spring (from version 4) claims to support generic type injection and I'm trying to do something like this:
public abstract class AbstractControl<T extends IService> {
#Autowired
private T service;
protected T getService(){
return service;
}
public void setService(T service) {
this.service = service;
}
}
then an extension of this class:
public class FooControl extends AbstractControl<LoginService> {
}
but Spring is trying to inject IService. Is it possible to inject the inherited type?
Try moving the #Autowired into the subclass, like this:
public abstract class AbstractControl<T extends IService> {
private T service;
protected T getService(){
return service;
}
public void setService(T service) {
this.service = service;
}
}
And the subclass:
public class FooControl extends AbstractControl<LoginService> {
#Override
#Autowired
public void setService(LoginService service) {
this.service = service;
}
}

#Inject in abstract class based on subclass

let imagine I have per entity a repository class (spring data jpa) for database access and a service class. The dependencies are managed by spring framework. Every service method does in most cases the same, so there is mainly code duplication:
public class NewsService {
#Inject
private NewsRepository newsRepository;
public void add(News news) {
// do some validation
newsRepository.save(news);
}
}
public class UserService {
#Inject
private UserRepository userRepository;
public void add(User user) {
// do some validation
userRepository.save(user);
}
}
Now i thought about creating an abstract class like this:
public abstract class AbstractService<T> {
private UnknownRepository unknownRepository;
public void add(T entity) {
// do some validation
unknownRepository.save(entity);
}
}
public class NewsService extends AbstractService<News> {
}
public class UserService extends AbstractService<User> {
}
My problem: How can i overwrite the repository used inside the abstract class based on my entities?
You can replace the UnknownRepository field with an abstract method and a type parameter:
// R is the type of the repository
public abstract class AbstractService<T,R extends BaseRepository> {
protected abstract R getRepository();
public void add(T entity) {
getRepository().save(entity);
}
}
And inject the specific repository to the implementations of this class:
public class NewsService extends AbstractService<News, NewsRepository> {
#Inject private NewsRepository newsRepository;
#Override
public NewsRepository getRepository() {
return newsRepository;
}
// the inherited add() method works now
}

Categories