0. 前言
刚接触Spring的小伙伴一定听说过AOP(面向切面编程),这个概念很抽象,AOP中的很多名次解释也很抽象,即使有一些通俗易懂的博客介绍AOP名词,还是很难了解AOP的应用。所以本文希望构建一个通俗易懂的AOP demo。
1. 项目创建
使用idea,新建项目时选择Spring initializr.
依赖简单选两个
删除一些没必要的文件(也可以不删)。
在pom.xml中加入以下依赖。
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.13</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
之后右击pom.xml,rmaven,reload。
2 切面构建
根据大雨将至大神的aop demo改写。
文件结构
StorageAdvisor.java
@Aspect声明该类为切面类,@Component声明该类为一个Spring bean,可以被Spring扫描到,可以通过@Autowired来注入。
@Around和@Before为 通知 的类型,其参数为切入点,切入点+通知就构成了一个切面。简单来说,其参数指定通知可以在哪些函数上生效,而不同类型的通知可以运行在函数运行的前中后。
当同一切入点有多种类型通知时,只会生效一个,见本文最后测试结果。
package com.example.springaopdemo.aop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class StorageAdvisor {
@Around("execution(* chop(..))")
public void aroundAttack(JoinPoint point) {
System.out.println("aroundAdvice: " + point.getTarget().getClass().getSimpleName() + "蓄力");
}
@Before("execution(* chop(..))")
public void beforeAttack(JoinPoint point) {
System.out.println("beforeAdvice: " + point.getTarget().getClass().getSimpleName() + "蓄力");
}
}
Horseman.java
package com.example.springaopdemo.charactor;
import org.springframework.stereotype.Component;
@Component
public class Horseman {
public void rush(String enemy) {
System.out.println(this.getClass().getSimpleName() + "冲刺攻击" + enemy);
}
public void chop(String enemy) {
System.out.println(this.getClass().getSimpleName() + "砍劈攻击" + enemy);
}
}
Swordman.java
package com.example.springaopdemo.charactor;
import org.springframework.stereotype.Component;
@Component
public class Swordman {
public void block(String enemy) {
System.out.println(this.getClass().getSimpleName() + "格挡" + enemy);
}
public void chop(String enemy) {
System.out.println(this.getClass().getSimpleName() + "砍劈攻击" + enemy);
}
}
DemoController.java
@Controller声明该类为一个controller类,可以通过访问url来触发类中方法。
@RequestMapping声明方法的url。
(其实应该单独写一个测试类,这样写很不规范)
package com.example.springaopdemo.cotroller;
import com.example.springaopdemo.charactor.Horseman;
import com.example.springaopdemo.charactor.Swordman;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class DemoController {
@Autowired
Horseman hm;
@Autowired
Swordman sm;
@RequestMapping("/test")
public String test(){
hm.rush("Ghoul");
hm.chop("Ghoul");
sm.block("Ghoul");
sm.chop("Ghoul");
return "hello";
}
}
下面给出测试类的写法
package com.example.springaopdemo;
import com.example.springaopdemo.charactor.Horseman;
import com.example.springaopdemo.charactor.Swordman;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
public class DemoTest {
@Autowired
Horseman hm;
@Autowired
Swordman sm;
@Test
void test(){
hm.rush("Ghoul");
hm.chop("Ghoul");
sm.block("Ghoul");
sm.chop("Ghoul");
}
}
3 测试
运行SpringAopDemoApplication.main,在浏览器地址栏输入
localhost/8080/test
或者运行测试文件中的test函数。
console输出结果如下
Horseman冲刺攻击Ghoul
aroundAdvice: Horseman蓄力
Swordman格挡Ghoul
aroundAdvice: Swordman蓄力