Spring AOP

AOP

AOP 的定义

AOP(Aspect-Oriented Programming, 面向切面编程): 是一种新的方法论, 是对传统 OOP(Object-Oriented Programming, 面向对象编程) 的补充.
AOP 的主要编程对象是切面(aspect), 而切面模块化横切关注点.
在应用 AOP 编程时, 仍然需要定义公共功能, 但可以明确的定义这个功能在哪里, 以什么方式应用, 并且不必修改受影响的类. 这样一来横切关注点就被模块化到特殊的对象(切面)里.

AOP 的好处

  • 每个事物逻辑位于一个位置, 代码不分散, 便于维护和升级
  • 业务模块更简洁, 只包含核心业务代码.

切面(Aspect) 横切关注点(跨越应用程序多个模块的功能)被模块化的特殊对象

通知(Advice)

切面必须要完成的工作

目标(Target)

被通知的对象

代理(Proxy)

向目标对象应用通知之后创建的对象

连接点(Joinpoint)

程序执行的某个特定位置:如类某个方法调用前、调用后、方法抛出异常后等。连接点由两个信息确定:方法表示的程序执行点;相对点表示的方位。例如 ArithmethicCalculator#add() 方法执行前的连接点,执行点为 ArithmethicCalculator#add(); 方位为该方法执行前的位置

切点(pointcut)

每个类都拥有多个连接点:例如 ArithmethicCalculator 的所有方法实际上都是连接点,即连接点是程序类中客观存在的事务。AOP 通过切点定位到特定的连接点。类比:连接点相当于数据库中的记录,切点相当于查询条件。切点和连接点不是一对一的关系,一个切点匹配多个连接点,切点通过 org.springframework.aop.Pointcut 接口进行描述,它使用类和方法作为连接点的查询条件。

原理

动态代理

代理设计模式

代理设计模式的原理: 使用一个代理将对象包装起来, 然后用该代理对象取代原始对象. 任何对原始对象的调用都要通过代理. 代理对象决定是否以及何时将方法调用转到原始对象上.

示例代码(使用动态代理自己实现的日志代理类)

package com.weixuan.springaop;

public interface Calculator {

    int add(int i, int j);

    int sub(int i, int j);

    int mul(int i, int j);

    int div(int i, int j);
}
package com.weixuan.springaop;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;

public class CalculatorLoggingProxy {

    /**
     * 要代理的对象
     */
    private Calculator calculator;

    public CalculatorLoggingProxy(Calculator calculator) {
        this.calculator = calculator;
    }

    public Calculator getLoggingProxy() {
        Calculator proxy = null;
        /**
         * 代理对象有哪一个类加载器来进行加载
         */
        ClassLoader classLoader = calculator.getClass().getClassLoader();

        /**
         * 代码对象有哪些方法
         */
        Class[] interfaces = new Class[] { Calculator.class };

        /**
         * 当调用代理对象的方法时,执行的代码
         */
        InvocationHandler invocationHandler = new InvocationHandler() {

            /**
             * @param proxy
             *            正在返回的对象,一般情况下,不使用
             * @param method
             *            正在被调用的方法
             * @param args
             *            调用方法时传入的参数
             */
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                /**
                 * 日志
                 */
                System.out.println("The mathod " + method.getName() + " begins with " + Arrays.asList(args));
                /**
                 * 执行
                 */
                Object result = method.invoke(calculator, args);
                /**
                 * 日志
                 */
                System.out.println("The mathod " + method.getName() + " ends with " + result);
                return result;
            }
        };
        return proxy = (Calculator) Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);
    }
}

package com.weixuan.springaop;

public class Client {

    public static void main(String[] args) {
        Calculator calculator = new CalculatorImpl();
        //System.out.println("----> " + calculator.div(4, 2));
        Calculator proxy = new CalculatorLoggingProxy(calculator).getLoggingProxy();
        proxy.add(2, 3);
    }
}

使用spring aop的注解

package com.weixuan.springsop.aspect;

import java.util.Arrays;
import java.util.List;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

/**
 * 把这个类声明为一个切面
 * 
 * @author Nicholas 
 * 1. 把该类放入IOC容器 @compont 
 * 2. 添加注解 @aspect
 */

