Spring -- AOP

Posted 西北野狼

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring -- AOP相关的知识,希望对你有一定的参考价值。

AOP:(Aspect Oriented Programing):面向切面编程

定义:指在程序运行期间,动态的将某段代码插入到指定方法的指定位置进行运行的一种编程方式;

SpringAOP:面向切面编程;底层就是动态代理;
Spring为了简化动态代理,提供了AOP功能;
使用SpringAOP完成日志记录动态切入的功能;
 

AOP功能使用步骤:

1)、导包;
          1、ioc包必须导:
commons-logging-1.1.3.jar
spring-beans-4.0.0.RELEASE.jar
spring-context-4.0.0.RELEASE.jar
spring-core-4.0.0.RELEASE.jar
spring-expression-4.0.0.RELEASE.jar

  2、AOP功能包;

spring-aop-4.0.0.RELEASE.jar
spring-aspects-4.0.0.RELEASE.jar
增强版的面向切面功能:
com.springsource.net.sf.cglib-2.2.0.jar
com.springsource.org.aopalliance-1.0.0.jar
com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
2)、写配置;
<?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"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
        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-4.0.xsd">
    <context:component-scan base-package="com.soyoungboy"></context:component-scan>
    <aop:aspectj-autoproxy/>
</beans>

 

3)、实现AOP:(将日志代码,在业务逻辑运行的时候切入到指定位置)
  1、在切面类里面配置每一个方法都是何时何地运行
  2、将切面类和业务逻辑组件都加入到容器
  3、告诉SpringIOC容器哪个类是切面类@Aspect
  4、开启基于注解的AOP功能;
代码举例子来通过Spring Aop实现日志代码的切入:
计算器接口类:
public interface Calculator {
    
    public int add(int i,int j);
    public int sub(int i,int j);
    public int mul(int i,int j);
    public int div(int i,int j);

}

实现类:

package com.soyoungboy.inter;

import org.springframework.stereotype.Service;

/**
 * 业务逻辑
 * @author soyoungboy
 *
 */
@Service
public class MathCalculator implements Calculator {

    public int add(int i, int j) {
        int result = i + j;
        System.out.println("======加法内部");
        return result;
    }

    public int sub(int i, int j) {
        int result = i - j;
        return result;
    }

    public int mul(int i, int j) {
        int result = i * j;
        return result;
    }

    public int div(int i, int j) {
        int result = i / j;
        return result;
    }

}

切面类:

package com.soyoungboy.util;

import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

/**
 * 切面类
 * @author soyoungboy
 *
 */
@Component
@Aspect
public class LogUtils {

    /**
     * try{
     *         @Before:
     *         method.invoke(.....);
     *         @AfterReturning
     * }catch(e){
     *         @AfterThrowing
     * }finally{
     *         @After
     * }
     *
     *
     * 提供了几个注解:
     * @Before:意思在目标方法运行之前运行:        前置通知
     * @After:目标方法结束之后                后置通知
     * @AfterReturning:方法正常执行并返回        返回通知
     * @AfterThrowing:方法出现异常以后调用        异常通知
     * @Around:最强大的通知(这就是动态代理)        环绕通知
     *
     * 使用切入点表达式,指定到底是来拦截哪些方法的;
     * execution(访问权限控制符   返回值类型   方法的全描述(参数表))
     */

    //日志开始
    @Before(value="execution(public int com.soyoungboy.inter.MathCalculator.*(int, int))")
    public static void logStart(){
        System.out.println("xxx方法开始,参数列表【xxx】");
    }

    //日志方法正常返回
    @AfterReturning(value="execution(public int com.soyoungboy.inter.MathCalculator.*(int, int))")
    public static void logReturn() {
        System.out.println("xxx方法正常返回,返回值是:xxx");
    }

    //日志记录异常
    @AfterThrowing(value="execution(public int com.soyoungboy.inter.MathCalculator.*(int, int))")
    public static void logException() {
        System.out.println("xxx方法出现异常,异常信息是:xxx");
    }

    //方法结束
    @After(value="execution(public int com.soyoungboy.inter.MathCalculator.*(int, int))")
    public static void logEnd() {
        System.out.println("xxx方法结束");
    }
}

junit测试代码:

package com.soyoungboy.test;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.soyoungboy.inter.Calculator;
import com.soyoungboy.inter.MathCalculator;

public class AOPTest {
    
    ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
    
    
    
    /**
     * 1、如果没有接口,我们的cglib会根据当前类作为父模板;创建出一个内置的代理对象;
     */
    @Test
    public void test02(){
        
        MathCalculator bean = (MathCalculator) ioc.getBean("mathCalculator");
        System.out.println(bean.getClass());
        bean.add(2, 3);
        
    }
    
    
    /**
     * 如果有接口一定要用接口类型,因为获取到的组件是一个代理对象。
     * 和被代理对象的共同特点就是实现了同一个接口
     */
    @Test
    public void test01(){
        //MathCalculator calculator = new MathCalculator();
        //calculator.add(1, 2);
        //1、有接口必须写接口类型
        //Calculator calculator = ioc.getBean(Calculator.class);
        Calculator calculator = (Calculator) ioc.getBean("mathCalculator");
        
        calculator.add(1, 3);
        
        
        //容器中保存的是这个组件的代理对象;如果是切面进行动态切入的组件。
        //com.sun.proxy.$Proxy12
        System.out.println(calculator.getClass());
        
        Class<?>[] classes = calculator.getClass().getInterfaces();
        System.out.println(classes[0]);
        
    }


}

test2方法在MathCalculator没实现Calculator的时候可以执行:

结果为:

class com.soyoungboy.inter.MathCalculator$$EnhancerByCGLIB$$97f6d938
xxx方法开始,参数列表【xxx】
======加法内部
xxx方法结束
xxx方法正常返回,返回值是:xxx

test01在MathCalculator实现Calculator的情况下执行:

结果为:

xxx方法开始,参数列表【xxx】
======加法内部
xxx方法结束
xxx方法正常返回,返回值是:xxx
class com.sun.proxy.$Proxy13
interface com.soyoungboy.inter.Calculator

 

以上是关于Spring -- AOP的主要内容,如果未能解决你的问题,请参考以下文章

JAVA之AOP

Spring AOP

Spring源码高级笔记之——Spring AOP应用

2018.12.24 Spring中的aop演示

Spring框架 AOP

Spring的AOP面向切面编程