初学者——SpringAOP

AOP简介

AOP:全称是 Aspect Oriented Programming 即:面向切面编程。
AOP,面向切面编程就是当你已经完成,或者更加注意一些核心功能的时候,需要使用的编程过程。这个使用,你会发现,一个核心的业务,是由一个核心功能,和额外服务类型的功能,统一组成的,比如数据验证,再比如日志的记录。

其实在场景中,我们的核心功能是针对数据库的操作,可能是增删过程,而服务类功能,比如记录日志,或者输入内容的验证过程。其实不是非有不可,没有这个功能也能正常执行。
这个时候,我们就要考虑一个问题,这些必要或者非必要的额外功能,是不是要写在你的核心代码过程当中啊?

我们现在,将核心业务,做一个简单的切分:核心功能,和辅助功能。
场景的举例
突然一天,你刚将一个功能代码完成,这个时候,产品经理过来告诉你,客户那边有变化,需要一些小变动,在执行你这个功能前需要去验证一下,参数的传递是否正确。

AOP名词术语
  • Aspect:切面,一个关注点的模块化,这个关注点可能会横切多个对象。
  • 切点: pointcut 在某些个核心方法之前,之后等位置 切面 : 很多个切点组成的过程叫做切面
  • Joinpoint:连接点,程序执行过程中的某一行为,即业务层中的所有方法。
  • 增强:(Advice)(也叫通知)
    主要是用来告诉AOP,我们在切点(目标方法上)应该在什么时机执行什么功能。
  • 织入 :将辅助功能安装在切点的过程
  • 目标(Target):被通知的对象
  • 代理(Proxy): 向目标对象应用通知之后创建的对象
基于配置文件的AOP

切点表达式

切点:execution(返回值 全包名.类名.方法名(参数集合))

两个点表示任意多个参数
execution(* com.lanou..(…))

增强时机的选择有以下几种:AspectJ(来源是AspectJ)

前置增强:before

后置增强:after

环绕增强:around

异常增强:throw

返回增强:after returning

三种实现增强方式

**1.继承Spring给出的对应的增强的接口(Spring优化后的)**

**2.在Aop配置的时候,通过aop:aspect来配置它的增强类型**

**(在方法里可以传入两个参数:**

**JoinPoint  joinPoint(除around以外的其他几个增强类型)**

**ProceedingJoinPoint pjp(around类型的参数))**

**3.通过注解来实现**

**在配置文件中加入 <context:component-scan> 和 <aop:aspectj-autoproxy>;**

**在增强类上加两个注解:@Aspect 和**
   **@Configuration**

1.继承Spring给出的对应的增强的接口(Spring优化后的)

Spring只能找到两个增强点

  • 前置增强继承MethodBeforeAdvice接口

  • 前置通知拿到的内容

    //参数method——将要执行的方法
    //参数objects——将要执行的方法的参数集合
    //参数Object o —— 是执行方法的那个类的对象,是内部真正执行方法的对象,

前置通知会做的一些事情

合法性判断,合理性判断,参数集合的验证(是否为空等等),开启事务


import org.springframework.aop.MethodBeforeAdvice;

import java.lang.reflect.Method;

public class MyBeforAdvice implements MethodBeforeAdvice {
    //参数method——将要执行的方法
    //参数objects——将要执行的方法的参数集合
    //参数Object o —— 是执行方法的那个类的对象目标对象,是内部真正执行方法的对象,
    //而在main方法里的target是它的代理,这个代理所有的使用过程和目标对象的使用过程一样,如果打印这个代理,这个代理将目标对象的toString方法拿过来使用的
    public void before(Method method, Object[] objects, Object o) throws Throwable {
      System.out.println("我是前置");
        //改变参数集合
        objects[0] = "nnnnnn";
    }
}

  • 后置增强实现AfterReturningAdvice接口,
public class MyAfterAdvice implements AfterReturningAdvice , AfterAdvice {

    public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {

    }
}

Object o 执行方法的返回值//

Method method 执行的方法//

Object[] objects 执行方法的参数列表//

Object o1 执行方法的对象

  • 在配置文件中开启AOP
   <bean id="myBeforAdvice" class="test.advice.MyBeforAdvice"></bean>

  <aop:config>
  <!--     aspect就是我们说的织入 advisor就是我们说的增强 pointcut就是我们说的切点-->
<!--      一般配完advisor织入就默认生效了-->
        <!--        切入到里面所有的方法-->
        <aop:pointcut id="methodsBefore" expression="execution(* test.service.*.*(..))"/>
<!--  如果想要切到某些方法就配置如下过程
下方就是切到所有以l开头的方法     是用通配符匹配的-->
<!-- <aop:pointcut  id="methodsBefore"expression="execution(* com.lanou.test.service.*.l(..))"/>-->
        <aop:advisor advice-ref="myBeforAdvice" pointcut-ref="methodsBefore"></aop:advisor>
    </aop:config>
2、使用aspectj 和Spring联合实现AOP ( 通用增强-)
  • 配置文件配置:
