Spring面向切面编程(AOP)学习

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_38737992/article/details/89431932

1.  为什么需要切面编程

如果要重复通用功能的话,最常见的面向对象的技术就是继承或委托。

继承的缺点:整个工程中都是用相同的基类,会导致一个脆弱的对象体系。脆弱的简单例子,你修改基类中的方法名,则需要在每一个派生类中去修改调用。

委托的缺点:可能需要对委托对象进行复杂的调用。

切面提供了一种更加清晰简洁重复通用功能的功能。例:有几个类都需要在一个点执行一个相同的方法,可以把这个方法抽象成一个切面类,然后通过声明的方法,在那几个类运行到指定点的时候调用切面类中指定的方法。如图所示:

2. AOP专业术语

AOP , Aspect - Oriented - Programming,面向切面编程。

Advice (通知,增强处理):AOP 在特定的切入点执行的增强处理,即要在定义好的切入点所要执行的程序代码。通知有5种类型,前置通知、后置通知、返回通知、异常通知、环绕通知。

(1)前置通知(Before):目标方法被调用之前调用通知。

(2)后置通知(After):目标方法完成之后或抛出异常后调用通知,方法的输出对调用通知无影响。

(3)返回通知(After-returning):目标方法成功执行之后调用通知。

(4)异常通知(After-throwing):目标方法抛出异常之后调用通知

扫描二维码关注公众号,回复: 6052002 查看本文章

(5)环绕通知(Around):在目标方法调用之前和调用之后执行自定义行为。

JoinPoint (连接点): 程序执行的一个点,如:方法的调用和异常抛出。

Pointcut ( 切入点 ) :切面和程序的交叉点,就是需要处理的连接点。 切点通常类或者方法名,如要通知到所有以 add 开头的 方法中,所有满足这一条件的都是切入点。

配置切入点: execution(修饰符 返回值类型 类路径 匹配方法名参数) 抛出异常的类型)

红色的不能被省略,各部分都支持通过 “*” 来匹配。

参数:支持两种通配符,”*“ , ”..“。

“*” 匹配任意一个参数。

“..” 匹配任意数量的参数和类型的方法。

下面这个切入点表达式,com.study.spring.dao 下所有的方法都是切入点。

// 定义切入点表达式
@Pointcut("execution(* com.study.spring.dao.*.*(..))")
private void myPointCut(){};

Aspect(切面):通知和切面,一个类,封装好的用于横向插入系统功能,如事务、日志等。

Target Object (目标对象) : 所有被通知的对象,也称为增强对象。

Proxy (代理):将通知应用到目标对象之后,动态创建的对象。

Weaving(织入): 将切面代码插入到目标对象上,从而生成代理对象的过程。

3. 代码实例

工程目录如下图所示,习惯性的创建了一个Web项目,web.xml和index.jsp 为空。

lib 中jar包:链接:https://pan.baidu.com/s/1abpL6_WT3XW7Aqh9QYzzEA ,提取码:qxif 。

目标对象,IUserDao.java,UserDAOImpl.java:

IUserDao.java:

package com.study.spring.dao;

public interface IUserDao {
    public void addUser();
    public void deleteUser();
}

UserDAOImpl.java:

package com.study.spring.dao.impl;

import com.study.spring.dao.IUserDao;
import org.springframework.stereotype.Repository;

import javax.annotation.Resource;

@Repository("userDao")
public class UserDAOImpl implements IUserDao {
    @Override
    public void addUser() {
        System.out.println("目标方法执行中:添加用户 ...");
        int i = 1;
        //i = i/0;
    }

    @Override
    public void deleteUser() {
        System.out.println("目标方法执行中:删除用户...");
    }
}

切面类,MyAspect.java:

package com.study.spring.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class MyAspect {

    // 定义切入点表达式
    @Pointcut("execution(* com.study.spring.dao.*.*(..))")
    private void myPointCut(){};

    /**
     * 前置通知
     * @param joinpoint
     */
    @Before("myPointCut()")
    public void myBefore(JoinPoint joinpoint) {
        System.out.print("前置通知, @Before");
        System.out.print("目标类是 " + joinpoint.getTarget());
        System.out.println("目标方法为 " + joinpoint.getSignature().getName());
    }

    /**
     * 返回通知
     * @param joinPoint
     */
    @AfterReturning("myPointCut()")
    public void myAfterReturning(JoinPoint joinPoint) {
        System.out.print("返回通知:@AfterReturning");
        System.out.println("目标方法 " + joinPoint.getSignature().getName());
    }

    /**
     * 环绕通知
     * @param proceedingJoinPoint
     * @return
     * @throws Throwable
     */
    @Around("myPointCut()")
    public Object myAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("环绕开始,@Around");
        Object obj = proceedingJoinPoint.proceed();
        System.out.println("环绕结束,@Around");
        return obj;
    }

    /**异常通知
     *
     * @param joinPoint
     * @param throwable
     */
    @AfterThrowing(value = "myPointCut()", throwing = "throwable")
    public void myAfterThrowing(JoinPoint joinPoint, Throwable throwable) {
        System.out.println("异常通知 @AfterThrowing:" + "错误如下" + throwable.getMessage());
    }

    /**
     * 后置通知
     */
    @After("myPointCut()")
    public void myAfter() {
        System.out.println("后置通知: @After");
    }

}

applicationContext.xml配置文件,指定需要扫描的包,启动基于注解的声明式AspectJ支持。

<?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.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
  <context:component-scan base-package="com.study.spring.*"/>
  <aop:aspectj-autoproxy/>
</beans>

测试代码,Test.java

package com.study.spring;

import com.study.spring.dao.IUserDao;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {
    public static void main(String[] args) {
        String xmlPath = "applicationContext.xml";

        ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);

        IUserDao iUserDao = (IUserDao) applicationContext.getBean("userDao");

        iUserDao.addUser();
        iUserDao.deleteUser();
    }
}

执行Test.java,测试结果如下所示:

测试异常通知,修改addUser方法为:

    @Override
    public void addUser() {
        System.out.println("目标方法执行中:添加用户 ...");
        int i = 1;
        i = i/0;
    }

执行Test.java,测试结果如下所示:

参考文献:

  • Spring实战:第四版 / 美(沃尔斯)著;张卫东译.——北京 :人民邮电出版社,2016.4 (2018.10 重印)

  • JavaEE 企业级应用开发教程(Spring + SpringMVC + MyBatis)/ 黑马程序员编著. ——北京 :人民邮电出版社,2017.9 (2018.2 重印)

猜你喜欢

转载自blog.csdn.net/qq_38737992/article/details/89431932
今日推荐