Spring AOP的相关内容

简介: 版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/w1lgy/article/details/81364815 一、AOP?1、AOP:面向切面编程,扩展功能不修改源代码实现。
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/w1lgy/article/details/81364815

一、AOP?

1、AOP:面向切面编程,扩展功能不修改源代码实现。
2、什么是面向切面编程

将系统逻辑定义为切面,使得业务逻辑中不需要关注系统逻辑的实现,由切面来负责系统逻辑的具体实现。

面向切面编程往往被定义为促使软件系统实现关注点的分离技术。系统由许多不同的组件组成,每一个组件各负责一块特定功能。除了实现自身核心的功能之外,这些组件还经常承担着额外的职责。诸如日志、事务管理和安全这样的系统服务经常融入到自身具有核心业务逻辑的组件中去,这些系统服务通常被称为横切关注点,因为它们会跨越系统的多个组件。
如果将这些关注点分散到多个组件中去,你的代码将会带来双重的复杂性。

  • 实现系统关注点功能的代码将会重复出现在多个组件中。
  • 组件会因为那些与自身核心业务无关的代码而变得混乱。

二、Spring AOP术语

通知(Advice)

通知定义了切面是什么及何时使用。除了描述切面要完成的工作(日志记录),通知还决定了何时执行。
1、前置通知(Befor):目标方法调用前调用通知;
2、后置通知(After):目标方法调用后调用通知;
3、返回通知(After-returning):目标方法返回成功后调用通知;
4、异常通知(After-throwing):目标方法抛出异常后调用通知;
5、环绕通知(Around):目标方法调用前与调用后都会调用通知;

连接点(Join point)

连接点是指在应用执行过程中能够插入切面的一个具体点(调用方法时),就是类里面可以被增强的方法。

切点(Poincut)

切点定义了何处使用通知。定义匹配通知所要织入的一个或多个连接点。通常使用明确的类和方法名,或是利用正则表达式匹配。

切面(Aspect)

切面就是通知和切点的结合。

织入(Weaving)

把切面应用到目标对象,并创建新的代理对象的过程。

三、Spring的aop操作

A:aspectj实现aop的方式

1、在spring里面进行aop操作,使用aspectj实现

(1)aspectj不是spring一部分,和spring一起使用进行aop操作
(2)Spring2.0以后新增了对AspectJ支持

2、使用aspectj实现aop有两种方式

(1)基于aspectj的xml配置
(2)基于aspectj的注解方式

3、使用表达式配置切入点

(1)切入点:实际增强的方法
(2)常用的表达式
execution(<访问修饰符>?<返回类型><方法名>(<参数>)<异常>)
eg1:execution(* cn.itcast.aop.Book.add(..))
eg2:execution(* cn.itcast.aop.Book.*(..))
eg3:execution(* *.*(..))
eg4:匹配所有save开头的方法 execution(* save*(..))

4、Aop操作准备

(1)除了导入基本的jar包之外,还需要导入aop相关的jar包
这里写图片描述
(2)创建spring核心配置文件,导入aop的约束
这里写图片描述

5、使用aspectj实现aop

方式一:基于aspectj的注解方式
通知配置:

@Pointcut:切点配置
@Befor:前置拦截器注解
@After:后置拦截器注解
@AfterThrowing:后置异常拦截器注解
@AfterReturning:后置正常拦截器注解
@Around:环绕拦截器注解

运用举例:
springConfig.xml

<?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"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/context  
        http://www.springframework.org/schema/context/spring-context-3.0.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop-3.2.xsd">
    <!-- 扫描包路径实例化bean -->
    <context:component-scan base-package="com.prosay.aop.aspectj"/>
</beans>

基本类:

public interface ComputerSystem {

    public void run(Person name);
}
@Component
public class Windows10 implements ComputerSystem {

    public void run(Person name) {
        System.out.println("ComputerSystem,arg:"+name.getName());
        System.out.println("启动了windows10系统");
        System.out.println("启动了windows10系统");
    }

}
public class Person {

    private String name;
    private int age;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
}
public interface ComputerInterface {

    public void work(Person name);
}
@Component
public class Computer implements ComputerInterface {

    @Autowired
    private ComputerSystem system;

