We are trying to use reflection to extract the exact mapped values to our methods. The code structure in the target module consists of multiple classes as follows -
#Controller
#RequestMapping(value = "/questions")
public class XYZController {
#RequestMapping(value = "/ask")
public boolean myMethod1() {..}
#RequestMapping(value = "/{questionNo.}/{questionTitle}")
public MyReturnObject myMethod2(){..}
}
What we are trying to grep here is the list of endpoints like /questions/ask and /questions/{questionNo.}/{questionTitle} for which the code we tried to execute filters all such classes based on the #Controller annotation and at the same time we are able to get the list of all the endpoints separately. The code we have tried so far is -
public class ReflectApi {
public static void main(String[] args) {
ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(true);
final List<String> filteredClasses = new ArrayList<>();
scanner.addIncludeFilter(new AnnotationTypeFilter(Controller.class));
Set<BeanDefinition> filteredPackage = scanner.findCandidateComponents("com.package.test");
// gives me a list of all the filtered classes
filteredPackage.stream().forEach(beanDefinition -> filteredClasses.add(beanDefinition.getBeanClassName()));
// call to get the annotation details
filteredClasses.stream().forEach(filteredClass -> getEndPoints(filteredClass));
}
public static void getEndPoints(String controllerClassName) {
try {
Class clazz = Class.forName(controllerClassName);
Annotation classAnnotation = clazz.getDeclaredAnnotation(RequestMapping.class);
if (classAnnotation != null) {
RequestMapping mappedValue = (RequestMapping) clazz.getAnnotation(RequestMapping.class);
System.out.println("Controller Mapped -> " + mappedValue.value()[0]); //This gives me the value for class like "/questions"
runAllAnnotatedWith(RequestMapping.class);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
// followed from a SO reference
public static void runAllAnnotatedWith(Class<? extends Annotation> annotation) {
Reflections reflections = new Reflections(new ConfigurationBuilder().setUrls(ClasspathHelper.forJavaClassPath())
.setScanners(new MethodAnnotationsScanner()));
Set<Method> methods = reflections.getMethodsAnnotatedWith(annotation);
methods.stream().forEach(m -> {
if (m != null) {
RequestMapping mappedValue = m.getAnnotation(RequestMapping.class); //this is listing out all the #RequestMapping annotations like "/questions" , "/ask" etc.
System.out.println(mappedValue.value()[0]);
}
});
}
}
But the missing part is the concatenation of the class to method RequestMapping value here.
How do we loop inside a class to search annotations only from its methods?
Or is there any simpler way of doing this from what we are using?
References used - Scanning Java annotations at runtime && How to run all methods with a given annotation?
Aren't endpoints methods in the end?
So, I think what you need to implement is:
=> Retrieve all declared methods of each and any class that has #RequestMapping and #Controller annotations.
=> Iterate the annotations on each of those Method objects to check if they contain #RequestMapping
OR
=> Iterate through all the Methods for each class in Step 1 and find the annotated one's using method.getDeclaredAnnotation(RequestMapping.class) and skip Step 3
=> Remember that combination of class and method for your mapping
=> Do some error handling, in case you don't find any annotated method in there.
Related
I want implement strategy design pattern in spring boot application. I create BeanPostProcessor for construct strategy resolver:
#Component
public class HandlerInAnnotationBeanPostProcessor implements BeanPostProcessor {
private final UnpHandlersResolver unpHandlersResolver;
public HandlerInAnnotationBeanPostProcessor(UnpHandlersResolver unpHandlersResolver) {
this.unpHandlersResolver = unpHandlersResolver;
}
#Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
Annotation[] annotations = bean.getClass().getAnnotations();
for (Annotation annotation : annotations) {
if (annotation instanceof HandlerIn) {
if (bean.getClass() != UnpHandler.class)
throw new RuntimeException("Not UnpHandler bean annotated by HandlerIn");
SmevMessageType[] type = ((HandlerIn) annotation).type();
for (SmevMessageType smevMessageType : type) {
unpHandlersResolver.setHandler(smevMessageType, (UnpHandler) bean);
}
}
}
return bean;
}
}
And I create resolver:
#Slf4j
#Component
public class UnpHandlersResolverImpl implements UnpHandlersResolver {
private Map<SmevMessageType, UnpHandler> map = new HashMap<>();
#Override
public void setHandler(SmevMessageType messageType, UnpHandler unpHandler) {
map.put(messageType, unpHandler);
}
#Override
public UnpHandler getUnpHandler(SmevMessageType type) {
UnpHandler sendRequestHandler = map.get(type);
if (sendRequestHandler == null)
throw new IllegalArgumentException("Invalid SendRequestHandler type: " + type);
return sendRequestHandler;
}
}
My BeanPostProcessor scan all beans with annotation HandlerIn and add to resolver's mup. I think it's wrong to do that:
unpHandlersResolver.setHandler(smevMessageType, (UnpHandler) bean);
But I not understand how can I add find beans to resolver. Before this implementation I faind beans in #Postconstruct method of resolver like:
context.getBeansWithAnnotation(HandlerIn.class);
But in this solution I have context in resolver and I think is bad.
Tell me how to properly implement what I want? In short, I want to have a set of classes that implement different behaviors. And the class that controls them. Give the class a parameter so that he chooses the right strategy and gives it to me. Like this:
Handler handler = handlersResolver.getHandler(messageType);
Result result = handler.somthing(param);
I'm going to try to make a simple example.
Interface Greeting {
void sayHello();
String getSupportedLanguage();
}
Then you have X number of implementations and you can loop through them in your "resolver"'s constructor to build the map. (I've seen this called a Proxy or a Decorator in code though, i.e. GreetingProxy or GreetingDecorator)
#Service
public GreetingResolver {
private Map<String, Greeting> languageToGreetingMap = new HashMap<>();
#Autowired
public GreetingResolver(List<Greeting> greetings) {
for (Greeting greeting : greetings) {
languageToGreetingMap.put(greeting.getSupportedLanguage(), greeting);
}
}
public void sayGreetingForLanguage(String language) {
languageToGreetingMap.get(language).sayHello();
}
}
This is a basic example of how one can do the strategy pattern in Spring. Every interface implementation of "Greeting" only knows about itself and what it can support. We then autowire all implementations in a list and loop through to create the map once and then during runtime only the relevant entry from the map in retrieved and used.
Note: this was typed "free hand" directly in the web page so please forgive any typos in the code.
An image taken from a book which I am going through,
The caption says it all. Please suggest or give me something as to what happens behind the scenes.
For example, how does #NotNull in Hibernate Bean Validation API works?
I know that through Reflection API, we can do something like this,
class Meta {
// Annotate a method.
#MyAnno(str = "Annotation Example", val = 100)
public static void myMeth() {
Meta ob = new Meta();
// Obtain the annotation for this method
// and display the values of the members.
try {
// First, get a Class object that represents
// this class.
Class c = ob.getClass();
// Now, get a Method object that represents
// this method.
Method m = c.getMethod("myMeth");
// Next, get the annotation for this class.
MyAnno anno = m.getAnnotation(MyAnno.class);
// Finally, display the values.
System.out.println(anno.str() + " " + anno.val());
} catch (NoSuchMethodException exc) {
System.out.println("Method Not Found.");
}
}
public static void main(String args[]) {
myMeth();
}
}
Annotations don't have any implemented code and actually don't do anything themself.
To make them "work", there should be some kind of annotation processor (initializer, loader or any class that works with annotated objects). This annotation processor checks annotation objects annotations and changes the way it is handled.
For example Spring annotation processor, when initializing an object, looks for #Autowired fields, to fill autowired fields.
Same goes for Hibernates #NotNull. it doesn't do anything actually. However, Hibernate, when persisting your object, checks if there should be something there.
I wish to implement dynamically changeable menu (updating whenever annotated method or controller added) for my Spring MVC application.
What i want is to introduce new annotation (#RequestMenuMapping) which will go to #Controller beans and their methods (just like #RequestMapping works).
Heres is what i want, User class, producing menu like
Users
Index | List | Signup | Login
with following code:
#Controller
#RequestMapping("user")
#RequestMenuMapping("Users")
public class User {
#RequestMapping("")
#RequestMenuMapping("Index")
public String index(/* no model here - just show almost static page (yet with JSP checks for authority)*/) {
return "user/index.tile";
}
#RequestMapping("list")
#RequestMenuMapping("List")
public String list(Model model) {
model.addAttribute("userList",/* get userlist from DAO/Service */);
return "user/list.tile";
}
#RequestMapping("signup")
#RequestMenuMapping("Signup")
public String signup(Model model) {
model.addAttribute("user",/* create new UserModel instance to be populated by user via html form */);
return "user/signup.tile";
}
#RequestMapping("login")
#RequestMenuMapping("Login")
public String login(Model model) {
model.addAttribute("userCreds",/* create new UserCreds instance to be populated via html form with login and pssword*/);
return "user/login.tile";
}
}
I think that Spring AOP may help me to pointcut methods with #RequestMenuMapping annotation and via #AfterReturning add something representing web-site menu to model.
But this raises two questions:
How do i get Model instance in #AfterReturning advice method in case it is missing in adviced method (as in .index())?
How do i get all methods (as in java reflection Method) and classes (as in java reflection Class) annotated with #RequestMenuMapping in order to build complete menu index?
I think a better soultion would be a bean post processor to scan all controller classes for the #RequestMenuMapping and a HandlerInterceptor to add the menu items to every model map.
InterceptorDemo:
#Aspect
#Component
public class InterceptorDemo {
#Pointcut("#annotation(org.springframework.web.bind.annotation.RequestMapping)")
public void requestMapping() {
}
#Pointcut("#annotation(you.package.RequestMenuMapping)")
public void requestMenuMapping() {
}
#AfterReturning("requestMapping() && equestMenuMapping()")
public void checkServer(JoinPoint joinPoint,Object returnObj) throws Throwable {
Object[] args = joinPoint.getArgs();
Model m = (Model)args[0];
// use joinPoint get class or methd...
}
}
If you want to intercept Contoller with you own, you can wirte another pointcut and ProceedingJoinPoint object can get what you want.
Q1:
ModelAndView object create at org.springframework.web.servlet.DispatcherServlet.doDispatch()
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
// Do we need view name translation?
if (mv != null && !mv.hasView()) {
mv.setViewName(getDefaultViewName(request));
}
So, you can intercept handle method after returing or override the method.
Q2:As far as i know, there are two ways getting annotation methods.
1.Use AOP:
You can declare a pointcut like this:
#Pointcut("#annotation(you.package.RequestMenuMapping)")
public void requestMenuMappingPountcut() {
}
2.Use reflection.
Class clazz = Class.forName(classStr);
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
if (method.isAnnotationPresent(RequestMapping.class)
&& method.isAnnotationPresent(RequestMenuMapping.class)) {
// do something
}
}
I've got a fairly standard Spring webapp, and I have a number of custom annotations that I would like to use to denote the requirements and constraints applied to a given web-service method. For instance, I might apply an #RequiresLogin annotation to any method that requires a valid user session, and #RequiresParameters(paramNames = {"name", "email"}) on a method that requires that "name" and "email" be set, and so on.
In support of this I implemented an ad-hoc utility for validating a method's annotated constraints at runtime, which basically followed a pattern of:
Map<Class<? extends Annotation>, Annotation> annotations = mergeConstraintsFromClassAndMethod(serviceClass, serviceMethod);
if (annotations.containsKey(AnnotationType1.class)) {
AnnotationType1 annotation = (AnnotationType1)annotations.get(AnnotationType1.class);
//do validation appropriate to 'AnnotationType1'
}
if (annotations.containsKey(AnnotationType2.class)) {
AnnotationType2 annotation = (AnnotationType2)annotations.get(AnnotationType2.class);
//do validation appropriate to 'AnnotationType2'
}
//...
This works fine, but has become a bit unwieldy as I have added additional annotations. I'd like to replace it with something a bit more maintainable. Ideally I'd like to be able to do:
List<ValidatableAnnotation> annotations = mergeConstraintsFromClassAndMethod(serviceClass, serviceMethod);
for (ValidatableAnnotation annotation : annotations) {
annotation.validate(request);
}
But I'm pretty sure that is not possible since annotations themselves cannot contain executable code and since the compiler will not let me extend java.lang.annotation.Annotation (not that I'd know how to go about allowing executable code to be contained in an annotation even if the compiler let me try).
What annotations can contain, however, is a nested inner class, and that inner class can do anything that a normal Java class can do. So what I've come up with based upon that and in the interest of keeping my validation code as closely associated with the annotation being validated as possible is:
public interface AnnotationProcessor {
public boolean processRequest(Annotation theAnnotation, HttpServletRequest request);
}
And then the annotations can be implemented like:
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.METHOD, ElementType.TYPE})
public #interface RequiresLogin {
public static class Processor implements AnnotationProcessor {
#Override
public boolean processRequest(Annotation theAnnotation, HttpServletRequest request) {
if (! (theAnnotation instanceof RequiresLogin)) {
//someone made an invalid call, just return true
return true;
}
return request.getSession().getAttribute(Constants.SESSION_USER_KEY) != null;
}
}
}
Which keeps the validation logic nice and tightly coupled with the annotation that is being validated. Then all my ad-hoc validation code can be replaced with:
List<Annotation> annotations = mergeConstraintsFromClassAndMethod(serviceClass, serviceMethod);
for (Annotation annotation : annotations) {
processAnnotation(annotation, request);
}
private static boolean processAnnotation(Annotation annotation, HttpServletRequest request) {
AnnotationProcessor processor = null;
for (Class<?> processorClass : annotation.annotationType().getDeclaredClasses()) {
if (AnnotationProcessor.class.isAssignableFrom(processorClass)) {
try {
processor = (AnnotationProcessor)processorClass.newInstance();
break;
}
catch (Exception ignored) {
//couldn't create it, but maybe there is another inner
//class that also implements the required interface that
//we can construct, so keep going
}
}
}
if (processor != null) {
return processor.processRequest(annotation, request);
}
//couldn't get a a processor and thus can't process the
//annotation, perhaps this annotation does not support
//validation, return true
return true;
}
Which leaves no more ad-hoc code that needs to be revised every time I add a new annotation type. I just implement the validator as part of the annotation, and I'm done.
Does this seem like a reasonable pattern to use? If not then what might work better?
You may want to investigate AOP. You can advise methods that expose certain annotations and perform pre/post processing accordingly.
I would just like to add that while AOP would be a good solution, the Spring framework already provides this functionality by way of the #Secured annotation.
#Secured("ROLE_USER")
public void foo() {
}
Spring also supports JSR-303 validation with the #Valid annotation. So for these use cases at least, it seems you are re-inventing the wheel.
IMHO one could think about the Visitor pattern in combination with a factory. The factory will return a wrapper object that knows the exact annotation type and which the visitor will be able...
class MyVisitor {
public void visit(VisitableAnnotationType1 at) {
//something AnnotationType1 specific
}
public void visit(VisitableAnnotationType2 at) {
//something AnnotationType2 specific
}
... // put methods for further annotation types here
}
class VisitableFactory {
public abstract class VisitableAnnotation {
public abstract void accept(MyVisitor visitor);
}
class VisitableAnnotationType1 implements VisitableAnnotation {
public void accept(MyVisitor visitor) {
visitor.visit(this);
}
}
public static VisitableAnnotation getVisitable(Annotation a) {
if(AnnotationType1.class.isAssignableFrom(a.getClass()) {
//explicitely cast to the respective AnnotationType
return new VisitableAnnotationType1((AnnotationType1)a);
} else if (AnnotationType2.class.isAssignableFrom(a.getClass()) {
//explicitely cast to the respective AnnotationType
return new VisitableAnnotationType1((AnnotationType1)a);
}
}
}
As we cannot extend Annotation, we need those wrapper classes in the factory. You could also pass the original annotation which is then contained in that wrapper class.
What you have to do: For each new AnnotationType add a new "wrapper" class to the factory, extend the factory's
getVisitable()
method accordingly and also add an according method to the Visitor:
public void doSomething(VisitableAnnotationTypeXYZ at) {
//something AnnotationTypeXYZ specific
}
now the generic validation (or whatever) code looks like:
List<ValidatableAnnotation> annotations = mergeConstraintsFromClassAndMethod(serviceClass, serviceMethod);
MyVisitor visitor = new MyVisitor();
for (ValidatableAnnotation annotation : annotations) {
VisitableFactory.getVisitable(annotation).accept(visitor);
}
The visiting works by the indirection that the visited object calls the visitor with itself as the argument and thus the correct visit method will be invoked.
Hope that helps ;-)
Code is not tested, though...
Coming from Struts2 I'm used to declaring #Namespace annotation on super classes (or package-info.java) and inheriting classes would subsequently pick up on the value in the #Namespace annotation of its ancestors and prepend it to the request path for the Action. I am now trying to do something similar in Spring MVC using #RequestMapping annotation as follows (code trimmed for brevity):
package au.test
#RequestMapping(value = "/")
public abstract class AbstractController {
...
}
au.test.user
#RequestMapping(value = "/user")
public abstract class AbstractUserController extends AbstractController {
#RequestMapping(value = "/dashboard")
public String dashboard() {
....
}
}
au.test.user.twitter
#RequestMapping(value = "/twitter")
public abstract class AbstractTwitterController extends AbstractUserController {
...
}
public abstract class TwitterController extends AbstractTwitterController {
#RequestMapping(value = "/updateStatus")
public String updateStatus() {
....
}
}
/ works as expect
/user/dashboard works as expected
However when I would have expected /user/twitter/updateStatus to work it does not and checking the logs I can see a log entry which looks something like:
org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping
- Mapped URL path [/tweeter/updateStatus] onto handler
'twitterController'
Is there a setting I can enable that will scan the superclasses for #RequestMapping annotations and construct the correct path?
Also I take it that defining #RequestMapping on a package in package-info.java is illegal?
The following basically becomes /tweeter/updateStatus and not /user/tweeter/updateStatus
public abstract class TwitterController extends AbstractTwitterController {
#RequestMapping(value = "/updateStatus")
public String updateStatus() {
....
}
}
That's the expected behavior since you've overriden the original #RequestMapping you've declared in the AbstractController and AbstractUserController.
In fact when you declared that AbstractUserController it also overriden the #RequestMapping for AbstractController. It just gives you the illusion that the / from the AbstractController has been inherited.
"Is there a setting I can enable that will scan the superclasses for #RequestMapping annotations and construct the correct path?" Not that I know of.
According to the technique explained in Modifying #RequestMappings on startup,
yes, it's possible to construct a URL pattern from superclasses in a way you want.
In essence, you have to subclass RequestMappingHandlerMapping (most likely, it will be your HandlerMapping implementation, but please check first)
and override protected getMappingForMethod method.
Once this renders to be feasible, you are in full control of URL pattern generation.
From the example you gave it's not completely clear the exact merging policy, for example, what path you want to have if
a superclass AbstractTwitterController also implements updateStatus() method with its own #RequestMapping, or how would you like to concatenate the URL patterns across the hierarchy, top-down or bottom-up, (I assumed the former below),
but, hopefully, the following snippet will give you some ideas :
private static class PathTweakingRequestMappingHandlerMapping extends RequestMappingHandlerMapping {
#Override
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
RequestMappingInfo methodMapping = super.getMappingForMethod(method, handlerType);
if (methodMapping == null)
return null;
List<String> superclassUrlPatterns = new ArrayList<String>();
boolean springPath = false;
for (Class<?> clazz = handlerType; clazz != Object.class; clazz = clazz.getSuperclass())
if (clazz.isAnnotationPresent(RequestMapping.class))
if (springPath)
superclassUrlPatterns.add(clazz.getAnnotation(RequestMapping.class).value()[0]);// TODO handle other elements in the array if necessary
else
springPath = true;
if (!superclassUrlPatterns.isEmpty()) {
RequestMappingInfo superclassRequestMappingInfo = new RequestMappingInfo("",
new PatternsRequestCondition(String.join("", superclassUrlPatterns)), null, null, null, null, null, null);// TODO implement specific method, consumes, produces, etc depending on your merging policies
return superclassRequestMappingInfo.combine(methodMapping);
} else
return methodMapping;
}
}
Another good question is how to intercept the instantiation of RequestMappingHandlerMapping. In the Internet there are quite a number of various examples for various configuration strategies.
With JavaConfig, however, remember that if you provide WebMvcConfigurationSupport in your #Configuration set, then your #EnableWebMvc(explicit or implicit) will stop to work. I ended up with the following:
#Configuration
public class WebConfig extends DelegatingWebMvcConfiguration{
#Configuration
public static class UnconditionalWebMvcAutoConfiguration extends WebMvcAutoConfiguration {//forces #EnableWebMvc
}
#Override
protected RequestMappingHandlerMapping createRequestMappingHandlerMapping() {
return new PathTweakingRequestMappingHandlerMapping();
}
#Bean
#Primary
#Override
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
return super.requestMappingHandlerMapping();
}
}
but would like to learn about better ways.