Warning: I realize this may be an abuse of the intention behind spring beans and/or Java 8's default interface methods. What I'm looking for is specific and reasoned criticisms of why this might be an unsafe approach that I am failing to recognize.
I've defined a class that gives me static access to the running application context:
@Component
public class BeanAccessor implements ApplicationContextAware {
private static ApplicationContext applicationContext;
public static <T> T getSingleton(Class<T> clazz){
return applicationContext.getBean(clazz);
}
public static <T> T getSingleton(String beanName, Class<T> clazz){
return applicationContext.getBean(beanName, clazz);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
BeanAccessor.applicationContext = applicationContext;
}
}
I then am able to use those BeanAccessor methods inside of default interface methods in order to gain access to spring managed beans from inside the method.
I realize that this functionality could be achieved by implementing other classes and services rather than feeling a need to mix it into the current one (at least for the example below, I'm not actually doing anything with 'this'). This is partly an 'artistic' preference and I'm sure I'll continue to find other use cases.
Note that I am NOT trying to preserve state per instance like described here https://kerflyn.wordpress.com/2012/07/09/java-8-now-you-have-mixins/ and I do recognize that there are dangers in doing that.
Here's an example usage:
public interface ClientAware {
String TENANT_NAME = "TENANT_NAME";
default ClientDetails clientDetails() {
ClientDetailsService service = BeanAccessor.getSingleton(ClientDetailsService.class);
return service.loadClientByClientId(SecurityUtil.getClientId());
}
default Map<String, Object> clientInfo() {
return clientDetails().getAdditionalInformation();
}
default String tenant() {
return (String) clientInfo().get(TENANT_NAME);
}
}
And then my actual class using the interface (and another interface that does similar things to add meta-information to the response):
@RestController
@RequestMapping("/documents")
public class Documents implements WrapAware, ClientAware {
@Autowired
private DocumentService docService;
@RequestMapping(method = RequestMethod.GET)
public Object byPathAndTenant(@RequestParam("path") String path) {
return ok(docService.getDocumentsByPathAndTenant(path, tenant()));
}
}
Note that it does resolve the beans appropriately and seems to work. Before I start utilizing a pattern like this I would like to be aware of the risks.