Spring MVC: globally injecting properties from persistent layer - java

I have a service bean capable of getting / setting property values from persistent layer (eg: database). Something like this:
#Service
public ConfigService {
public String getConfig(String key);
}
The problem is for each controller class I write I have to autowire and populate my model with the property key/values:
#Controller
#RequestMapping("/foo")
public FooController {
#Autowired private ConfigService configService;
#RequestMapping("/login")
public String login(Model model) {
model.addAttribute("site.name", configService.getConfig("site.name"));
//...
}
}
Is there any way I can automatically get the value of this property on my spring JSP view? I don't want to have to inject this to my model object for each controller class I write.
The closest I can get so far is using Spring ResourceBundleMessageSource bean and <spring:message> tags, however I am constrained to using properties file, can't store it in database.

I found another way of doing this. Use a #ControllerAdvice class combined with #ModelAttribute method. Something like this:
#ControllerAdvice
public class ConfigAdvice {
#Autowired private ConfigService configService;
#ModelAttribute
public void populateConfig(Model model) {
for(Config config : configService.getAll()) {
model.addAttribute(config.getKey(), config.getValue());
}
}
}
The #ModelAttribute annotated populateConfig() method above will run prior to any #RequestMapping method on all other controller classes.
One drawback is config key can't contain any dot characters.
So far this looks to be my best option. Please let me know if there's a better way.

Related

Inject multiple beans of the same type and automatically select between them based on generic type

I have two (more in the future) implementations of ImportantService – VeryImportantService and LessImportantService:
public interface ImportantService<T extends ImportantRequest> {}
#Service
public class VeryImportantService implements ImportantService<VeryImportantRequest> {}
#Service
public class LessImportantService implements ImportantService<LessImportantRequest> {}
And then I have a controller, in which I want to inject all of the implementations of ImportantService:
#RequiredArgsConstructor
#RestController
#RequestMapping("/api/important")
public class ImportantController<T extends ImportantRequest> {
private final ImportantService<T> importantService;
#PostMapping
public ResponseEntity<ImportantResponse> create(#RequestBody #Valid T request) {
// very important code here
}
}
Obviously, such king of injecting fails:
UnsatisfiedDependencyException: Error creating bean with name 'importantController' defined in file ...
...
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
What I want is:
Inject all of the implementations of ImportantService, and then, based on the T automatically select required bean. I know I can add method to ImportantService, which returns the type that implementation works with and then inject ImportantService as List<ImportantService> importantServices and then filter like this:
importantServices.stream()
.filter(importantService -> importantService.getType().equals(request.getClass()))
.findFirst()
.ifPresent(importantService -> importantService.doImportantJob(request));
BUT! I have hundreds of services to refactor like this and I really don't want to write additional logic to controllers.
I know about #Conditional annotation and Condition interface, but AFAIK there's no way to make them do what I want.
Why not implement the proxy pattern?
example:
#Service
#Primary
#RequiredArgsConstructor
public class ImportantServiceProxy implements ImportantService<T extends ImportantRequest> {
private final List<ImportantService> importantServices;
private ImportantService getImportantService(ImportantRequest request){
return this.importantServices.stream()
.filter(importantService -> importantService.getType().equals(request.getClass()))
.findFirst()
.get();
}
public void doImportantJob(ImportantRequest request){
this.getImportantService(request).doImportantJob(request);
}
}
Then in your controller you can call the function without check the type.
#RequiredArgsConstructor
#RestController
#RequestMapping("/api/important")
public class ImportantController<T extends ImportantRequest> {
private final ImportantService<T> importantService;
#PostMapping
public ResponseEntity<ImportantResponse> create(#RequestBody #Valid T request) {
importantService.doImportantJob(request);
}
}
what you want is a list of beans which are of type ImportantService
so you have to declare a variable like this.
final List<ImportantService> importantServices;
demoController(List<ImportantService> importantServices) {
this.importantServices = importantServices;
}

Autowired not working in a Class #Entity

