Introdução às expressões pointcut na primavera
1. Visão geral
Neste tutorial, discutiremos a linguagem de expressões de corte de pontos do Spring AOP.
Introduziremos primeiro uma terminologia usada na programação orientada a aspectos. Umjoin point é uma etapa da execução do programa, como a execução de um método ou o tratamento de uma exceção. No Spring AOP, um ponto de junção sempre representa uma execução de método. Umpointcut é um predicado que corresponde aos pontos de junção e umpointcut expression language é uma maneira de descrever os pontos de corte de forma programática.
2. Uso
Uma expressão de pointcut pode aparecer como um valor da anotação@Pointcut:
@Pointcut("within(@org.springframework.stereotype.Repository *)")
public void repositoryClassMethods() {}
A declaração do método é chamada depointcut signature. Ele fornece um nome que pode ser usado por anotações de conselhos para se referir a esse apontamento.
@Around("repositoryClassMethods()")
public Object measureMethodExecutionTime(ProceedingJoinPoint pjp) throws Throwable {
...
}
Uma expressão de pointcut também pode aparecer como o valor da propriedadeexpression de uma tagaop:pointcut:
3. Pointcut Designators
Uma expressão de pointcut começa compointcut designator (PCD), que é uma palavra-chave dizendo ao Spring AOP o que corresponder. Existem vários designadores de ponto de corte, como a execução de um método, um tipo, argumentos de método ou anotações.
3.1 execution
O Spring PCD primário éexecution, que corresponde aos pontos de junção de execução do método.
@Pointcut("execution(public String org.example.dao.FooDao.findById(Long))")
Este pointcut de exemplo corresponderá exatamente à execução do métodofindById da classeFooDao. Isso funciona, mas não é muito flexível. Suponha que gostaríamos de combinar todos os métodos da classeFooDao, que podem ter assinaturas, tipos de retorno e argumentos diferentes. Para conseguir isso, podemos usar curingas:
@Pointcut("execution(* org.example.dao.FooDao.*(..))")
Aqui, o primeiro curinga corresponde a qualquer valor de retorno, o segundo corresponde a qualquer nome de método e o padrão(..) corresponde a qualquer número de parâmetros (zero ou mais).
3.2 within
Outra maneira de obter o mesmo resultado da seção anterior é usando owithin PCD, que limita a correspondência para pontos de junção de certos tipos.
@Pointcut("within(org.example.dao.FooDao)")
Também podemos combinar qualquer tipo no pacoteorg.example ou em um subpacote.
@Pointcut("within(org.example..*)")
3.3 this and target
this limita a correspondência para pontos de junção onde a referência do bean é uma instância do tipo fornecido, enquantotarget limita a correspondência para pontos de junção onde o objeto de destino é uma instância do tipo dado. O primeiro funciona quando o Spring AOP cria um proxy baseado em CGLIB e o último é usado quando um proxy baseado em JDK é criado. Suponha que a classe de destino implemente uma interface:
public class FooDao implements BarDao {
...
}
Nesse caso, Spring AOP usará o proxy baseado em JDK e você deve usar otarget PCD porque o objeto com proxy será uma instância da classeProxy e implementará a interfaceBarDao:
@Pointcut("target(org.example.dao.BarDao)")
Por outro lado, seFooDao não implementar nenhuma interface ou a propriedadeproxyTargetClass for definida como verdadeira, o objeto proxy será uma subclasse deFooDaoe o PCDthis poderia ser usado:
@Pointcut("this(org.example.dao.FooDao)")
3.4 args
Este PCD é usado para corresponder argumentos de métodos específicos:
@Pointcut("execution(* *..find*(Long))")
Este pointcut corresponde a qualquer método que comece com find e tenha apenas um parâmetro do tipoLong. Se quisermos combinar um método com qualquer número de parâmetros, mas tendo o primeiro parâmetro do tipoLong, poderíamos usar a seguinte expressão:
@Pointcut("execution(* *..find*(Long,..))")
3.5 @target
O@target PCD (não deve ser confundido com otarget PCD descrito acima) limita a correspondência para pontos de junção onde a classe do objeto em execução tem uma anotação do tipo dado:
@Pointcut("@target(org.springframework.stereotype.Repository)")
3.6 @args
Esse PCD limita a correspondência aos pontos de junção nos quais o tipo de tempo de execução dos argumentos reais passados possui anotações do (s) tipo (s) fornecido (s). Suponha que desejamos rastrear todos os métodos que aceitam beans anotados com a anotação@Entity:
@Pointcut("@args(org.example.aop.annotations.Entity)")
public void methodsAcceptingEntities() {}
Para acessar o argumento, devemos fornecer um argumentoJoinPoint para o conselho:
@Before("methodsAcceptingEntities()")
public void logMethodAcceptionEntityAnnotatedBean(JoinPoint jp) {
logger.info("Accepting beans with @Entity annotation: " + jp.getArgs()[0]);
}
3.7 @within
Esse PCD limita a correspondência aos pontos de junção em tipos com a anotação fornecida:
@Pointcut("@within(org.springframework.stereotype.Repository)")
O que equivale a:
@Pointcut("within(@org.springframework.stereotype.Repository *)")
3.8 @annotation
Esse PCD limita a correspondência aos pontos de junção em que o assunto do ponto de junção possui a anotação fornecida. Por exemplo, podemos criar uma anotação@Loggable:
@Pointcut("@annotation(org.example.aop.annotations.Loggable)")
public void loggableMethods() {}
Então, podemos registrar a execução dos métodos marcados por essa anotação:
@Before("loggableMethods()")
public void logMethod(JoinPoint jp) {
String methodName = jp.getSignature().getName();
logger.info("Executing method: " + methodName);
}
4. Combinando Expressões de Pointcut
Expressões de ponto de corte podem ser combinadas usando os operadores&&,||e!:
@Pointcut("@target(org.springframework.stereotype.Repository)")
public void repositoryMethods() {}
@Pointcut("execution(* *..create*(Long,..))")
public void firstLongParamMethods() {}
@Pointcut("repositoryMethods() && firstLongParamMethods()")
public void entityCreationMethods() {}
5. Conclusão
Nesta introdução rápida ao Spring AOP e pointcuts, ilustramos alguns exemplos do uso de expressões pointcut.
O conjunto completo de exemplos pode ser encontrado emmy github project - este é um projeto baseado em Eclipse, portanto, deve ser fácil de importar e executar como está.