注释仅适用于接口的方法

时间:2017-12-16 18:29:08

标签: java annotations aop aspect

我有注释:

@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中描述),注释可以正常工作。

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

1 个答案:

答案 0 :(得分: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而不仅仅是通过不支持call()个连接点的Spring AOP的“AOP lite”:

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

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

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