    //业务逻辑
    //连接点
    public void work(Person name){
        this.system.run(name);
    }
}
public class Client {

    public static void main(String[] args) {
        AbstractApplicationContext context = new ClassPathXmlApplicationContext("/com/prosay/aop/aspectj/springConfig.xml");
        ComputerInterface computer = (ComputerInterface) context.getBean("computer");
        Person obj = new Person();
        obj.setName("张三");
        obj.setAge(15);
        computer.work(obj);
    }
}

关键的aop类,AopAdvice是通过注解实现aop的:

/**
 * 
 * @author Kevin老师
 * @createTime 2018年5月23日 下午9:37:49
 * @Aspect :标识这是一个配置切面的类
 * @EnableAspectJAutoProxy : 开启切面自动代理
 */
@Aspect
@Component
@EnableAspectJAutoProxy
public class AopAdvice {
@Pointcut("execution(** com.prosay.aop.aspectj.Computer.work(..)) && args(arg)")
        public void aopDemo(Person arg){}

        @Before("aopDemo(arg)")
        public void aopMethodBefor(Person arg){
            System.out.println("After,arg:"+arg.getName());
            System.out.println("Befor the system work, do something.");
        }

        @After("aopDemo(arg)")
        public void aopMethodAfter(Person arg){
            System.out.println("After,arg:"+arg.getName());
            System.out.println("After the system work, do something.");
        }

        @Around("aopDemo(arg)")
        public void aroundMethod(ProceedingJoinPoint proc,Person arg){
            System.out.println("arg:"+arg.getName());
            try {
                proc.proceed();
            } catch (Throwable e) {
                e.printStackTrace();
            }
        }
}

方式二:基于aspectj的xml配置
通知配置:

<aop:pointcut />: 配置一个切点
<aop:befor />: 配置前置拦截器
<aop:after />: 配置后置拦截器
<aop:after-throwing />: 配置后置异常拦截器
<aop:after-returning />: 配置后置正常拦截器
<aop:around />: 配置环绕拦截器
Object[] args1= jp.getArgs();
Object[] args2= proc.getArgs();
ProceedingJoinPoint.proceed(): 执行被代理的具体方法
JoinPoint.getSignature().getName(): 被代理的方法名
JoinPoint.getArgs(): 被代理方法的参数数组
JoinPoint.getTarget: 具体被代理的对象

运用举例:
其他代码可以不用改变,需要改变的只有:springConfig.xml、和aop类AopAdvice;
springConfig.xml

<?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"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/context  
        http://www.springframework.org/schema/context/spring-context-3.0.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop-3.2.xsd">

    <bean id="windows10" class="com.prosay.aop.pojo.Windows10"/>
    <bean id="computer" class="com.prosay.aop.pojo.Computer">
        <!-- 构造函数注入 -->
        <constructor-arg ref="windows10"></constructor-arg> 
    </bean>

    <bean id="beforAdvice" class="com.prosay.aop.pojo.AopAdvice"></bean>

    <aop:config>
        <aop:aspect ref="beforAdvice">
            <aop:pointcut id="myPointcut" expression="execution(** com.prosay.aop.pojo.Computer.work(..))" />
            <!-- <aop:before method="beforAdvice" pointcut-ref="myPointcut"/>
            <aop:after method="afterAdvice" pointcut-ref="myPointcut"/> -->
            <aop:around method="aroundAdvice" pointcut-ref="myPointcut"/>
        </aop:aspect>
    </aop:config>
</beans>

AopAdvice:

public class AopAdvice {

    public void beforAdvice(JoinPoint point){
        System.out.println("在启动电脑前,做一些事情....");
//      point.getSignature().getName() //获取被代理的方法名称
//      point.getArgs();
//      point.getTarget();
    }

    public void afterAdvice(){
        System.out.println("在启动电脑后,做一些事情....");
    }

    public void aroundAdvice(ProceedingJoinPoint proc){
        try {
            beforAdvice(proc);
            Object returnValue = proc.proceed();
            afterAdvice();
        } catch (Throwable e) {
            e.printStackTrace();
        }
    }
}

补充:这两种方式可以混合使用。

6、切面参数获取

