【问题标题】:Aspect deactivates Spring Controller MappingAspect 停用 Spring 控制器映射
【发布时间】:2016-08-30 01:17:39
【问题描述】:

我创建了一个 Spring 网站。 我使用了一个抽象的通用控制器类,具有不同的实现。 如果我不在任何控制器上激活 Aspect 类,它会很好地工作。

如果我激活了一个方面,那么所有的映射似乎都被停用了:

调试:org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping - 未找到 [/contact/2] 的处理程序方法
警告:org.springframework.web.servlet.PageNotFound - 在名为“appServlet”的 DispatcherServlet 中找不到带有 URI [/clubhelperbackend/contact/2] 的 HTTP 请求的映射

这是我的抽象控制器:

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;
}
}

还有一个实现:

@Controller
@RequestMapping("/contact")
public class ContactController extends AbstractController<Contact> {

    @Autowired
    public ContactController(Dao<Contact> contactDao) {
        super(contactDao, Contact.class);
    }

}

这是我的观点:

@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);
    }
}

这是我的 beans.xml 的一部分:

<aop:aspectj-autoproxy>
    <aop:include name="mysqlDbCheckAspect" />
    <aop:include name="daoLoggerAspect" />
    <aop:include name="controllerSecurityAspect" />
    <aop:include name="deletedStorageAspect" />
</aop:aspectj-autoproxy>

我可以通过评论 deletedStorageAspect 来恢复全部功能。

是什么导致了这种行为?为什么映射无法识别它们的方面?控制器上是否不允许使用方面?

希望得到一些帮助。

【问题讨论】:

标签: java spring aop spring-aop


【解决方案1】:

在默认情况下将 AOP 与 Spring 结合使用时,spring 会创建代理。根据您是否在类上实现接口的事实,它将创建一个 JDK 动态代理(基于接口)或基于 CGLIB 的代理(基于类)。

public abstract class AbstractController<T extends Data> implements ClubController<T> {

在基于接口的代理(适用于您)的情况下,MVC 基础架构无法再看到@RequestMapping 注释,也不会再检测到您的映射。当您实现接口时,这也是适用于您的情况。另请参阅 the reference guide 关于使用请求映射进行代理的问题。

要修复它,您必须强制使用基于类的代理,为此将proxy-target-class="true" 添加到&lt;aop:aspectj-auto-proxy /&gt;

<aop:aspectj-autoproxy proxy-target-class="true">

【讨论】:

  • 我理解正确吗,删除界面也应该有效?
  • 理论上应该像创建一个基于类的代理一样工作。
【解决方案2】:

我不是 AOP 专家。但是通过查看您的代码,我可以说抽象类没有执行,这可能是根本原因。所以必须修改Pointcut执行表达式。

解决方案 1

如果你没有对子类使用删除签名,你可以很容易地转移到像抽象表达式一样的bello。它说只在包装内。

@Pointcut("execution (public * de.kreth.clubhelperbackend.controller.*.*.delete(..))")
private void invocation() {
}

解决方案 2

你可以像这样使用逻辑门来表达

@Pointcut("target(de.kreth.clubhelperbackend.controller.abstr.AbstractController)")
private void anyAbstractOperation() {}

@Pointcut("execution(public * *.delete(..))")
private void anyDeleteOperation() {}


@Pointcut("anyAbstractOperation() && anyDeleteOperation()")
private void invocation() {}

参考:
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

解释

关于这个和目标

this 限制匹配到 bean 引用是给定类型实例的连接点,而 target 限制匹配到目标对象是实例的连接点给定的类型。前者在 Spring AOP 创建基于 CGLIB 的代理时工作,后者在创建基于 JDK 的代理时使用。假设目标类实现了一个接口:

public class FooDao implements BarDao {
    ...
}

在这种情况下,Spring AOP 将使用基于 JDK 的代理,您应该使用目标 PCD,因为代理对象将是 Proxy 类的实例并实现 BarDao 接口:

@Pointcut("target(org.baeldung.dao.BarDao)")

另一方面,如果 FooDao 没有实现任何接口或 proxyTargetClass 属性设置为 true,则代理对象将是 FooDao 的子类,并且可以使用此 PCD:

@Pointcut("this(org.baeldung.dao.FooDao)")

【讨论】:

  • 如果我删除组件注释,则不会触发任何方面(甚至实例化?)。我尝试了适用于其他类的 Aspects。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-01-16
  • 2012-05-12
  • 1970-01-01
  • 2017-04-07
  • 1970-01-01
  • 2018-03-27
  • 2015-11-21
相关资源
最近更新 更多