Aspektorientierte Programmierung (AOP) ist ein Programmierparadigma, das eine Weiterentwicklung der prozeduralen und objektorientierten Programmierung (OOP) darstellt. Die Idee von AOP ist es, die sogenannte End-to-End-Funktionalität hervorzuheben. Und damit alles in Ordnung ist, werde ich hier zeigen, wie es in Java gemacht wird - Spring @AspectJ Annotation Style (es gibt auch einen schemabasierten XML-Stil, die Funktionalität ist ähnlich).
Hervorheben der End-to-End-Funktionalität
Zu

und danach

Das heißt, Es gibt Funktionen, die mehrere Module betreffen, die sich jedoch nicht direkt auf den Geschäftscode beziehen. Es wäre schön, sie an einem separaten Ort abzulegen. Dies ist in der obigen Abbildung dargestellt.
Verbindungspunkt

Verbindungspunkt - das nächste Konzept von AOP, dies sind die Beobachtungspunkte, der Zugang zum Code, an dem die Einführung von Funktionen geplant ist.
Pointcut

Ein Pointcut ist ein Slice, eine Abfrage nach Anhangspunkten. Es kann sich um einen oder mehrere Punkte handeln. Die Regeln für das Abfragen von Punkten sind sehr unterschiedlich. In der obigen Abbildung gibt es eine Anforderung für Anmerkungen zu einer Methode und eine bestimmte Methode. Regeln können durch &&, ||, kombiniert werden!
Beratung

Hinweis - Eine Reihe von Anweisungen, die an den Schnittpunkten ausgeführt werden (Pointcut). Anweisungen können für eine Veranstaltung verschiedener Art ausgeführt werden:
- Vor - vor einem Methodenaufruf
- Nach - nach einem Methodenaufruf
- Nach der Rückgabe - nach der Rückgabe eines Wertes von einer Funktion
- Nach dem Werfen - im Ausnahmefall
- Nach finally - wenn der finally-Block ausgeführt wird
- Around - Sie können die Vor-, Nach- und Verarbeitung vor dem Methodenaufruf durchführen und den Methodenaufruf im Allgemeinen umgehen.
An einem Pointcut können Sie mehrere Ratschläge verschiedener Typen "aufhängen".
Aspekt

Aspekt - ein Modul, das Pointcut- und Hinweisbeschreibungen enthält.
Jetzt werde ich ein Beispiel geben und schließlich wird alles (oder fast alles) zusammenpassen. Wir alle kennen den Protokollierungscode, der viele Module durchdringt und nicht mit dem Geschäftscode zusammenhängt, aber ohne ihn ist dies nicht möglich. Und so trenne ich diese Funktionalität vom Geschäftscode.
Beispiel - Code-Protokollierung
Zieldienst
@Service public class MyService { public void method1(List<String> list) { list.add("method1"); System.out.println("MyService method1 list.size=" + list.size()); } @AspectAnnotation public void method2() { System.out.println("MyService method2"); } public boolean check() { System.out.println("MyService check"); return true; } }
Ein Aspekt, der Pointcut und Ratschläge beschreibt.
@Aspect @Component public class MyAspect { private Logger logger = LoggerFactory.getLogger(this.getClass()); @Pointcut("execution(public * com.example.demoAspects.MyService.*(..))") public void callAtMyServicePublic() { } @Before("callAtMyServicePublic()") public void beforeCallAtMethod1(JoinPoint jp) { String args = Arrays.stream(jp.getArgs()) .map(a -> a.toString()) .collect(Collectors.joining(",")); logger.info("before " + jp.toString() + ", args=[" + args + "]"); } @After("callAtMyServicePublic()") public void afterCallAt(JoinPoint jp) { logger.info("after " + jp.toString()); } }
Und der aufrufende Testcode
@RunWith(SpringRunner.class) @SpringBootTest public class DemoAspectsApplicationTests { @Autowired private MyService service; @Test public void testLoggable() { List<String> list = new ArrayList(); list.add("test"); service.method1(list); service.method2(); Assert.assertTrue(service.check()); } }
Erklärungen Im Zieldienst wird die Protokollierung nicht erwähnt, im aufrufenden Code wird die gesamte Protokollierung
in einem separaten Modul konzentriert@Aspect
class MyAspect ...
In pointcut
@Pointcut("execution(public * com.example.demoAspects.MyService.*(..))") public void callAtMyServicePublic() { }
Ich habe alle öffentlichen MyService-Methoden mit einem beliebigen Rückgabetyp
* und der Anzahl der Argumente
(..) angefordert
.In den
Hinweisen vor und nach dem
Hinweis auf
Pointcut (callAtMyServicePublic) habe ich Anweisungen zum Schreiben in das Protokoll geschrieben. JoinPoint ist kein erforderlicher Parameter, der zusätzliche Informationen bereitstellt. Wenn er jedoch verwendet wird, sollte er der erste sein.
Alles ist in verschiedene Module unterteilt! Anrufercode, Ziel, Protokollierung.
Ergebnis in der Konsole