<aop:config> 
     <aop:aspect ref="myAdvisor">
<!--            不关心method的名字,只关心这是不是before-->
            <aop:before method="before" pointcut-ref="methodsBefore"></aop:before>
            <aop:after method="after" pointcut-ref="methodsBefore"></aop:after>
            <aop:after-returning method="afterReturning" pointcut-ref="methodsBefore">               </aop:after-returning>
            <aop:after-throwing method="afterThrowing" pointcut-ref="methodsBefore">                 </aop:after-throwing>
          <aop:around method="around" pointcut-ref="methodsBefore"></aop:around>
      </aop:aspect>
 </aop:config>

从理论过程来讲after先afterReturning后,after执行完了有可能改结果;afterReturning不能改结果,可以改结果里面的内容,在五种增强中,Around环绕增强是最强的

around

在所有的增强中around是最强的,参数是ProceedingJoinPoint pjp

   around可以针对方法内部的执行过程产生影响的一种通知,甚至可以拒绝方法的执行-
      around既可以改变参数,也能拒绝方法正常执行

around可以改变方法的执行过程,可以改变返回值的内容

可以在之前截获参数改变参数,能在之后截获返回值改变返回值

public Object around(ProceedingJoinPoint pjp) {
        System.out.println("around");
        Object obj = null;
        try {
            obj =  pjp.proceed(pjp.getArgs());
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        obj = null;
        return obj;
    }

注:在执行AOP的过程中,其实是Spring将service封装了一个代理类,真正执行的是Proxy,它去帮你执行service里的方法,你执行的是代理类的方法。

3、异常中断和正常结束执行对afterReturning和afterThrowing所谓影响

一个方法的正常结束和抛异常的异常结束是不一样的:

异常抛出是异常中断;异常捕获是正常结束

就是说异常我捕获以后,try块里面的内容不执行了,但是执行catch块,是正常执行完了。此时afterThrowing就不会执行。

我抛出异常要不要捕获异常,抛出异常同时捕获异常,就等于没抛出这个异常,等于直接捕获异常了,继续执行了。

after不管是正常结束还是异常中断,他一定会执行。
afterReturning和afterThrowing执行条件:

如果在目标方法执行过程中碰见了异常并且没有捕获而抛出了异常,就会执行afterThrowing,不会执行afterReturning;

如果是正常结束没有抛出任何的异常,就会执行afterReturning,不会执行afterThrowing。

4、基于注解式开启AOP

在配置文件中开启AOP的注解:

<!--开启AOP的注解-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

在自定义增强类MyAdvisor类上加上:

@Aspect
@Configuration或加@Component 
public class MyAdvisor {}

在方法上加上:

@Before("execution(* com.lanou.test.service.*.*(..))")

 @After("execution(* com.lanou.test.service.*.*(..))")

 @AfterReturning("execution(* com.lanou.test.service.*.*(..))")

  @AfterThrowing("execution(* com.lanou.test.service.*.*(..))")

  @Around("execution(* com.lanou.test.service.*.*(..))")

或者写一个空的方法,无返回值无方法体,然后在上面加上*@Pointcut(value = “execution(* com.lanou.test.service…(…))”)*,然后在后面所有的增强方法上将这个方法名传进来即可。
如:

    @Pointcut(value = "execution(* com.lanou.test.service.*.*(..))")
    public void point() {
    }
      @After("point()")
    public void after(JoinPoint joinPoint) {
        System.out.println("after");
    }
Spring的动态代理方案

Aop底层原理使用的是动态代理。

Spring的动态代理方案有两种: 原生的jdk动态代理CGLIB动态代理

CGLIB(Code Generator Library) 代码生成器库 如果拿的是个类,就用CGLIB创建这个类的子类,然后调用这个类的子类去实现动态代理
原生的jdk动态代理:JdkDynamicAopProxy(Spring提供的)

默认使用AOP的过程中(不加proxy-target-class),使用的是原生jdk动态代理实现

加入proxy-target-class="true"就从jdk的动态代理实现方案就变为了CGLIB动态代理实现方案 ;"false"就变为jdk动态代理实现方案。

<aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>

当把实现类和接口分离后,写"false"使用的是CGLIB的动态代理方案

java的jdk的原生动态代理生成模式只支持接口的代理生成,只要这个类不是final类。

CGLIB将整个代理类切分的非常细,所以启动的慢 。

CGLIB动态代理和jdk动态代理的区别:

(1)在生成代理类的速度上jdk比CGLIB快

​ (2) 运行速度上CGLIB速度快

(3)jdk动态代理只能对接口创建

(4)CGLIB既可以对接口创建,也可以对类创建

使用场景:

如果这个类是单例的类,只创建一次,多次调用,使用CGLIB合适; 如果创建的特别的多,调用的比较少,使用jdk合适— 常规情况下,单例用的多,所以CGLIB用的多

★只要是Spring里的代理生成都是通过ClassLoader重新创建的

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值