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">