I have a Interface I and a Abstract Class A , I have My custom annotation MyAnnotation which should take parameter as subclass S of A, now while processing annotation I want to call method of concrete class S
public interface I{
void m1();
}
public abstract class A implements I {
public abstract void m1();
}
public #interface MyAnnotation {
public Class< ? extends A> ref();
public Class< ? super A> ref2();
}
public S extends A{
public void m1() {}
}
I am annotating method like
#MyAnnotation(ref= new XX() ) or #MyAnnotation(ref= XX.class )
#MyAnnotation(ref= new yy() ) or #MyAnnotation(ref= yy.class )
whichever works
//In spring aspect before processing I am getting method annotation and trying to call m1()
annotation.ref().m1() //Error
annotation.ref2().m1() //Error
You can't use new XX() in an annotation. Annotations parameters can use a very specific set of types:
primitive
String
Class
an Enum
another Annotation
an array of any of the above
See this answer.
So to accomplish what you're trying to accomplish, you'd have to use a class.
You would then have to use reflection to create an instance and invoke the method.
Class<?> clazz = annotation.ref();
I instance = (I) cls.getConstructor().newInstance();
instance.m1();
See this answer.
Your classes must all have no-argument constructors, else you'll only be able to instantiate some this way but not others (leading you to have to conditionally branch based on the class).
You can't do that simply like that. You need an instance of the class first.
If your A class is a Spring's bean, you can inject ApplicationContext and get the bean from there. Then you can call a method.
#Autowired
private ApplicationContext context;
void test(MyAnnotation annotation) {
A bean = context.getBean(annotation.ref());
bean.m1();
}
Related
I have a parent class called BaseService and I have other services that inherit from BaseService as they all need those methods to do their jobs. As always the methods of the superclass are available inside the subclass... However, when I use #Autowired DI to inject the subclass I am not able to use the methods defined by the parent class, I am only able to use what is defined separately in the subclass. How can I inject the subclass and have it properly instantiate the subclass such that the methods of the parent class are available to me?
Ex. Parent Class
#Service
public class BaseService{
public BooleanExpression combineBools(Predicate predicate, BooleanExpression bool){
BooleanExpression result = runupBool.and(predicate);
return result;
}
}
Ex. Child Class
#Service
public class EqServiceImpl extends BaseService implements EqService{
public EqServiceImpl(){
super();
}
#Override
public Iterable getAllData(Map<String, String> params, Predicate predicate) {
// Some Method Specific to Child Class
}
}
Ex. Controller
#RestController
public class EqController {
#Autowired
EqService eqService
...
}
If I wanted to access the method eqService.combineBools() inside the controller I am unable to. Why is this? How can I fix it?
As DarrenForsythe pointed out I am instantiating with EqService as the type so it would not have all of the methods of the BaseService since it does not extend that class, rather it is the EqServiceImpl that extends that class. Therefore I would need the type to be EqServiceImpl. Without making some other changes #Autowired is not the best choice for DI here.
I am new to Guice DI. And I would like to get my scenario clarified.
To put it simple, Is there any replacement of MapBinder through Guice #annotations?
My Scenario:
Interface A{}
Class A1 implements A{}
Class A2 implements A{}
I would like to Inject the implementation class of A as follows,
if(param = One) then Inject A1 to A
if(param = Two) then Inject A2 to A
I understand that the above could be done with MapBinder, but I would like to do it through annotations as follows,
Class A1 implements A
{
#Inject(param = One)
A1(){}
}
Class A2 implements A
{
#Inject(param = Two)
A2(){}
}
So making the class annotated with params could automatically picks and inject the class based on the parameter (One or Two).
Since #Inject cannot accept params, overriding #Inject would help in this scenario? if so, how do we do so?
Or Is this scenario could only be achieved through binding using MapBinder (The reason why I wouldn't want to use binder is that we would want to define the binding map of key value pair explicitly, but using annotations just simply annotate the implementation class with params - easier maintenance).
Thanks in advance.
To answer your follow-up questino, I believe what you are looking for are named injects. See this example:
public class GuiceNamedTest extends AbstractModule {
public static void main(String[] args) {
Injector i = Guice.createInjector(new GuiceNamedTest());
i.getInstance(InstaceOne.class);
i.getInstance(InstaceTwo.class);
}
#Override
protected void configure() {
Bean beanOne = new Bean();
beanOne.name = "beanOne";
Bean beanTwo = new Bean();
beanTwo.name = "beanTwo";
bind(Bean.class).annotatedWith(Names.named("one")).toInstance(beanOne);
bind(Bean.class).annotatedWith(Names.named("two")).toInstance(beanTwo);
bind(InstaceOne.class);
bind(InstaceTwo.class);
}
public static class Bean {
String name;
}
public static interface A {}
public static class InstaceOne implements A {
#javax.inject.Inject
public InstaceOne(#Named("one") Bean b1) {
System.out.println(b1.name);
}
}
public static class InstaceTwo implements A {
#javax.inject.Inject
public InstaceTwo(#Named("two") Bean b1) {
System.out.println(b1.name);
}
}
}
Here, I am using annotatedWith to name my guice-handled instances. One of them corresponds to the String "one" and the other to "two", analogue to your example.
I can then, in my implementations of A have specific injections of these beans using the #Named annotation.
The result when running above code is:
beanOne
beanTwo
As you can see, it injected the correct instance of my bean into the right implementation.
Hope that helps,
Artur
From the JLS, ยง9.6,
"By virtue of the AnnotationTypeDeclaration syntax, an annotation type declaration cannot be generic, and no extends clause is permitted.
"A consequence of the fact that an annotation type cannot explicitly declare a superclass or superinterface is that a subclass or subinterface of an annotation type is never itself an annotation type. Similarly, java.lang.annotation.Annotation is not itself an annotation type."
So, no, "overriding [sic]" will not help, because no extending type can be an annotation type.
I am trying to create an annotation that will allow me to wrap a (spring) bean with an instance of the supplied class. The interface has a type parameter which is (should not, if possible) not specified by the wrapping class. See the code below for an example of what I mean.
I managed to get a (compilation, haven't tried runtime yet) fix by making MyWrapperImpl implement the type parameter with super class of the class used by MyWrappedClass, however I would rather not specify it.
How can I keep the type parameter? In other words how can I keep MyWrapperImpl as generic as possible?
Annotation:
#Documented
#Target(ElementType.TYPE)
#Inherited
#Retention(RetentionPolicy.Runtime)
public #interface Wrap {
Class<? extends MyInterface<?>> classToWrapWith();
}
Interface:
public interface MyInterface<T> {
T getSomething();
}
A wrapper class:
public class MyWrapperImpl<T> implements MyInterface<T> {
private MyInterface<T> wrapped;
public T getSometing() {
// Do something special, such as:
System.out.println("Calling get something from wrapped object");
return wrapped.getSomething(); // MyWrapperImpl should "use" the type from the wrapped instance.
}
}
Annotated class:
// Attempt 1
#Wrap(classToWrapWith = MyWrapperImpl.class) // <-- Compile error "found class<MyWrapperImpl>, required class<? extends MyInterface<?>>"
// Attempt 2
#Wrap(classToWrapWith = MyWrapperImpl<T>.class) // <-- Compile error, cannot select from parameterized type.
public class MyWrappedClass implements MyInterface<SubObject> {
public SubObject getSomething() {
return new SubObject();
}
}
Wrapper class with a working fix (Where SuperObject is a parent class of SubObject, which is used in MyWrappedClass (see above)):
public class MyWrapperImpl<SuperObject> implements MyInterface<T> {
private MyInterface<SuperObject> wrapped;
public SuperObject getSometing() {
return wrapped.getSomething();
}
}
I am trying to do this, I have some "base" annotation
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.ANNOTATION_TYPE})
public #interface A
{
}
and I have annotaion B which is annotated by A
#A
#Retention(RetentionPolicy.RUNTIME)
#Target({ ElementType.METHOD })
public #interface B {
String value();
}
I want to have interface which behaves something like this, being sure that T is annotation which is annotated by A.
interface SomeInterface<T extends A>
{
void method(T argument);
}
So that I implement that something like this
public class Implementation implements SomeInterface<B>
{
public void method(B argument);
}
How to do that? When I use "T extends A" in SomeInterface, when I implement that, it says that B is not a valid substitue.
Thanks!
B is not a valid substitution for <T extends A> because B does not extend A.
Java does not include a way to require that a generic type parameter has a particular annotation.
If you can refactor SomeInterface to be a class instead of an interface, you could put a runtime check in the constructor:
protected SomeInterface(Class<T> classOfT) {
if(classOfT.getAnnotation(A.class) == null)
throw new RuntimeException("T must be annotated with #A");
}
Annotation inheritance is not possible in Java.
What you have written is simply an annotation annotated by another annotation, not an annotation extending another one.
If annotation inheritance would have been possible, I guess it would have been something like this:
public #interface B extends A {
String value();
}
But it just does not exist.
Check also this link and this link
I write JUnit tests for some Spring MVC Controllers. The initialization of the JUnit test is common for all my Controllers tests, so I wanted to create an abstract class that does this initialization.
Thus, I created the following code:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "classpath*:spring/applicationContext-test.xml", "classpath*:spring/spring-mvc-test.xml" })
#Transactional
public abstract class AbstractSpringMVCControllerTest<T> {
#Autowired
protected ApplicationContext applicationContext;
protected MockHttpServletRequest request;
protected MockHttpServletResponse response;
protected HandlerAdapter handlerAdapter;
protected T controller;
#SuppressWarnings("unchecked")
#Before
public void initContext() throws SecurityException, NoSuchFieldException {
request = new MockHttpServletRequest();
response = new MockHttpServletResponse();
handlerAdapter = applicationContext.getBean(AnnotationMethodHandlerAdapter.class);
// Does not work, the problem is here...
controller = applicationContext.getBean(T);
}
}
The idea is to create, for each controller I want to test a JUnit test class that extends my AbstractSpringMVCControllerTest. The type given in the extends declaration is the class of the Controller.
For example, if I want to test my AccountController, I will create the AccountControllerTest class like that:
public class AccountControllerTest extends AbstractSpringMVCControllerTest<AccountController> {
#Test
public void list_accounts() throws Exception {
request.setRequestURI("/account/list.html");
ModelAndView mav = handlerAdapter.handle(request, response, controller);
...
}
}
My problem is located in the last line of the initContext() method of the abstract class. This abstract class declares the controller object as a T object, but how can say to the Spring Application Context to return the bean of type T?
I've tried something like that:
Class<?> controllerClass = this.getClass().getSuperclass().getDeclaredField("controller").getType();
controller = (T) applicationContext.getBean(controllerClass);
but controllerClass returns the java.lang.Object.class class, not AccountController.class.
Of course, I can create a public abstract Class<?> getControllerClass(); method, which will be overriden by each JUnit Controller test class, but I prefer to avoid this solution.
So, any idea?
This is possible if your subclasses of AbstractSpringMVCControllerTest bind T at compile time. That is, you have something like
public class DerpControllerTest extends AbstractSpringMVCControllerTest<DerpController> { }
rather than
public class AnyControllerTest<T> extends AbstractSpringMVCControllerTest<T> { }
I'm guessing you probably have the former. In this case, the type of T is erased from the Class object for AbstractSpringMVCControllerTest at runtime, but the Class object for DerpControllerTest does provide a way to know what T is, since it bound T at compile time.
The following classes demonstrate how to access the type of T:
Super.java
import java.lang.reflect.ParameterizedType;
public abstract class Super<T> {
protected T object;
#SuppressWarnings("unchecked")
public Class<T> getObjectType() {
// This only works if the subclass directly subclasses this class
return (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
}
}
Sub.java
public class Sub extends Super<Double> {
}
Test.java
public class Test {
public static void main(String...args) {
Sub s = new Sub();
System.out.println(s.getObjectType()); // prints "Class java.lang.Double"
}
}
This is different from the type erasure we normally see. With type erasure, we don't know the parameter of the current class (the one you get with getClass()), but you can get those in super class / super interface (those you get with getGenericSuperxxxxx()) because this is part of the type declaration.
This won't give your the type of controller field, but I hope this is enough for your purpose.
Code:
public class A<P> {
}
import java.lang.reflect.ParameterizedType;
public class B extends A<String> {
public static void main(String[] arg) {
System.out.println(
((ParameterizedType)B.class.getGenericSuperclass()).getActualTypeArguments()[0]);
}
}
Output:
class java.lang.String
In your case, it would be
Class controllerClass = (Class)( ((ParameterizedType)getClass().getGenericSuperclass()).getActualTypeArguments()[0]);
Something to notes:
If the class B is also parameterized like this:
public class B<X> extends A<X> {}
This won't work. Or if you have another class extends B, it will have problem too. I won't go into all those cases, but you should get the idea.
You can't because at runtime, due to ERASURE, the JVM cannot know the class of your "controller" attribute. It is considered as Object...