I have a custom Annotation like this -
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface ControllerAction {
String value();
}
I have a class that uses this annotation like this -
public class TestController extends AbstractController {
public TestController () {
super();
}
#ControllerAction("add")
public void addCandidate(){
}
}
The super class looks like this -
public abstract class AbstractController {
public AbstractController (){
}
public CustomBean processRequest(ServletAction action, HttpServletRequest request) {
Class<AbstractController > controllerClass = AbstractController.class;
for (Method method : controllerClass.getDeclaredMethods()) {
if (method.isAnnotationPresent(ControllerAction.class)) {
Annotation annotation = (ControllerAction) method.getAnnotation(ControllerAction.class);
if(annotation != null){
if(annotation.value().equals(action.getAction())){
method.invoke(controllerClass.newInstance());
}
}
}
}
return null;
}
}
The processRequest(...) method in AbstractController is called from a servlet directly. The processRequest() method figures out the servlet action, and based on that, it should call the method appropriately.
For example, if the ServletAction.getAction() == 'add', processRequest() should automatically call addCandidate() in TestController. But I am not able to get the value of the Annotation. Somehow annotation.value() is giving a compilation error in eclipse. Eclipse is not showing any method I can use to get the annotation value.
I want to know if there is a way to get value() of the Custom Annotation. I dont want to define my Annotation with anything else other than String value(). I want to know if it is possible to achieve what I want with just String value() in my custom Annotation?
Any help is greatly appreciated. Thanks.
You probably need to change
Annotation annotation = (ControllerAction) method.getAnnotation(ControllerAction.class);
To
ControllerAction annotation = method.getAnnotation(ControllerAction.class);
Otherwise the methods specific to ControllerAction will not be known to the compiler as annotation is of type Annotation
Additionally - as pointed out by Sharon Ben Asher - instead of AbstractController.class you should use getClass() to get the class of the actual implementation at runtime. Given the current code only the methods of AbstractController will be checked but not the ones of implementing classes.
Related
I want to mark a field of a class with my custom annotation. And whenever any method is invoke I want to do some modification on that field.
public class Message{
public Integer id;
#FreeText // this is my custom annotation
public String htmlMsg;
public String textMsg ;
}
This annotation (#FreeText) can be used in any class.
In seasar framework, I can do this by create an interceptor and override invoke method. The I can get the object of this class and the find the field that marked with my annotation and modify it. However, i cannot find a way to do it in Spring.
In spring, I found some method like MethodInvocationInterceptor, but I don't know how to implement it. Can you suggest any way to do this in Spring?
Seasar2 and Spring are very close. I have not tested but you can do something like this.
First create FreeText custom annotation
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.FIELD)
#Documented
public #interface FreeText {}
Then create the following interceptor
public class EncryptSensitiveDataInterceptor extends MethodInterceptor {
#Override
public Object invoke(MethodInvocation in) throws Throwable {
Object[] params = in.getArguments();
Object param = params[0];
for (Field field : param.getClass().getDeclaredFields()) {
for (Annotation anno : field.getDeclaredAnnotations()) {
if (anno instanceof FreeText) {
field.set(param, [YOUR CUSTOM LOGIC METHOD]);
}
}
}
return in.proceed();
}
Hope this help.
I've got a simple aspect that supposed to set the value of class fied, that has annotation #GuiceInject.
Originally I have this
#GuiceInject(module=RepositoryModule.class)
private IRacesRepository repository;
And I expect to get similar to this
private IRacesRepository repository = GuiceInject.getInstance(IRacesRepository.class);
And here is my aspect
public aspect InjectionAspect {
Object around(): get(#GuiceInject * *) {
System.out.println(thisJoinPointStaticPart);
// instantiate object as it supposed to be null originally
return GuiceInjector.getInstance(thisJoinPoint.getTarget().getClass());
}
}
As far as I understand - I am new to AOP - it supposed to replace get invokations of the field with the code in aspect.
It compiles fine, but when I run the application - nothing happens. I get NullPointerException for readRaces method as it stays null so aspect did not work.
My main class looks like this
public class Example {
#GuiceInject(module=RepositoryModule.class)
private IRacesRepository racesRepository;
private void execute() {
System.out.println("List of races: " + racesRepository.readRaces());
}
public static void main(String[] args) {
new Example().execute();
}
}
What is the problem? Annotation has this definition
#Target(ElementType.FIELD)
// make annotation visible in runtime for AspectJ
#Retention(RetentionPolicy.RUNTIME)
public #interface GuiceInject {
Class<? extends AbstractModule> module();
}
Please try to redefine pointcut syntax as
Object around(): get(#package.subpackage.GuiceInject * *.*)
Correct field signature must specify the type of the field, the declaring type, and name. If your annotation is in different package, it should be fully qualified.
Hej,
I want to use the #Validated(group=Foo.class) annotation to validate an argument before executing a method like following:
public void doFoo(Foo #Validated(groups=Foo.class) foo){}
When i put this method in the Controller of my Spring application, the #Validated is executed and throws an error when the Foo object is not valid. However if I put the same thing in a method in the Service layer of my application, the validation is not executed and the method just runs even when the Foo object isn't valid.
Can't you use the #Validated annotation in the service layer ? Or do I have to do configure something extra to make it work ?
Update:
I have added the following two beans to my service.xml:
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/>
<bean class="org.springframework.validation.beanvalidation.MethodValidationPostProcessor"/>
and replaced the #Validate with #Null like so:
public void doFoo(Foo #Null(groups=Foo.class) foo){}
I know it is a pretty silly annotation to do but I wanted to check that if I call the method now and passing null it would throw an violation exception which it does. So why does it execute the #Null annotation and not the #Validate annotation ? I know one is from javax.validation and the other is from Spring but I do not think that has anything to do with it ?
In the eyes of a Spring MVC stack, there is no such thing as a service layer. The reason it works for #Controller class handler methods is that Spring uses a special HandlerMethodArgumentResolver called ModelAttributeMethodProcessor which performs validation before resolving the argument to use in your handler method.
The service layer, as we call it, is just a plain bean with no additional behavior added to it from the MVC (DispatcherServlet) stack. As such you cannot expect any validation from Spring. You need to roll your own, probably with AOP.
With MethodValidationPostProcessor, take a look at the javadoc
Applicable methods have JSR-303 constraint annotations on their
parameters and/or on their return value (in the latter case specified
at the method level, typically as inline annotation).
Validation groups can be specified through Spring's Validated
annotation at the type level of the containing target class, applying
to all public service methods of that class. By default, JSR-303 will
validate against its default group only.
The #Validated annotation is only used to specify a validation group, it doesn't itself force any validation. You need to use one of the javax.validation annotations like #Null or #Valid. Remember that you can use as many annotations as you would like on a method parameter.
As a side note on Spring Validation for methods:
Since Spring uses interceptors in its approach, the validation itself is only performed when you're talking to a Bean's method:
When talking to an instance of this bean through the Spring or JSR-303 Validator interfaces, you'll be talking to the default Validator of the underlying ValidatorFactory. This is very convenient in that you don't have to perform yet another call on the factory, assuming that you will almost always use the default Validator anyway.
This is important because if you're trying to implement a validation in such a way for method calls within the class, it won't work. E.g.:
#Autowired
WannaValidate service;
//...
service.callMeOutside(new Form);
#Service
public class WannaValidate {
/* Spring Validation will work fine when executed from outside, as above */
#Validated
public void callMeOutside(#Valid Form form) {
AnotherForm anotherForm = new AnotherForm(form);
callMeInside(anotherForm);
}
/* Spring Validation won't work for AnotherForm if executed from inner method */
#Validated
public void callMeInside(#Valid AnotherForm form) {
// stuff
}
}
Hope someone finds this helpful. Tested with Spring 4.3, so things might be different for other versions.
#pgiecek You don't need to create a new Annotation. You can use:
#Validated
public class MyClass {
#Validated({Group1.class})
public myMethod1(#Valid Foo foo) { ... }
#Validated({Group2.class})
public myMethod2(#Valid Foo foo) { ... }
...
}
Be careful with rubensa's approach.
This only works when you declare #Valid as the only annotation. When you combine it with other annotations like #NotNull everything except the #Valid will be ignored.
The following will not work and the #NotNull will be ignored:
#Validated
public class MyClass {
#Validated(Group1.class)
public void myMethod1(#NotNull #Valid Foo foo) { ... }
#Validated(Group2.class)
public void myMethod2(#NotNull #Valid Foo foo) { ... }
}
In combination with other annotations you need to declare the javax.validation.groups.Default Group as well, like this:
#Validated
public class MyClass {
#Validated({ Default.class, Group1.class })
public void myMethod1(#NotNull #Valid Foo foo) { ... }
#Validated({ Default.class, Group2.class })
public void myMethod2(#NotNull #Valid Foo foo) { ... }
}
As stated above to specify validation groups is possible only through #Validated annotation at class level. However, it is not very convenient since sometimes you have a class containing several methods with the same entity as a parameter but each of which requiring different subset of properties to validate. It was also my case and below you can find several steps to take to solve it.
1) Implement custom annotation that enables to specify validation groups at method level in addition to groups specified through #Validated at class level.
#Target({ElementType.METHOD})
#Retention(RetentionPolicy.RUNTIME)
#Documented
public #interface ValidatedGroups {
Class<?>[] value() default {};
}
2) Extend MethodValidationInterceptor and override determineValidationGroups method as follows.
#Override
protected Class<?>[] determineValidationGroups(MethodInvocation invocation) {
final Class<?>[] classLevelGroups = super.determineValidationGroups(invocation);
final ValidatedGroups validatedGroups = AnnotationUtils.findAnnotation(
invocation.getMethod(), ValidatedGroups.class);
final Class<?>[] methodLevelGroups = validatedGroups != null ? validatedGroups.value() : new Class<?>[0];
if (methodLevelGroups.length == 0) {
return classLevelGroups;
}
final int newLength = classLevelGroups.length + methodLevelGroups.length;
final Class<?>[] mergedGroups = Arrays.copyOf(classLevelGroups, newLength);
System.arraycopy(methodLevelGroups, 0, mergedGroups, classLevelGroups.length, methodLevelGroups.length);
return mergedGroups;
}
3) Implement your own MethodValidationPostProcessor (just copy the Spring one) and in the method afterPropertiesSet use validation interceptor implemented in step 2.
#Override
public void afterPropertiesSet() throws Exception {
Pointcut pointcut = new AnnotationMatchingPointcut(Validated.class, true);
Advice advice = (this.validator != null ? new ValidatedGroupsAwareMethodValidationInterceptor(this.validator) :
new ValidatedGroupsAwareMethodValidationInterceptor());
this.advisor = new DefaultPointcutAdvisor(pointcut, advice);
}
4) Register your validation post processor instead of Spring one.
<bean class="my.package.ValidatedGroupsAwareMethodValidationPostProcessor"/>
That's it. Now you can use it as follows.
#Validated(groups = Group1.class)
public class MyClass {
#ValidatedGroups(Group2.class)
public myMethod1(Foo foo) { ... }
public myMethod2(Foo foo) { ... }
...
}
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.