AspectJ实际上是对AOP编程思想的一个实践
有二种实践方式:
- advisor(写自定义的通知)
- aspect(提供现成的通知)
两者都需要有切入点表达式
需要导包:
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.6</version>
</dependency>
切入点表达式
使用标签 aop:pointcut(注意需要引入相关的schema)
有两种方式:execution(群攻) 和 使用注解(精准打击)
1️⃣execution(群攻)
配置标签:
<aop:pointcut id=“切入点id” expression="execution(修饰符 返回值 包名、类名、方法名(形参))/>
所谓群攻就是execution中涵盖的所有方法都会被增强
写法:
①修饰符
- 可以省略,省略不写代表任意修饰符
②返回值
- 不能省略
- 可以使用*来通配
- 如果返回值为pojo,需要写全类名
③包名、类名、方法名
- 可以部分省略 ,除了头和尾,中间的任意一部分都可以使用…来进行省略
- 可以通配 ,*来通配一整个词或词的一部分
④形参
- 可以省略 ,省略表示它(们)是无参方法
- 可以通配,* 代表的单个任意参数;
… 任意数量任意类型的参数 - 和返回值一样,pojo类要写全类名
2️⃣annotation(精准打击)
配置标签:
<aop:pointcut id=“切入点id” expression="@annotation(自定义注解的全类名)"/>
所谓精准打击就是注解写在哪个容器组件的方法上,哪个方法就会被增强
精准打击需要自己定义一个注解
Advisor(通知器)
Advisor更侧重于写一个自定义的通知,需要实现接口MethodInterceptor
1️⃣与execution结合(群攻)
例如,现在我们需要让特定的方法执行完成后输出方法执行的时间。
第一步
写一个通知器,实现MethodInterceptor接口,需要重写接口中的invoke方法,methodInvocation.proceed()会执行委托类中需要被增强的方法。
@Component
public class CountTimeAdvice implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
long start = System.currentTimeMillis();
Object proceed = methodInvocation.proceed();
long end = System.currentTimeMillis();
System.out.println(end - start);
return proceed;
}
}
第二步
配置advisor和pointcut
<context:component-scan base-package="com.cskaoyan"/>
<aop:config>
<aop:pointcut id="pointcut01" expression="execution(* com.cskaoyan.sevice..*(..))"/>
<aop:advisor advice-ref="countTimeAdvice" pointcut-ref="pointcut01"/>
</aop:config>
execution括号里所有的方法都会被增强
测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:application.xml")
public class Mytest {
@Autowired
OrderService orderService;
@Test
public void test01(){
orderService.method1();
orderService.method2();
orderService.method3();
}
}
测试结果:
method1
0
method2
0
method3
500
2️⃣与annotation结合(精准打击)
与之前一样,我们需要让特定的方法执行完成后输出方法执行的时间。
第一步
同上,写一个通知器,实现MethodInterceptor接口,需要重写接口中的invoke方法,methodInvocation.proceed()会执行委托类中需要被增强的方法。
@Component
public class CountTimeAdvice implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
long start = System.currentTimeMillis();
Object proceed = methodInvocation.proceed();
long end = System.currentTimeMillis();
System.out.println(end - start);
return proceed;
}
}
第二步
自定义注解,用来标记需要被增强的方法
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CountTime {
}
第三步
配置advisor和pointcut
<context:component-scan base-package="com.cskaoyan"/>
<aop:config>
<aop:pointcut id="pointcut02" expression="@annotation(com.cskaoyan.anno.CountTime)"/>
<aop:advisor advice-ref="countTimeAdvice" pointcut-ref="pointcut02"/>
</aop:config>
第四步
在需要被增强的方法上添加上该注解
@Service
public class OrderServiceImpl implements OrderService{
@Override
public void method1() {
System.out.println("method1");
}
@Override
@CountTime
public void method2() {
System.out.println("method2");
}
@Override
@CountTime
public void method3() {
System.out.println("method3");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
在这里,方法1未添加注解。方法2,3添加了注解,并且方法3睡眠了500毫秒。
测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:application.xml")
public class Mytest {
@Autowired
OrderService orderService;
@Test
public void test01(){
orderService.method1();
orderService.method2();
orderService.method3();
}
}
测试结果:
method1
method2
1
method3
500
Aspect
不同于advisor的通知以类的方法出现,aspect的通知以方法的形式存在,是一些现成的通知。
可以使用配置文件或者注解的方式
一共有五个通知,相较于委托类方法的顺序如下:

- before:在委托类方法执行之前执行
- after:在委托类方法执行之后执行,类似于finally,就算委托类方法有异常,也会执行
- around:“环绕”着委托类方法执行,需要将连接点作为参数传入,连接点可以实现
ProceedingJoinPoint接口也可以实现JoinPoint接口。利用joinPoint.proceed()方法可以调用委托类的方法。(!重要)JoinPoint中有很多方法,可以用来获得方法签名、方法参数、委托类对象、代理类对象等等。 - afterReturning:需要传入一个Object类型的参数,就是方法的执行结果。可以执行结果做一些事情,如:计算出购物车商品的价格后,将该价格记录到数据库。
- afterThrowing:将异常作为参数传入,只有委托类方法抛出异常的时候才会执行,如果try-catch了就不会执行
1️⃣使用配置文件
第一步
新建一个类,可以不用实现任何接口,注册成组件
其中的方法名可以任意写,只需要在后续的配置中配置对即可
@Component
public class CustomAspect {
public void before(){
System.out.println("before");
}
public void after(){
System.out.println("after");
}
//注意返回值为Object
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("around-before");
Object proceed = joinPoint.proceed();
System.out.println("around-after");
return proceed;
}
//需要将执行结果传入
public void afterReturning(Object result){
System.out.println("afterReturning" + result);
}
//将异常作为参数传入
public void afterThrowing(Exception e){
System.out.println("afterThrowing" + e.getMessage());
}
}
第二步
配置:
<context:component-scan base-package="com.cskaoyan"/>
<aop:config>
<!--service层的任意方法-->
<aop:pointcut id="mypointcut" expression="execution(* com.cskaoyan.sevice..*(..))"/>
<aop:aspect ref="customAspect">
<!--pointcut和通知的结合
method属性: 方法名,父标签中的ref属性对应的组件中的方法名 👉 切面组件中的方法名
-->
<aop:before method="before" pointcut-ref="mypointcut"/>
<aop:after method="after" pointcut-ref="mypointcut"/>
<aop:around method="around" pointcut-ref="mypointcut"/>
<!--需要通过returning属性指定method对应方法的哪一个参数来接收委托类方法的执行结果-->
<aop:after-returning method="afterReturning" pointcut-ref="mypointcut" returning="result"/>
<!--需要通过throwing属性指定method对应方法的哪一个参数来接收委托类方法执行过程中抛出的异常-->
<aop:after-throwing method="afterThrowing" pointcut-ref="mypointcut" throwing="e"/>
</aop:aspect>
</aop:config>
测试:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:application.xml")
public class Mytest2 {
@Autowired
UserService userService;
@Test
public void test01(){
userService.sayHello("octavius");
}
}
测试结果:
before
around-before
hello octavius
afterReturning hello octavius
around-after
after
2️⃣使用注解
第一步
同上
第二步
打开开关:打开使用注解的开关
在配置文件中做如下配置
<context:component-scan base-package="com.cskaoyan"/>
<aop:aspectj-autoproxy/>
第三步
添加注解
首先,在类上添加注解@Aspect,注明这是一个Aspect类
然后需要在类中新添加一个方法
返回值为void,方法名任意写(作为id),参数不需要,方法体不需要写东西
在方法名上使用@Pointcut注解
后续的方法注解中的value值可以写范围,也可以写@Pointcut注解的id,例子中用后者
@Aspect
@Component
public class CustomAspect {
//value为范围
@Pointcut("execution(* com.cskaoyan.sevice..*(..))")
public void mypointcut(){}
@Before("mypointcut()")
public void before(){
System.out.println("before");
}
@After("mypointcut()")
public void after(){
System.out.println("after");
}
//注意返回值为Object
@Around("mypointcut()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("around-before");
Object proceed = joinPoint.proceed();
System.out.println("around-after");
return proceed;
}
//需要将执行结果传入
@AfterReturning(value = "mypointcut()",returning = "result")
public void afterReturning(Object result){
System.out.println("afterReturning " + result);
}
//将异常作为参数传入
@AfterThrowing(value = "mypointcut()",throwing = "e")
public void afterThrowing(Exception e){
System.out.println("afterThrowing " + e.getMessage());
}
}
测试
测试的办法和结果和之前一模一样

3053