Pointcut-Regeln können variieren
Einige Beispiele für Pointcut und Ratschläge:
Anforderung einer Anmerkung zu einer Methode.
@Pointcut("@annotation(AspectAnnotation)") public void callAtMyServiceAnnotation() { }
Rat für ihn
@Before("callAtMyServiceAnnotation()") public void beforeCallAt() { }
Anforderung einer bestimmten Methode, die die Parameter der Zielmethode angibt
@Pointcut("execution(* com.example.demoAspects.MyService.method1(..)) && args(list,..))") public void callAtMyServiceMethod1(List<String> list) { }
Rat für ihn
@Before("callAtMyServiceMethod1(list)") public void beforeCallAtMethod1(List<String> list) { }
Pointcut für das Rückgabeergebnis
@Pointcut("execution(* com.example.demoAspects.MyService.check())") public void callAtMyServiceAfterReturning() { }
Rat für ihn
@AfterReturning(pointcut="callAtMyServiceAfterReturning()", returning="retVal") public void afterReturningCallAt(boolean retVal) { }
Ein Beispiel für die Überprüfung von Rechten für einen Hinweis vom Typ Around mithilfe von Anmerkungen
@Retention(RUNTIME) @Target(METHOD) public @interface SecurityAnnotation { }
Die Methoden, die vor dem Aufruf auf der rechten Seite überprüft werden müssen, können mit "SecurityAnnotation" versehen werden. In Aspect erhalten wir dann einen Teil davon, und alle werden abgefangen, bevor der Aufruf und die Rechteprüfung durchgeführt werden.
Zielcode:
@Service public class MyService { @SecurityAnnotation public Balance getAccountBalance(User user) {
Anrufercode:
balance = myService.getAccountBalance(user); if (balance == null) { accessDenied(user); } else { displayBalance(balance); }
Das heißt, Im aufrufenden Code und im Ziel gibt es keine Überprüfung der Rechte, nur den Geschäftscode selbst.
Ein Beispiel für die Profilerstellung desselben Dienstes mithilfe eines Hinweises vom Typ Around
@Aspect @Component public class MyAspect { @Pointcut("execution(public * com.example.demoAspects.MyService.*(..))") public void callAtMyServicePublic() { } @Around("callAtMyServicePublic()") public Object aroundCallAt(ProceedingJoinPoint call) throws Throwable { StopWatch clock = new StopWatch(call.toString()); try { clock.start(call.toShortString()); return call.proceed(); } finally { clock.stop(); System.out.println(clock.prettyPrint()); } } }
Wenn wir den aufrufenden Code mit Aufrufen der MyService-Methoden ausführen, erhalten wir die Zeit des Aufrufs für jede Methode. Ohne den aufrufenden Code und das Ziel zu ändern, habe ich neue Funktionen hinzugefügt: Protokollierung, Profiler und Sicherheit.
Beispiel für die Verwendung in UI-Formularen
Es gibt einen Code, der durch Festlegen von Feldern im Formular ein- / ausgeblendet wird:
public class EditForm extends Form { @Override public void init(Form form) { formHelper.updateVisibility(form, settingsService.isVisible(COMP_NAME)); formHelper.updateVisibility(form, settingsService.isVisible(COMP_LAST_NAME)); formHelper.updateVisibility(form, settingsService.isVisible(COMP_BIRTH_DATE));
Sie können updateVisibility auch in einem Hinweis vom Typ
Around entfernen
@Aspect public class MyAspect { @Pointcut("execution(* com.example.demoAspects.EditForm.init() && args(form,..))") public void callAtInit(Form form) { }
usw.
Projektstruktur

POM-Datei <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.example</groupId> <artifactId>demoAspects</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>demoAspects</name> <description>Demo project for Spring Boot Aspects</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.6.RELEASE</version> <relativePath/> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
Material
Aspektorientierte Programmierung mit Feder