小白学习Spring-AOP 笔记

Spring-AOP

AOP(Aspect Oriented Programming),意为:面向切面编程,是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

在Spring框架中,aop是基于动态代理实现的。简单来说,aop有以下几种功能:

1、增强功能
2、减少重复代码
3、使得你专注于业务代码
4、解耦合

那么作为新手,该如何学习面向切面编程呢?主要有以下几步:

1、分析项目功能时,找出切面
2、合理安排切面的切入时间(在目标方法之前还是之后)
3、合理安排切面的切入位置(在哪个类,哪个方法增加功能…)

面向切面的几个术语

1、Aspect:切面,也叫要增强的功能,属于非业务功能
2、JoinPoint:连接点,连接业务方法和切面的位置。通俗讲就是某类中的业务(目标)方法,是一个方法
3、PointCut:切入点,多个连接点的集合,是多个方法
4、目标对象:给哪个类的方法增加功能,该类就是目标对象
5、Advice:通知,用来表示切面功能执行的时间

aop的实现:
Spring在内部实现了aop规范,主要在做事务使用;目前使用比较广泛的是aspectj,spring中也集成了该框架,通过spring可以实现aop的功能。

先做个小项目试试吧~~~~

我使用的是idea编译工具

使用aspectj实现aop的基本步骤:

1、新建一个maven项目
小白学习Spring-AOP 笔记_第1张图片
2、加入依赖
主要是spring和aspectj的依赖 。

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
    <!--   spring依赖 -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.2.6.RELEASE</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.springframework/spring-aspects -->
    <!--    aspectj的依赖-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aspects</artifactId>
      <version>5.2.5.RELEASE</version>
    </dependency>
  </dependencies>

3、创建目标类及其实现类

方便起见,全都放到一个包里了 。

// 目标类的实现类
package com.dec07.NO01;

import com.dec07.NO01.SomeService;

public class SomeServiceImpl implements SomeService {
    @Override
    public void doSomeThing(String name, Integer age) {
        if (age>40){
            System.out.println("大家好,我是"+name+",今年"+age+"岁!!!");
        }else {
            System.out.println("这俩"+age+"岁的"+name+"不讲武德!!!");
        }
    }
}

4、创建切面类
切面类就是一个普通类,在类的上面加@Aspect注解,告诉spring该类是切面类;
在类中定义方法,方法就是切面要执行的代码啦。

// 在方法上加上@Before表示切面方法在目标方法之前执行,execution内是切入点表达式
// 测试前置通知
package com.dec07.NO01;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

import java.text.SimpleDateFormat;
import java.util.Date;

@Aspect
public class MyAspectj {
    @Before(value = "execution(* *..SomeServiceImpl.doSomeThing(..))")
    public void testBefore(){
        Date date=new Date();
        SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd  HH:mm:ss");
        String time = sdf.format(date);
        System.out.println("程序执行了前置通知,当前时间是:"+time);
    }
}

5、在spring中加入配置文件
定义对象,交给spring容器管理。

目标对象,切面对象,aspectj的自动代理生成器标签
该标签来完成代理对象的创建
// 生成对象
<?xml version="1.0" encoding="UTF-8"?>
<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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">

    
    <!--    目标对象-->
    <bean id="myService"  class="com.dec07.NO01.SomeServiceImpl"/>
    <!--    切面类对象-->
    <bean id="myAspectj" class="com.dec07.NO01.MyAspectj"/>
    <!--    aspectj框架的自动代理生成器标签-->
    <aop:aspectj-autoproxy />
</beans>

6、测试
从spring容器拿到对象,实际上是代理对象。

//代理对象执行方法,实现功能增强
//测试代码
package com.dec07;

import com.dec07.NO01.SomeService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {
    @Test
    public  void test01(){
        String config="applicationContext.xml";
        ApplicationContext ac=new ClassPathXmlApplicationContext(config);
        SomeService service = (SomeService) ac.getBean("mySomeService");
        service.doSomeThing("马保国",69);

    }

}

7、输出

// 输出结果
程序执行了前置通知,当前时间是:2020-12-07  16:00:02
大家好,我是马保国,今年69岁!!!

Process finished with exit code 0

做个简单的小测试后,对其他几个注解也加以使用~~~~~~~

后置通知 可以拿到目标方法的返回值。

// doOther
@Override
   public String doOther(String name) {
       System.out.println("我"+name+"望你耗子尾汁!!!");
       return "江湖大骗子";
   }
returning的值是自定义的,但是要和方法的形参名一致;
// 后置通知@AfterReturning
  @AfterReturning(value = "execution(* *..SomeServiceImpl.*(..))",returning = "res")
    public void testReturning(Object res){
        System.out.println("执行了后置通知,可以得到返回值");
        if (res!=null){
            System.out.println("他是 "+res);
        }

    }
  

public class MyTest02 {
    @Test
    public  void test01(){
        String config="applicationContext.xml";
        ApplicationContext ac=new ClassPathXmlApplicationContext(config);
        SomeService service = (SomeService) ac.getBean("mySomeService");
//        service.doSomeThing("马保国",69);
        service.doOther("马保国");

    }
// 结果
我马保国望你耗子尾汁!!!
执行了后置通知,可以得到返回值
他是 江湖大骗子

环绕通知
最强大的通知,可以在目标方法前后执行。
可以控制方法是否被调用
可以修改方法的执行结果

// A code block
var foo = 'bar';
// 目标方法
 @Override
    public String doOther2(String name) {
        System.out.println("我"+name+"劝你"+"好好范斯!!!");
        return "左正蹬,油煸腿";
    }
// 控制方法是否执行,并且能改变输出结果
@Around(value = "execution(* *..SomeService.*(..))")
    public Object testAround(ProceedingJoinPoint pjp) throws Throwable {
        Object[] args = pjp.getArgs();
        String name="";
        if (args!=null&&args.length>0){
            Object obj=args[0];
            name=(String) obj;
        }
        Object po=null;
        if ("马保国".equals(name)){
            po = pjp.proceed();
            System.out.println("环绕通知是最强大的通知!!!");
            System.out.println();
            po="左正蹬,油煸腿,左呲拳";
            System.out.println("它打出一套"+po);

        }
        return po;

    }
// 测试
public class MyTest03 {
    @Test
    public  void test01(){
        String config="applicationContext.xml";
        ApplicationContext ac=new ClassPathXmlApplicationContext(config);
        SomeService service = (SomeService) ac.getBean("mySomeService");
//        service.doSomeThing("马保国",69);
//        service.doOther("马保国");
        String str = service.doOther2("马保国");
        System.out.println("str=====>"+str);

    }

// 输出结果   str就是目标方法doOther2的返回值"左正蹬,油煸腿",但是结果被环绕通知的方法修改了,变成了修改后的结果"左正蹬,油煸腿,左呲拳"

我马保国劝你好好范斯!!!
环绕通知是最强大的通知!!!

它打出一套左正蹬,油煸腿,左呲拳
执行了后置通知,可以得到返回值
他是 左正蹬,油煸腿,左呲拳
str=====>左正蹬,油煸腿,左呲拳

@Before、@AfterReturning、@Around是使用最多的三个注解,来完成前置、后置、环绕通知。

路漫漫其修远兮,吾将上下而求索。

加油加油,为了自己的梦想,为了鸣鸣 ^ _ ^

你可能感兴趣的:(笔记,java学习,spring,aop,java,编程语言)