Spring AOP

AOP (Aspect Orient Programming),直译过来就是 面向切面(方面)编程。AOP 是一种编程思想,是面向对象编程(OOP)的一种补充。面向对象编程将程序抽象成各个层次的对象,而面向切面编程是将程序抽象成各个切面。

  • AOP编程可不是Spring独有的,Spring只是支持AOP编程的框架之一。

  • AOP分两类,一类可以对方法的参数进行拦截,一类是对方法进行拦截,SpringAOP属于后者,所以Spring的AOP是属于方法级

    OOP与AOP

  • OOP三大特性?

  • 以下问题如果解决?

public void doSameBusiness (long lParam,String sParam){
     // 1.记录日志
     log.info("调用 doSameBusiness方法,参数是:"+lParam);
     // 2.输入合法性验证
     if (lParam<=0){
          throws new IllegalArgumentException("xx应该大于0");
     }
     if (sParam==null || sParam.trim().equals("")){
          throws new IllegalArgumentException("xx不能为空");
     }
     // 3.异常处理
     try{ 
        真正的业务处理
     }catch(...){
     }catch(...){
     }
     // 4.事务控制
     tx.commit();
 }

AOP带来的好处

AOP是公用的框架代码放置的理想地方,将公共代码与业务代码分离,使我们在处理业务时可以专心的处理业务。

通过使用AOP我们可以将日志记录,数据合法性验证,异常处理等功能放入AOP中,那么在编写业务时就可以专心实现真正的业务逻辑代码。

基本概念

  • 连接点(Joinpoint) 程序执行的某个特定位置,如某个方法调用前,调用后,方法抛出异常后,这些代码中的特定点称为连接点。简单来说,就是在哪加入你的逻辑增强 连接点表示具体要拦截的方法,上面切点是定义一个范围,而连接点是具体到某个方法

  • 切点(PointCut) 每个程序的连接点有多个,如何定位到某个感兴趣的连接点,就需要通过切点来定位。比如,连接点--数据库的记录,切点--查询条件 切点用于来限定Spring-AOP启动的范围,通常我们采用表达式的方式来设置,所以关键词是范围

  • 增强(Advice) 增强是织入到目标类切点上的一段程序代码。在Spring中,像BeforeAdvice等还带有方位信息 通知是直译过来的结果,我个人感觉叫做“业务增强”更合适 对照代码就是拦截器定义的相关方法,通知分为如下几种:

    • 前置通知(before):在执行业务代码前做些操作,比如获取连接对象

    • 后置通知(after):在执行业务代码后做些操作,无论是否发生异常,它都会执行,比如关闭连接对象

    • 异常通知(afterThrowing):在执行业务代码后出现异常,需要做的操作,比如回滚事务

    • 返回通知(afterReturning),在执行业务代码后无异常,会执行的操作

    • 环绕通知(around),这个目前跟我们谈论的事务没有对应的操作,所以暂时不谈

  • 目标对象(Target) 需要被加强的业务对象

  • 织入(Weaving) 织入就是将增强添加到对目标类具体连接点上的过程。 织入是一个形象的说法,具体来说,就是生成代理对象并将切面内容融入到业务流程的过程。

  • 代理类(Proxy) 一个类被AOP织入增强后,就产生了一个代理类

  • 切面(Aspect) 切面由切点和增强组成,它既包括了横切逻辑的定义,也包括了连接点的定义,SpringAOP就是将切面所定义的横切逻辑织入到切面所制定的连接点中。 比如上文讨论的数据库事务,这个数据库事务代码贯穿了我们的整个代码,我们就可以这个叫做切面。 SpringAOP将切面定义的内容织入到我们的代码中,从而实现前后的控制逻辑。 比如我们常写的拦截器Interceptor,这就是一个切面类

AOP运行原理:目标对象只负责业务逻辑,通知只负责AOP增强逻辑(如日志,数据验证等),而代理对象则将业务逻辑而AOP增强代码组织起来(组织者)

Spring AOP

通知类型

  1. 前置通知

  2. 后置通知

  3. 环绕通知

  4. 返回后通知

  5. 异常通知

实现

pom.xml

<!--属性,公共配置-->
    <properties>
        <spring.version>5.3.6</spring.version>
    </properties>

    <dependencies>
        <!--spring aop-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>${spring.version}</version>
        </dependency>
    </dependencies>

准备工作

 UserDao接口及实现类,UserService接口及其实现类

public class UserDaoImpl implements UserDao {

    public void save(User user) {
        // 这里并未实现完整的数据库操作,仅为说明问题
        System.out.println("保存用户信息到数据库");
    }
}

public class UserServiceImpl implements UserService {

    // 声明接口类型的引用,和具体实现类解耦合
    private UserDao userDao;

    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }
    public void add(User user) {
        // 调用用户DAO的方法保存用户信息
        userDao.save(user);
    }
}

