Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using spring managed beans in an interface default method?

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.

like image 792
RutledgePaulV Avatar asked Nov 09 '22 11:11

RutledgePaulV


1 Answers

There are the already discussed caveats of on staying away from statics

In your case, you may face an issue if the interface gets referenced before your spring context is initialized. So the static ClientDetailsService service would be null resulting in an NPE.

Generally as the code grows, you may lose control of when this interface is referenced during app startup.

like image 104
6ton Avatar answered Nov 14 '22 22:11

6ton