ProceedingJoinPoint.proceed(): 执行被代理的具体方法
JoinPoint.getSignature().getName(): 被代理的方法名
JoinPoint.getArgs(): 被代理方法的参数数组
JoinPoint.getTarget: 具体被代理的对象
单参数:
上面基于aspectj的注解方式的例子就是单参数的例子,上例输出结果:

arg:张三
After,arg:张三
Befor the system work, do something.
ComputerSystem,arg:张三
启动了windows10系统
After,arg:张三
After the system work, do something.

多参数
参数的传递
1. 表达式中需要加入参数
2. 利用argNames属性,声明参数
3. 对于符合execution表达式,但不符合参数类型的方法,不会被织入切面
还是用上面的例子,把参数改成两个

参数的修改
只有在环绕通知中可以修改参数.
1.获得参数的数组:Object[] args = pjp.getArgs();
2.修改参数的数组元素:args[i]=XXX;
3.执行目标对象方法时候,传入新数组;Object ret = pjp.proceed(args);

public interface ComputerInterface {

//  public void work(Person name);
    public void work(Person name,String str);
}
@Component
public class Computer implements ComputerInterface {

    @Autowired
    private ComputerSystem system;

    //业务逻辑
    //连接点
    public void work(Person name,String str){
        this.system.run(name,str);
    }
//  public void work(Person name){
//      this.system.run(name);
//  }

}
public interface ComputerSystem {

//  public void run(Person name);
    public void run(Person name,String str);
}
@Component
public class Windows10 implements ComputerSystem {

    public void run(Person name,String str) {

        System.out.println("ComputerSystem,arg:"+name.getName());
        System.out.println("ComputerSystem,str:"+str);
        System.out.println("启动了windows10系统");
    }
//  public void run(Person name) {
//      
//      System.out.println("ComputerSystem,arg:"+name.getName());
////        System.out.println("ComputerSystem,str:"+str);
//      System.out.println("启动了windows10系统");
//  }

}
public class Client {

    public static void main(String[] args) {
        AbstractApplicationContext context = new ClassPathXmlApplicationContext("/com/prosay/aop/aspectj/springConfig.xml");
        ComputerInterface computer = (ComputerInterface) context.getBean("computer");
        Person obj = new Person();
        obj.setName("张三");
        obj.setAge(15);
//      computer.work(obj);
        computer.work(obj,"test");
    }
}
@Aspect
@Component
@EnableAspectJAutoProxy
public class AopAdvice {

     @Pointcut(value="execution(** com.prosay.aop.aspectj.Computer.work(..)) && args(arg,str)",argNames="arg,str")
        public void aopDemo(Person arg,String str){}

//      @Before("aopDemo()")
        public void aopMethodBefor(){
            System.out.println("Befor the system work, do something.");
        }

        @After(value="aopDemo(arg,str)",argNames="arg,str")
        public void aopMethodAfter(Person arg,String str){
            System.out.println("After,arg:"+arg.getName());
            System.out.println("After,str:"+str);
            System.out.println("After the system work, do something.");
        }

        @Around(value="aopDemo(arg,str)",argNames="arg,str")
        public void aroundMethod(ProceedingJoinPoint proc,Person arg,String str){
            System.out.println("arg:"+arg.getName());
            //修改参数
            arg.setName("叫爸爸");
            aopMethodBefor();
            try {
//              proc.proceed();
                Object[] args = proc.getArgs();
                args[0]=arg;
                args[1]="hahah";
               proc.proceed(args);
            } catch (Throwable e) {
                e.printStackTrace();
            }
//          aopMethodAfter();

        }

//      @Pointcut("execution(** com.prosay.aop.aspectj.Computer.work(..)) && args(arg)")
//      public void aopDemo(Person arg){}
//
//      @Before("aopDemo(arg)")
//      public void aopMethodBefor(Person arg){
//          System.out.println("After,arg:"+arg.getName());
//          System.out.println("Befor the system work, do something.");
//      }
//
//      @After("aopDemo(arg)")
//      public void aopMethodAfter(Person arg){
//          System.out.println("After,arg:"+arg.getName());
//          System.out.println("After the system work, do something.");
//      }
//
//      @Around("aopDemo(arg)")
//      public void aroundMethod(ProceedingJoinPoint proc,Person arg){
//          System.out.println("arg:"+arg.getName());
//          try {
//              proc.proceed();
//          } catch (Throwable e) {
//              e.printStackTrace();
//          }
//      }
}

