Spring-AOP面向切面编程

AOP面向切面编程,基于动态代理的,可以使用jdk,cglib两种代理方式

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

AOP(Aspect Orient Programming),面向切面编程,面向切面编程是从动态角度考虑程序运行过程。

AOP底层就是采用动态代理模式实现的,采用了两种代理:jdk的动态代理与cglib的动态代理。

怎么理解面向切面编程?

1.需要在分析项目功能时,找出切面。

2.合理安排切面的执行位置,(在目标方法前,还是目标方法后)

3.合理安排切面执行的位置,在哪个类,哪个方法增加增强功能。

术语:

1.Aspect:切面,表示增强的功能,非业务功能,常见的切面功能有日志,事务,信息统计,参数检查,权限验证。

aspectJ:一个开源的专门做aop的框架,spring框架中继承了aspectJ框架,通过spring就能使用aspectJ功能。aspectJ框架实现aop有两种方式:

1.使用xml的配置文件,配置全局事务

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

3.aspectJ有5个注解:

                @Before前置通知,在目标方法执行前,执行增强功能

                @AfterReturing后置通知,在目标方法执行后,执行增强功能

                @Around环绕通知,在目标方法之前之后,执行增强功能

                @Aft erThrowing异常通知,目标方法有异常,执行

                @After最终通知,在目标方法执行后,执行,不管目标方法是否有异常抛出

AspectJ的切入点表达式

AspectJ定义了专门的表达式用于指定切入点,表达式原型:

execution(modifiers-pattern? ret-type-pattern

                  declaring-type-pattern?name-pattern(param-pattern)

                  throws-pattern?)

解释:

modifiers-pattern?访问权限类型

ret-type-pattern返回值类型

declaring-type-pattern?包名类名

name-pattern(param-pattern)方法名(参数类型和参数个数)

throws-pattern?抛出异常类型

?表示可选部分

以上表达式公4个部分

execution(访问权限 方法返回值 方法声明(参数) 异常类型)

使用aspectJ实现aop的基本步骤:

前置通知 @Before

1.新建maven项目

2.加入依赖,spring依赖,aspectJ依赖,junit单元测试

  <!--spring依赖-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.2.5.RELEASE</version>
    </dependency>
    <!--aspectJ依赖-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aspects</artifactId>
      <version>5.2.5.RELEASE</version>
    </dependency>

3.创建目标类,接口和他的实现类,要做的是给类中的方法增加功能

 4.创建切面类,就是一个普通的类,但是创建时有要求

4.1.在类的上面加入@Aspect

4.2.在类中定义方法,方法就是切面要执行的功能代码,在方法的上面加上aspectJ中的通知注解,例如

前置通知@Before,有需要指定切入点表达式execution()

/**
 * @Aspect:是spring框架中的注解
 *    作用:表示当前类是切面类
 *    切面类:用来给目标类增加功能
 *
 */
@Aspect
public class MyAspect {
    /*
    * 方法的定义要求
    * 1.必须是公共方法public
    * 2.方法没有返回值void
    * 3.方法名称自定义
    * 4.方法可以有参数,参数不是自定义,有几个参数类型可以使用
    * */


    /*
    * @Before:前置通知注解,目标类方法执行前,执行的功能,标志
    * 属性value:是切入点的表达式,表示切面功能执行位置
    * */
    @Before(value = "execution(public void com.powernode.ba01.SomeServiceImpl.doSome(String,Integer))")
    public void MyBefore(){
        System.out.println("切面功能那个:输出方法执行时间"+new Date());
    }
}

5.创建spring的配置文件:声明对象,把对象交给容器统一管理,声明对象可以使用注解或者xml配置文件<bean>

 5.1.声明目标对象

5.2.声明切面对象

5.3.声明aspectJ框架中的自动代理生成器标签,用来完成代理对象的自动创建功能。

