Class 拦截具有字段访问的方法调用的切入点

Class pointcut that intercepts a method call with field access

我正在编写一些方面,以使我的 gradle pugin 开发更清晰一些。在gradle里面有一个界面,像这样

interface Plugin { 
  def apply(Project project); 
}

应用于插件

class MyPlugin implements Plugin<Project> {
  def apply(Project project) {
    do stuff
  }
}

现在,我希望能够像这样注释 class

@OnlyAllowedOnRoot
class MyPlugin implements Plugin<Project> {
  def apply(Project project) {
    do stuff
  }
}

并有一个在触发 'apply' 方法时触发的切入点,然后获取参数。因为切入点的逻辑是

if (project.rootProject.name != project.name) {
  throw new GradleScriptExeption("This plugin can only be applied to root")
}

我该怎么做?这个例子是我想写的其他十几个切入点的基础,但我真的不知道从哪里开始。我知道我可以直接注释 apply 方法,但我担心可读性,这主要是我开始这样做的原因。如果必须的话,我可以,但我宁愿不这样做。而且因为Gradle的生命周期,所以必须在调用'apply'方法的时候检查,不能在实例化的时候检查。

这是一个完整的示例,但在 Java 中,而不是在 Groovy 中。不过,如果您使用 Groovy,应该没有任何区别。顺便说一句,我不想​​将 Gradle API 添加为我的项目的依赖项,所以我只是用正确的包名称和签名复制了其 API 的相关部分。我自己不是 Gradle 用户,我将根项目 属性 分层实施为直接父级,而不是绝对根。如果Gradle做的不一样,只需要调整抛出异常的方面的条件回到你自己的示例代码。

Gradle API:

package org.gradle.api;

public class Project {
  private String name;
  private Project rootProject;

  public Project(String name, Project rootProject) {
    this.name = name;
    this.rootProject = rootProject;
  }

  public String getName() {
    return name;
  }

  public Project getRootProject() {
    return rootProject;
  }

  @Override
  public String toString() {
    return "Project(name = " + name + ", rootProject = " + rootProject + ")";
  }
}
package org.gradle.api;

public interface Plugin<T> {
  void apply(T target);
}
package org.gradle.api;

public class GradleScriptExeption extends RuntimeException {
  private static final long serialVersionUID = 1L;

  public GradleScriptExeption(String message, Throwable cause) {
    super(message, cause);
  }
}

标记注释+插件:

package de.scrum_master.app;

import org.gradle.api.Plugin;
import org.gradle.api.Project;

class NormalPlugin implements Plugin<Project> {
  public void apply(Project project) {}
}
package de.scrum_master.app;

import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;

@Retention(RUNTIME)
public @interface OnlyAllowedOnRoot {}
package de.scrum_master.app;

import org.gradle.api.Plugin;
import org.gradle.api.Project;

@OnlyAllowedOnRoot
class RootPlugin implements Plugin<Project> {
  public void apply(Project project) {}
}

驱动申请:

package de.scrum_master.app;

import org.gradle.api.Project;

public class Application {
  public static void main(String[] args) {
    Project rootProject = new Project("root", null);
    Project childProject = new Project("child", rootProject);
    Project grandChildProject = new Project("grandchild", childProject);

    NormalPlugin normalPlugin = new NormalPlugin();
    normalPlugin.apply(rootProject);
    normalPlugin.apply(childProject);
    normalPlugin.apply(grandChildProject);

    RootPlugin rootPlugin = new RootPlugin();
    rootPlugin.apply(rootProject);
    rootPlugin.apply(childProject);
    rootPlugin.apply(grandChildProject);
  }
}

看点:

package de.scrum_master.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.gradle.api.GradleScriptExeption;
import org.gradle.api.Plugin;
import org.gradle.api.Project;

@Aspect
public class GradlePluginAspect {
  @Pointcut("execution(void apply(*)) && target(plugin) && args(project)")
  private static void pluginExecution(Plugin plugin, Project project) {}

  @Before("pluginExecution(plugin, project) && @target(de.scrum_master.app.OnlyAllowedOnRoot)")
  public void illegalRootPlugin(JoinPoint thisJoinPoint, Plugin plugin, Project project) {
    if (project.getRootProject() != null)
      throw new GradleScriptExeption("Cannot apply " + plugin.getClass().getSimpleName() + " to non-root project " + project, null); 
  }

  @Before("pluginExecution(plugin, project)")
  public void logPluginApply(JoinPoint thisJoinPoint, Plugin plugin, Project project) {
    System.out.println("Applying " + plugin.getClass().getSimpleName() + " to " + project);
  }
}

控制台日志:

Applying NormalPlugin to Project(name = root, rootProject = null)
Applying NormalPlugin to Project(name = child, rootProject = Project(name = root, rootProject = null))
Applying NormalPlugin to Project(name = grandchild, rootProject = Project(name = child, rootProject = Project(name = root, rootProject = null)))
Applying RootPlugin to Project(name = root, rootProject = null)
Exception in thread "main" org.gradle.api.GradleScriptExeption: Cannot apply RootPlugin to non-root project Project(name = child, rootProject = Project(name = root, rootProject = null))
    at de.scrum_master.aspect.GradlePluginAspect.illegalRootPlugin(GradlePluginAspect.aj:19)
    at de.scrum_master.app.RootPlugin.apply(RootPlugin.java:8)
    at de.scrum_master.app.Application.main(Application.java:18)