开发者社区> 我巴巴> 正文
阿里云
为了无法计算的价值
打开APP
阿里云APP内打开

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代理模式

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
Spring Aop 详解(下)
面向切面编程(AOP)是面向对象编程(OOP)的补充,它提供了另一种关于程序结构的思考方式。OOP中模块化的关键单元是类,而在AOP中,模块化单元是切面。切面支持跨多个类型和对象的切点(如事务管理)的模块化。 Spring AOP 是 Spring 框架的关键组件之一。Spring IOC 容器不依赖于AOP组件,如果不要我们项目中不需要 AOP 功能那么就可以不加载这个模块。AOP 补充了 Spring IOC,以提供一个非常强大的中间件解决方案。
52 0
Spring Aop 详解(上)
面向切面编程(AOP)是面向对象编程(OOP)的补充,它提供了另一种关于程序结构的思考方式。OOP中模块化的关键单元是类,而在AOP中,模块化单元是切面。切面支持跨多个类型和对象的切点(如事务管理)的模块化。 Spring AOP 是 Spring 框架的关键组件之一。Spring IOC 容器不依赖于AOP组件,如果不要我们项目中不需要 AOP 功能那么就可以不加载这个模块。AOP 补充了 Spring IOC,以提供一个非常强大的中间件解决方案。
65 0
Spring之AOP详解
AOP介绍 AOP(Aspect Oriented Programming),即面向切面编程,可以说是OOP(Object Oriented Programming,面向对象编程)的补充和完善。
69 0
Spring AOP详解
AOP AOP(Aspect Oriented Programming),即面向切面编程,它利用一种称为"横切"的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其命名为"Aspect",即切面。
1299 0
Spring AOP 详解
AOP使用场景 AOP用来封装横切关注点,具体可以在下面的场景中使用: Authentication 权限 Caching 缓存 Context passing 内容传递 Error handling 错误处理 Lazy loading 懒加载 Debugging  调试 loggi...
1236 0
Spring AOP详解
程序员还是需要把基础打扎实,修炼自己的内功。” 所以赶紧把学习的东西总结一下,加深印象。 。基于代理模式,了解了jdk动态代理和cglib的用法。
1128 0
Spring AOP 详解
原理 AOP(Aspect Oriented Programming),也就是面向方面编程的技术。AOP基于IoC基础,是对OOP的有益补充。
1358 0
Spring AOP详解(http://sishuok.com/forum/posts/list/281.html)
三6.5  AspectJ切入点语法详解 6.5.1  Spring AOP支持的AspectJ切入点指示符        切入点指示符用来指示切入点表达式目的,,在Spring AOP中目前只有执行方法这一个连接点,Spring AOP支持的AspectJ切入点指示符如下:          execution:用于匹配方法执行的连接点;      
1505 0
+关注
我巴巴
编程语言 架构 相关技术专家
86
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
低代码开发师(初级)实战教程
立即下载
阿里巴巴DevOps 最佳实践手册
立即下载
冬季实战营第三期:MySQL数据库进阶实战
立即下载