Spring AOP一

130 阅读4分钟

这是我参与11月更文挑战的第12天,活动详情查看:2021最后一次更文挑战

AOP是什么

AOP是面向切面编程(Aspect Oriented Programming) Aspect表示给业务方法增加的功能叫做切面,切面一般不是主要的业务功能,一般都是可以服用的一些功能,如:日志输出,权限检查等等不是业务逻辑的功能。AOP就是要在不影响逻辑的情况下把这些功能加进去。在开发时我们要找出切面的功能,选择切面执行的时间,执行的位置。

AOP的作用

  • 让切面的功能可以复用
  • 让我们开发时只专注业务逻辑的开发,提升开发效率
  • 解耦合我们的业务功能和非业务的功能
  • 可以给原有业务添加功能时,只需修改切面,而不去动我们的业务代码

AOP的术语

  • Aspect :切面 增加的非业务的功能
  • JoinPoint:连接点 连接切面的业务方法,执行业务代码时执行我们的切面
  • PointCut : 切入点 切面执行的位置
  • Target : 目标对象 要增加切面的功能的对象
  • Advice : 增强,表示切面执行在业务代码的执行时间 串起来就是在Advice的时间在PointCut的地点执行Aspect,AOP是一个动态的思想,是在程序执行期间进行的操作,程序执行时创基代理,使用代理对象去执行方法,就增加上了切面的功能。

什么时候用AOP

给代码增加功能但不改变原代码

AOP的技术实现

一个是Spring的实现了AOP中的部分功能,但是比较繁琐笨重,另一个是AspectJ,独立的框架专门做AOP的,专业的人做专业的事

使用AspectJ框架实现AOP

Advice

AspectJ表示切面执行的时间使用Advice可以使用注解来实现,有五个通知注解表示切面的五个执行时间

  • @Before 前置通知
  • @AfterReturing 后置通知
  • @Around 环绕通知
  • @AfterThrowing 异常通知
  • @After 最终通知

PointCut

切面执行的位置,使用AspectJ的切入点表达式,切入点表达式的语法如下

execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern) throws-pattern?)

  • modifiers-pattern] 访问权限类型
  • ret-type-pattern 返回值类型
  • declaring-type-pattern 包名类名
  • name-pattern(param-pattern) 方法名(参数类型和参数个数)
  • throws-pattern 抛出异常类型
  • ?表示可选的部分

execution(访问权限 方法返回值 包名类名 方法声明(参数) 异常类型)除去带问号的可以省略的可以简写为 execution( 方法返回值 方法声明(参数))

image.png

execution(public * *(..))
指定切入点为:任意公共方法。
execution(* set*(..))
指定切入点为:任何一个以“set”开始的方法。
execution(* com.xyz.service.*.*(..))
指定切入点为:定义在 service 包里的任意类的任意方法。
execution(* com.xyz.service..*.*(..))
指定切入点为:定义在 service 包或者子包里的任意类的任意方法。“..”出现在类名中时,后
面必须跟“*”,表示包、子包下的所有类。
execution(* *..service.*.*(..))
指定所有包下的 serivce 子包下所有类(接口)中所有方法为切入点

@Before

定义方法 定义切面的具体功能前置通知方法的定义 1) 方法是public 2) 方法是void 3) 方法名称自定义 4) 方法可以有参数,如果是PointCut也可以没有

   @Before; 前置通知
       属性: value 切入点表达式。切面的执行位置
       位置: 方法之上
   特点
       1 执行时间,在目标方法之前执行
       2 不会要意向目标方法的原有功能
       3 不会修改原方法的执行结果
public interface doService {

    public void doSome(String name,Integer id);
}
public class doSomeImpl implements doService {
    @Override
    public void doSome(String name,Integer id) {
        System.out.println("做了业务");

    }
}

写了很多种形式的表达式

 //@Before(value = "execution(public void org.example.Service.impl.doSomeImpl.doSome(String,Integer))")
 //@Before("execution(void doSome(String,Integer))")
// @Before("execution(void *..do*(..))")
 //@Before("execution(void org..*(..))")
 @Before("execution(void *..*(..))")
public void beforeAdvice(){
    //切面功能,开始执行时间
    System.out.println("@Before 前置切面功能 在切点之前执行"+new Date());
}
<bean id="doSomeImpl" class="org.example.Service.impl.doSomeImpl"></bean>

<bean id="myHander" class="org.example.hander.myHander"></bean>

<!--
    声明自动代理生成器用来创建目标对象的代理,调用AspectJ的功能,寻找所有的目标增强对象
    把每个目标对象加入到切面类的功能生成代理对象,去执行功能
-->
<aop:aspectj-autoproxy />

测试类

@Test
public void shouldAnswerWithTrue()
{
    //配置文件地址
    String configPath="ApplicationConttext.xml";
    //2创建对象容器
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext(configPath);
    doService service= (doService) applicationContext.getBean("doSomeImpl");
    System.out.println(service.getClass().getName());
    service.doSome("1",1);
}

完美的把切面功能加进去了,对象类型是一个com.sun.proxy.$Proxy6代理类

image.png

JoinPoint

用来获取正在执行的方法信息

public void beforeAdvice(JoinPoint jp){

System.out.println("正在执行的方法"+ jp.getSignature());
//方法名称
System.out.println("正在执行的方法名称"+ jp.getSignature().getName());
//参数
System.out.println("正在执行方法的参数");
Object[] args = jp.getArgs();
for (Object arg : args) {
    System.out.println(arg);
}
System.out.println("正在执行方法的参数");

    
    //切面功能,开始执行时间
    System.out.println("@Before 前置切面功能 在切点之前执行"+new Date());
}

image.png

@AfterRetueuing

后置通知,可以拿到方法的返回值,对结果进行操作

@AfterReturning(value = "execution(* *..doOther(..)))",returning = "res")
    public void beforeAdvice(Object res){
        //切面功能,开始执行时间
        System.out.println("@AfterReturning 前置切面功能 在切点之后执行,能拿到你的执行结果"+res);
    }
@Override
public String doOther(String name, Integer id) {
    System.out.println("other");
    return name+id;
}
@Test
public void shouldAnswerWithTrue()
{
    //配置文件地址
    String configPath="ApplicationConttext.xml";
    //2创建对象容器
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext(configPath);
    doService service= (doService) applicationContext.getBean("doSomeImpl");
    service.doOther("张三身高",188);
}

image.png

明天继续肝,坚持不断更