I would like to achieve autoconfiguration for a RestRepositoryResource (and some additional standard functionality) for an entity. I am trying to achieve it through an annotation on a #Configuration or #SpringBootApplication annotated class.
something like this:
#EnableRestRepo(single="foo", collection="foos",entity=Foo.class, id=String.class)
#SpringBootApplication
public class App{
public void main(String[] args){
SpringApplication.run(App.class,args);
}
}
#Entity
public class Foo{
String id;
String bar;
... getters & setters
}
this should then setup a (or functionality similar too, I'm fine with creating my own endpoints if need be) #RestRepositoryResource like this:
#RestRepositoryResource(itemResourceRel = "foo", collectionResourceRel = "foos")
public interface Repo extends CrudRepository<Foo,String> {
#RestResource(rel = "foo")
Foo findOneById(#Param("id") String id);
}
The goal here is to reduce some boiler plate on configuring some basic functionality. Obviously this example will be extended with some more autoconfiguration stuff, but that should work in a similar way.
The question is not so much about the RestRepositoryResource as it is about autoconfiguration with annotations which require arguments and generic type classes. I would not mind spending some time implementing this, however I have no idea where to start.
Is something like this even possible and if so, how?
Not sure if I understood you 100 %, but the example code here runs fine and creates beans runtime based on an annotation. Annotation also has som metadata.
The generic interface, will be proxied later:
public interface GenericRepository<T extends GenericType, Long> extends JpaRepository<GenericType, Long> {
}
Annotation to put on different entities:
#Target(ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
#OverrideAutoConfiguration(enabled = false)
#ImportAutoConfiguration
#Import({RestResourceAutoConfiguration.class})
public #interface EnableRestRepo {
Class<?> entity();
String id();
}
A configuration class that can register beans in runtime:
#Configuration
#AutoConfigureAfter(value = WebMvcAutoConfiguration.class)
#ConditionalOnClass({CrudRepository.class})
public class RestResourceAutoConfiguration implements BeanDefinitionRegistryPostProcessor {
#Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
Reflections reflections = new Reflections("jav");
Set<Class<?>> annotated = reflections.getTypesAnnotatedWith(EnableRestRepo.class);
for (Class<?> c : annotated) {
EnableRestRepo declaredAnnotation = c.getDeclaredAnnotation(EnableRestRepo.class);
Class<?> entity = declaredAnnotation.entity();
String id = declaredAnnotation.id();
Supplier<GenericRepository> genericRepositorySupplier = () -> (GenericRepository) Proxy.newProxyInstance( // register a proxy of the generic type in spring context
c.getClassLoader(),
new Class[]{GenericRepository.class},
new MyInvocationHandler(entity));
beanDefinitionRegistry.registerBeanDefinition(id + "-" + UUID.randomUUID().toString(),
new RootBeanDefinition(GenericRepository.class, genericRepositorySupplier)
);
}
}
spring.factories under META-INF
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
jav.RestResourceAutoConfiguration
Related
I am pretty new to Spring Boot and its flavor of AOP, but not new to programming in other languages and AOP frameworks. This one challenge I am not sure how to solve.
I have a simple metadata decorator:
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
public #interface GreetingsMeta {
public float version() default 0;
public String name() default "";
}
It works just fine with dependency injection:
public GreetingController(List<IGreetingService> greetings) throws Exception {
this.greetings = new HashMap<>();
greetings.forEach(m -> {
Class<?> clazz = m.getClass();
if (clazz.isAnnotationPresent(GreetingsMeta.class)) {
GreetingsMeta[] s = clazz.getAnnotationsByType(GreetingsMeta.class);
this.greetings.put(s[0].name(), m);
}
});
}
Until I applied a standard logging aspect:
#Aspect
#Component
public class LoggingAspect {
#Around("execution(* com.firm..*(..)))")
public Object profileAllMethods(ProceedingJoinPoint joinPoint) throws Throwable {
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
String methodName = methodSignature.getName();
final StopWatch stopWatch = new StopWatch();
stopWatch.start();
Object result = joinPoint.proceed();
stopWatch.stop();
LogManager.getLogger(methodSignature.getDeclaringType())
.info(methodName + " " + (stopWatch.getTotalTimeSeconds() * 1000) + " µs");
return result;
}
}
Then the list of annotationsData becomes empty, even the #Component annotation is gone.
Sample meta-decorated class:
#Component
#GreetingsMeta(name = "Default", version = 1.0f)
public class DefaultGreetingsService implements IGreetingService {
#Override
public String message(String content) {
return "Hello, " + content;
}
}
How should I troubleshoot?
How do I prevent Spring Boot AOP from removing type annotations?
Spring Boot does not remove anything, but for Spring AOP is uses dynamic proxies generated during runtime, i.e. subclasses or interface implementations with event hooks (joinpoints) for aspect advice code wired in via pointcuts. By default, annotations are not inherited, so this is just a JVM feature.
There is one exception for subclasses inheriting annotations from parent classes: You can add the meta annotation #Inherited to your own annotation class GreetingsMeta. The effect will be that if you annotate any class with it, all subclasses (also dynamic proxies created by Spring AOP) will inherit the annotation and your original code should run as expected.
So in this case there is no need to use AnnotationUtils as suggested by JC Carrillo. His approach works too, of course. It is just more complicated because AnnotationUtils uses a lot of reflection magic and lots of helper classes internally in order to compute results. Thus, I would only use AnnotationUtils in cases where you don't directly annotate a class but e.g. methods or interfaces because #Inherited has no effect on them as documented. Or if you rely on a hierarchy of Spring (or own) meta annotations (annotations on annotations) and you need to get information from them all merged into one, AnnotationUtils or MergedAnnotations are appropriate.
You may want to look into AnnotationUtils
Method method = methodSignature.getMethod();
GreetingsMeta greetingsMeta = AnnotationUtils.findAnnotation(method, GreetingsMeta.class);
My current situation:
I want to inject the following class into my application:
public interface IConfigAccessor<T extends IConfig> {
...
}
ConfigAccessors are a proxy-objects, created dynamically at runtime. The creation of these object works as follows:
public class ConfigFactory implements IConfigFactory {
private final IConfigUpdater updater;
#Inject
public ConfigFactory(IConfigUpdater updater) {
this.updater = updater;
}
#Override
public <T extends IConfig> IConfigAccessor<T> register(final String configKey, final Class<T> configClass) {
ConfigCache<T> configCache = new ConfigCache<>(new SomeOtherThings(), configKey, configClass);
updater.register(configCache);
return new ConfigAccessor<>(configCache, configKey, configClass);
}
}
As you can see, to create these objects, I need to inject the ConfigUpdater and other depdencies. This means, that guice needs to be fully configured already.
To get the instance out of Guice, I use the following code:
IConfigFactory configClient = injector.getInstance(IConfigFactory.class);
IConfigAccessor<ConcreteConfig> accessor = configClient.register("key", ConcreteConfig.class)
How I want to inject them via Guice:
Currently, I can get the requried objects, but I have to manually pass them around in my application.
Instead, what I want to have is the following:
public class SomeClass {
#Inject
public SomeClass(#Config(configKey="key") IConfigAccessor<ConcreteConfig> accessor) {
// hurray!
}
}
What's the correct approach/technology to get this working?
After a lot of research, I'm feeling a bit lost on how to approach this topic. There are a lot of different things Guice offers, including simple Providers, custom Listeners which scan classes and identify custom annotations, FactoryModuleBuilders and more.
My problem is quite specific, and I'm not sure which of these things to use and how to get it working. I'm not even sure if this is even possible with Guice?
Edit: What I have so far
I have the following annotation which I want to use inside constructor paramters:
#Target({ ElementType.FIELD, ElementType.PARAMETER })
#Retention(RetentionPolicy.RUNTIME)
public #interface InjectConfig {
String configKey();
}
Inside the module, I can bind a provider to IConfigAccessor (with the above annotation) as such:
bind(IConfigAccessor.class).annotatedWith(InjectConfig.class)
.toProvider(new ConfigProvider<>());
However, there are two problems whith this:
The provider cannot provide IConfigAccessor. To create such an instance, the provider would need an IConfigUpdater, but since I use 'new' for the provider, I can't inject it.
Inside the provider, there is no way to find out about the configKey used in the Annotation.
Second approach:
Let's assume that I already know all configurations and configKeys I want to inject during startup. In this case, I could loop over all possible configKeys and have the following binding:
String configKey = "some key";
final Class<? extends IConfig> configClass =...;
bind(IConfigAccessor.class).annotatedWith(Names.named(configKey))
.toProvider(new ConfigProvider<>(configKey, configClass));
However, problem (1) still resides: The provider cannot get an IConfigUpdater instance.
The main problem here is that you cannot use the value of the annotation in the injection. There is another question which covers this part:
Guice inject based on annotation value
Instead of binding a provider instance, you should bind the provider class, and get the class by injecting a typeliteral.
That way, your config factory can look like that:
public class ConfigFactory<T extends IConfig> implements IConfigFactory {
#Inject private final IConfigUpdater updater;
#Inject private TypeLiteral<T> type;
#Override
public IConfigAccessor<T> register(final String configKey) {
Class<T> configClass = (Class<T>)type.getRawType();
ConfigCache<T> configCache = new ConfigCache<>(new SomeOtherThings(), configKey, configClass);
updater.register(configCache);
return new ConfigAccessor<>(configCache, configKey, configClass);
}
}
And then SomeClass:
public class SomeClass {
#Inject
public SomeClass(ConfigFactory<ConcreteConfig> accessor) {
ConcreteConfig config = accessor.register("key");
}
}
Since SomeClass needs to know "key" anyway, this is not too much a change information-wise. The downside is that the SomeClass API now gets a factory instead of the concrete config.
[EDIT]
And here is someone who actually did inject annotated values using custom injection.
I have a class annotated with #Component and #Service. The usual way of getting the instance of this class from the beans container is using #Reference:
#Reference private MyClass myclass;
How could I retrieve the instance using a string key as we do with ApplicationContext in Spring Web applications? I've tried with ComponentContext.locateService but I don't know if this is correct nor which key value I have to use.
MyClass myclass = (MyClass)context.locateService("????"); //Which is the way?
I edit my question, I'll try to explain better.
I have a class like this:
#Component
#Service
class MySvcImpl implements MySvc { ... }
injected this way:
class Main {
#Reference private MySvc svc;
void method1(){
svc.doXXX();
}
}
In this case I have a single class. I'd want to have a bunch of component subclasses (MySvcSubX) so that I can use any of them according to a parameter. Allegedly, I'd not need the #Reference line anymore.
#Component
#Service
class MySvcSub1Impl extends MySvcImpl implements MySvc { ...}
//The same with MySvcSub2Impl, 3, and so forth
And then in my Main class:
void method1(String key){
MySvc svc = callToLocateBeanById(key);
}
I should not need to add a reference for each of the subclasses to be able to locate them by their name.
Here you go:
ApplicationContext context = new ClassPathXmlApplicationContext(
"classpath*:mySpringConfig.xml");
MyClass myClassBean = (MyClass) context.getBean("myClass");
You can publish a #Component with a property :
#Component(property="type=xxx")
public class MyComponent implements MySvc {
}
and then query the osgi registry with a filter on this property :
BundleContext bc = ..;
Collection<ServiceReference<MySvc>> ref = bc.getServiceReferences(MySvc.class, "(type=xxx)")
// check cardinality, etc
MySvc svc = bc.getService(ref);
// use svc
bc.ungetService(ref);
If you know the key you are looking for at compile time (or though some configuration), you can inject a reference with a target :
#Reference(target = "(type=xxx)")
private MySvc component;
Old answer
You should use the name of the reference. This name can be specified in #Reference, but the default is generated from the name of the property, or the bind/unbind method (see the javadoc for this annotation)
In your example, it should be :
MyClass myclass = (MyClass)context.locateService("myClass");
If you're not sure, you can look at the generated xml from your component, you'll see the generated name.
I've read a lot about getting generic type at runtime and I've understood that to prevent full type erasure and get generic type without giving it to constructor I can use an anonymous class plus an utility method, i.e.
interface Generic<T> {
public Class<T> getGenericType();
}
#Component
class GenericImpl<T> extends AbstractGenericImpl<T> {
}
abstract class AbstractGenericImpl<T> implements Generic<T> {
protected Class<T> klass;
#SuppressWarnings("unchecked")
public Class<T> getGenericType() {
if (klass == null) {
// this is a spring utility method
klass = (Class<T>) GenericTypeResolver.resolveTypeArgument(getClass(), AbstractGenericImpl.class);
}
return klass;
}
}
Now using the previous class hierarchy I can have a working getGenericType method if and only if I instantiate a Generic<Anything> using an anonymous class. In fact in this test only the first two assertions are working:
#Test
public void testGeneric() throws Exception {
Generic<String> anonymous = new AbstractGenericImpl<String>() {};
Generic<String> anonymous2 = new GenericImpl<String>() {};
Generic<String> concrete = new GenericImpl<String>();
// assertion
assertThat("Anonymous of abstract class", anonymous.getGenericType(), equalTo(String.class));
assertThat("Anonymous of concrete subclass", anonymous2.getGenericType(), equalTo(String.class));
assertThat("With non anonymous class it fails", concrete.getGenericType(), equalTo(String.class));
}
The third one is failing with Expected: <class java.lang.String> but: was <class java.lang.Object>
Now I'd like to use the Generic class with spring #Autowired annotation i.e.
#Autowired Generic<String> auto;
#Test
public void testAutowiring() {
assertThat(auto, instanceOf(Generic.class));
assertThat(auto.getGenericType(), equalTo(String.class));
}
but the second assertion fails with the same error as above (Object instead of String), because spring container internally instantiate it with new GenericImpl<String>()
I've already tried to make constructor of GenericImpl<T> protected and also to declare GenericImpl<String> itself abstract but in both cases spring fail with a Cannot instantiate bean exception.
Is there any simple way to tell spring to instantiate classes using anonymous classes?
Additional details
The final class will convert a json stream into a POJO with Jackson and the Jackson library needs the Class<T> field to unmarshal objects.
// here I convert json stream to a POJO and I need the generic type
mapper.readValue(hit.source(), getGenericType());
Since I have multiple POJO classes to convert from to JSON I've implemented all the logic in a common class with generics called Retriever. At the end I'll have one Retriever for each POJO and often those retrievers are autowired in other classes.
#Autowired Retriever<Artifact> retriever;
Currently I've a constructor in Retriever which takes a Class<T> parameter and use it later to perform conversion. In the spring context I've this for autowiring
<!-- Since retriever has a Class<T> constructor this is the only way I found to resolve its dependency -->
<bean id="artifactRetriever" class="a.b.c.RetrieverImpl">
<constructor-arg value="a.b.c.Artifact"/>
</bean>
and I need one of this for each POJO for which I need conversion. This approach works but it's a little verbose and it clutters the application context with useless lines. So I was looking for a way to get rid of all this noise in application context.
It's not possible to create and instantiate anonymous classes in-place with Spring, not with XML configuration (since it needs class name, and you don't have one).
Ok, final solution for my use case will use the approach described in this answer. It would be better because it will be possible to track usages and I'll get rid of every problem I'm having with the current approach.
In that way I can do the following
#Component
public class ArtifactImpl extends AbstractGenericImpl<Artifact> {
}
#Component
public class MaterialImpl extends AbstractGenericImpl<Material> {
}
#Component
class Usage {
#Autowired ArtifactImpl foo;
#Autowired MaterialImpl bar;
}
In this way everything is checked at compile time and I got rid of Class<T> constructor in fact I have autowiring in place (without #Qualifier) and the following test is working:
#RunWith(SpringJUnit4ClassRunner.class)
public class AutowiringTest {
#Autowired Usage test;
public void testAutowiring() {
assertThat(test.foo.getGenericType(), equalTo(Artifact.class));
assertThat(test.bar.getGenericType(), equalTo(Material.class));
}
}
Original answer
Ok, I've found out that what I'm asking will be useless because autowiring happens at runtime and so having two autowired object with different objects will lead to spring errors, i.e. this won't work:
#Configuration
class RetrieverProvider {
#Bean
Retriever<Artifact> getArtifact() {
return new RetrieverImpl<Artifact>() {};
}
#Bean
Retriever<Material> getMaterial() {
return new RetrieverImpl<Material>() {};
}
}
class InjectedAttempt {
// at injection time, i.e. runtime, type erasure prevent spring to distinguish
#Autowired Retriever<Artifact> foo; // this type
#Autowired Retriever<Material> bar; // from this type
// so it cannot perform injection by type
}
The only way to get that working is to use qualifiers in this way, but I don't like this approach, so I'll remain with xml configuration and constructor arguments.
#Configuration
class RetrieverProvider {
#Bean #Qualifier("artifact") Retriever<Artifact> getArtifact() {
return new RetrieverImpl<Artifact>() {};
}
#Bean #Qualifier("material")
Retriever<Material> getMaterial() {
return new RetrieverImpl<Material>() {};
}
}
class Injected {
#Autowired #Qualifier("artifact") Retriever<Artifact> foo;
#Autowired #Qualifier("material") Retriever<Material> bar;
}
As a side note guice has support for generic injections, maybe spring has something similar.
I have some code that uses these spring annotations:
org.springframework.jmx.export.annotation.ManagedAttribute;
org.springframework.jmx.export.annotation.ManagedOperation;
org.springframework.jmx.export.annotation.ManagedOperationParameter;
org.springframework.jmx.export.annotation.ManagedOperationParameters;
org.springframework.jmx.export.annotation.ManagedResource;
I want to generate some documentation (even just javadocs) using the comments in the annotations, for example consider the following method?
#ManagedOperation(description="Does foo to bar")
#ManagedOperationParameters({
#ManagedOperationParameter(name = "bar", description = "The bar you want to foo.")})
public long fooBar( Bar bar) throws Exception {
...
}
Is there some way I can automatically generate docs for this, or will I have to duplicate all the annotation strings in javadoc in addition to it?
First, create a custom AnnotationMbeanExporter with a public method that delegates to getRegisteredObjectNames(). Use this as your mbeanExporter.
For example:
#Component
// This is a copy of the AnnotationMBeanExporter with a public version of getRegisteredObjectNames()
public class AnnotationMBeanExporter extends MBeanExporter {
#Autowired
MBeanServer mbeanServer;
AnnotationJmxAttributeSource annotationSource = new AnnotationJmxAttributeSource();
AnnotationMBeanExporter() {
setServer(mbeanServer);
setNamingStrategy(new MetadataNamingStrategy(annotationSource));
setAssembler(new MetadataMBeanInfoAssembler(annotationSource));
setAutodetectMode(MBeanExporter.AUTODETECT_ALL);
}
public ObjectName[] getExportedObjectNames() {
return getRegisteredObjectNames();
}
}
Then for your report, iterate over the object names returned from getExportedObjectNames() and get the relevant metadata for each JMX bean.
For example:
for (ObjectName objectName: mbeanExporter.getExportedObjectNames()) {
MBeanInfo mbeanInfo = mbeanServer.getMBeanInfo(objectName);
MBeanOperationInfo[] operations = mbeanInfo.getOperations();
// etc.
}