Spring5总述(六)—— AOP基础知识简述(代码演示)

文章目录

  • 1、AOP的概念
  • 2、AOP底层原理(JDK动态代理代码,多理解理解)
  • 3、AOP操作术语
  • 4、AspectJ
  • 5、利用AspectJ基于注解方式实现AOP操作(代码演示)
  • 6、完全使用注解实现AOP操作

了解更多关于Spring的基础知识

1、AOP的概念

1、什么是AOP

  1. 面向切面编程,利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
  2. 通俗理解:不通过修改源代码的方式,就可以在主干功能中添加新的功能。
  3. 使用登录的例子来说明AOP操作
    Spring5总述(六)—— AOP基础知识简述(代码演示)_第1张图片

2、AOP底层原理

AOP底层使用动态代理

  1. 第一种有接口情况,使用JDK动态代理创建接口实现类代理对象,增强类的方法(下面会介绍这种方式)

  2. 第二种没有接口情况,使用CGLIB动态代理创建子类的代理对象,增强类里面的方法


2、AOP底层原理(JDK动态代理代码,多理解理解)

1、使用JDK动态代理,需要使用proxy类里面的方法创建代理对象

(1)需要调用newProxyInstance方法
在这里插入图片描述

方法里面有三个参数:
  1. classLoder:类加载器
  2. 增强的方法所在的类所实现的接口的集合,支持多个接口共同传入
  3. 实现接口InvocationHandler的类,这个参数传入的就是代理对象,在这个类中写入增强的方法

2、编写JDK动态代理代码

  1. 创建接口UserDao,并创建类UserDaoIml实现UserDao接口
public interface UserDao {
    public int add(int a,int b);
}
public class UserDaoIml implements UserDao{

    @Override
    public int add(int a, int b) {
        System.out.println("add方法执行了");
        return a+b;
    }
}
  1. 创建UserDaoProxy类,实现接口InvocationHandler,重写invoke方法
class UserDaoProxy implements InvocationHandler{
    //把创建的是谁的代理对象,把谁传递过来
    //有参构造传递
    private Object obj;
    public UserDaoProxy(Object obj){
        this.obj=obj;
    }
    //增强的逻辑
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //方法之前
        System.out.println("方法之前执行"+method.getName()+":传递的参数..."+ Arrays.toString(args));
        //被增强的方法执行
        Object res = method.invoke(obj,args);
        //方法之后
        System.out.println("方法之后执行..."+obj);
        return res;
    }
}
  1. 创建JDKProxy,实现方法的增强、添加
public class JDKProxy {

    public static void main(String[] args) {
    	//用于存储接口的数组,用于newProxyInstance函数的第二个参数
        Class[] interfaces = {UserDao.class};
        //将被增强的方法所在类作为参数传入InvocationHandler接口实现类
        UserDaoIml userDaoIml = new UserDaoIml();
        UserDao userDao = (UserDao) Proxy.newProxyInstance(JDKProxy.class.getClassLoader(),interfaces,new UserDaoProxy(userDaoIml));
        System.out.println(userDao.add(1,2));
    }
}
  1. 测试结果
方法之前执行add:传递的参数...[1, 2]
add方法执行了
方法之后执行...spring.UserDaoIml@179d3b25
执行结果为:3

3、AOP操作术语

  1. 连接点(JoinPoint):类里面的那些方法可以被增强,也就是Spring里面允许你使用通知的地方,这个就有很多了,基本上所有的方法前后都可以使用通知。
  2. 切入点(Pointcut):类里面实际被增强的方法
  3. 通知(Advice)(注:增强方法的逻辑部分,在被用于增强的方法上书写注释,下面会介绍部分注释的写法)

通知的类型:

  1. 前置通知:在需要增强的方法前执行——@Before
  2. 后置通知:不管什么情况都会执行,即使有异常也会执行——@After
  3. 环绕通知:在需要增强的方法前后都要执行——@Around
  4. 异常通知:在需要增强的方法出现异常时执行——@AfterThrowing
  5. 返回通知:在需要增强的方法后执行——@AfterReturning
  1. 切面(Aspect):把通知应用到切入点的过程,这是一个动作

4、AspectJ

1、AspectJ介绍

  1. Spring框架一般都是基于AspectJ实现的AOP操作

  2. AspectJ不是Spring组成部分,这是一个独立的AOP框架,一般把AspectJ和Spring框架一起使用来实现AOP操作

  3. 基于AspectJ的实现AOP操作主要有两种方式:

    1. 基于xml配置文件
    2. 基于注解方式实现(在项目中一般使用该方式)

2、切入点表达式

@Before(value = "切入点表达式放入的位置")

(1)切点表达式的作用:指明对某个类里面的某个方法进行增强

(2)语法结构:execution([权限修饰符][返回类型][类全路径][方法名称][参数列表])

  1. 举例1:execution(*spring.Userdao.add(..))
    含义:对spring.Userdao接口中的add方法进行增强
  2. 举例2:execution(*spring.Userdao.*(..))
    含义:对Spring.Userdao中的所有方法进行增强
  3. 举例3:execution(public double spring.Userdao.*(..))
    含义:对Spring.Userdao中返回类型为double类型的方法进行增强
  4. 举例4:execution (* *.add(int,..)) || execution(* *.sub(int,..))
    含义:对任意类中第一个参数为int类型的add方法或sub方法进行增强

