I'm interested in using HK2 or Guice for a dependency injection framework.
I know of #Named, #Qualifier, and custom annotations etc. But these are all compile-time.
I am looking for a facility to dynamically determine the desired concrete type based on runtime context and inject the correct implementation.
Is there something like that in HK2 or Guice or a recommended way of achieving this?
For example:
// I would want to turn this...
public final class Handler
{
private final Session session;
#Inject
public Handler(#Named("Database") final Session session)
{
this.session = session;
}
...
}
// into something like this...
public final class Handler
{
private final Session session;
#Inject
public Handler(final Session session)
{
this.session = session;
}
}
// where "session" is injected based on some previous context value ("Database")
// or something to that effect.
I ended up using a feature in HK2 called Operations (link to docs). It allows a user of HK2 to define custom scopes and manage them as "operations". You can find a more detailed example of how to use the feature on HK2's github project: operations example.
This is a simplified example of how I ended up using this feature to inject things based on context or in this case "scope".
Here is some almost-working pseudo-code to demonstrate my usage:
// Create the custom scope annotation.
#Scope
#Proxiable(proxyForSameScope = false)
#Documented
#Target({ ElementType.TYPE, ElementType.METHOD })
#Retention(RetentionPolicy.RUNTIME)
public #interface BatchScope
{
public static final BatchScope INSTANCE = new BatchScopeEnvoy();
}
final class BatchScopeEnvoy extends AnnotationLiteral<BatchScope> implements BatchScope
{
private static final long serialVersionUID = 938233179310254573L;
}
// Create a context used by the HK2 operation feature.
#Singleton
public final class BatchScopeContext extends OperationContext<BatchScope>
{
#Override
public Class<? extends Annotation> getScope()
{
return BatchScope.class;
}
}
// Create a class that holds your custom scope data/context.
public final class BatchScopeRuntime
{
// ... Arbitrary runtime data here ...
public SomeData getData()
{
return this.data;
}
}
// Create a factory that serves up something you want to inject from a custom scope.
#Singleton
public final class DataFactory implements Factory<SomeData>
{
private final OperationManager operations;
#Inject
public BatchInfoFactory(final OperationManager operations)
{
Sentinel.assertIsNotNull(operations);
this.operations = operations;
}
// The #BatchScope on the provide() method indicates that objects returned
// from this factory are in the "BatchScope".
#Override
#BatchScope
public IBatchInfo provide()
{
final OperationHandle handle = this.operations.getCurrentOperation(BatchScope.INSTANCE);
final BatchScopeRuntime runtime = (BatchScopeRuntime)handle.getOperationData();
return runtime.getData();
}
#Override
public void dispose(final IBatchInfo instance)
{
// Do nothing.
}
}
// Setup the injector.
public static ServiceLocator createInjector(final String name)
{
final ServiceLocator injector = ServiceLocatorFactory.getInstance().create(name);
ServiceLocatorUtilities.bind(
injector,
new AbstractBinder()
{
#Override
protected void configure()
{
// This creates a "Singleton" factory that provides
// "SomeData" instances at "BatchScope".
bindFactory(DataFactory.class, Singleton.class)
.to(SomeData.class)
.in(BatchScope.class);
}
}
return injector;
}
// Create a class that needs something in the custom scope.
public final class Foo
{
#Inject
public Foo(final SomeData data)
{
System.out.printf("I got: %s%n", data);
}
}
// Usage: how to manage the scopes using the operations feature.
final SomeData data = ... // get some data
final BatchScopeRuntime runtime = new BatchScopeRuntime(data); // Setup the runtime information.
// Create an operation handle for the custom scope and associate the custom data with it.
final ServiceLocator injector = createInjector("test");
ServiceLocatorUtilities.addClasses(injector, BatchScopeContext.class, Foo.class);
final OperationManager operations = injector.getService(OperationManager.class);
final OperationHandle<BatchScope> batchScope = operations.createAndStartOperation(BatchScope.INSTANCE);
// Operation/scope is now associated with the current thread.
batchScope.setOperationData(runtime);
// Foo will now be injected with: "data" from above.
final Foo foo = injector.getService(Foo.class);
// Do some work...
// Close the operation (make it go out of scope) on the current thread.
batchScope.closeOperation();
Related
I define an object as with scope "thread".
In some place in the code the instance is obtained with #Autowired and in other place with context getBean(), when comparing the objects, they are different.
Since the object is "thread" scoped I was expecting the same instance of the object to be returned.
Following is the code, first I define a custom scope
#Configuration
public class ThreadScopeRegisteringBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
#Override
public void postProcessBeanFactory (ConfigurableListableBeanFactory beanFactory) throws BeansException {
beanFactory.registerScope("thread", new SimpleThreadScope());
}
}
A test object is defined as:
#Component
#Scope("thread")
public class ThreadScopeObject implements InitializingBean {
private static final Logger LOGGER = LoggerFactory.getLogger(ThreadScopeObject.class);
private String field1;
public String getField1() {
return field1;
}
public void setField1(String field1) {
this.field1 = field1;
}
#Override
public void afterPropertiesSet() throws Exception {
LOGGER.info("****************** new object - " + this);
}
}
Also a service is defined as:
#Service
public class ThreadScopeService {
private static final Logger LOGGER = LoggerFactory.getLogger(ThreadScopeService.class);
#Autowired
private ThreadScopeObject threadScopeObject;
public void showObject() {
LOGGER.info ("ShowObject: " + threadScopeObject);
}
}
And finally a Async method is defined [RunnableService.java]:
#Async
public CompletableFuture<Map> objectInstanceTest() {
ThreadScopeObject o = ApplicationContextHolder.getContext().getBean(ThreadScopeObject.class);
LOGGER.info ("Thread object: " + o);
service.showObject();
return CompletableFuture.completedFuture(new HashMap<>());
}
When running the application I get the following log:
19:15:27.094 [mcd-async-1] INFO com.mono.threadSample.ThreadScopeObject - ****************** new object - com.mono.threadSample.ThreadScopeObject#69c8f2bb
19:15:27.094 [mcd-async-1] INFO com.mono.threadSample.RunnableService - Thread object: com.mono.threadSample.ThreadScopeObject#69c8f2bb
19:15:27.094 [mcd-async-1] INFO com.mono.threadSample.ThreadScopeService - ShowObject: com.mono.threadSample.ThreadScopeObject#fd0e5b6
I would like to know the reason why an object "thread" scoped is instantiated twice in the same thread.
Code: https://github.com/saavedrah/spring-threadSample
Thank you.
The ThreadScopeService is a singleton by default, so when it is constructed by spring it will get a ThreadScopeObject from the thread that created it and it won't be updated afterwards. There are two ways to solve this:
inject a Provider<ThreadScopeObject> or ObjectFactory<ThreadScopeObject> into ThreadScopeService and call their get methods to retrieve the scoped object when needed.
annotate the ThreadScopeObject with #Scope("thread", proxyMode = ScopedProxyMode.TARGET_CLASS). This will make spring create a proxy around your object, which will delegate all calls to the correctly scoped instance.
Assume I have a following code in Java EE / EJB / JAX-RS:
#POST
#Path("some/path")
#MyAnnotation
public MyResponse createActivation(MyRequest request, CustomValue value) {
// ...
}
How do I check for the presence of custom #MyAnnotation annotation and populate CustomValue value method parameter based on some request context parameters in case the annotation is present?
Note: I already have this code in Spring using HandlerInterceptorAdapter and HandlerMethodArgumentResolver. Now I need to do the same without Spring. I have already discovered the ContainerRequestFilter and I use it to check for the annotation, but now I am struggling with injecting the method parameter.
Custom method parameter injection is handled a little differently from normal (i.e. field, constructor) injection. With Jersey, this requires the implementation of a ValueFactoryProvider. For your case it would look something like
public class MyAnnotationParamValueProvider implements ValueFactoryProvider {
#Inject
private ServiceLocator locator;
#Override
public Factory<?> getValueFactory(Parameter parameter) {
if (parameter.getAnnotation(MyAnnotation.class) != null
&& parameter.getRawType() == CustomValue.class) {
final Factory<CustomValue> factory
= new AbstractContainerRequestValueFactory<CustomValue>() {
#Override
public CustomValue provide() {
final ContainerRequest request = getContainerRequest();
final String value = request.getHeaderString("X-Value");
return new CustomValue(value);
}
};
locator.inject(factory);
return factory;
}
return null;
}
#Override
public PriorityType getPriority() {
return Priority.NORMAL;
}
}
Then you need to register it with the ResourceConfig
public class AppConfig extends ResourceConfig {
public AppConfig() {
register(new AbstractBinder() {
#Override
protected void configure() {
bind(MyAnnotationParamValueProvider.class)
.to(ValueFactoryProvider.class)
.in(Singleton.class);
}
});
}
}
See a complete example in this Gist
See also:
Custom Method Parameter Injection with Jersey. It shows another way to do this, where you don't need to explicitly inject, and also you will be able to inject the value in all three areas (field, constructor, and method param).
We have an annotation on a function as follows
public class AnInterfaceImpl implements AnInterface {
#FairThreadUsageByEntity(entityName = "XYXYXYX",
numberOfThreads = 1)
public Report getReport(final String One, final String Two) {
//implementation.
}
}
public interface AnInterface {
String BEAN_NAME = "AnInterface"; //used for injection in spring.
Report getReport(final String One, final String two);
}
Spring configuration:
<aop:aspectj-autoproxy />
<bean class="com.amazon.utils.fairthreadusage.aspect.FairThreadUsageByEntityAdvice" />
The annotation is implemented as an aspect. Basic functionality is to limit the number of thread used by a particular type of functionality, let us say download. Below is the code for annotation FairThreadUsageByEntity:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface FairThreadUsageByEntity {
public String entityName();
public int numberOfThreads();
}
#Aspect
public class FairThreadUsageByEntityAdvice extends FairThreadUsageBase {
#Around("#annotation(fairUsage)")
public Object fairThreadUsageByEntity(final ProceedingJoinPoint pjp, final FairThreadUsageByEntity fairUsage)
throws Throwable {
//Implementation
}
}
The annotation does not work somehow. I am using AspectJWeaver 1.7 and java 1.7.
Let me know if anything else is needed. Any help appreciated.
EDIT: Adding controller as well which call getReport function
public class ReportDownloadRootController extends BaseRootController {
public static final String REQUEST_MAPPING_REPORT_DOWNLOAD = "/hz/inventory/addproducts/status/report";
public static final String PARAM_REFERENCE_ID = "reference_id";
private AnInterface anInterface;
#RequestMapping(REQUEST_MAPPING_REPORT_DOWNLOAD)
public void execute(#RequestParam(value = PARAM_REFERENCE_ID, required = true) final String referenceId,
final HttpServletRequest request, final HttpServletResponse response) {
try {
Report report = AnInterface.getReport(referenceId, getContext().getMerchantId()); //breakpoint here
} catch {
//something
}
}
#Resource(name = AnInterface.BEAN_NAME)
public void setAnInterface(final AnInterface anInterface) {
this.anInterface = anInterface;
}
}
EDIT 2: Spring bean for AnInterface
<bean id="AnInterface" class="com.facade.feed.AnInterfaceImpl" />
I created simple project with all provided by you information and can't reproduce your problem in simple setup, so you have correct implementation of your beans/aspects.
One possible and common error is defining aspect in one context and bean in another, for example aspect is defined in applicationContext and bean is defined in dispatcherServletContext. In such configuration aspect will not work on beans defined in child dispatcherServletContext, only in parent applicationContext
I'm trying to use cometd as a servlet to dropwizard but BayeuxServer doesn't seem to get injected in my service. I'm adding my two servlets as so (note, I'm not using web.xml so I'm defining the params myself):
cometdConfig.put("services", MyService.class.getCanonicalName());
System.out.print("OrderService name: " + MyService.class.getCanonicalName());
environment.addServlet(AnnotationCometdServlet.class, "/cometd/*").addInitParams(cometdConfig).setInitOrder(1);
environment.addServlet(new MyServiceServlet(), "/orders/*").setInitOrder(2);
And my service (this is where my code fails):
public class MyService
implements MyWatcher.Listener
{
#Inject
private BayeuxServer bayeuxServer;
#Session
private LocalSession sender;
private final String _channelName;
private ServerChannel _channel = null;
public OrderService() {
_channelName = "/cometd/";
initChannel();
}
private void initChannel() {
// I get an NPE here
bayeuxServer.createIfAbsent(_channelName, new ConfigurableServerChannel.Initializer() {
#Override
public void configureChannel(ConfigurableServerChannel channel) {
// ...
}
});
_channel = bayeuxServer.getChannel(_channelName);
}
}
I have also tried creating my own instance of the BayeuxServer but then that leads to a separate NPE in BayeuxServerImpl.freeze();
Anyone know how to properly use cometd with dropwizard?
In order to inject the BayeuxServer instance, CometD must have the instance of the service to inject to, in this case an instance of your class MyService.
Unfortunately, from the constructor (which I think you misnamed above, calling it OrderService) you are calling the initChannel() method, which tries to use the BayeuxServer field which is not yet injected because the constructor is still executing.
The solution is to defer your channel initialization to a different method annotated with #PostConstruct:
public class MyService
{
#Inject
private BayeuxServer bayeuxServer;
#Session
private LocalSession sender;
private final String _channelName;
private ServerChannel _channel;
public MyService()
{
_channelName = "/cometd/";
}
#PostConstruct
private void initChannel()
{
_channel = bayeuxServer.createChannelIfAbsent(_channelName).getReference();
}
}
The CometD API used is from CometD 2.7.0, which I recommend to use if you are on older CometD versions.
#Bean
public TimedRepository timedRepository(RealRepository repo) {
return new TimedRepository(repo, timer); // Adds some metrics
}
#Bean
public RealRepository realRepository(DataSource ds) {
return new RealRepository(ds); // The real jdbc implementation
}
In the old XML days I would configure the real repository as an anonymous inner bean. Is it possible to do something similar with the new Java configuration approach? Instantiating the real repository inside the timedRepository factory method is not an option because I want Spring to pick up on annotations on RealRepository.
The motivation is to avoid any other beans to get hold of the real repository implementation. I should also mention that both beans implement a Repository interface that'll be used by any beans depending on the repository (they should not have to know about TimedRepository or RealRepository.
I dont think theres an equivalent to inner or local beans when using java based configuration. I'd probably try to create the RealRepository in the TimedRepositories bean method as well by asking for all dependencies in the method signature. But if you really need to have spring to take care of the RealRepository dependencies you need to use the bean factory.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(loader=AnnotationConfigContextLoader.class)
public class ConfigTest {
#Autowired TimedRepository timedRepo;
#Test
public void testRepository() {
Assert.assertNotNull(timedRepo);
}
#Configuration
static class TimedRepositoryConfiguration {
#Autowired
private AutowireCapableBeanFactory beanFactory;
#Bean
public TimedRepository timedRepository() {
RealRepository realRepository = (RealRepository) beanFactory.createBean(RealRepository.class, AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR, true);
return new TimedRepository(realRepository);
}
public RealRepository realRepository() {
return new RealRepository();
}
}
static class RealRepository {
}
static class TimedRepository {
private RealRepository realRepo;
public TimedRepository(RealRepository r) {
this.realRepo = r;
}
}
}
You can just instantiate the beans manually:
public class BeanThatDependsOnRealRepository() {
private final Repository repository;
#Inject
public BeanThatDependsOnRealRepository(DataSource dataSource) {
this.repository = new RealRepository(dataSource);
}
}
This is essentially what an anonymous inner bean does in XML. You've just explicitly constructed it and obtained its dependencies from Spring in the constructor of the enclosing class.
Late answer, but this is possible in Spring Core 4+ (and possibly Spring Core 3) with some trickery.
While standard Spring semantics do not support inner bean creation using JavaConfig, the internal functionality around inner beans can be taken advantage of to produce the same results.
Inner beans are produced during property value resolution by the BeanDefinitionValueResolver (see BeanDefinitionValueResolver#resolveValueIfNecessary). The concept of "inner beans" within Spring is primarily enclosed within this value resolver (which is the only producer of inner beans) and within bean factories under the term "contained beans" (from parent class DefaultSingletonBeanRegistry).
We can trick Spring into producing additional inner beans by defining a property as a BeanDefinition, according to the resolution strategy presented in BeanDefinitionValueResolver:
#Configuration
public class MyConfiguration {
private static Logger logger = LoggerFactory.getLogger(MyConfiguration.class);
private RealRepository realRepository;
private Timer timer;
public MyConfiguration(#SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection") RealRepository realRepository, Timer timer) {
this.realRepository = realRepository;
this.timer = timer;
logger.info("Constructed MyConfiguration {}", this);
}
#Bean
public TimedRepository timedRepository() {
TimedRepository timedRepository = new TimedRepository(this.realRepository, this.timer);
logger.info("Created timed repo: {}", timedRepository);
return timedRepository;
}
public RealRepository realRepository(DataSource dataSource) {
RealRepository realRepository = new RealRepository(dataSource);
logger.info("Created real repo: {}", realRepository);
return realRepository;
}
#Override
public String toString() {
return "MyConfiguration{" +
"realRepository=" + realRepository +
", timer=" + timer +
'}';
}
}
#Component
public class InnerBeanInjectionBeanFactoryPostProcessor implements BeanDefinitionRegistryPostProcessor, PriorityOrdered {
#Override
public int getOrder() {
// Preempt execution of org.springframework.context.annotation.ConfigurationClassPostProcessor
return 0;
}
#Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
String[] beanDefinitionNameList = ((ConfigurableListableBeanFactory) registry).getBeanNamesForType(MyConfiguration.class, true, false);
assert beanDefinitionNameList.length == 1;
BeanDefinition configurationBeanDefinition = registry.getBeanDefinition(beanDefinitionNameList[0]);
BeanDefinition realRepositoryBeanDefinition = BeanDefinitionBuilder.genericBeanDefinition(MyConfiguration.class)
.setScope(BeanDefinition.SCOPE_SINGLETON)
.setFactoryMethod("realRepository")
.setAutowireMode(RootBeanDefinition.AUTOWIRE_CONSTRUCTOR)
.getBeanDefinition();
configurationBeanDefinition.getConstructorArgumentValues()
.addGenericArgumentValue(realRepositoryBeanDefinition);
}
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
// Do nothing
}
}
The obvious issue with this solution is that it requires manual processing through a BeanDefinitionRegistryPostProcessor, which is a lot of work for a small gain here. What I would suggest instead is the following:
Create a custom annotation (e.g., #InnerBean)
Attach this annotation to methods in #Configuration classes and candidate component classes where desired
Adapt the BeanDefinitionRegistryPostProcessor to scan classes for #InnerBean-annotated static methods (component-classes should be scanned in #postProcessBeanFactory and configuration classes in #postProcessBeanDefinitionRegistry)
Attach the bean definition to the containing bean definition's autowired constructor fields (or setter fields if that is your convention)
The following is an example:
#Target(ElementType.METHOD)
public #interface InnerBean {
}
#Configuration
public class MyConfiguration {
private static Logger logger = LoggerFactory.getLogger(MyConfiguration.class);
private RealRepository realRepository;
private Timer timer;
public MyConfiguration(#SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection") RealRepository realRepository, Timer timer) {
this.realRepository = realRepository;
this.timer = timer;
logger.info("Constructed MyConfiguration {}", this);
}
#Bean
public TimedRepository timedRepository() {
TimedRepository timedRepository = new TimedRepository(this.realRepository, this.timer);
logger.info("Created timed repo: {}", timedRepository);
return timedRepository;
}
#InnerBean
public static RealRepository realRepository(DataSource dataSource) {
RealRepository realRepository = new RealRepository(dataSource);
logger.info("Created real repo: {}", realRepository);
return realRepository;
}
#Override
public String toString() {
return "MyConfiguration{" +
"realRepository=" + realRepository +
", timer=" + timer +
'}';
}
}
#Component
public class InnerBeanInjectionBeanFactoryPostProcessor implements BeanDefinitionRegistryPostProcessor, PriorityOrdered {
private static Logger logger = LoggerFactory.getLogger(InnerBeanInjectionBeanFactoryPostProcessor.class);
private Set<BeanDefinition> processedBeanDefinitionSet = new HashSet<>();
#Override
public int getOrder() {
// Preempt execution of org.springframework.context.annotation.ConfigurationClassPostProcessor
return 0;
}
#Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
ConfigurableListableBeanFactory beanFactory = (ConfigurableListableBeanFactory) registry;
String[] configBeanDefinitionNames = beanFactory.getBeanNamesForAnnotation(Configuration.class);
Arrays.stream(configBeanDefinitionNames)
.map(beanFactory::getBeanDefinition)
.filter(this::isCandidateBean)
.peek(this.processedBeanDefinitionSet::add)
.forEach(this::autowireInnerBeans);
}
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
Arrays.stream(beanFactory.getBeanDefinitionNames())
.map(beanFactory::getBeanDefinition)
.filter(this::isCandidateBean)
.filter(beanDefinition -> !this.processedBeanDefinitionSet.contains(beanDefinition))
.forEach(this::autowireInnerBeans);
}
private boolean isCandidateBean(BeanDefinition beanDefinition) {
return beanDefinition.getBeanClassName() != null && beanDefinition.getBeanClassName().startsWith("com.example.demo.");
}
private void autowireInnerBeans(BeanDefinition beanDefinition) {
// Get #InnerBean methods
assert beanDefinition instanceof AnnotatedBeanDefinition;
AnnotatedBeanDefinition annotatedBeanDefinition = (AnnotatedBeanDefinition) beanDefinition;
Set<MethodMetadata> innerBeanMethods = annotatedBeanDefinition.getMetadata().getAnnotatedMethods(InnerBean.class.getName());
// Attach inner beans as constructor parameters
for (MethodMetadata method : innerBeanMethods) {
String innerBeanName = method.getMethodName();
if (!method.isStatic()) {
logger.error("#InnerBean definition [{}] is non-static. Inner beans must be defined using static factory methods.", innerBeanName);
continue;
}
BeanDefinition innerBeanDefinition = BeanDefinitionBuilder.genericBeanDefinition(beanDefinition.getBeanClassName())
.setScope(BeanDefinition.SCOPE_SINGLETON)
.setAutowireMode(RootBeanDefinition.AUTOWIRE_CONSTRUCTOR)
.setFactoryMethod(innerBeanName)
.getBeanDefinition();
beanDefinition.getConstructorArgumentValues()
.addGenericArgumentValue(new ConstructorArgumentValues.ValueHolder(innerBeanDefinition, method.getReturnTypeName(), method.getMethodName()));
}
}
}
There will be a few benefits and caveats of doing this. One large benefit is that the bean lifecycle will be managed by the Spring IoC container, meaning that lifecycle callbacks (such as #PostConstruct and #PreDestroy) will be called. The bean can be automatically managed according to the lifecycle of the parent. Caveats include that the beans cannot be injected as factory-method parameters (although with a bit of work you might able to fix this) and that AOP proxying will not be applied to these methods within #Configuration classes (i.e., realRepository() should never be called as it will not reference the singleton inner bean -- instead, the instance field should always be referenced). Further proxying (similar to ConfigurationClassEnhancer.BeanMethodInterceptor) would need to be added in order to apply this.