spring AOP常用注解介绍使用(实现四种通知的小案例)

11 篇文章 0 订阅

Aop 简介:

Aop(Aspect Oriented Programming)面向切面编程,是OOP面向对象编程的一种补充将程序中交叉业务(事务,日志)代码提取出来,封装成切面,由AOP容器在适当时机位置将封装成的切面动态的织入到具体业务逻辑中。

AOP不是spring特有的。
应用场合:适用于具有横切逻辑的场合,如事务管理,日志记录,性能检测,异常通知,访问控制等。
作用:不改变原有代码的基础动态添加新的功能。
模块化。

术语:

**连接点(JoinPoint)**程序执行的某个特定位置,如方法调用前,方法调用后,方法抛出异常时,方法调用前后等。

**切入点(PointCut)**定位位置找到需要切入的连接点,即切点。

增强Advice也称为通知 在切点上执行的一段代码用来实现某些功能。

目标对象 Target 将执行增强处理的目标类。

织入(Weaving) 将增强添加到目标类具体切入点的过程。

代理(Proxy) 一个类被织入增强后,会产生一个代理类。

切面 Aspect 切点和增强的组合。

引介 Introduction 也称为引入。

实现原理

代理模式:

概念:为其他对象提供一种代理,以控制对这个对象的访问,起到中介的作用,通过代理对象访问目标对象,可以增强额外的操作,扩展目标对象的功能。
分类:
**静态代理:**代理类是程序员创建或工具生成的,所谓静态代理就是程序运行之前就已经存在代理类的字节码文件。缺点:代理对象需要和目标对象实现相同的接口,如果接口增加方法,目标对象和代理对象都要维护。
动态代理:代理类是程序在运行期间由JVM根据反射等机制动态生成的,自动生成代理类和代理对象,所谓动态就是在程序运行之前不存在代理类的字节码文件。

代理三要素:

一:实现目标类的接口。

二:初始化目标类的实例。

三:交叉业务,要执行的操作。

动态代理:

JDK技术:

proxy.newProxyInstance(
classloader  目标类的类加载器
interfaces    目标类得到接口列表
InvocationHandler   交叉业务逻辑

)

缺点:目标对象必须实现一个或多个接口,如果没有实现接口,则无法使用JDK的动态代理,此时可以使用cglib技术
- cglib技术

  • 添加jar包maven依赖
-     <dependency>
          <groupId>cglib</groupId>
          <artifactId>cglib</artifactId>
      </dependency>
  • cglib技术,适用于无接口的使用
    通过继承实现的cglib技术要
    (1):添加jar包
    (2):用法:
 Enhance.create(
  class,  目标类的对象
  InvocationHandler  交叉业务逻辑
  )

Aop原理

SpringAop 原理就是动态代理
对于实现接口的目标类使用的是jdk动态代理
对于没有实现任何接口的目标类,使用的是cglib的动态代理
代理类是程序在运行期间由JVM根据反射等机制动态生成的自动生成代理类和代理对象。
所谓动态就是指在程序运行前不存在代理类的字节码文件。

AOP基于注解的配置介绍

一:@Component 此注解使用在class上来声明一个Spring组件(Bean), 将其加入到应用上下文中 ,添加到IOC容器中,不区分组件类型。
二:@Aspect 此注解使用在class类上表示这是一个切面
三: @Pointcut定义切点表达式,可以使用within语法,也可以使用execution语法。
例如: @Pointcut("execution(* aop.service.impl.*ServiceImpl.*(..))")

AspectJ表达式

简介:切点表达式,一种表达式,用来定义切点位置。
用法:
within 语法:within(包名.类名) 匹配该类中的所有方法。
execution
匹配特定包中的特定类中特定返回值类型的特定参数的特定方法。
语法:execution(表达式)
表达式:返回值类型 包名.类名.方法名(参数类型)
通配符:和…
四:
@Before
此注解表示的前置通知在方法执行之前执行。
五:
@AfterReturning
此注解表示的是后置通知在方法执行之后执行该方法。
六: @AfterThrowing 此注解表示的是异常通知,在方法执行的时候出现异常时执行。
七:
@Around*此注解表示的是环绕通知,在方法执行前后都会执行,该通知而用于做性能的监测,比如方法执行的时间统计。
八: contxt:aspectj-autoproxy/ 表示自动创建注解并织入

使用aop注解开发的小案例进一步了解。

配置通知类并基于注解实现的通知(增强)此处根据自己需要实现通知

package aop.advice;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import sun.awt.SunHints;

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

/**
 * package_name:aop.advice
 *
 * @author:徐亚远 Date:2020/2/21 14:09
 * 项目名:springDemo01
 * Description:TODO
 * Version: 1.0
 **/