将service,dao配置到spring_aop.xml配置文件中,以便于被spring管理,按自己的实际情况配置

     <bean id="userDao" class="com.zking.springdemo.dao.impl.UserDaoImpl"></bean>

    <bean id="userService" class="com.zking.springdemo.service.impl.UserServiceImpl">
        <property name="userDao" ref="userDao"/>
    </bean>

aop增强(通知)

/**
 * 日志
 */
public class LoggerAdvice {
    private static final Logger log = Logger.getLogger(LoggerAdvice.class);

    public void before(JoinPoint jp) {
        log.info("before调用 " + jp.getTarget() + " 的 " + jp.getSignature().getName()
                + " 方法。方法入参:" + Arrays.toString(jp.getArgs()));
    }

    public Object aroundLogger(ProceedingJoinPoint jp){
        Object result = null;
        try {
            System.out.println("aroundLogger调用之前");
            result = jp.proceed();//调用代理目标方法
            System.out.println("aroundLogger调用之后");
        } catch (Throwable e) {
            e.printStackTrace();
            System.out.println("aroundLogger异常处理");
        }
        return result;
    }
    public void afterLogger(JoinPoint jp) {
        log.info("afterLogger:"+jp.getSignature().getName() + " 方法结束执行。");
    }
    public void afterReturning(JoinPoint jp, Object result) {
        log.info("afterReturning调用 " + jp.getTarget() + " 的 " + jp.getSignature().getName()
                + " 方法。方法返回值:" + result);
    }

    /**
     * RuntimeException是抛出的异常的父类
     */
    public void afterThrowing(JoinPoint jp,RuntimeException e){
        System.out.println("afterThrowing"+e.getMessage());
    }
}

  • JoinPoint

JoinPoint对象封装了SpringAop中切面方法的信息,在切面方法中添加JoinPoint参数,就可以获取到封装了该方法信息的JoinPoint对象.

方法名功能
Signature getSignature();获取封装了署名信息的对象,在该对象中可以获取到目标方法名,所属类的Class等信息
Object[] getArgs();获取传入目标方法的参数对象
Object getTarget();获取被代理的对象
Object getThis();获取代理对象
  • ProceedingJoinPoint

ProceedingJoinPoint对象是JoinPoint的子接口,该对象只用在@Around的切面方法中 添加了 Object proceed() throws Throwable //执行目标方法

aop配置

在spring_aop.xml中添加aop相关配置,注意先添加aop命名空间

<?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:p="http://www.springframework.org/schema/p"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd">
    
    <bean id="userDao" class="com.zking.springdemo.dao.impl.UserDaoImpl"></bean>
    
    <!--1.配置目标-->
    <bean id="userService" class="com.zking.springdemo.service.impl.UserServiceImpl">
        <property name="userDao" ref="userDao"/>
    </bean>

    <!--2.配置增强-->
    <bean id="loggerAdvice" class="com.zking.springdemo.aop.LoggerAdvice"/>

    <!-- aop配置-->
    <aop:config proxy-target-class="false">
        <!-- 定义切入点 execution(方法返回类型 包名.类名.方法名(参数的类型))-->
        <aop:pointcut id="pointcut" expression="execution(* com.zking.springdemo.service..*.*(..))" />
        <!-- 引用包含增强方法的Bean -->
        <aop:aspect ref="loggerAdvice">
            <!-- 指定名为before方法作为前置通知 -->
            <aop:before method="before" pointcut-ref="pointcut"/>
            <!--返回后通知-->
            <aop:after-returning method="afterReturning"
                                 pointcut-ref="pointcut" returning="result" />
            <!--后置通知-->
            <aop:after method="afterLogger" pointcut-ref="pointcut"/>
            <!--环绕通知-->
            <aop:around method="aroundLogger" pointcut-ref="pointcut"/>
            <!--异常通知-->
            <aop:after-throwing method="afterThrowing" pointcut-ref="pointcut" throwing="e"/>
        </aop:aspect>
    </aop:config>

</beans>

1. aop:config aop装配

  • proxy-target-class默认值为false,默认采用JDK动态代理实现;否则使用cglib来实现

2.  aop:pointcut:点定切点

        execution():中是AspectJ中的exection表达式

                1)  execution(<修饰符模式>?<返回类型模式><方法名模式>(<参数模式>)<异常模式>?) 除了返回类型模式、方法名模式和参数模式外,其它项都是可选的。

                2) serviece.. 为service当前包及其下所有子包

                3) serviece..*.*为service包及其下所有子包下所有类及所有方法

                4)(..)方法任意参数

                5) 多个包中间用||或or隔开

3. aop:aspect

        ref:指定增强处理类

4. aop:指定位置

        有五个候选字

                1)before:前置通知,目标方法运行之前调用

                2)alter-Returning:后置通知,在目标方法运行之后调用

                3)around:环绕通知在目标方法之前和之后都调用

                4)after-throwing:异常拦截通知,如果出现异常,就会调用

                5)after:后置通知,在目标方法运行之后调用

        method:选定要织入到切入点的通知类中的方法。

        pointcut-ref:选定切入点依赖对象,值为配置切入点时的id属性。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值