Вы и другие отделы можете скомпилировать весь код с помощью компилятора AspectJ, либо вручную из командной строки, через пакетные файлы, через IDE, настроенную для AspectJ (например, Eclipse, IDEA), либо через Maven. Я создал Maven для вас на GitHub, просто клонируйте проект . Извините, он не использует ваши классы MCVE, потому что я видел их слишком поздно и не хотел начинать все сначала.
Интерфейсный подход
Теперь давайте предположим, что существует интерфейс , который должны быть реализованы всеми соответствующими приложениями:
package de.scrum_master.base;
public interface BasicInterface {
void doSomething(String name);
String convert(int number);
}
package de.scrum_master.app;
import de.scrum_master.base.BasicInterface;
public class ApplicationOne implements BasicInterface {
@Override
public void doSomething(String name) {
System.out.println("Doing something with " + name);
}
@Override
public String convert(int number) {
return new Integer(number).toString();
}
public static void main(String[] args) {
System.out.println("BasicInterface implementation");
ApplicationOne application = new ApplicationOne();
application.doSomething("Joe");
System.out.println("Converted number = " + application.convert(11));
}
}
Подход базового класса
Или, альтернативно, существует базовый класс приложения должны расширяться:
package de.scrum_master.base;
public abstract class ApplicationBase {
public abstract void doSomething(String name);
public String convert(int number) {
return ((Integer) number).toString();
}
}
package de.scrum_master.app;
import de.scrum_master.base.ApplicationBase;
public class ApplicationTwo extends ApplicationBase {
@Override
public void doSomething(String name) {
System.out.println("Doing something with " + name);
}
public static void main(String[] args) {
System.out.println("ApplicationBase subclass");
ApplicationTwo application = new ApplicationTwo();
application.doSomething("Joe");
System.out.println("Converted number = " + application.convert(11));
}
}
нежелательное приложение
И теперь у нас есть приложение, которое делает свое дело, не реализуя интерфейс и не расширяя базовый класс:
package de.scrum_master.app;
public class UnwantedApplication {
public void sayHello(String name) {
System.out.println("Hello " + name);
}
public String transform(int number) {
return new Integer(number).toString();
}
public static void main(String[] args) {
System.out.println("Unwanted application");
UnwantedApplication application = new UnwantedApplication();
application.sayHello("Joe");
System.out.println("Transformed number = " + application.transform(11));
}
}
Аспект исполнения контракта
Теперь давайте просто напишем аспект AspectJ, который выдает ошибку компилятора с помощью declare error
(предупреждение также возможно с помощью declare warning
, но это ничего не навязывает, только сообщит о проблеме) .
package de.scrum_master.aspect;
import de.scrum_master.base.BasicInterface;
import de.scrum_master.base.ApplicationBase;
public aspect ApplicationContractEnforcer {
declare error :
within(de.scrum_master..*) &&
execution(public static void main(String[])) &&
!within(BasicInterface+) &&
!within(ApplicationBase+)
: "Applications with main methods have to implement BasicInterface or extend ApplicationBase";
}
Смысл этого кода: искать все классы с основными методами внутри de.scrum_master
или любым подпакетом, но не реализующим BasicInterface
и не расширяющим ApplicationBase
. В действительности вы, конечно, выбрали бы только один из двух последних критериев. Я делаю оба здесь, чтобы дать вам выбор. Если любой такой класс, если найден, отображается ошибка компилятора с указанным сообщением об ошибке.
По какой-то причине некоторым людям не нравится удивительно выразительный родной язык AspectJ (расширенный набор синтаксиса Java), но они предпочитают писать некрасивые аспекты в стиле аннотаций, упаковывая все свои аспектные указатели в строковые константы. Это тот же аспект, просто в другом синтаксисе. Выбирай любой. (В проекте GitHub я деактивировал нативный аспект, разрешив поиск несуществующего пакета xde.scrum_master
, чтобы избежать двойных ошибок компилятора.)
package de.scrum_master.aspect;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.DeclareError;
@Aspect
public class ApplicationContractEnforcer2 {
@DeclareError(
"within(de.scrum_master..*) && " +
"execution(public static void main(String[])) && " +
"!within(de.scrum_master.base.BasicInterface+) && " +
"!within(de.scrum_master.base.ApplicationBase+)"
)
static final String errorMessage =
"Applications with main methods have to implement BasicInterface or extend ApplicationBase";
}
Компилировать с Maven
При запуске mvn clean compile
(см. Проект GitHub для POM) вы увидите этот вывод (немного укороченный):
[INFO] ------------------------------------------------------------------------
[INFO] Building AspectJ sample with declare error 1.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- aspectj-maven-plugin:1.10:compile (default) @ aspectj-application-contract-enforcer ---
[INFO] Showing AJC message detail for messages of types: [error, warning, fail]
[ERROR] "Applications with main methods have to implement BasicInterface or extend ApplicationBase"
C:\Users\alexa\Documents\java-src\SO_AJ_EnforceMainClassImplementingInterface\src\main\java\de\scrum_master\app\UnwantedApplication.java:12
public static void main(String[] args) {
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
Ошибка просмотра в Eclipse
В Eclipse с AJDT (AspectJ Development Tools) это выглядит так:
Просто переименуйте метод main
в UnwantedApplication
во что-то еще, например mainX
, и ошибка исчезнет.