@Component
@Aspect
public class LoggingAspect {
    /**
     * 声明该方法是一个前置通知 joinpoint 切入点
     */
    @Before("execution(public int com.weixuan.springsop.inter.Calculator.*(int , int ))")
    public void beforeMessage(JoinPoint joinpoint) {
        String methodName = joinpoint.getSignature().getName();
        List<Object> args = Arrays.asList(joinpoint.getArgs());
        System.out.println("The method " + methodName + " begin with " + args);
    }
}

package com.weixuan.springsop.impl;
import org.springframework.stereotype.Component;
import com.weixuan.springsop.inter.Calculator;
@Component
public class CalculatorImpl implements Calculator {
    @Override
    public int add(int i, int j) {
        return i + j;
    }
    @Override
    public int sub(int i, int j) {
        return i - j;
    }
    @Override
    public int mul(int i, int j) {
        return i * j;
    }

    @Override
    public int div(int i, int j) {
        if (j != 0)
            return i / j;
        return 0;
    }
}

package com.weixuan.springsop.client;

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

import com.weixuan.springsop.inter.Calculator;

public class Main {

    public static void main(String[] args) {

        // 1. 创建ioc容器
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("com/weixuan/springsop/config/application-context.xml");
        // 2. 获取实例
        Calculator calculator = applicationContext.getBean(Calculator.class);
        // 3. 使用
        System.out.println(calculator.add(2, 4));
    }
}
<?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.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.1.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd">
<!-- 配置自动扫描的包 -->
    <context:component-scan base-package="com.weixuan.springsop"></context:component-scan>

    <!-- 使aspect注解起作用  ,自动为匹配的类生成代理对象-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>

各种通知

package com.weixuan.springsop.aspect;

import java.util.Arrays;
import java.util.List;

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

/**
 * 把这个类声明为一个切面
 * 
 * @author Nicholas 1. 把该类放入IOC容器 @compont 2. 添加注解 @aspect
 */

@Component
@Aspect
public class LoggingAspect {
    /**
     * 声明该方法是一个前置通知 joinpoint 切入点
     */
    @Before("execution(public int com.weixuan.springsop.inter.Calculator.*(int , int ))")
    public void beforeMessage(JoinPoint joinpoint) {
        String methodName = joinpoint.getSignature().getName();
        List<Object> args = Arrays.asList(joinpoint.getArgs());
        System.out.println("The method " + methodName + " begin with " + args);
    }

    /**
     * 无论是否有异常,都执行 还不能访问目标方法的返回结果,返回结果要在返回通知中才能访问
     * 
     * @param joinpoint
     */
    @After("execution(public int com.weixuan.springsop.inter.Calculator.*(int , int ))")
    public void afterMessage(JoinPoint joinpoint) {
        String methodName = joinpoint.getSignature().getName();
        List<Object> args = Arrays.asList(joinpoint.getArgs());
        System.out.println("The method " + methodName + " end with " + args);
    }

    /**
     * 返回通知
     * 
     * @param joinpoint
     *            在方法正常返回的通知
     */
    @AfterReturning(value = "execution(public int com.weixuan.springsop.inter.Calculator.*(int , int ))", returning = "result")
    public void returnMessage(JoinPoint joinpoint, Object result) {
        String methodName = joinpoint.getSignature().getName();
        System.out.println("The method " + methodName + " return with " + result);
    }

    /**
     * 返回通知
     * 
     * @param joinpoint
     *            在方法正常返回的通知
     */
    @AfterThrowing(value = "execution(public int com.weixuan.springsop.inter.Calculator.*(int , int ))", throwing = "exception")
    public void throwingMessage(JoinPoint joinpoint, Exception exception) {
        String methodName = joinpoint.getSignature().getName();
        System.out.println("The method " + methodName + " occurs exception " + exception);
    }

    /**
     * 环绕通知 类似于 动态代理的全过程 这个类型参数可以决定是否执行目标方法,必须有返回值。
     * 
     * @param ProceedingJoinPoint
     *            在方法正常返回的通知s
     */
    @Around(value = "execution(public int com.weixuan.springsop.inter.Calculator.*(int , int ))")
    public Object aroundMessage(ProceedingJoinPoint proceedingJoinPoint) {
        System.out.println("The method ");
        Object result = null;
        try {
            result = proceedingJoinPoint.proceed();
        } catch (Throwable e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return result;
    }
}

切面的优先级

多个切面,那个先执行?

使用@order(1..n)指定优先级,值越小,优先级越高

@Component
@Aspect
@Order(1)
public class LoggingAspect {}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值