欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Spring(三)spring核心技术——aop

程序员文章站 2022-06-21 14:03:10
一、AOP简介  AOP是Aspect Orient Programming的缩写,即面向切面编程。基于动态代理的,可以使用jdk和cglib两种代理方式。Aspect: 切面,给你的目标类增加的功能,就是切面。像上面用的日志,事务都是切面。切面的特点:一般都是非业务方法,独立使用的。Orient:面向,对着。Programming:编程  AOP就是动态代理的规范化,把动态代理的实现步骤,方式都定义好了,让开发人员用一种统一的方式,使用动态代理。作用:在目标类不修改的情况...

一、AOP简介

  AOP是Aspect Orient Programming的缩写,即面向切面编程。基于动态代理的,可以使用jdk和cglib两种代理方式。

  • Aspect: 切面,给你的目标类增加的功能,就是切面。像上面用的日志,事务都是切面。切面的特点:一般都是非业务方法,独立使用的。

  • Orient:面向,对着。

  • Programming:编程

  AOP就是动态代理的规范化,把动态代理的实现步骤,方式都定义好了,让开发人员用一种统一的方式,使用动态代理。

作用:

  • 在目标类不修改的情况下增加功能

  • 减少代码的重复

  • 使开发人员专注业务功能的实现

  • 解耦合:业务功能和日志、事务等非业务功能的耦合

二、动态代理的实现方式

  • jdk动态代理

  使用jdk中的Proxy,Method,InvocaitonHanderl创建代理对象。jdk动态代理要求目标类必须实现接口

  • cglib动态代理

  第三方的工具库,创建代理对象,原理是继承。 通过继承目标类,创建子类。子类就是代理对象。要求目标类不能是final的,方法也不能是final的。

三、AOP中名词概念

  • aspect(切面):表示给业务方法增加的功能,一般为*输出、事务、权限检查等

  • JoinPoint:连接点,连接业务方法和切面的位置,就某类中的业务方法

  • pointcut(切入点):是一个或多个JoinPoint的集合,表示切面功能执行的位置

  • 目标对象:给哪个类的方法增加功能,这个类就是目标对象

  • advice(通知):也叫增强。表示切面执行的时间,在方法前或后

四、何时使用AOP

  • 某项目功能类不完善,需要增加功能,但是没有源代码

  • 给项目的多个类需要增加相同功能

  • 为业务功能增加事务、日志输出

五、AOP的实现

1、Spring

  Spring在内部实现了aop规范,能做aop的工作。spring主要在事务处理时使用aop,在项目开发中很少使用spring的aop实现,因为spring的aop比较笨重。

2、使用Aspectj框架实现AOP

  一个开源的专门做aop的框架。spring框架中集成了aspectj框架,通过spring就能使用aspectj的功能。

aspectJ框架实现aop有两种方式:

1)使用xml的配置文件 : 配置全局事务

2)使用注解,项目中要做aop功能,一般都使用注解

  • 使用相应注解确定切面执行的时间

  • 使用切面表达式确定切面执行的位置

  切面位置的切入点表达式:execution(修饰符 返回值 包名.类名.方法名(方法参数) 异常)。用来指定切面执行的位置。

aspectj中常用注解如下:

  1. @Aspect:声明该类是切面类
/*
*   @Aspect:是Aspectj框架中的注解
*       作用:声明该类是切面类
*       切面类:为目标类增加功能的类,包含切面的功能代码
*       位置:在类的上面
* */
@Aspect
public class MyAspect {}
  1. @Before:前置通知,在目标方法之前执行切面的功能