5、利用AspectJ基于注解方式实现AOP操作(代码演示)

1、代码演示

  1. 在项目工程里面引入AOP相关的依赖
    Spring5总述(六)—— AOP基础知识简述(代码演示)_第2张图片
    AspectJ:aspectjweaver下载地址:
    https://www.eclipse.org/downloads/download.php?file=/tools/aspectj/aspectj-1.9.6.jar
    aopalliance.jar下载地址:
    https://sourceforge.net/projects/aopalliance/files/aopalliance/1.0/aopalliance.zip/download
    cglib-2.2.0.jar下载地址:
    http://www.xwood.net/site_domain/_root/5870/5930/5932/9330/9352/t_c41554.html

  2. 创建一个User类,在User类中创建方法

@Component (value = "user")
public class User {
    public void add(){
        System.out.println("add......");
    }
}
  1. 创建Userproxy增强类,在增强类中创建方法,添加了注解@Aspect

环绕通知:

  1. 对于环绕通知来说,连接点的参数类型必须是ProceedingJoinPoint。它是 JoinPoint的子接口,允许控制何时执行,是否执行连接点。
  2. 在环绕通知中需要明确调用ProceedingJoinPoint的proceed()方法来执行被代理的方法。如果忘记这样做就会导致通知被执行了,但目标方法没有被执行。
  3. 环绕通知的方法需要返回目标方法执行之后的结果,即调用 joinPoint.proceed();的返回值,否则会出现空指针异常。
@Component
@Aspect
public class Userproxy {

    @Before(value = "execution(* Aop.User.add(..))")
    public void before(){
        System.out.println("@Before 前置通知");
    }

    @After(value = "execution(* Aop.User.add(..))")
    public void after(){
        System.out.println("@After 后置通知");
    }

	@AfterThrowing(value = "execution(* Aop.User.add(..))")
	public void afterThrowing(){
        System.out.println("@AfterThrowing 异常通知");
    }
    
    @Around(value = "execution(* Aop.User.add(..))")
    public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
        System.out.println("@Around 环绕通知前");
        proceedingJoinPoint.proceed();
        System.out.println("@Around 环绕通知后");
    }

    @AfterReturning(value = "execution(* Aop.User.add(..))")
    public void afterreturning(){
        System.out.println("@AfterReturning 返回后通知");
    }
}
  1. 在Spring配置文件中进行通知(代理)的配置
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       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/context http://www.springframework.org/schema/context/spring-context.xsd
                           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
    
    <context:component-scan base-package="Aop">context:component-scan>
    
    <aop:aspectj-autoproxy>aop:aspectj-autoproxy>
beans>
  1. 创建测试类,进行测试
public class Test_AOP {

    @Test
    public void testAop(){
        ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
        User user = context.getBean("user",User.class);
        user.add();
    }
}
  1. 测试结果
@Around 环绕通知前
@Before 前置通知
add......
@AfterReturning 返回后通知
@After 后置通知
@Around 环绕通知后

2、基于AspectJ的进阶操作

  1. 可以利用@Pointcut注解对相同的切入点表达式进行抽取
    @Pointcut(value = "execution(* Aop.User.add(..))")
    public void pointdemo(){

    }
    @Before(value = "pointdemo()")
    public void before(){
        System.out.println("before.....");
    }
  1. 如果有多个增强类对同一个方法进行增强,可以设置增强类的优先级
    注:在增强类上面添加注解@Order(数字类型值),数字类型值越小它的优先级越高
@Component
@Aspect
@Order(1)
public class personProxy {
    @Before(value = "execution(* Aop.User.add(..))")
    public void before(){
        System.out.println("person Before..........");
    }
}

@Component
@Aspect
@Order(2)
public class UserProxy {
    @Before(value = "execution(* Aop.User.add(..))")
    public void before(){
        System.out.println("User Before..........");
    }
}

6、完全使用注解实现AOP操作

  1. 创建一个配置类

@Configuration:表明这是一个配置类
@ComponentScan(basePackages = {“Aop”}):表示扫描包的路径为:Aop
@EnableAspectJAutoProxy(proxyTargetClass = true):表示开启AOP自动代理

@Configuration
@ComponentScan(basePackages = {"Aop"})
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class configAOP {

}
  1. 创建测试类,进行测试(测试类和测试结果与上面的代码演示相同)
 @Test
public void testAop1(){
   	ApplicationContext context = new AnnotationConfigApplicationContext(configAOP.class);
   	User user = context.getBean("user", User.class);
   	user.add();
}

❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤
若对Spring基础知识感兴趣的可以关注一下博主,我会持续更新Spring基础知识(一边学习一边记录),一起进步,有错误的地方也可以在评论区指出来喔,谢谢大家啦!!!

你可能感兴趣的:(Spring5学习,spring,java,aop)