运行结果

arg:张三
Befor the system work, do something.
ComputerSystem,arg:叫爸爸
ComputerSystem,str:hahah
启动了windows10系统
After,arg:叫爸爸
After,str:test
After the system work, do something.

注意:环绕对所有符合表达式的方法,都会进行织入切面即没有形参的方法,且符合表达式,也会被织入切面 而加入形参pjp.proceed(args);会报错.

B:基于代理的经典Spring AOP

这种模式的AOP通知类要继承接口,有三个接口,可以根据需要选择继承
MethodBeforAdvice:前置拦截器
befor(Method method, Object[] args, Object target) throws Throwable {
method: 被代理的方法名
args: 被代理方法的参数数组
target: 具体被代理的对象
}
AfterReturningAdvice:后置拦截器
afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
returnValue: 被代理方法的返回值
method: 被代理的方法名
args: 被代理方法的参数数组
target: 具体被代理的对象
}
MethodInterceptor:环绕拦截器拦
invoke(MethodInvacation invacation) throws Throwable {
invacation.proceed(): 执行被代理的具体方法
invacation.getMethod(): 被代理的方法名
invacation.getArguments(): 被代理方法的参数数组
invacation.getThis(): 具体被代理的对象
}

springConfig.xml:

<?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:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
    http://www.springframework.org/schema/context  
    http://www.springframework.org/schema/context/spring-context-3.0.xsd">

    <bean id="windows10" class="com.prosay.aop.spring.Windows10"/>
    <bean id="computer" class="com.prosay.aop.spring.Computer">
        <!-- 构造函数注入 -->
        <constructor-arg ref="windows10"></constructor-arg> 
    </bean>

    <bean id="beforAdvice" class="com.prosay.aop.spring.AopAdvice"></bean>
    <!-- 切点 -->
    <bean id="computerProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
        <property name="interfaces">
            <!-- 被代理的对象所实现接口 -->
            <value>com.prosay.aop.spring.ComputerInterface</value>
        </property>
        <!-- 具体被代理的对象/实例 -->
        <property name="target" ref="computer"></property>
        <property name="interceptorNames">
            <!-- 配置通知 -->
            <list>
                <value>beforAdvice</value>
            </list>
        </property>
    </bean>

</beans>

基本类型:

public interface ComputerSystem {

    public void run();
}
public class Windows10 implements ComputerSystem {

    public void run() {
        System.out.println("启动了windows10系统");
    }

}
public interface ComputerInterface {

    public void work(String name);
}
public class Computer implements ComputerInterface {

    private ComputerSystem system;

    //1.构造注入
    public Computer(ComputerSystem system) {
        this.system = system;
    }

    //业务逻辑
    //连接点
    public void work(String name){
        this.system.run();
    }


    //连接点
    public void work2(){
        this.system.run();
    }
}

通知类

//通知
public class AopAdvice implements MethodBeforeAdvice,AfterReturningAdvice, MethodInterceptor {

    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {

        System.out.println("完事之后, 抽根烟");
    }

    //  具体时间点
    public void before(Method method, Object[] args, Object target) throws Throwable {
        //谁谁谁 什么时间 访问了什么接口。。。
        System.out.println("method:"+method);
        System.out.println("args:"+args[0]);
        System.out.println("target:"+target);
        LogUtil.insertLog();
    }

    public Object invoke(MethodInvocation invocation) throws Throwable {
        System.out.println("around 开始");
//      invocation.getMethod()
//      invocation.getArguments()
//      invocation.getThis()
        Object returnValue = null;
        try {
            //具体业务方法
            returnValue = invocation.proceed();
        } catch (Exception e) {
            System.out.println("around 异常");
            e.printStackTrace();
        }
        System.out.println("around 正常返回");

        return returnValue;
    }

}

测试类

public class Client {

    public static void main(String[] args) {
        AbstractApplicationContext context = new ClassPathXmlApplicationContext("/com/prosay/aop/spring/springConfig.xml");
        ComputerInterface computer = (ComputerInterface) context.getBean("computerProxy");
        computer.work("张三");
    }
}