@Aspect
public class MyAspect {
    /*
    *   定义方法,是实现切面功能的方法
    *     方法定义要求:
    *       1、方法时公共的
    *       2、方法名自定义
    *       3、方法没有返回值
    *       4、可以有参数,也可以无参数
    *           如果有参数,参数不是自定义的
    *   @Before:前置通知注解
    *       属性:value,是切入点表达式,表明切面的功能执行的位置
    *       位置:在方法的上面
    *   特点:
    *       1、在目标方法前执行
    *       2、不会改变和影响目标方法的执行
    * */
    @Before(value = "execution(public void cn.krain.ba01.SomeServiceImpl.doSome(String,Integer))")
    public void myBefore(){
        System.out.println("前置通知,切面功能,在目标方法执行前输出执行的时间:"+new Date());
    }
  1. @AfterReturning:后置通知,在目标方法之后执行切面的功能,能获取返回值
@Aspect
public class MyAspect {
    /*
    *   @AfterReturning定义方法,是实现切面功能的方法
    *     方法定义要求:
    *       1、方法时公共的
    *       2、方法名自定义
    *       3、方法没有返回值
    *       4、方法有参数
    *   @AfterReturning:后置通知
    *       属性:1、value:切入点表达式
    *             2、returning:自定义变量,目标方法的返回值
    *                           自定义变量名称和通知方法的形参名一致
    *       位置:在方法定义的上面
    *   特点:
    *   1、在目标方法之后执行
    *   2、能获取目标方法的返回值
    *   3、可修改这个返回值
    * */
    @AfterReturning(value = "execution(* cn.krain.ba02.SomeServiceImpl.doOther(..))",
                    returning="res")
    public void myAfterReturning(Object res){
        System.out.println("后置通知,切面功能,在目标方法执行前输出执行的时间:"+res);
        if (res!=null){
            res = "Hello";
        }
    }
}
  1. @Around:环绕通知,能在目标方法前后增强功能,能够控制目标方法的执行,修改返回值
@Aspect
public class MyAspect {
    /*
    *   环绕通知的定义格式
    *     方法定义要求:
    *       1、方法时公共的
    *       2、方法名自定义
    *       3、必须有返回值
    *       4、方法有固定的参数:proceedingJoinPoint
    *   @Around:环绕通知
    *       属性:value 切入点表达式
    *       位置:在方法的定义上面
    *   特点:
    *       1、是功能最强的通知
    *       2、能够在目标方法前后增强功能
    *       3、控制目标方法是否被执行
    *       4、修改原来目标方法的执行结果,影响最后的调用结果
    * */
    @Around(value = "execution(* *..SomeServiceImpl.doFirst(..))" )
    public Object myAround(ProceedingJoinPoint joinPoint) throws Throwable {

        //获取第一个参数的值
        String name = null;
        Object args [] = joinPoint.getArgs();
        if (args!=null && args.length>=1){
            Object arg = args[0];
            name = (String) arg;
        }

        Object result = null;

        //实现环绕通知
        System.out.println("环绕通知,在目标方法之前,输出时间:"+new Date());

        //1、目标方法调用
        //控制目标方法的执行
        if (name.equals("张三")){
            result = joinPoint.proceed();       //等同于执行目标方法
        }

        System.out.println("环绕通知,在目标方法之后,提交事务");

        //修改目标函数返回结果
        if (result!=null){
            result = "Hello Aop";
        }
        return result;
    }
}
  1. @AfterThrowing:异常通知,在目标方法抛出异常后执行
@Aspect
public class MyAspect {
    /*  异常通知的定义格式
    *     方法定义要求:
    *       1、方法时公共的
    *       2、方法名自定义
    *       3、方法没有返回值
    *       4、固定参数Exception,如果还有参数则是:JoinPoint
    *       5、throwing的值要与Exception的参数名相同
    *   @AfterThrowing:异常通知
    *       属性:1、value 切入点表达式
    *            2、throwing 自定义变量,表示目标方法抛出的异常对象
    *                       如果有异常,通过邮件、短信通知
    *   特点:
    *       1、在目标方法出现异常时执行
    *       2、监控目标方法是否存在异常
    *
    *   执行过程:
    *       try{
    *           SomeServiceImpl.doSecond();
    *       }catch(Exception e){
    *           myAfterThrowing(e);
    *       }
    * */
    @AfterThrowing(value = "execution(* *..SomeServiceImpl.doSecond())",
                    throwing = "ex")
    public void myAfterThrowing(Exception ex){
        System.out.println("异常通知,方法执行异常,执行:"+ex.getMessage());
        //发送短信
    }
}
  1. @After:最终通知,总是会执行
@Aspect
public class MyAspect {
    /*  最终通知的定义格式
    *   方法定义要求:
    *       1、方法时公共的
    *       2、方法名自定义
    *       3、方法没有返回值
    *       4、没有参数,如果有参数则是:JoinPoint
    * */

    /*
    *   @After  最终通知
    *       属性:value  切入表达式
    *       位置:在方法的上面
    *
    *   特点:
    *       1、总是会执行
    *       2、在目标方法之后执行
    *
    *   执行过程:
    *       try{
    *           //目标方法
    *       }catch(){
    *
    *       }finally{
    *           myAfter();
    *       }
    * */
    @After(value = "execution(* *..SomeServiceImpl.doThird(..))")
    public void myAfter(){
        System.out.println("执行最终通知,总是会被执行的代码");
    }
}
  1. @Pointcut:定义和管理切入点的辅助注解
@Aspect
public class MyAspect {
    @After(value = "myPT()")
    public void myAfter(){
        System.out.println("执行最终通知,总是会被执行的代码");
    }

    @Before(value = "myPT()")
    public void myBefore(){
        System.out.println("执行前置通知");
    }
    /*
    *   @Pointcut:定义和管理切入点表达式,如果项目中的切入点表达式是重复的,可使用Pointcut
    *       属性:value  切入点表达式
    *       位置:在自定义方法上面
    *   为切入点表达式定义别名,使用"方法名称()"代替切入点表达式
    * */
    @Pointcut(value = "execution(* *..SomeServiceImpl.doThird(..))")
    private void myPT(){
        //无功能代码
    }
}
  • 编写spring主配置文件
<?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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">

    <!--把对象交给spring,有spring统一创建和管理-->
    <!--创建SomeService目标对象-->
    <bean name="someService" class="cn.krain.ba.SomeServiceImpl" />

    <!--创建aspect切面对象-->
    <bean name="myAspect" class="cn.krain.ba.MyAspect" />

    <!--
        声明自动代理生成器:使用aspectj的内部功能,创建目标对象的代理对象。
        创建代理对象是在内存中实现的,修改目标对象在内存中的结构,创建成代理对象
        因此,目标对象就是被修改后的代理对象
    -->
    <aop:aspectj-autoproxy />

    <!--
        如果项目中有接口时,仍希望使用CGHLIB动态代理
        proxy-target-class="true":告诉框架使用cglib动态代理
    -->
    <aop:aspectj-autoproxy proxy-target-class="true" />
</beans>
  • 测试类MyTest.java
public class MyTest {
    public void Test(){
        String config = "applicationContext.xml";
        ApplicationContext ac = new ClassPathXmlApplicationContext(config);
        //根据对象名获取代理对象
        SomeService proxy = (SomeService) ac.getBean("someService");
        //proxy."目标方法名"();
    }
}

本文地址:https://blog.csdn.net/ccccc_chuang/article/details/107389467

相关标签: Spring spring