6.创建测试类,从spring容器中获取目标对象(实际就是代理对象),通过代理执行方法,实现aop的功能增强

public class MyTest01 {
    @Test
    public void test01(){
        String ing = "applicationContext.xml";
        ApplicationContext ac = new ClassPathXmlApplicationContext(ing);
        SomeService somServiec = (SomeService) ac.getBean("somServiec");
        somServiec.doSome("李四",20);
    }
}

------------------------------------------------------------------------------------------

后置通知

@AfterReturing

属性:1.value 切入点表达式

           2.returning 自定义的变量,表示目标方法的返回值,自定义变量名必须和通知方法的形参名一样

位置:在方法定义的上面

特点:

1.在目标方法之后执行

2.能够获取到目标方法的返回值,可以根据这个返回者做不通的处理功能

3.可以修改这个返回值

 实现步骤:

1.在接口中定义方法

 2.在实现类,去实现这个方法

 3.spring配置问价中指定类的位置

 4.编写切面类

Object res可以获取到目标方法的返回值,我们可以对这个值,进行修改

JoinPoint jp作用:可以在通知方法中获取到目标方法执行时的信息,例如方法名,方法参数

如果切面功能需要用到目标方法的信息,就加入JoinPoint

这个JoinPoint参数值是由框架赋予,必须是第一位的参数

jp.getSignature()方法获取目标方法的返回值 包名类名方法名(参数类型)

package com.powernode.ba02;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;


import java.util.Date;
/**
 * @Aspect:是spring框架中的注解
 *    作用:表示当前类是切面类
 *    切面类:用来给目标类增加功能
 *
 */
@Aspect
public class MyAspect {
    /*
    * 方法的定义要求
    * 1.必须是公共方法public
    * 2.方法没有返回值void
    * 3.方法名称自定义
    * 4.方法有参数,推荐是Object
    * */


    /*
    * 后置通知@AfterReturing
    * */
  
    @AfterReturning(value = "execution(* *..SomeServiceImpl.doOther2(..))",returning = "res")
    /*
    * returning = "res"就是doOther方法的返回值
    * */
    public void myAfterReturing2(JoinPoint jp,Object res){//res就是表达式,切入doOther()方法的返回值
        System.out.println(jp.getSignature());
        System.out.println("res---->"+res);
        if (res != null){
            Student student = (Student) res;
            student.setName("马超");
            student.setAge(28);
            res= student;
        }
        System.out.println("后置通知,是在目标方法之后执行的,获取的返回值是:"+res);
    }
}

5.创建测试类,测试

public class MyTest02 {
    @Test
    public void test01(){
        String ing = "applicationContext.xml";
        //容器对象创建,读取spring配置文件
        ApplicationContext ac = new ClassPathXmlApplicationContext(ing);
        SomeService somServiec = (SomeService) ac.getBean("somServiec");
        String ss = somServiec.doOther("吕布", 30);
        System.out.println(ss);
    }
}

------------------------------------------------------------------------------------

@Around环绕通知

增强方法有ProceedingJoinPoint参数

在目标方法执行之前之后执行,被注解为环绕增强的方法要有返回值,Object类型。并且方法可以包含一个ProceedingJoinPoint类型的参数。接口ProceedingJoinPoint其中一个proceed()方法,用于执行目标方法,若目标方法有返回值,则该方法的返回值就是目标方法的返回值。最后,环绕增强方法将其返回值返回,该增强方法实际是拦截了目标方法的执行。

1.环绕通知方法定义格式

1.1.public

1.2.必须有一个返回值,推荐使用Object

1.3.方法名称自定义

1.4.方法有参数,固定的参数ProceedingJionPoint

实现步骤:

1.在接口中定义目标方法

 2.实现类实现这个方法

 //加入环绕通知环绕通知
    @Override
    public String doFirst(String name, Integer age) {
        System.out.println("===doFirst()方法执行了===");
        return "doFirst";
    }

3.在切面类中增加方法

