spring4(二) AOP详解-面向切面编程

spring AOP-面向切面编程

AOP简介

  1. AOP:中文名称面向切面编程
  2. 英文名称:( Aspect Oriented Programming )
  3. 正常程序执行流程都是纵向执行流程
    1. 在原有纵向执行流程中添加横切面,叫面向切面编程
  4. 面向切面编程(AOP)是什么?
    1. 在程序原有纵向执行流程中,针对某一个或某一些方法添加通知,形成横切面过程就叫做面向切面编程.
    2. 见下图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Onvbun2X-1573477147038)(assets/aop.png)]

优点

  • 面向切面编程的优点
    1. 不需要修改原有程序代码
    2. 高扩展性
    3. 原有功能相当于释放了部分逻辑.让职责更加明确.

AOP常用概念

  1. 原有功能: 切点, pointcut
  2. 前置通知: 在切点之前执行的功能. before advice
  3. 后置通知: 在切点之后执行的功能,after advice
  4. 异常通知: 出现异常的时候执行的功能
  5. 环绕通知: 在切点执行之前和之后都会执行的功能
  6. 如果切点执行过程中出现异常,会触发异常通知。 throws advice
  7. 所有功能总称叫做切面.
  8. 织入: 把切面嵌入到原有功能的过程叫做织入

两种AOP实现方式

一、Schema-based

  1. 每个通知都需要实现接口或类
  2. 配置 spring 配置文件时在 <aop:config> 配置
代码实现
  1. 导包

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hsM0uHqD-1573477147040)(assets/aop-jar.png)]

  1. 创建Demo类
public class Demo {

    public void demo1() {
        System.out.println("demo1");
    }
    public void demo2() {
        System.out.println("demo2");
    }
    public void demo3() {
        System.out.println("demo3");
    }
}
  1. 创建自定义通知类
// 前置通知类
public class MyBeforeAdvice implements MethodBeforeAdvice {
    /**
     * @param method    切点方法对象 Method 对象
     * @param objects   切点方法参数
     * @param o         切点在哪个对象中
     * @throws Throwable
     */
    @Override
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        System.out.println("前置通知====");
    }
}
// 后置通知类
public class MyAfterAdvice implements AfterReturningAdvice {
    /**
     * @param o         切点方法返回值
     * @param method    切点方法对象
     * @param objects   切点方法参数
     * @param o1        切点方法所在类的对象
     * @throws Throwable
     */
    @Override
    public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
        System.out.println("后置通知=====");
    }
}
  1. 书写spring配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns="http://www.springframework.org/schema/beans"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
    <!-- 1. 引入aop命名空间:xmlns:aop -->

    <!-- 2. 配置自定义通知类的<bean> -->
    <bean id="myAfterAdvice" class="com.fu.advice.MyAfterAdvice"></bean>
    <bean id="myBeforeAdvice" class="com.fu.advice.MyBeforeAdvice"></bean>

    <!-- 3. 配置切面 -->
    <aop:config>
        <!-- 配置切点:execution()
            * 通配符,匹配任意方法名,任意类名,任意一级包名
            如果希望匹配任意方法参数 (..)
         -->
        <aop:pointcut expression="execution(* com.fu.bean.Demo.demo2())" id="myPoint"></aop:pointcut>
        <!-- 配置织入: Schema-based 方式
            自定义通知类必须实现 MethodBeforeAdvice 或 AfterReturningAdvice 接口,指定前置或后置通知
         -->
        <aop:advisor advice-ref="myBeforeAdvice" pointcut-ref="myPoint"></aop:advisor>
        <aop:advisor advice-ref="myAfterAdvice" pointcut-ref="myPoint"></aop:advisor>
    </aop:config>

    <!-- 4. 配置演示类IOC -->
    <bean id="demo" class="com.fu.bean.Demo"></bean>
</beans>
  1. 书写测试类
// 测试类
public class Test {
    public static void main(String[] args) {
        ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
        Demo demo = ac.getBean("demo", Demo.class);

        demo.demo1();
        demo.demo2();
        demo.demo3();
    }
}

/* 代码运行结果 */
/*
demo1
前置通知====
demo2
后置通知=====
demo3
*/

二、AspectJ

  1. 每个通知不需要实现接口或类
  2. 配置 spring 配置文件是在 <aop:config> 的子标签 <aop:aspect> 中配置
