aop术语:
- 通知(advice)
切面的工作被称为通知,分为以下几种
类型 | 注解 |
---|---|
前置通知(before) | @Before |
后置通知(after) | @After |
返回通知(after-returning) | @AfterReturning |
异常通知(after-throwing) | @AfterThrowing |
环绕通知(around) | @Around |
- 连接点(join point)
符合插入通知规则的点 - 切点(pointcut)
定义连接点的范围,常用切点表达式语言(部分举例)
aspectJ指示器 | 描述 | 举例 | 举例描述 |
---|---|---|---|
arg() | 限制连接点匹配参数为指定类型的执行方法 | execution(* springBase.aop.IService.count(int)) && args(count) | 指定使用IService接口中count方法的方法,并将方法的参数传入同通知 |
@args | 限制连接点匹配参数由指定注解标注的执行方法 | ||
execution() | 用于匹配连接点的执行方法 | execution(* springBase.aop.IService.add(…)) | 指定使用IService接口中add方法的方法 |
this() | 限制连接点匹配AOP代理的bean引用为指定类型的类 | ||
target | 限制连接点匹配目标对象为指定类型的类 | ||
@target | 限制连接点匹配特定的执行对象,这些对象对应的类要具有指定类型的注解 | ||
with | 限制连接点匹配指定的类型 | execution(* springBase.aop.IService.add(…)) && within(springBase.aop.demo.*) | 使用within限定包范围 |
@with | 限制连接点匹配指定注解所标注的类型 | ||
@annotation | 限定匹配带有指定注解的连接点 | @annotation(springBase.aop.Action) | 指定标有@Action注解的方法 |
bean | 限定匹配指定的bean | execution(* springBase.aop.IService.add(…)) && bean(serviceImpl) | 指定实现IService接口并且名称为serviceImpl的bean |
- 切面(aspect)
通知和切点的结合,定义在何时何地完成此通知的功能 - 引入(introduction)
- 织入(weaving)
将通知的代码在连接点调用,织入时机可以发生在编译期、类加载期、运行期(spring aop的方式)
举例
配置类:
@Configuration
@ComponentScan("springBase.aop")
@EnableAspectJAutoProxy//启动aspectj自动代理
public class JavaConfig {
}
切面类:
//注解切面:
@Aspect // 声明一个切面
@Component
public class LogAspect {
@Pointcut("@annotation(springBase.aop.Action)") // 注解声明切点,也可以声明其他的条件的切点
public void annotationPointCut() {
};
//注解切点和获取注解的方法一:通过获取方法对象获取注解对象
@After("annotationPointCut()")
public void after(JoinPoint joinPoint) {//这是一个通知
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
Action action = method.getAnnotation(Action.class);
System.out.println("注解式拦截 " + action.name());
}
//注解切点和获取注解的方法二:通过参数直接获取注解对象(推荐)
@After("annotationPointCut()&&@annotation(action)")
public void after(JoinPoint joinPoint,Action action) {
System.out.println("注解式拦截 " + action.name());
}
}
//方法过滤:
@Aspect // 声明一个切面
@Component
public class LogAspect {
// 表示接口springBase.aop.IService名为add的方法(任意返回值和任意参数),使用within限定必须在springBase.aop.demo包下
@Before("execution(* springBase.aop.IService.add(..)) && bean(serviceImpl)")
public void before(JoinPoint joinPoint) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
Class<?> c = joinPoint.getTarget().getClass();
System.out.println("方法式拦截, " + c.getName() + "," + method.getName());
}
}
//环绕:
@Aspect // 声明一个切面
@Component
public class LogAspect {
// 环绕必须要调用原方法
@Around("execution(* springBase.aop.IService.add(..)) && bean(serviceImpl)")
public void around(ProceedingJoinPoint pj) {
Object[] args = pj.getArgs();// 可以获取方法入参
System.out.println("方法入参:" + args);
MethodSignature signature = (MethodSignature) pj.getSignature();
Method method = signature.getMethod();
Class<?> c = pj.getTarget().getClass();
System.out.println("around前, " + c.getName() + "," + method.getName());
try {
Object ret = pj.proceed();// 调用原方法,在此处可以获取方法返回值
System.out.println("返回值:" + ret);
} catch (Throwable e) {
e.printStackTrace();
}
System.out.println("around后, " + c.getName() + "," + method.getName());
}
}
//处理参数:
@Aspect // 声明一个切面
@Component
public class LogAspect {
// 带参数的情况处理,声明一个入参为int并且入参名称为count的切点
@Pointcut("execution(* springBase.aop.IService.count(int)) && args(count)")
public void parameters(int count) {
};
@Before("parameters(count)")
public void countBefore(int count) {
System.out.println("count=" + count);
}
}
//增强接口:
//注入新功能,此处实现给IService接口增加IOtherService接口的功能,并提供默认实现OtherServiceImpl
@DeclareParents(value = "springBase.aop.IService+", defaultImpl = OtherServiceImpl.class)
public static IOtherService otherService;
客户端:
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(JavaConfig.class);
DemoAnnotationService demoAnnotationService = context.getBean(DemoAnnotationService.class);
IService demoMethodService = context.getBean("demoMethodService", IService.class);
IService serviceImpl = context.getBean("serviceImpl", IService.class);
//注解切面
demoAnnotationService.add();
//方法过滤
demoMethodService.add();
serviceImpl.add();
//处理参数
serviceImpl.count(10);
//增强接口
IOtherService otherService = (IOtherService)serviceImpl;IOtherService otherService = (IOtherService)serviceImpl;
otherService.otherMethod();
context.close();
}