/**
 * @Aspect:是spring框架中的注解
 *    作用:表示当前类是切面类
 *    切面类:用来给目标类增加功能
 *
 */
@Aspect
public class MyAspect {
    /*
    * 方法的定义要求
    * 1.必须是公共方法public
    * 2.方法必须有一个返回值,推荐是public
    * 3.方法名称自定义
    * 4.方法有参数,推荐是ProceedingJionPoint
    * */

    /*
    * Around 环绕通知
    * 属性value:切入点表达式
    * 特点:
    * 1.他是最强的通知
    * 2.在目标方法的前后增强功能
    * 3.控制目标方法时候被调用
    * 4.修改原来目标的执行方法的结果,影响最后的调用结果
    * 环绕通知等同于jdk动态代理的中的,InvocationHandler接口
    *
    * 参数:ProceedingJoinPoint 就等同于Method,作用:执行目标方法
    * 返回值:就是目标方法的执行结果,可以修改
    *
    * */
    @Around(value = "execution(* *..SomeServiceImpl.doFirst(..))")
    public Object myAround(ProceedingJoinPoint pjp) throws Throwable {
        String name = "";
        Object proceed = null;
        //获取目标方法的参数  doFirst("孙策", 30);
        Object[] args = pjp.getArgs();
        if(args != null && args.length>1){
            name= (String) args[0];
        }
               System.out.println("------------环绕通知");//前置环绕增强功能
        if(name.equals("孙策")){
            //执行目标方法
            proceed = pjp.proceed();
        }
        System.out.println("============环绕通知");//后置环绕增强功能
        //修改目标方法的结果
        proceed = "ProceedingJoinPoint";
        //返回目标方法执行结果
        return proceed;
    }

}

4.测试类,测试

public class MyTest03 {
    @Test
    public void test01(){
        String ing = "applicationContext.xml";
        //容器对象创建,读取spring配置文件
        ApplicationContext ac = new ClassPathXmlApplicationContext(ing);
        SomeService somServiec = (SomeService) ac.getBean("somServiec");
        String ss = somServiec.doFirst("孙策", 30);
        System.out.println(ss);
    }
}

---------------------------------------------------------------------------------

@Aft erThrowing异常通知【了解】

---------------------------------------------------------------------------------------

@After最终通知【了解】

无论目标方法是否抛出异常,该功能均会被执行

 --------------------------------------------------------------------------------

@Pointcut辅助注解【了解会用就行】

就是一个辅助注解,不是通知注解

 

总结

aop:看做是动态代理的规范化,把实现动态代理的步骤进行了一个规范化定义

aop作用:

1.在目标类不修改源代码的情况下,增加功能

2.减少代码的重复

3.专注业务功能的实现

4.解耦合

什么时候考虑使用aop技术?

1.给类修改功能,但是没有源代码,使用aop技术

2.项目中多个类添加相同功能

3.给业务增加事务,日志输出

aop的实现框架?

spring实现了aop,实现方式时接口

aspectJ框架,使用注解可以实现aop,使用xml配置文件中的标签实现aop功能

aop中的概念

1.aspect:切面,表示给业务方法增加功能

2.pointcut:切入点,是一个或多个JopinPoint集合,表示切面功能执行的位置

3.advice:通知,也叫增强,表示切面执行的时间,在方法前,方法后等

aspectJ框架的使用

1.表示切面的执行时间,使用的通知注解,@Before:前置通知,在目标方法执行前,执行切面方法功能

2.表示切面位置的切入点表达式:execution(访问修饰符 返回值 包名.类名.方法名(参数类型) 异常类型)

目标类没有接口spring框架自动使用cglib,如果目标类有接口还想用cglib怎么实现?

在spring配置文件中加入<aop:aspectj-autoproxy proxy-target-class="teue">

proxy-target-class="true":告诉框架,要使用cglib动态代理

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

没有腰的嘟嘟嘟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值