It's strange that my PUT operations is not mapped to spring method, I've following classes...
#Controller
#RequestMapping(value = { "/**/applicationContext/abcde" })
public abstract class AbstractController {
// Exception handlers
}
This class is extended by another abstract class, as below
public abstract class AbstractAdapter extends AbstractController{}
Finally, above class is extended by below class
public class SpringAdapter extends AbstractAdapter implements MyInterface{}
And I've some put operations to be made which looks like below :-
#Override
#ResponseBody
#RequestMapping(method = { RequestMethod.PUT }, consumes = "application/x-www-form-urlencoded", produces = "application/xml", params = {
"param1=some_value", param2 })
public MyObject update(
#RequestParam(value = param2, required = true) #ContentType(value = Encoding.XML) MyObejct myObject, HttpServletRequest request) throws Exception{ }
But when I invoke put method then the request is not mapping to my update method.
Can anybody please help.
Your subclass isn't marked as a #Controller and therefor Spring (when it scans for controllers) doesn't find it.
Please see this answer
Basically, annotations aren't inherited by default.
You can use an annotation in the declaration of an annotation to mark it as inherited as is shown here
by using:
public #interface Inherited
but if you look at the controller source, this keyword is not used in the Controller implementation spring uses.
#Target({ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
#Documented
#Component
public #interface Controller {
So I would assume #Controller is not inherited in your situation. I would first try out the inheritance with a simple example (class A extends class B , with just a simple unadorned method) to verify this.
Related
I'm writing an aspect to log Request and Response of each API call in a controller.
I want to be able to use this annotation on a class, hence used #Target(ElementType.TYPE)
Previously I had added #Target(ElementType.Method) and I was using this annotation on methods and it was working fine.
Now I want to change it to #Target(ElementType.TYPE)
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
public #interface ReLogger {}
#Aspect
#Component
public class ReLoggerAspect {
public static final Logger log = LoggerFactory.getLogger("ReLoggerAspect");
#PostConstruct
private void postConstruct() {
log.info("ReLoggerAspect Created");
}
#Around("#annotation(ReLogger)")
private Object reqLoggingAspect(ProceedingJoinPoint joinPoint) throws Throwable {
log.info("Request {}",jointPoint.getArgs()[0);
}
}
Using #ReLoggerAspect on a class
#RestController
#RequestMapping(value = "....", produces = { "application/json" })
#ReLogger
public class Samplecontroller {
/** Some logic here**/.....
}
It doesn't print the Request when an API SampleController is invoked
Your premise that #annotation would match type annotations is wrong, see (Spring AOP manual](https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#aop-pointcuts-designators):
#within: Limits matching to join points within types that have the given annotation (the execution of methods declared in types with the given annotation when using Spring AOP).
#annotation: Limits matching to join points where the subject of the join point (the method being executed in Spring AOP) has the given annotation.
Thus, you ought to use #within(fully.qualified.AnnotationType).
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.
I created a Spring website.
I used an abstract generic controller class, with different implementations.
It works well if I don't activate the Aspect class on any Controllers.
If I activate an Aspect, then all Mappings seem to be deactivated:
DEBUG: org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping - Did not find handler method for [/contact/2]
WARN : org.springframework.web.servlet.PageNotFound - No mapping found forHTTP request with URI [/clubhelperbackend/contact/2] in DispatcherServlet with name 'appServlet'
This is my abstract controller:
public abstract class AbstractController<T extends Data> implements ClubController<T> {
protected Dao<T> dao;
private Class<T> elementClass;
public AbstractController(Dao<T> dao, Class<T> element) {
super();
this.dao = dao;
this.elementClass = element;
}
#Override
#RequestMapping(value = "/{id}", method = RequestMethod.GET)
public String getAsView(#PathVariable("id") long id, #RequestParam(required = false) boolean ajax, Model m) {
String mapping = elementClass.getSimpleName();
m.addAttribute(mapping, getById(id));
return mapping + "Get" + (ajax ? "Ajax" : "");
}
#Override
#RequestMapping(value = "/{id}", method = RequestMethod.DELETE, produces = "application/json")
public T delete(#PathVariable("id") long id) {
T obj = getById(id);
// dao.delete(id);
return obj;
}
}
And an implementation:
#Controller
#RequestMapping("/contact")
public class ContactController extends AbstractController<Contact> {
#Autowired
public ContactController(Dao<Contact> contactDao) {
super(contactDao, Contact.class);
}
}
This is my Aspect:
#Aspect
#Component
public class DeletedStorageAspect {
//
// private DeletedEntriesDao deletedEntriesDao;
//
// #Autowired
// public DeletedStorageAspect(DeletedEntriesDao deletedEntriesDao) {
// super();
// this.deletedEntriesDao = deletedEntriesDao;
// }
#Pointcut("execution (public * de.kreth.clubhelperbackend.controller.abstr.AbstractController.delete(..))")
private void invocation() {
}
#AfterReturning(pointcut = "invocation()", returning = "deleted")
public void storeDeleted(JoinPoint joinPoint, Data deleted) {
System.out.println("Deleted: " + deleted);
String tableName = deleted.getClass().getSimpleName();
long id = deleted.getId();
Date now = new Date();
DeletedEntries entry = new DeletedEntries(-1L, tableName, id, now, now);
System.out.println(entry);
// deletedEntriesDao.insert(entry);
}
}
This is part of my beans.xml:
<aop:aspectj-autoproxy>
<aop:include name="mysqlDbCheckAspect" />
<aop:include name="daoLoggerAspect" />
<aop:include name="controllerSecurityAspect" />
<aop:include name="deletedStorageAspect" />
</aop:aspectj-autoproxy>
I can restore full functionality by commenting deletedStorageAspect.
What causes this behaviour? Why are the mappings not recognized with an aspect on them? Are aspects not allowed on Controllers?
Hoping for some help, please.
When using AOP with Spring by default spring creates proxies. Depending on the fact if you implement interfaces on your class (or not) it will create a JDK Dynamic proxy (interface based) or CGLIB based proxy (class based).
public abstract class AbstractController<T extends Data> implements ClubController<T> {
In the case of an interface based proxy (which applies to you) the MVC infrastructure isn't able to see the #RequestMapping annotations anymore and will not detect your mappings anymore. This is also the case that applies to you as you are implementing an interface. Also see the reference guide on the matter of proxying with request mappings.
To fix it you must force the use of class based proxies, to do so add proxy-target-class="true" to the <aop:aspectj-auto-proxy />.
<aop:aspectj-autoproxy proxy-target-class="true">
I am not a AOP expert.But by looking at your code I can say Abstract class in not executing and that may be the root cause. So have to modify Pointcut execution expression.
Solution 1
If you are not using delete signature for child classes you can easlily move to bello like abstract expression. It say's only within package like thing.
#Pointcut("execution (public * de.kreth.clubhelperbackend.controller.*.*.delete(..))")
private void invocation() {
}
Solution 2
You can use logic gates for expression like this
#Pointcut("target(de.kreth.clubhelperbackend.controller.abstr.AbstractController)")
private void anyAbstractOperation() {}
#Pointcut("execution(public * *.delete(..))")
private void anyDeleteOperation() {}
#Pointcut("anyAbstractOperation() && anyDeleteOperation()")
private void invocation() {}
reference :
1.http://docs.spring.io/spring/docs/2.5.x/reference/aop.html#aop-pointcuts-combining
2.http://www.baeldung.com/spring-aop-pointcut-tutorial
3.http://docs.spring.io/spring/docs/current/spring-framework-reference/html/aop.html#aop-using-aspectj
Explanation
About this and target
this limits matching to join points where the bean reference is an instance of the given type, while target limits matching to join points where the target object is an instance of the given type. The former works when Spring AOP creates a CGLIB-based proxy, and the latter is used when a JDK-based proxy is created. Suppose that the target class implements an interface:
public class FooDao implements BarDao {
...
}
In this case, Spring AOP will use the JDK-based proxy and you should use the target PCD because the proxied object will be an instance of Proxy class and implement the BarDao interface:
#Pointcut("target(org.baeldung.dao.BarDao)")
On the other hand if FooDao doesn’t implement any interface or proxyTargetClass property is set to true then the proxied object will be a subclass of FooDao and the this PCD could be used:
#Pointcut("this(org.baeldung.dao.FooDao)")
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.
As of Spring MVC 3, AbstractCommandController is deprecated so you can no longer specify the command class in setCommandClass(). Instead you hard-code the command class in the parameter list of a request handler. For example,
#RequestMapping(method = RequestMethod.POST)
public void show(HttpServletRequest request, #ModelAttribute("employee") Employee employee)
My problem is that I'm developing a generic page that allows the user to edit a generic bean, so the command class isn't known until the run-time. If the variable beanClass holds the command class, with AbstractCommandController, you would simply do the following,
setCommandClass(beanClass)
Since I can't declare the command object as a method parameter, is there any way to have Spring bind request parameters to a generic bean in the body of the request handler?
Instantiation of the command object is the only place where Spring needs to know a command class. However, you can override it with #ModelAttribute-annotated method:
#RequestMapping(method = RequestMethod.POST)
public void show(HttpServletRequest request,
#ModelAttribute("objectToShow") Object objectToShow)
{
...
}
#ModelAttribute("objectToShow")
public Object createCommandObject() {
return getCommandClass().newInstance();
}
By the way, Spring also works fine with the real generics:
public abstract class GenericController<T> {
#RequestMapping("/edit")
public ModelAndView edit(#ModelAttribute("t") T t) { ... }
}
#Controller #RequestMapping("/foo")
public class FooController extends GenericController<Foo> { ... }