I have a class called Menu, annnotated as #Entity where i need to use a method inside a class called GestoreMessaggi
....
#Component
#Entity
#Table(name="menu")
public class Menu implements Serializable{
#Autowired
#Transient // because i dont' save this field on the DB
private GestoreMessaggi gestoreMessaggi;
.....
public void setCurrentLanguage(){
/* I got the error both if use gestoreMessaggi
this way and if I use the autowired istance of GestoreMessaggi*/
GestoreMessaggi gestoreMessaggi = new GestoreMessaggi();
gestoreMessaggi.gest();
}
.....
This is the relevant code of the class GestoreMessaggi
#Component
public class GestoreMessaggi {
#Autowired
private ReloadableResourceBundleMessageSource messageSource;
public void gest(){
messageSource.doSomething() <--- here messageSource is null
}
}
When, I call gestoreMessaggi.gest(); from Menu class, I got an error because messageSource is null. The gestoreMessaggi istance is NOT null, is null just messageSource
IMPORTANT: I get null on messageSource only when I call GestoreMessaggi from classes annotated as #Entity.
In ds-servlet.xml I tell Spring to scan the pakages that contain Menu and GestoreMessaggi classes:
//Menu package
<context:component-scan base-package="com.springgestioneerrori.model"/>
//Gestore messaggi package
<context:component-scan base-package="com.springgestioneerrori.internazionalizzazione"/>
Thank you
You can follow two approaches:
Try to get an instance of a Spring-managed bean from within a method of your #Entity class
Change the design as suggested by #Stijn Geukens and make your entity a POJO without any logic or dependency injection machanisms
If you go by option 1, you have to explicitly access Spring's context and retrieve an instance of the bean you need:
#Component
public class Spring implements ApplicationContextAware {
private static final String ERR_MSG = "Spring utility class not initialized";
private static ApplicationContext context;
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
context = applicationContext;
}
public static <T> T bean(Class<T> clazz) {
if (context == null) {
throw new IllegalStateException(ERR_MSG);
}
return context.getBean(clazz);
}
public static <T> T bean(String name) {
if (context == null) {
throw new IllegalStateException(ERR_MSG);
}
return (T) context.getBean(name);
}
}
You need to make Spring scan this class in order for this to work.
Then, inside your #EntityClass, do this:
public void setCurrentLanguage(){
GestoreMessaggi gestoreMessaggi = Spring.bean(GestoreMessaggi.class);
gestoreMessaggi.gest();
}
And that would be all. Note that you wouldn't need to autowire GestoreMessaggi into your #Entity any more. Please also note that this approach is not recommended neither by Spring nor by most of the community, since it couples your domain classes (your #Entity class) to Spring classes.
If you go by option 2, then all you need to do is let Spring resolve the autowiring as usual, but outside of your entity (i.e. in a dao or service), and if your entity needs you to fill it with some message or whatever, just invoke a setter on it. (Then it's up to you to make your #Entitys attribute #Transient or not, depending on your requirements).
Spring context does not manage entities (in general does not manage objects instantiated using new), this is why you cannot autowire beans (that come from Spring's context) in entities.
Best practices suggest to keep only getter and setter in your entities, leaving the business logic to the service layer.
A common approach is Service <-> DAO <-> Entity. Example:
Service layer:
#Service
public interface GestoreMessaggi {
public void gest();
}
public class GestoreMessaggiImpl implements GestoreMessaggi {
#Autowired
private MenuDao menuDao;
#Override
public void gest() {
// 1) retrieve your entity instance with menuDao
// 2) do stuffs with your entity
// 3) maybe save your entity using menuDao
}
}
DAO layer:
If you use Spring Data Jpa:
public interface MenuDao extends JpaRepository<Menu, [menu-id-type]> {}
Else:
public interface MenuDao {
public Menu findOne([menu-id-type] id);
public Menu save(Menu menu);
// other methods for accessing your data
}
#Repository
public class MenuDaoImpl {
// inject EntityManager or Hibernate SessionFactory
// implement your DAO interface accessing your entities
}
Finally remember to configure Spring's beans including your #Services and your #Repositorys in your configuration (explicitly or by package scanning).
Using Spring MVC, you should inject (autowire) your #Services in a #Controller class, so the controller can call services methods, which call DAOs methods, which access your data.

How To Access Spring Bean name?

#Named("myUniqueName")
public class ReportDashboardDao implements DashboardDAO{
//STUFF
}
how can i access the string inside #Named tag when i am injecting DashboardDAO like this :
#Named
public class DshboardDaoConsumer(){
#Inject List<DashboardDAO> dashboardDAO;
//STUFF
}
Use a Map instead
#Inject
Map<String, DashboardDao> dashBoardDaos;
This will inject a Map with bean names as keys and daos as values.
Of course, you could also read the annotation value from class instances.
You can't. You're injecting by type. After injection has been done, Spring does not leave behind any relation between the bean's object and the bean's name.
You might want to check out ApplicationContext#getBeanNamesByType() depending on what you want to do.
By implementing BeanNameAware.
#Named("myUniqueName")
public class ReportDashboardDao implements DashboardDAO, BeanNameAware{
//STUFF
private String beanName;
#Override
public Void setBeanName(String beanName) {
this.beanName = beanName;
}
}
So that Spring can inject the beanName into the bean. If you add a public String getBeanName(); in your DashboardDAO interface, DashboardDaoConsumer will be able to obtain it.
In this particular case, Spring will inject the name you specified in the annotation.

Why autowiring doesn't work?

I'm Working on a Spring web App with Hibernate and Spring Mvc, And I'm wondering why Autowiring works only inside of the controller
this is a simple example :
#Controller
#RequestMapping(value="SW/excel")
public class ExcelController
{
#Autowired
private BlablaService blablaService;
#RequestMapping({""})
public ModelAndView indexPage()
{
List<Blabla> blablas=BlablaService.getAllBlablas();
}
}
This code is working fine for me, it returns the list of Blablas I have in my Database.
but when I work with my BlablaService outside of the controller, it doesn't work and here is and example
#Controller
#RequestMapping(value="SW/excel")
public class ExcelController
{
#RequestMapping({""})
public ModelAndView indexPage()
{
BlablaLister lister= new ExcelExporter();
List<Blabla> blablas=lister.getList();
}
}
And here is the Excel Exporter:
Class BlablaLister {
#Autowired BlablaService blablaService;
public List<Blabla> getList()
{
return blablaService.getAllBlablas;
}
}
But I always get , NullPointerException, the getAllBlablas returns Null whenever used in the a class out of the controller.
Your BlablaLister have to be initiated via spring in order for autowiring to work
In order for the autowiring to work Spring must know about the object - either by having it instantiated in the config file or by using one of the annotations that instantiate a bean. To make it work you probably just need to add the #Component annotation to identify it as a spring-managed bean.
#Component
Class BlablaLister {
#Autowired BlablaService blablaService;
public List<Blabla> getList()
{
return blablaService.getAllBlablas;
}
}

Spring #Controller and RequestMapping calling different services depending on a given parameter

Let's assume I have this code:
#Controller
#RequestMapping("/something")
public class SomeController {
#Autowired
private SomeService aService;
#RequestMapping("/doStuff")
public void doStuff(#RequestParam("type") String type) {
aService.doStuff();
}
}
In my application I need to call a specific service depending on the specified type. All services implements the same interface. If I understand correctly SomeService cannot be an interface. I could use a service factory and instantiate the service depending on the type every time a new request is done, but this doesn't look very efficient.
Alternatively I could use a different controller for each different type of service (and encode the type in the REST URI), but this would imply a lot of code duplication since all the services basically implements the same interface.
My question is, assuming the called service depends on the passed parameter, what is the best pattern to adopt for this scenario?
Similar to RC.'s answer, instead of using a Map and adding the values by you, just let the Spring BeanFactory handle this for you:
#Controller
#RequestMapping("/something")
public class SomeController {
#Autowired
private BeanFactory beanFactory;
#RequestMapping("/doStuff")
public void login(#RequestParam("type") String type) {
SomeService aService = (SomeService)beanFactory.getBean(type);
aService.doStuff();
}
}
You could use a map here, something along this:
#Controller
#RequestMapping("/something")
public class SomeController {
#Autowired
private SomeService someService;
#Autowired
private SomeOtherService someOtherService;
// ...
private final Map<String, ServiceCommonInterface> serviceMap = new HashMap<>();
#PostConstruct
private void postConstruct() {
serviceMap.put(typeForSomeService, someService);
serviceMap.put(typeForSomeOtherService, someOtherService);
}
#RequestMapping("/doStuff")
public void login(#RequestParam("type") String type) {
// TODO: ensure type is correct (an enum might be handy here)
serviceMap.get(type).doStuff();
}
}
Or better, as stated in comments you can leverage qualifiers:
#Controller
#RequestMapping("/something")
public class SomeController {
#Autowired
private ApplicationContext applicationContext;
#RequestMapping("/doStuff")
public void login(#RequestParam("type") String type) {
// TODO: ensure type is a correct bean name
applicationContext.getBean(type, ServiceCommonInterface.class).doStuff();
}
}
Depending on the number of types you wish to support there are two options I see.
1) Autowire in a factory as you mentioned and lazily create each service as needed. If the services are stateless you could keep a reference to the object after created so would only need to create once per type.
2) Autowire in a Spring Map with the key being your types and the value being the correct service to use. Then when your receive the type, can retrieve the correct service impl for your map.
For example for map: http://www.mkyong.com/spring/spring-collections-list-set-map-and-properties-example/ or see How to inject a Map<String, List> in java springs?
Both of these require you to create an interface for your service which is something you said is possible.

Categories