运行结果

around 开始
method:public abstract void com.prosay.aop.spring.ComputerInterface.work(java.lang.String)
args:张三
target:com.prosay.aop.spring.Computer@15d0c81b
插入日志
启动了windows10系统
完事之后, 抽根烟
around 正常返回

补充:这种方式参数传递不需要做过多的处理,因为Advice方法中自带参数。

AOP的实现原理是代理模式,需要了解的朋友可以去看我之前的博客:Java代理模式

目录
相关文章
|
1月前
|
XML Java 数据安全/隐私保护
Spring Aop该如何使用
本文介绍了AOP(面向切面编程)的基本概念和术语,并通过具体业务场景演示了如何在Spring框架中使用Spring AOP。文章详细解释了切面、连接点、通知、切点等关键术语,并提供了完整的示例代码,帮助读者轻松理解和应用Spring AOP。
Spring Aop该如何使用
|
24天前
|
监控 安全 Java
什么是AOP?如何与Spring Boot一起使用?
什么是AOP?如何与Spring Boot一起使用?
48 5
|
28天前
|
Java 开发者 Spring
深入解析:Spring AOP的底层实现机制
在现代软件开发中,Spring框架的AOP(面向切面编程)功能因其能够有效分离横切关注点(如日志记录、事务管理等)而备受青睐。本文将深入探讨Spring AOP的底层原理,揭示其如何通过动态代理技术实现方法的增强。
54 8
|
28天前
|
Java 开发者 Spring
Spring AOP 底层原理技术分享
Spring AOP(面向切面编程)是Spring框架中一个强大的功能,它允许开发者在不修改业务逻辑代码的情况下,增加额外的功能,如日志记录、事务管理等。本文将深入探讨Spring AOP的底层原理,包括其核心概念、实现方式以及如何与Spring框架协同工作。
|
28天前
|
XML 监控 安全
深入调查研究Spring AOP
【11月更文挑战第15天】
41 5
|
28天前
|
Java 开发者 Spring
Spring AOP深度解析:探秘动态代理与增强逻辑
Spring框架中的AOP(Aspect-Oriented Programming,面向切面编程)功能为开发者提供了一种强大的工具,用以将横切关注点(如日志、事务管理等)与业务逻辑分离。本文将深入探讨Spring AOP的底层原理,包括动态代理机制和增强逻辑的实现。
41 4
|
2月前
|
存储 缓存 Java
Spring高手之路23——AOP触发机制与代理逻辑的执行
本篇文章深入解析了Spring AOP代理的触发机制和执行流程,从源码角度详细讲解了Bean如何被AOP代理,包括代理对象的创建、配置与执行逻辑,帮助读者全面掌握Spring AOP的核心技术。
50 3
Spring高手之路23——AOP触发机制与代理逻辑的执行
|
1月前
|
Java Spring
[Spring]aop的配置与使用
本文介绍了AOP(面向切面编程)的基本概念和核心思想。AOP是Spring框架的核心功能之一,通过动态代理在不修改原代码的情况下注入新功能。文章详细解释了连接点、切入点、通知、切面等关键概念,并列举了前置通知、后置通知、最终通知、异常通知和环绕通知五种通知类型。
37 1
|
3月前
|
设计模式 Java 测试技术
spring复习04,静态代理动态代理,AOP
这篇文章讲解了Java代理模式的相关知识,包括静态代理和动态代理(JDK动态代理和CGLIB),以及AOP(面向切面编程)的概念和在Spring框架中的应用。文章还提供了详细的示例代码,演示了如何使用Spring AOP进行方法增强和代理对象的创建。
spring复习04,静态代理动态代理,AOP
|
1月前
|
安全 Java 测试技术
Java开发必读,谈谈对Spring IOC与AOP的理解
Spring的IOC和AOP机制通过依赖注入和横切关注点的分离,大大提高了代码的模块化和可维护性。IOC使得对象的创建和管理变得灵活可控,降低了对象之间的耦合度;AOP则通过动态代理机制实现了横切关注点的集中管理,减少了重复代码。理解和掌握这两个核心概念,是高效使用Spring框架的关键。希望本文对你深入理解Spring的IOC和AOP有所帮助。
42 0