关于注解的实现
切入点
切入点表达式
Spring AOP 支持的切入点主要有以下几种:
-
execution:用于匹配方法执行的连接点。这是最常用的切入点指示器。你可以指定具体的方法,或者类来匹配。 例如:
execution(* com.example.service.*.*(..))
,这个表达式表示匹配 com.example.service 包下的所有类的所有方法。 -
within:用于匹配特定类型内的方法执行。 例如:
within(com.example.service.*)
,这个表达式表示匹配 com.example.service 包下所有类的所有方法。 -
this:用于匹配创建了 AOP 代理的对象的方法执行。 例如:
this(com.example.service.MyService)
,这个表达式表示匹配实现了 com.example.service.MyService 接口的代理对象的方法。 -
target:用于匹配目标对象的方法执行。 例如:
target(com.example.service.MyService)
,这个表达式表示匹配实现了 com.example.service.MyService 接口的目标对象的方法。 -
args:用于匹配方法参数。 例如:
args(java.io.Serializable)
,这个表达式表示匹配那些接收单个参数,且参数类型是 java.io.Serializable 的方法。 -
@target:用于匹配持有指定注解的类的所有方法。 例如:
@target(org.springframework.stereotype.Service)
,这个表达式表示匹配所有使用 @Service 注解的类的方法。 -
@args:用于匹配传入参数持有指定注解的方法。 例如:
@args(org.springframework.validation.annotation.Validated)
,这个表达式表示匹配所有接收了使用 @Validated 注解的参数的方法。 -
@within:用于匹配持有指定注解的类及其子类的所有方法。 例如:
@within(org.springframework.stereotype.Service)
,这个表达式表示匹配所有使用 @Service 注解的类及其子类的方法。 -
@annotation:用于匹配当前执行方法持有指定注解的方法。 例如:
@annotation(org.springframework.transaction.annotation.Transactional)
,这个表达式表示匹配所有使用 @Transactional 注解的方法。
这些切入点定义方式在实际使用时可以相互组合,例如:execution(* com.example..*.*(..)) && @annotation(org.springframework.transaction.annotation.Transactional)
,这表示匹配 com.example 包及其子包下所有类中使用了 @Transactional 注解的方法。
不同的切入点有不同的适用场景,你可以根据需要选择合适的切入点表达式。例如,如果你想要针对某个特定的类或者特定的方法进行增强,那么你可能会选择使用 execution;如果你想要针对某种特定的注解进行增强,那么你可能会选择使用 @annotation。
target
和 @target
都是 Spring AOP 提供的两种不同类型的切入点声明,它们的主要区别在于匹配对象的方式和范围。
-
target
:这种类型的切入点表达式用于匹配目标对象的方法执行。在这种情况下,Spring AOP 会检查被增强的对象(即 AOP 的目标对象)是否符合指定的类型。例如,target(com.example.service.MyService)
,这个表达式表示匹配所有实现了com.example.service.MyService
接口的目标对象的方法。 -
@target
:这种类型的切入点表达式用于匹配持有指定注解的类的所有方法。Spring AOP 会检查目标对象的类是否有指定的注解。例如,@target(org.springframework.stereotype.Service)
,这个表达式表示匹配所有使用@Service
注解的类的方法。
总的来说,target
是根据类(或接口)类型来匹配的,而 @target
是根据类是否标注了特定注解来匹配的。这两者可以根据具体需求来选择使用。
在Spring AOP中,主要有两种风格的切入点定义:Spring风格和AspectJ风格。
-
Spring风格:Spring风格的切入点表达式主要使用“execution”、“args”、“@args”、“@within”、“within”、“@annotation”和“@target”等关键字,这些关键字可以定义匹配方法的规则。例如,“execution(* com.example.service.*(..))”可以匹配com.example.service包下的所有方法。
-
AspectJ风格:AspectJ风格的切入点表达式则更为强大和灵活,它支持所有Spring风格的关键字,此外,它还支持“call”、“get”、“set”、“handler”、“preinitialization”、“staticinitialization”和“@this”等关键字。例如,“call(* com.example.service.*(..))”可以匹配对com.example.service包下的所有方法的调用。
这两种风格的切入点定义的主要区别在于支持的关键字和匹配的粒度。Spring风格主要关注于方法的执行,而AspectJ风格则更为全面,除了方法执行外,还可以匹配方法调用、字段读写等操作,因此,AspectJ风格可以提供更为细致的控制。
值得注意的是,虽然AspectJ风格更为强大,但是Spring AOP的实现仅支持部分AspectJ的切入点表达式,这是因为Spring AOP底层基于动态代理实现,部分切入点(例如“call”)在动态代理中无法实现。因此,在使用AspectJ风格时,还需要考虑Spring AOP的限制。
自定义切入点
通过Pointcut获取MethodMatcher
MethodMatcher实现matches方法
在Spring AOP中,
Pointcut
接口包含两个重要的组件:ClassFilter
和MethodMatcher
。
ClassFilter
用于确定某个类是否应被AOP代理影响。matches(Class)
方法在这个接口中定义,用于判断给定的类是否应用切面。
MethodMatcher
接口用于判断类中的哪些方法应该被AOP框架拦截。在这个接口中定义了matches(Method, Class)
和isRuntime()
方法。如果isRuntime()
方法返回false,那么matches(Method, Class)
将会被调用,用来做静态的匹配检查;如果isRuntime()
返回true,那么在静态检查通过后,还会进行一次运行时的检查,也就是调用matches(Method, Class, Object[])
方法。
Advisor通知
AuthorizationAttributeSourceAdvisor
它定义了切点(即哪些方法应当被拦截)以及通知Advice(即在匹配的方法上执行的动作)。
这里的Advice就是Interceptor.
拦截器
AnnotationsAuthorizingMethodInterceptor
是 Shiro 的一个类,它是一个方法拦截器,专门用来处理与授权相关的注解。这个类是 org.apache.shiro.aop.MethodInterceptor
的实现,其主要作用是在方法调用之前进行授权检查。
当一个方法被调用时,AnnotationsAuthorizingMethodInterceptor
会检查该方法是否有与授权相关的注解(如 @RequiresPermissions
, @RequiresRoles
, @RequiresUser
, @RequiresGuest
, @RequiresAuthentication
等)。如果有,它将根据这些注解进行授权检查。如果授权检查失败,它会阻止方法的调用并抛出一个异常。
AnnotationsAuthorizingMethodInterceptor
主要通过 invoke
方法来完成这项工作。在 invoke
方法中,它首先获取方法上的所有授权相关注解,然后通过调用 assertAuthorized
方法来进行授权检查。如果检查失败,assertAuthorized
方法将抛出一个 AuthorizationException
异常。如果检查成功,invoke
方法将调用下一个拦截器或者目标方法。
总的来说,AnnotationsAuthorizingMethodInterceptor
的作用是在方法调用前进行基于注解的授权检查。