spring配置文件
  1. <aop:after/> 后置通知,是否出现异常都执行
  2. <aop:after-returing/> 后置通知,只有当切点正确执行时执行
  3. <aop:after/> 和 <aop:after-returing/> 和 <aop:after-throwing/>执行顺序和配置顺序有关
  4. execution() 括号不能扩上 args
  5. 中间使用 and 不能使用&& 由 spring 把 and 解析成&&
  6. args(名称) 名称自定义的.顺序和 demo1(参数,参数)对应
  7. <aop:before/> arg-names=” 名 称 ” 名 称 来 源 于 expression=”” 中 args(),名称必须一样
  8. args() 有几个参数,arg-names 里面必须有几个参数
  9. arg-names=”” 里面名称必须和通知方法参数名对应
代码实现
  • Demo.java
public class Demo {
    public void demo1() {
        int a = 5 / 0;
        System.out.println("demo1");
    }
}
  • MyAdvice.java
// 自定义通知类
public class MyAdvice {
    public void beforeAspect() {
        System.out.println("前置通知:beforeAspect");
    }

    public void afterAspect() {
        System.out.println("后置通知, 不管有没有异常都执行:afterAspect");
    }

    public void throwAspect() {
        System.out.println("异常通知:throwAspect");
    }

    public Object aroundAspect(ProceedingJoinPoint p) throws Throwable {
        System.out.println("前置环绕通知:aroundAspect");
        Object result = p.proceed();
        System.out.println("后置环绕通知:aroundAspect");
        return result;
    }

    public void returningAspect() {
        System.out.println("后置通知,只有当切点正确执行时执行:returningAspect");
    }

}
  • applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns="http://www.springframework.org/schema/beans"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">


    <bean id="myAspect" class="com.fu.advice.MyAdvice"></bean>
    <!-- 切面 -->
    <aop:config>
        <aop:pointcut expression="execution(* com.fu.bean.Demo.demo1())" id="myPoint"></aop:pointcut>
        <!-- 织入通知:aspect方式
                ref : 表示方法在哪个类中
         -->
        <aop:aspect ref="myAspect">
            <aop:after method="afterAspect" pointcut-ref="myPoint"></aop:after>
            <aop:before method="beforeAspect" pointcut-ref="myPoint"></aop:before>
            <!-- 环绕通知 -->
            <aop:around method="aroundAspect" pointcut-ref="myPoint"></aop:around>
            <!-- 异常通知 -->
            <aop:after-throwing method="throwAspect" pointcut-ref="myPoint" ></aop:after-throwing>
            <!-- 在目标方法正常完成后被织入 -->
            <aop:after-returning method="returningAspect" pointcut-ref="myPoint"></aop:after-returning>
        </aop:aspect>
    </aop:config>
    <bean id="demo" class="com.fu.bean.Demo"></bean>
</beans>
  • 测试类
public class Test {

    public static void main(String[] args) {
        ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext2.xml");
        Demo demo = ac.getBean("demo", Demo.class);

        demo.demo1();
        /*  有异常时
        前置通知:beforeAspect
        前置环绕通知:aroundAspect
        后置通知:afterAspect
        异常通知:throwAspect
         */
        /*  无异常时
        前置通知:beforeAspect
        前置环绕通知:aroundAspect
        demo1
        后置通知:afterAspect
        后置环绕通知:aroundAspect
        运行成功后的通知:returningAspect
         */
    }
}

环绕通知

  • 把前置通知和后置通知都写到一个通知中, 组成了环绕通知
schema-besed方式
  1. 新建一个类实现 MethodInterceptor 接口

  2. 重写 invoke 方法

    public class MyArround implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation arg0) throws Throwable {
        System.out.println("环绕-前置");
        Object result = arg0.proceed();//放行,调用切点方式
        System.out.println("环绕-后置");
        return result;
    }
    
AspectJ方式
  1. 新建类,不用实现任何接口

  2. 类中方法名任意,添加参数 (ProceedingJoinPoint p)

    public Object myarround(ProceedingJoinPoint p) throws Throwable{
        System.out.println("执行环绕");
        System.out.println("环绕-前置");
        Object result = p.proceed();
        System.out.println("环绕后置");
        return result;
    }
    
发布了21 篇原创文章 · 获赞 5 · 访问量 2061

猜你喜欢

转载自blog.csdn.net/fan521dan/article/details/102968911
今日推荐