@Component
//表示这是一个切面
@Aspect
public class AnnotationAdvice {
    //定义切点表达式
    @Pointcut("execution(* aop.service.impl.*ServiceImpl.*(..))")
    public void pt() {
    }

    @Before("pt()")
    public void begoreAdvice(JoinPoint joinPoint) {
        //签名
        Signature signature = joinPoint.getSignature();
        //方法名
        String methodName = signature.getName();
        //转换为方法签名,本质上是方法签名
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();
        //参数
        Object[] args = joinPoint.getArgs();
        //目标类
        Object target = joinPoint.getThis();

        System.out.println("前置通知: " + "methodName: " + methodName + " " + "args:" + Arrays.toString(args) + " " +
                "target: " + target);
    }

    @AfterReturning(value = "pt()", returning = "value")
    public void afterAdvice(JoinPoint joinPoint, Object value) {
        //签名
        Signature signature = joinPoint.getSignature();
        //方法名
        String methodName = signature.getName();
        //转换为方法签名,本质上是方法签名
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();
        //参数
        Object[] args = joinPoint.getArgs();
        //目标类
        Object target = joinPoint.getThis();

        System.out.println("后置通知: " + "methodName: " + methodName + " " + "args:" + Arrays.toString(args) + " " +
                "target: " +
                "" + target);
    }

    @AfterThrowing(value = "pt()", throwing = "e")
    public void throwAdvice(JoinPoint joinPoint, Exception e) {

//签名
        Signature signature = joinPoint.getSignature();
        //方法名
        String methodName = signature.getName();
        //转换为方法签名,本质上是方法签名
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();
        //参数
        Object[] args = joinPoint.getArgs();
        //目标类
        Object target = joinPoint.getThis();

        System.out.println("异常通知: " + "methodName: " + methodName + " " + "args:" + Arrays.toString(args) + " " +
                "target: " +
                "" + target);
    }

    @Around("pt()")
    public Object aroundAdvice(ProceedingJoinPoint joinPoint){
         Long startTime = System.currentTimeMillis();
        Object proceed = null;
        try {
            System.out.println("开启事务:");
            proceed = joinPoint.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            System.out.println("回滚事务:");
            return "出错";
        }
        Long endTime = System.currentTimeMillis();
        System.out.println("方法执行时间:"+(endTime-startTime)+"ms");
         return proceed;
    }
}

书写登录的接口:

package aop.service;

/**
 * package_name:com.anno.service
 *
 * @author:徐亚远 Date:2020/2/20 17:19
 * 项目名:springDemo01
 * Description:TODO
 * Version: 1.0
 **/
public interface UserService {
    /**
      * @Author : 徐亚远 
      * @Date : 2020/2/21 14:08
      * @Description :
     * @param password
     * @param username
      */
      
    void login(String username, String password);
}

实现登录接口。

package aop.service.impl;

import aop.service.UserService;
import org.springframework.stereotype.Service;

/**
 * package_name:com.anno.service.impl
 *
 * @author:徐亚远 Date:2020/2/20 17:19
 * 项目名:springDemo01
 * Description:TODO
 * Version: 1.0
 **/
@Service()
public class UserServiceImpl implements UserService {
    /**
     * @param username
     * @param password
     * @Author : 徐亚远
     * @Date : 2020/2/21 14:08
     * @Description :
     */
    @Override
    public void login(String username, String password) {
        int i = 5/0;
        System.out.println("loginUserServiceImpl登录方法执行:" + username + "    " + password);
    }
}

配置配置文件(spring.xmll)

<?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:contxt="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="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.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!--扫包把使用注解的实体类配置到IOC容器中,交给容器管理-->
    <context:component-scan base-package="aop.service.impl"/>
    <context:component-scan base-package="aop.advice"/>
    <!--自动创建代理并织入切面
    proxy-target-class=""默认false 表示使用jdk动态代理,true表示使用cglib动态代理 使用cglib要加入依赖-->
    <contxt:aspectj-autoproxy/>
</beans>

书写测试类:

package aop;

import aop.service.UserService;
import aop.service.impl.UserServiceImpl;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * package_name:aop
 *
 * @author:徐亚远 Date:2020/2/21 14:16
 * 项目名:springDemo01
 * Description:TODO
 * Version: 1.0
 **/

public class controller {
    public static void main(String [] args){
        ApplicationContext ac = new ClassPathXmlApplicationContext("aop/spring.xml");
 UserService userService = (UserService) ac.getBean("userServiceImpl");
       userService.login("12","123" );
        System.out.println(userService.getClass());
    }
}

测试结果如图:
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值