携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第11天,点击查看活动详情
哈喽,大家好!我是Why,一名在读学生,目前刚刚开始进入自己的编程学习生涯。虽然学习起步较晚,但我坚信做了才有0或1的可能。学了一段时间以后也是选择在掘金上分享自己的日常笔记,也希望能够在众多道友的大家庭中打成一片。 本文主要讲解注解方式与XML方式实现AOP,如果大家读后觉得有用的话,还请大家多多支持博主:欢迎 ❤️点赞👍、收藏⭐、留言💬 ✨✨✨个人主页:JinHuan
注解方式实现AOP
开发阶段:关注核心业务和AOP代码 运行阶段:spring框架会在运行的时候将核心业务和AOP代码通过动态代理的方式编织在一起代理方式的选择:是否实现了接口:有接口就选择JDK动态代理;没有就选择CGLIB动态代理。
1、创建项目引入依赖
<dependencies>
<!--spring 核心依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.13.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.13.RELEASE</version>
</dependency>
<!--测试依赖-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependencies>
<build>
<plugins>
<plugin>
<!--编译依赖-->
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
2、创建spring配置文件引入约束
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--在beans标签中 引入AOP和context约束-->
</beans>
3、创建核心业务类
package com.jinhuan.service;
import org.springframework.stereotype.Service;
/**
* @Author jinhuan
* @Date 2022/7/4 17:35
* Description:没有实现接口的Service
*/
@Service
public class BookService {
public void add(Integer id,String name){
System.out.println("BookService---add()---");
}
public String say(){
System.out.println("BookService-----say()---");
return "你好";
}
/**
* 测试异常通知
* */
public void excep(){
System.out.println("BookService-----ex()---");
//int num = 1/0;
}
}
4、定义切面类
package com.jinhuan.aop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
/**
* @Author jinhuan
* @Date 2022/5/16 11:12
* Description:
*/
@Component //切面对象的创建权限依旧交给spring
//@Aspect //aspectj框架中的注解,表明当前类是一个切面
public class MyAspect {
/**
* pointcut
* 当较多的通知使用相同的execution切入点表达式的时候
* 编写维护起来均十分麻烦
* AspectJ提供了@PointCut注解在一个方法上
* 使得以后所有的execution 的value属性值均可以使用这个方法名代替
* 一般该方法使用private做为权限修饰
* */
@Pointcut(value = "execution(* com.jinhuan.service.*.*(..))")
private void pointcut01(){}
@Pointcut(value = "execution(* com.jinhuan.service.*.add(..))")
private void pointcut02(){}
/**
* @Before 注解声明前置通知
*/
@Before(value = "pointcut01()")
public void before() {
System.out.println("前置通知----before()------");
}
/**
* @AfterReturning 注解声明后置通知
* value表名切入点的表达式
* returning表名 返回的结果
* */
@AfterReturning(value = "pointcut02()")
public void after() {
System.out.println("后置通知------after()-------");
}
/**
* @param point: 环绕节点
* @return java.lang.Object
* decription:
* Around 注解声明环绕通知
* ProceedingJoinPoint中的proceed方法表示目标方法被执行
* 有时候为了方便就把前置通知写进环绕通知里
*/
@Around(value = "pointcut02()")
public Object around(ProceedingJoinPoint point) throws Throwable {
System.out.println("环绕方法执行之前--------");
Object proceed = point.proceed();
System.out.println("环绕方法执行值之后-------");
return proceed;
}
/**
* @AfterThrowing 注解声明异常通知方法
* value表明切入点的表达式
*
* */
@AfterThrowing(value = "pointcut01()",throwing = "ex")
public void exception( JoinPoint jp,Throwable ex) {
//一般会记录下异发生的时间、位置等
System.out.println("异常通知------exception()-------");
System.out.println(jp.getSignature()+"方法出现异常,异常信息为:"+ex.getMessage());
}
/**
* @After 表示最终通知
* */
@After(value = "pointcut02()")
public void myFinally() {
System.out.println("结束通知------myFinally()-------");
}
}
5、业务类和切面类添加注解
在定义好切面 Aspect 后,需要通知 Spring 容器,让容器生成“目标类+ 切面”的代理对象。这个代理是由容器自动生成的。只需要在 Spring 配置文件中注册一个基于 aspectj 的自动代理生成器,其就会自动扫描到@Aspect 注解,并按通知类型与切入点,将其织入,并生成代理。
5、spring.xml配置文件中开启包扫描和 注册aspectj的自动代理
<!--包扫描-->
<context:component-scan base-package="com.kkb.service,com.kkb.aop"/>
<!--开启注解AOP的使用-->
<aop:aspectj-autoproxy proxy-target-class="true"/>
<!--aop:aspectj-autoproxy的底层是由 AnnotationAwareAspectJAutoProxyCreator 实现的,是基于 AspectJ 的注解适配自动代理生成器。其工作原理是,aop:aspectj-autoproxy通过扫描找到@Aspect 定义的切面类,再由切面类根据切入点找到目标类的目标方法,再由通知类型找到切入的时间点。-->
6、测试类
package com.jinhuan.test;
import com.jinhuan.service.BookService;
import com.jinhuan.service.TeamService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @Author jinhuan
* @Date 2022/5/16 11:29
* Description:
*/
public class Test01 {
@Test
public void test(){
ApplicationContext ac = new ClassPathXmlApplicationContext("spring.xml");
BookService bookService = (BookService) ac.getBean("bookService");
bookService.add(1,"666");
System.out.println("=======分界线================");
String say = bookService.say();
bookService.excep();
System.out.println("=======分界线================");
/**
* 测试多个被代理对象
* */
TeamService teamService = (TeamService) ac.getBean("teamService");
teamService.add(1,"热火");
}
}
XML方式实现AOP
package com.jinhuan.aop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
/**
* @Author jinhuan
* @Date 2022/7/4 11:20
* Description:使用XML方式实现AOP
*/
@Component//切面对象的创建权限交给Spring
@Aspect//Aspectj框架的注解,标识当前类是切面类
public class MyAsp {
public void before(JoinPoint jp) {
System.out.println("MyAsp前置通知----before()------");
}
public void around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("MyAsp方法执行之前环绕------");
Object proceed = pjp.proceed();//核心方法
System.out.println("MyAsp方法执行之后环绕------");
}
public void after(Object result) {
System.out.println("MyAsp后置通知------after()-------");
}
public void exception(JoinPoint jp,Throwable ex) {
System.out.println("MyAsp异常通知------exception()-------");
System.out.println(jp.getSignature()+"方法出现了"+ex.getMessage()+"异常");
}
public void myFinally() {
System.out.println("MyAsp结束通知------myFinally()-------");
}
}
<!--XML方式实现AOP-->
<aop:config>
<aop:pointcut id="pt1" expression="execution(* com.jinhuan.service.*.*(..))"/>
<aop:pointcut id="pt2" expression="execution(* com.jinhuan.service.*.add(..))"/>
<aop:aspect ref="myAsp">
<aop:before method="before" pointcut-ref="pt1"/>
<aop:after-returning method="after" pointcut-ref="pt1" returning="result"/>
<aop:after-throwing method="exception" pointcut-ref="pt1" throwing="ex"/>
<aop:after method="myFinally" pointcut-ref="pt1"/>
<aop:around method="around" pointcut-ref="pt2"/>
</aop:aspect>
</aop:config>