Artículos relacionados en el pasado :
conceptos básicos de AOP y aspectos de SpringAOP Introducción de
10 minutos a SpringAOP
Directorio de artículos
prefacio
Que es PCD
PCD (designadores de corte de puntos) es la expresión de corte de puntos de SpringAOP. El PCD de SpringAOP es totalmente compatible con AspectJ, y hay 10 tipos en total.
PCD de un vistazo
orientación del usuario
Spring AOP se implementa en base a un proxy dinámico, lo siguiente se usa para 目标对象
representar el bean proxy y el objeto proxy representa el bean construido por AOP. El método de destino representa el método que se está transfiriendo.
ejecución
La ejecución es el PCD más utilizado. Su plantilla coincidente se muestra a continuación:
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern)
throws-pattern?)
execution(修饰符匹配式? 返回类型匹配式 类名匹配式? 方法名匹配式(参数匹配式) 异常匹配式?)
Las ?
fórmulas de coincidencia firmadas en el bloque de código son todas opcionales y execution PCD
solo hay tres esenciales:
- Valor de retorno valor coincidente
- Coincidencia de nombre de método
- Coincidencia de parámetros
Análisis de ejemplo: execution(public * ServiceDemo.*(..))
coincide con el modificador público, el valor de retorno es *
, es decir , cualquier tipo de valor de retorno es correcto, ServiceDemo
el tipo de coincidencia de nombre de clase no tiene que ser la ruta completa, siempre que la visibilidad global sea única , .*
es el tipo de coincidencia de nombre de método, coincidente con todos los métodos, ..
sí Tipo de coincidencia de parámetros, un método para hacer coincidir cualquier número y cualquier tipo de parámetro.
Dé algunos otros ejemplos:
-
execution(* com.xyz.service..*.*(..))
: Haga coincidir cualquier método en com.xyz.service y sus subpaquetes. -
execution(* joke(Object+)))
: Coincide con cualquierjoke
método cuyo nombre sea , y su parámetro de entrada dinámica sea Tipo de objeto o una subclase de esta clase. -
execution(* joke(String,..))
: Haga coincidirjoke
el método con cualquier nombre , un parámetro de entrada de este método es String (no puede ser una subclase), puede haber cualquier número de parámetros de entrada y el tipo de parámetro de entrada es ilimitado -
execution(* com..*.*Dao.find*(..))
: Coincide con el método al principio de buscar en el paquete especificado -
execution(* com.baobaotao.Waiter+.*(..))
: Coincide conWaiter
todos los métodos del paquete com.baobaotao y sus subclases.
dentro
Filtre todas las clases de un determinado paquete y preste atención a ellas *
.
-
within(com.xyz.service.*)
Clases del paquete com.xyz.service, excluidos los subpaquetes -
within(com.xyz.service..*)
Clases del paquete com.xyz.service y sus subpaquetes
esta
Usado a menudo 命名绑定模式
. Filtrar por tipo de objeto proxy.
Si la clase de destino se implementa en base a una interfaz, this()
se puede completar el 该接口
nombre de ruta completo. De lo contrario, debido a que la implementación sin interfaz se basa en CGLIB, 目标类
el nombre de ruta completo se puede completar aquí .
this(com.xyz.service.AccountService)
: La clase de proxy es com.xyz.service.AccountService o sus subclases.
El uso @EnableAspectJAutoProxy(proxyTargetClass = true)
puede forzar el uso de CGLIB. De lo contrario, el proxy dinámico jdk se usa primero por defecto, y CGLIB solo se usará si jdk no es un proxy.
objetivo
Esto actúa sobre el objeto proxy y el objetivo actúa sobre el objeto objetivo.
target(com.xyz.service.AccountService)
: La clase de proxy (objeto de destino) es com.xyz.service.AccountService o sus subclases
argumentos
A menudo se utiliza para hacer coincidir los parámetros del método de destino. Generalmente no se usa solo, sino que se usa junto con otros PCD. args puede usar 命名绑定
modos, como se muestra a continuación:
@Aspect // 切面声明
@Component // 注入IOC
@Slf4j
class AspectDemo {
@Around("within(per.aop.*) && args(str)") // 在per.aop包下,且被代理方法的只有一个参数,参数类型是String或者其子类
@SneakyThrows
public Object logAspect(ProceedingJoinPoint pjp, String str) {
String signature = pjp.getSignature().toString();
log.info("{} start,param={}", signature, pjp.getArgs());
Object res = pjp.proceed();
log.info("{} end", signature);
return res;
}
}
- Si la respuesta es sí en los argumentos,
参数名
utilice el método de asesoramiento para determinar el tipo de parámetro del método que debe coincidir. - Si args es un tipo, por ejemplo
@Around("within(per.aop.*) && args(String)”)
, no necesita usar el método de aspecto para determinar el tipo, pero no puede usar el enlace de parámetros en este momento . Consulte a continuación .
Aunque se args()
admiten +
símbolos, esta provincia args()
admite comodines de subcategoría.
execution
Diferencia con la coincidencia de parámetros
Por ejemplo: args(com.xgj.Waiter)
equivalente a execution(* *(com.xgj.Waiter+))
. Y la ejecución no puede soportar consejos con parámetros.
@objetivo
Ejemplos de escenarios de uso: cuando un servicio tiene varias subclases, algunas subclases deben registrarse y algunas subclases no necesitan registrarse, lo que se puede manejar de la siguiente manera (con polimorfismo de Java):
Filtre si el objeto proxy con la anotación dada es un objeto o no una clase, y @target es dinámico . Personalice una anotación de la siguiente manera LogAble
:
//全限定名: annotation.LogAble
@Target({
ElementType.TYPE,ElementType.PARAMETER}) // 支持在方法参数、类上注
@Retention(RetentionPolicy.RUNTIME)
public @interface LogAble {
}
Si necesita " public
registrar los métodos de todas las clases con esta anotación ", entonces la lógica de registro debe personalizarse . Puede escribir PCD de la siguiente manera. Por supuesto, el bean de método correspondiente debe inyectarse en el contenedor SpringIOC:
@Around("@target(annotation.LogAble) && execution(public * *.*(..))")
// 自定义日志逻辑
@args
运行时类型
Deben @args
especificarse anotaciones para los parámetros del método de destino . Es el tipo de parámetro de método que tiene una anotación específica, no la anotación en el parámetro de método.
Escenario de uso: si el tipo de parámetro tiene varias subclases, solo una determinada subclase puede coincidir con el PCD.
-
@args(com.ms.aop.jargs.demo1.Anno1)
: Se requiere el parámetro Match 1 y la anotación Anno1 cuando se ejecuta el primer parámetro -
@args(com.ms.aop.jargs.demo1.Anno1,..)
Para hacer coincidir uno o más parámetros, el primer parámetro debe anotarse con Anno1 cuando se ejecuta. -
@args(com.ms.aop.jargs.demo1.Anno1,com.ms.aop.jargs.demo1.Anno2)
: Un parámetro coincide con Anno1, dos parámetros coinciden con Annno2.
@dentro
No 运行时类型的
de @target
. @target se enfoca en el objeto que se llama y @within se enfoca en la clase donde se llama al método.
La diferencia entre @target y @within:
@target (Anotación A): determina 目标对象
si la anotación A está declarada en la llamada , si la hay, será interceptada
@within (Anotación A): determina 类
si la anotación A está declarada en el método al que pertenece el método llamado . Si lo hay, será interceptado
@anotación
Haga coincidir el método con la anotación especificada (la anotación actúa sobre el método)
frijol
Según beanNam para que coincida. Soporta *
comodines.
bean(*Service) // 匹配所有Service结尾的Service
otro
Uso combinado
&& || !
Se admiten tres tipos de operadores entre PCD . El operador && se utiliza en el ejemplo anterior. ||
Significa o (sin cortocircuito o). !
Representa no.
Modo de enlace con nombre
El @Around("within(per.aop.*) && args(str)")
ejemplo anterior es utilizar el modo de vinculación con nombre, escribir el nombre de la variable en el PCD y limitar el tipo de nombre de la variable en el método.
@Around("within(per.aop.*) && args(str)")
public Object logAspect(ProceedingJoinPoint pjp, String str) {
...}
Como en el ejemplo anterior, si str es de tipo String o su subclase, y el método solo puede tener un parámetro.
el enlace de nombre solo se permite en los pcds de destino, esto y args
El modo de enlace mencionado solo admite target、this、args
tres PCD.
argNames
Observando el código fuente, podemos encontrar que todas las anotaciones de Advice tienen argNames
campos, como @Around:
@Retention(RetentionPolicy.RUNTIME)
@Target({
ElementType.METHOD})
public @interface Around {
String value();
String argNames() default "";
}
En qué circunstancias se utilizará este atributo, el siguiente ejemplo explica:
@Around(value = "execution(* TestBean.paramArgs(..)) && args(decimal,str,..)&& target(bean)", argNames = "pjp,str,decimal,bean")
@SneakyThrows // proceed会抛受检异常
Object aroundArgs(ProceedingJoinPoint pjp,/*使用命名绑定模式*/ String str, BigDecimal decimal, Object bean) {
// 在方法执行前做一些操作
return pjp.proceed();
}
Los nombres de arg deben usarse con args, target, this tags. Aunque no es necesario en funcionamiento real, la recomendación oficial es traer todos los parámetros con parámetros por las siguientes razones:
Por lo tanto, si no se especifica el atributo 'nombres de archivo', Spring AOP observará la información de depuración de la clase e intentará determinar el nombre del parámetro a partir de la tabla de variables locales. Este mensaje aparecerá siempre que la clase se compile con información de depuración (al menos '-g: vars'). El resultado de compilar con esta bandera es:
(1) Su código será más fácil de realizar ingeniería inversa)
(2) El tamaño del archivo de la clase será muy grande (generalmente irrelevante)
(3) El compilador no aplicará la optimización de la eliminación de variables locales no utilizadas.
Además, si el código compilado no tiene la información de depuración necesaria, Spring AOP intentará inferir el emparejamiento de variables de enlace y parámetros. Si el enlace de la variable es ambiguo bajo la información disponible, se lanzará una AmbiguousBindingException. Si las estrategias anteriores fallan, se lanzará una IllegalArgumentException.
Se recomienda incluir todos los comentarios de consejos,
argNames
y la idea se lo recordará de todos modos.