注释仅适用于接口的方法

问题描述 投票:0回答:1

我有一个注释:

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface Loggable { }

和方面:

@Aspect
public class AspectLogger {
    @Around("@annotation(aspects.Loggable)")
    public void aroundLogging(ProceedingJoinPoint joinPoint) {
        System.out.println("aroundLogging()");
        throw new AuthentificationFailException();
    }
}

我也有接口和类:

public interface IAuthInteractor {
    public User authorization(String login, String password);
}

public class AuthInteractor implements IAuthInteractor {
    private EntityDAO<User> userDAO;
    private ITokenGenerator tokenGenerator;

    public AuthInteractor(EntityDAO<User> userDAO,
                      ITokenGenerator tokenGenerator) {
        this.userDAO = userDAO;
        this.tokenGenerator = tokenGenerator;
    }

    @Loggable
    public User authorization1(String login, String password) {
        return null;
    }

    @Loggable
    public User authorization(String login, String password) {
        return null;
    }
}

对于第一种方法(authorization1),注释不起作用。对于方法授权(在interafce中描述),注释有效。

为什么这样工作?如何在没有界面的情况下工作?

java annotations aop aspect
1个回答
1
投票

首先,方面的建议有一个void返回类型,即它将永远不会为返回其他类型的方法启动,例如User。方面甚至不应该编译。在任何情况下它都不适合我。 AspectJ编译器说:

applying to join point that doesn't return void: method-execution(de.scrum_master.app.User de.scrum_master.app.AuthInteractor.authorization(java.lang.String, java.lang.String))

所以,假设你改变了你的建议

@Around("@annotation(aspects.Loggable)")
public Object aroundLogging(ProceedingJoinPoint joinPoint) {
  System.out.println("aroundLogging()");
  throw new AuthentificationFailException();
}

它将编译并启动。我在本地测试它。

现在让我快速更改建议以实际进入原始方法,而不是总是抛出异常,这样我们可以测试更多,而不会一直捕获异常。我还想打印实际的连接点签名,这样我们就可以看到发生了什么:

@Around("@annotation(aspects.Loggable)")
public Object aroundLogging(ProceedingJoinPoint joinPoint) throws Throwable {
  System.out.println(joinPoint);
  //throw new AuthentificationFailException();
  return joinPoint.proceed();
}

如果那时你将这个main方法添加到你的接口实现类:

public static void main(String[] args) {
  System.out.println("Interface object");
  IAuthInteractor iAuthInteractor = new AuthInteractor(null, null);
  iAuthInteractor.authorization("user", "pw");

  System.out.println("\nImplementation object");
  AuthInteractor authInteractor = new AuthInteractor(null, null);
  authInteractor.authorization("user", "pw");
  authInteractor.authorization1("user", "pw");
}

控制台日志应该打印这样的东西,假设您使用AspectJ而不仅仅是通过Spring AOP的“AOP lite”,它不支持call()连接点:

Interface object
execution(User de.scrum_master.app.AuthInteractor.authorization(String, String))

Implementation object
call(User de.scrum_master.app.AuthInteractor.authorization(String, String))
execution(User de.scrum_master.app.AuthInteractor.authorization(String, String))
call(User de.scrum_master.app.AuthInteractor.authorization1(String, String))
execution(User de.scrum_master.app.AuthInteractor.authorization1(String, String))

如您所见,执行总是被捕获,但是调用不适用于接口类型实例,因为接口方法没有注释,只有实现。

顺便说一句,方法注释无论如何都不会被继承,因此使用@Inherited对注释类型的@Target({ElementType.METHOD})元注释有点无用。

© www.soinside.com 2019 - 2024. All rights reserved.