A programação orientada a aspectos (AOP) é um paradigma de programação que é um desenvolvimento adicional da programação procedural e orientada a objetos (OOP). A idéia do AOP é destacar a chamada funcionalidade de ponta a ponta. E, como tudo está em ordem, mostrarei aqui como fazê-lo no estilo de anotação Java - Spring @AspectJ (também existe um estilo xml baseado em esquema, a funcionalidade é semelhante).
Destacando a funcionalidade de ponta a ponta
Para

e depois

I.e. existe uma funcionalidade que afeta vários módulos, mas não está diretamente relacionada ao código comercial, e seria bom colocá-lo em um local separado, conforme mostrado na figura acima.
Ponto de união

Ponto de junção - o próximo conceito de AOP, esses são os pontos de observação, adesão ao código onde a introdução da funcionalidade é planejada.
Pointcut

Um pointcut é uma fatia, uma consulta para pontos de anexo, pode ser um ou mais pontos. As regras para consultar pontos são muito diversas, na figura acima, uma solicitação de anotação em um método e um método específico. As regras podem ser combinadas com &&, ||,!
Conselhos

Conselho - um conjunto de instruções executadas nos pontos de corte (Pointcut). As instruções podem ser executadas em um evento de vários tipos:
- Antes - antes de uma chamada de método
- Depois - após uma chamada de método
- Após retornar - após retornar um valor de uma função
- Depois de jogar - em caso de exceção
- Depois de finalmente - se o bloco finalmente for executado
- Around - você pode fazer o pré., Post., Processando antes da chamada do método, e também geralmente ignorar a chamada do método.
em um Pointcut, você pode "travar" vários conselhos de diferentes tipos.
Aspecto

Aspecto - um módulo que contém descrições Pointcut e Conselhos.
Agora vou dar um exemplo e, finalmente, tudo se encaixará (ou quase todos). Todos sabemos sobre o código de registro que permeia muitos módulos, não relacionados ao código comercial, mas, no entanto, é impossível sem ele. E, portanto, separo essa funcionalidade do código comercial.
Exemplo - registro de código
Serviço de destino
@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; } }
Um aspecto com uma descrição de Pointcut e Conselhos.
@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()); } }
E o código de teste de chamada
@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()); } }
Explicações No serviço de destino, não há menção ao log, no código de chamada ainda mais, todo o log é concentrado
em um módulo separado@Aspect
class MyAspect ...
Em pointcut
@Pointcut("execution(public * com.example.demoAspects.MyService.*(..))") public void callAtMyServicePublic() { }
Solicitei todos os métodos públicos do MyService com qualquer tipo de retorno
* e o número de argumentos
(..)No
aviso Antes e Depois, consulte o
Pointcut (callAtMyServicePublic) , escrevi instruções para escrever no log. O JoinPoint não é um parâmetro obrigatório, que fornece informações adicionais, mas, se usado, deve ser o primeiro.
Tudo é espaçado em diferentes módulos! Código de chamada, destino, registro.
Resultado no console

As regras do pointcut podem variar
Alguns exemplos de Pointcut and Advice:
Solicitação de anotação em um método.
@Pointcut("@annotation(AspectAnnotation)") public void callAtMyServiceAnnotation() { }
Conselhos para ele
@Before("callAtMyServiceAnnotation()") public void beforeCallAt() { }
Solicitação de um método específico indicando os parâmetros do método de destino
@Pointcut("execution(* com.example.demoAspects.MyService.method1(..)) && args(list,..))") public void callAtMyServiceMethod1(List<String> list) { }
Conselhos para ele
@Before("callAtMyServiceMethod1(list)") public void beforeCallAtMethod1(List<String> list) { }
Pointcut para resultado de retorno
@Pointcut("execution(* com.example.demoAspects.MyService.check())") public void callAtMyServiceAfterReturning() { }
Conselhos para ele
@AfterReturning(pointcut="callAtMyServiceAfterReturning()", returning="retVal") public void afterReturningCallAt(boolean retVal) { }
Um exemplo de verificação de direitos para o Aviso do tipo Around, através da anotação
@Retention(RUNTIME) @Target(METHOD) public @interface SecurityAnnotation { }
Os métodos que precisam ser verificados antes da chamada podem ser anotados com "SecurityAnnotation"; em Aspect, temos uma fatia deles, e todos serão interceptados antes da chamada e a verificação de direitos.
Código de destino:
@Service public class MyService { @SecurityAnnotation public Balance getAccountBalance(User user) {
Código do chamador:
balance = myService.getAccountBalance(user); if (balance == null) { accessDenied(user); } else { displayBalance(balance); }
I.e. no código de chamada e no destino, não há verificação de direitos, apenas o próprio código comercial.
Um exemplo de criação de perfil do mesmo serviço usando um aviso do tipo 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()); } } }
Se executarmos o código de chamada com chamadas para os métodos MyService, obteremos o tempo da chamada para cada método. Assim, sem alterar o código de chamada e o destino, adicionei novos recursos: registro, criação de perfil e segurança.
Exemplo de uso em formulários de interface do usuário
Há um código que, ao definir oculta / mostra os campos no formulário:
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));
você também pode remover updateVisibility em um Conselho do tipo
Around @Aspect public class MyAspect { @Pointcut("execution(* com.example.demoAspects.EditForm.init() && args(form,..))") public void callAtInit(Form form) { }
etc.
Estrutura do projeto

arquivo pom <?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>
Materiais
Programação Orientada a Aspectos com Spring