测试环境:
jdk1.8
maven3.6.3
一、Pom依赖
//这些依赖不能以注解方式实现Aop,下文有详细的相关错误说明
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.2</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.5.4</version>
</dependency>
<dependency>
<groupId>org.apache.geronimo.bundles</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.6.8_2</version>
</dependency>
二、实现的方式
关于Aop的一些名词解释:
- 横切关注点:我们想要给目标类添加的业务功能,如:日志、安全、缓存等。
- 切面(ASPECT):横切关注点 被模块化的特殊对象。即,它是一个类。
- 通知(Advice):切面必须要完成的工作。即,它是切面类中的一个方法。
- 目标(Target):被通知对象。也就是需要增加功能的目标类。
- 代理(Proxy):向目标对象应用通知之后创建的对象。
- 切入点(PointCut):切面通知 执行的 “地点”的定义。
- 连接点(JointPoint):与切入点匹配的执行点。
(以上名词解释:参考了—狂神—的博客https://blog.csdn.net/weixin_44207403/article/details/106736102)
1.实现Spring提供的接口
在Spring-aop这个包下,有一些接口,实现这些接口,就生成了通知类。通知可以被织入或者说添加到目标类中去,达到增强其功能的作用;织入通知会生成代理类,我们后续在调用目标类的时候实际上调用的是代理类(代理类包含了对目标类的引用和一些扩展的方法-----我们织入的通知),该接口就是生成通知------也可以理解为这是一个特殊的切面,只有一个方法的切面。
aop包:
以上的一些接口,直接或者间接的实现了Advice(通知)接口。实现这些接口,重写方法,生成通知。
实例如下:
(1)创建一个类,实现AfterReturningAdvice 接口
@Component
public class Log implements AfterReturningAdvice {
@Override
public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
System.out.println("【Log】:"+method.getName()+"被执行了");
}
}
(2)spring配置文件—配置
<aop:config>
<aop:pointcut id="pointcut" expression="execution(* com.yh.pojo.aop.obj.Book.*(..))"/>
<aop:advisor advice-ref="log" pointcut-ref="pointcut"></aop:advisor>
</aop:config>
(3) 测试
public class Test {
public static void main(String[] args) {
ClassPathXmlApplicationContext context =
new ClassPathXmlApplicationContext("bean.xml");
Book book = (Book) context.getBean("book");
book.add();
}
}
(4)结果
add方法被执行
【Log】:add被执行了
2.自定义切面
切面是对要增加的功能的抽象,是一个类,里面包含了一些方法,这些方法也就是通知。上面的那种方式可以添加一个通知,而切面可以是多个,并且切面可以是一个普通的Java类。(ps:如果增加一个业务功能需要执行多个方法的话,那么需要定义多个通知类,而切面只需要定义一个就够了)。
实例如下:
(1)定义切面
@Component
//定义一个普通的java类作为切面
public class Log2 {
public void before(){
System.out.println("这是一个切面的前置");
}
public void after(){
System.out.println("切面定义的后置通知");
}
}
(2)spring配置文件—配置
<aop:config>
<aop:aspect id="myAspect" ref="log2">
<aop:pointcut id="pointcut" expression="execution(* com.yh.pojo.aop.obj.Book.*(..))"/>
<aop:before method="before" pointcut-ref="pointcut"></aop:before>
<aop:after-returning method="after" pointcut-ref="pointcut"></aop:after-returning>
</aop:aspect>
</aop:config>
(3)测试类
public class Test {
public static void main(String[] args) {
ClassPathXmlApplicationContext context =
new ClassPathXmlApplicationContext("bean.xml");
Book book = (Book) context.getBean("book");
book.add();
}
}
(4)结果
这是一个切面的前置
add方法被执行
切面定义的后置通知
(5) 结论
切面类可以是普通java类,通过spring配置文件创建为切面,其中需要定义切入点,并将设置其方法作为前置通知、后置通知等。
3.注解方式
这种方式,是容易出错的地方,因为这东西跟jar包版本以及jdk环境之类的相关。上文引入的pom依赖是无法以注解方式运行的,会报错。具体报的错及解决方式会在下面做阐述。
使用方式:
(1)定义一个普通的java类,然后再类上添加@Aspect注解,表明他是一个切面,而不必像第二种方式那样再配置文件中声明定义。如下:
@Component //表示是Spring的一个Bean,可以被扫描进入Spring容器
@Aspect//表示切面
public class LogAnnoattion {
@Pointcut(value = "execution(* com.yh.pojo.aop.obj.Book.*(..))")
public void pointcut(){
}
@Before(value = "pointcut()")
public void logBefore(){
System.out.println("声明----Before通知");
}
@AfterReturning(value = "pointcut()")
public void logAfter(){
System.out.println("声明-----AfterReturning通知");
}
}
(2)Spring配置文件----配置
<aop:aspectj-autoproxy />//开启自动代理,即:为@Aspect标记的类的目标类创建代理,然后将该切面织入进入代理类中
(3)测试
public class Test {
public static void main(String[] args) {
ClassPathXmlApplicationContext context =
new ClassPathXmlApplicationContext("bean.xml");
Book book = (Book) context.getBean("book");
book.add();
}
}
(4)结果
对于上面的错误,我百度了很多博客,才找到一些有帮助的解释:aspectjweaver的版本太低了,要换成高版本的。aspectjweaver的依赖如下:
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.5.4</version>
</dependency>
最初的 groupId都不是上面这个,上面是正确的依赖,不过被我调低了版本然后让它报错。我最开始的那个groupID即使被我调到了最新版本,也会报错,然后我就去MVN上面找了其他groupID的aspectjweaver,改完之后,就可以正常运行了 。
解决方式:
调高版本即可。
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.6</version>
</dependency>
结果:
声明----Before通知
add方法被执行
声明-----AfterReturning通知
以上。(PS:如上述有错,欢迎指正,谢谢!!!)