1.Spring AOP五种通知详情
1)环绕通知 @Around 环绕通知围绕在连接点前后
2)前置通知 @Before 在连接点前面执行
方法进行
3)环绕通知 @Around 环绕通知围绕在连接点前后
4)后置通知 @After 在连接点执行完成后执行,不管是正常执行完成,还是抛出异常,都会执行返回通知中的内容
5)正常返回通知 @AfterReturning 在连接点正常执行完成后执行,如果连接点抛出异常,则不会执行
2.切入点指示符
execution 用于匹配方法执行的连接点;
within 用于匹配指定类型内的方法执行;
this 用于匹配当前AOP代理对象类型的执行方法;注意是AOP代理对象的类型匹配,这样就可能包括引入接口也类型匹配;
target 用于匹配当前目标对象类型的执行方法;注意是 目标对象的类型匹配,这样就不包括引入接口也类型匹配;
args 用于匹配当前执行的方法传入的参数为指定类型的执行方法;
@within 用于匹配所以持有指定注解类型内的方法;
@target 用于匹配当前目标对象类型的执行方法,其中目标对象持有指定的注解;
@args 用于匹配当前执行的方法传入的参数持有指定注解的执行;
@annotation 用于匹配当前执行方法持有指定注解的方法;
bean Spring AOP扩展的,AspectJ没有对于指示符,用于匹配特定名称的Bean对象的执行方法;
reference pointcut 表示引用其他命名切入点,只有@ApectJ风格支持,Schema风格不支持。
3.AOP切入点详解
1)execution(public * (…)) 匹配所有的public修饰符的方法
execution( com.test.method.des….(…))
1, execution() 表达式的主体
2, 第一个“”符号 表示返回值的类型任意
3, com.test.method.des AOP所切的服务的包名,即,需要进行横切的业务类
4, 包名后面的“…” 表示当前包及子包
5, 第二个“” 表示类名,即所有类
6, .(…) 表示任何方法名,括号表示参数,两个点表示任何参数类型
范例
- public * *(…) 任何公共方法的执行
- com.learn…IHelloService.*() com.learn包及所有子包下IHelloService接口中的任何无参方法
- com.learn….(…) com.learn包及所有子包下任何类的任何方法
- com.learn…IHelloService.() com.learn包及所有子包下IHelloService接口的任何只有一个参数方法
- (!com.learn…IHelloService+).*(…) 非“com.learn包及所有子包下IHelloService接口及子类型”的任何方法
- com.learn…IHelloService+.() com.learn包及所有子包下IHelloService接口及子类型的的任何无参方法
com.learn…IHelloService.test(java.util.Date) com.learn包及所有子包下IHelloService前缀类型的的以test开头的只有一个参数类型为java.util.Date的方法,注意该匹配是根据方法签名的参数类型进行匹配的,而不是根据执行时传入的参数类型决定的,如定义方法:public void test(Object obj);即使执行时传入java.util.Date,也不会匹配的;
*com.learn…IHelloService.test(…) throws IllegalArgumentException, ArrayIndexOutOfBoundsException com.learn包及所有子包下IHelloService前缀类型的的任何方法,且抛出IllegalArgumentException和ArrayIndexOutOfBoundsException异常 - (com.learn…IHelloService+ && java.io.Serializable+).*(…) 任何实现了com.learn包及所有子包下 IHelloService接口和java.io.Serializable接口的类型的任何方法
@java.lang.Deprecated * *(…) 任何持有@java.lang.Deprecated注解的方法
@(java.lang.Deprecated || com.learn…Secure) * *(…) 任何持有@java.lang.Deprecated或@com.learn…Secure注解的方法
(@com.learn…Secure *) *(…) 任何返回值类型持有@com.learn…Secure的方法 - (@com.learn…Secure ).(…) 任何定义方法的类型持有@com.learn…Secure的方法
- (@com.learn…Secure () , @com.learn…Secure (*)) 任何签名带有两个参数的方法,且这个两个参数都被@Secure标记了,如public void test(@Secure String str1, @Secure String str1)
- *((@ com.learn…Secure *))或 * *(@ com.learn…Secure *) 任何带有一个参数的方法,且该参数类型持有@com.learn…Secure;如public void test(Model model);且Model类上持有@Secure注解
- *( @com.learn…Secure (@com.learn…Secure *) , @ com.learn…Secure (@com.learn…Secure *)) 任何带有两个参数的方法,且这两个参数都被@com.learn…Secure标记了;且这两个参数的类型上都持有@ com.learn…Secure;
*( java.util.Map<com.learn…Model, com.learn…Model> , …) 任何带有一个java.util.Map参数的方法,且该参数类型是以< com.learn…Model, com.learn…Model >为泛型参数;注意只匹配第一个参数为java.util.Map,不包括子类型;如public void test(HashMap<Model, Model> map, String str);将不匹配,必须使用“ *( java.util.HashMap<com.learn…Model,com.learn…Model> , …)”进行匹配; 而public void test(Map map, int i);也将不匹配,因为泛型参数不匹配 * *(java.util.Collection<@com.learn…Secure *>) 任何带有一个参数(类型为java.util.Collection)的方法,且该参数类型是有一个泛型参数,该泛型参数类型上持有@com.learn…Secure注解; 如public void test(Collection collection);Model类型上持有@com.learn…Secure * *(java.util.Set<? extends HashMap>) 任何带有一个参数的方法,且传入的参数类型是有一个泛型参数,该泛型参数类型继承与HashMap; Spring AOP目前测试不能正常工作 - *(java.util.List<? super HashMap>) 任何带有一个参数的方法,且传入的参数类型是有一个泛型参数,该泛型参数类型是HashMap的基类型;如public voi test(Map map);Spring AOP目前测试不能正常工作
- (<@com.learn…Secure *>) 任何带有一个参数的方法,且该参数类型是有一个泛型参数,该泛型参数类型上持有@com.learn…Secure注解;Spring AOP目前测试不能正常工作
4.demo
- 依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
2.创建注解
//作用域 作用于方法和类上
@Target(value = {ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented //表示把注解生成在Javadoc中
@Inherited //表示可以被继承
public @interface MySecure {
//注解的参数:类型+参数名()[default 默认值];
String name() default "";
String[] role() default {"user"};
}
- 切面
@Component
@Aspect
public class MySecureAspect {
@Before("@annotation(MySecure)")
public void dopointcut(JoinPoint jp){
System.out.println("方法运行前检查");
MethodSignature signature = (MethodSignature)jp.getSignature();
//获取注解上的值
String[] role = signature.getMethod().getAnnotation(MySecure.class).role();
for (String s : role) {
System.out.println("========获取传入注解的role值========="+s);
}
//获取该方法上的请求参数
Parameter[] parameters = signature.getMethod().getParameters();
for (Parameter s : parameters) {
System.out.println("========获取该方法上的请求参数========="+s.toString());
}
//获取方法
String method = signature.getMethod().toString();
System.out.println("========method========="+method);
//获取用户ip地址
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
String IpAdr = request.getRemoteAddr();
System.out.println("========获取用户ip地址========="+IpAdr);
}
//扫描com.example.demo.aop.service包下的所有的类的方法
@AfterReturning("execution(* com.example.demo.aop.service..*.*(..))")
public void log(){
System.out.println("========方法正常执行完成======");
}
}
4.测试 (UserServcie的包com.example.demo.aop.service)
@Service
public class UserServcie {
@MySecure(role = {"admin"})
public void login(String username,String password){
System.out.println("=======这是一个登录方法========");
}
}
打印结果