spring Aop(四)

一、概述

1.1、何为AOP

  • AOP是Spring框架中的一个重要内容。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
    原理图
  • 应用范围:将日志记录,性能统计,安全控制,事务处理,异常处理等代码从业务逻辑代码中划分出来,通过对这些行为的分离,我们希望可以将它们独立到非指导业务逻辑的方法中,进而改变这些行为的时候不影响业务逻辑的代码。

1.2、AOP 基本概念

1、切面的基本属性

  • 切面(aspect):用来切插业务方法的类。
  • 连接点(joinpoint):是切面类和业务类的连接点,其实就是封装了业务方法的一些基本属性,作为通知的参数来解析。
  • 通知(advice):在切面类中,声明对业务方法做额外处理的方法。
  • 切入点(pointcut):业务类中指定的方法,作为切面切入的点。其实就是指定某个方法作为切面切的地方。
  • 目标对象(target object):被代理对象。
  • AOP代理(aop proxy):代理对象。
  • 前置通知(before advice):在切入点之前执行。
  • 后置通知(after returning advice):在切入点执行完成后,执行通知。
  • 环绕通知(around advice):包围切入点,调用方法前后完成自定义行为。
  • 异常通知(after throwing advice):在切入点抛出异常后,执行通知。

2、切入点表达式

二、AOP实例

2.1、基于xml配置文件实现

1、定义切面类HomeAspect

public class HomeAspect {

    public void before(){
        System.out.println("homeAspect before --- 业务执行前调用 before 通知" );
    }

    public void afterReturning(String name){
        System.out.println("homeAspect afterReturning --- 业务执行完成后调用 afterReturning 通知并且可以获得方法的返回值,如果应用编译报错此方法不执行");
        System.out.println("homeAspect afterReturning --- 对有返回值业务方法进行返回值大写功能加强"+name.toUpperCase());
    }

    public void after(){
        System.out.println("homeAspect after(or finally advice) --- 业务执行完毕后时调用 after 通知,此通知无论是否报错最终都会执行,不能获取方法的返回值");
    }

    public Object around(ProceedingJoinPoint joinPoint){
        Object proceed = null;
        try {
            System.out.println("homeAspect around ...1");
            proceed = joinPoint.proceed();
            System.out.println("homeAspect around ...2");
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        return proceed;
    }

    public void afterThrowing(){
        System.out.println("homeAspect afterThrowing --- 业务逻辑编译报错时执行此通知");
    }

    public Object aroundInit(ProceedingJoinPoint joinPoint,String name,int age){
        System.out.println("增强并大写应用方法的参数    "+name.toUpperCase()+"     "+age);
        Object proceed = null;
        try {
            System.out.println("homeAspect aroundInit ...1");
            proceed = joinPoint.proceed();
            System.out.println("homeAspect aroundInit ...2");
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        return proceed;
    }

}

2、xml配置

<!--aop依赖包:aspectjrt.jar,aspectjweaver.jar以及aopalliance_1.0.jar-->
    <!--注入主业务类-->
    <bean id="homeService" class="com.charge.aopdemo.basedaop.HomeService"></bean>

    <!--注入自定义切面-->
    <bean id="homeAspect" class="com.charge.aopdemo.basedaop.HomeAspect"></bean>

    <!--aop配置:CGLIB代理-->
    <aop:config >
        <!--配置切面,指明切面是哪一个类,里面包含了切入点-->
        <aop:aspect id="myAspect" ref="homeAspect" order="1">
            <aop:pointcut id="homePointcut" expression="execution(* com.charge.aopdemo.basedaop.HomeService.*(..))" />
            <!--配置应用业务执行前通知-->
            <aop:before method="before" pointcut-ref="homePointcut"></aop:before>
            <!--配置应用执行之后的通知并得到业务方法返回值并加强它,只有有返回值的方法才会执行此通知j-->
            <aop:after-returning method="afterReturning" pointcut-ref="homePointcut" returning="name"></aop:after-returning>
            <!--此通知又名finally通知,主要是在切入点目标对象的应用方法完全执行完毕以后执行-->
            <aop:after method="after" pointcut-ref="homePointcut"></aop:after>
            <!--此通知执行后抛异常通知,如果业务方法在执行的过程中出现异常就会执行这个通知业务-->
            <aop:after-throwing method="afterThrowing" pointcut-ref="homePointcut"></aop:after-throwing>
            <!--环绕通知,在运行前后均会执行,最重要的一个通知-->
            <aop:around method="around" pointcut-ref="homePointcut"></aop:around>
            <!--增强型环绕通知-->
            <aop:around method="aroundInit" pointcut="execution(* com.charge.aopdemo.basedaop.HomeService.doThird(String,int)) and args(name,age)"></aop:around>

        </aop:aspect>
    </aop:config>

  • 注意:定义切面、定义切入点、定义通知三者的顺序一定不能变。
  • order:定义切面的优先级

3、自定义HomeService类

public class HomeService {

    public void doFirst(){
        System.out.println("homeService主业务类执行了   doFirst方法。。。");
        throw new RuntimeException();
    }

    public String doSecond(String name){
        System.out.println("homeService主业务类执行了  doSecond方法。。。");
        return name;
    }

    public void  doThird(String name,int age){
        System.out.println("homeService主业务类执行了  doThird方法。。。");
    }

}

4、测试类

 @Test
    public void test01(){
//        homeService.doFirst();
//        homeService.doSecond("mark jack");
          homeService.doThird("mark",23);
    }
  • 测试结果
homeAspect before --- 业务执行前调用 before 通知
homeAspect around ...1
增强并大写应用方法的参数    MARK     23
homeAspect aroundInit ...1
homeService主业务类执行了  doThird方法。。。
homeAspect aroundInit ...2
homeAspect around ...2
homeAspect after(or finally advice) --- 业务执行完毕后时调用 after 通知,此通知无论是否报错最终都会执行,不能获取方法的返回值
  • 注意:业务执行完成后调用 afterReturning 通知并且可以获得方法的返回值,如果应用编译报错此方法不执行
  • 注释掉doThird方法,调试doFirst方法,doFirst方法中抛出了一个运行时异常,结果如下:
homeAspect before --- 业务执行前调用 before 通知
homeAspect around ...1
homeService主业务类执行了   doFirst方法。。。
java.lang.RuntimeException
	at com.charge.aopdemo.basedaop.HomeService.doFirst(HomeService.java:13)
	at com.charge.aopdemo.basedaop.HomeService$$FastClassBySpringCGLIB$$afd28d2d.invoke(<generated>)
homeAspect after(or finally advice) --- 业务执行完毕后时调用 after 通知,此通知无论是否报错最终都会执行,不能获取方法的返回值

如上所示:afterReturningaroundInit方法均未执行

2.2、基于注解实现

1、xml配置

<context:component-scan base-package="com.charge.aopdemo.aspecj"/>

        <!--配置aspectj的xml配置,声明要使用aspectj-->
     <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

        </beans>
  • aop:aspectj-autoproxy:声明使用aspectj的标签,只有实现了这一步,MyAspect中的切面标签才会生效。

2、自定义切面MyAspect

@Aspect
@Component
public class MyAspect {

    @Pointcut("execution(* com.charge.aopdemo.aspecj.service.*Biz.do*(..))")
    public void bizPoint(){

    }

    @Pointcut("execution(* com.charge.aopdemo.aspecj.service.MyService.*(..))")
    public void servicePoint(){

    }

    //注意:||是交集两个表达式都包含,&& 主要作用于两个表达式的公共部分
    @Before(value = "bizPoint() || servicePoint() ")
    public void before(){
        System.out.println("执行了  before  通知");
    }

    //如果主业务方法中含参数,则执行此通知并将arg参数传入此方法
    @Before("bizPoint() && args(arg)")
    public void beforeWithParam(String arg){
        System.out.println("bizPoint切入点   ===>>  执行了 beforeWithParam  带参的before通知,业务方法传入的参数是:  "+ arg);
    }

    @Before("bizPoint() && @annotation(homeMethod)")
    public void beforeWithAnnotation(HomeMethod homeMethod){
        System.out.println("bizPoint和servicePoint切入点   ===>>  执行了 beforeWithAnnotation  带自定义注解参数的before通知,业务方法传入的参数是:  "+homeMethod.value());
    }

    //注意:如果主业务方法没有返回值,则此通知不会执行
    @AfterReturning(pointcut = "bizPoint() || servicePoint()",returning = "returnValue")
    public void afterReturning(String returnValue){
        System.out.println("执行了 afterReturning  通知,主业务方法返回的参数是:  " + returnValue);
    }

    @After("bizPoint() || servicePoint()")
    public void after(){
        System.out.println("执行了  after(finally)  通知");
    }

    @AfterThrowing(value = "bizPoint() || servicePoint()",throwing = "ex")
    public void afterThrowing(Exception ex){
        System.out.println("执行了  afterThrowing  通知,返回的错误参数是:  "+ex.getMessage());
    }

    @Around("bizPoint() || servicePoint()")
    public Object around(ProceedingJoinPoint joinPoint) {
        Object proceed = null;
        try {
             System.out.println("执行了   around    通知01.。。。。。。");
             proceed = joinPoint.proceed();
             System.out.println("执行了   around    通知02.。。。。。。");
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        return proceed;
    }

}
  • 注意:一定要使用**@Aspect**申明

2.3、jdk相关接口实现AOP

1、自定义HomeAfterReturningAdvice后置通知

+继承 org.springframework.aop.AfterReturningAdvice接口

public class HomeAfterReturningAdvice implements AfterReturningAdvice {
    @Override
    public void afterReturning(Object o, Method method, Object[] objects, Object target) throws Throwable {
        System.out.println("我是后置通知,执行了  "+method.getName()+"方法的目标对象是:  " + target.getClass().getName());
    }
}

2、自定义HomeBeforeAdvice前置通知

  • 继承org.springframework.aop.MethodBeforeAdvice接口
public class HomeBeforeAdvice implements MethodBeforeAdvice {
    @Override
    public void before(Method method, Object[] objects, Object target) throws Throwable {
        System.out.println("我是前置通知,拦截的方法名是: "+method.getName()+"  目标对象是:  " + target.getClass().getName());
    }
}

3、自定义HomeInterceptorAdvice环绕通知

  • 继承org.aopalliance.intercept.MethodInterceptor接口
public class HomeInterceptorAdvice implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        System.out.println("执行环绕通知  ====  HomeInterceptorAdvice 01,拦截的方法:"+methodInvocation.getMethod().getName());
        Object proceed = methodInvocation.proceed();
        System.out.println("执行环绕通知  ====  HomeInterceptorAdvice 02");
        return proceed;
    }
}

4、自定义HomeThrowingAdvice异常通知

  • 继承org.springframework.aop.ThrowsAdvice接口
public class HomeThrowingAdvice implements ThrowsAdvice {

      //异常通知有四个方法,每一个方法的作用均不同
      /*
      public void afterThrowing(Exception ex)
      public void afterThrowing(RemoteException ex)
      public void afterThrowing(Method method,Object[] args,Object target,Exception ex)
      public void afterThrowing(Method method,Object[] args,Object target,ServletException ex)
       */
      public void afterThrowing(Method method, Object[] objects, Object target,Exception ex){
          System.out.println(target.getClass().getName()+"目标对象的"+method.getName()+"方法遇到了"+ex+"异常,");
      }

}

5、自定义切点类HomePointcut:根据目标对象的方法名进行匹配

  • 继承org.springframework.aop.support.NameMatchMethodPointcut接口
public class HomePointcut extends NameMatchMethodPointcut {


    @Override
    public boolean matches(Method method, Class<?> targetClass) {
        //设置一个方法名匹配
        this.setMappedName("doFirst");
        //设置多个方法名匹配
        String[] methods = {"doSecond","doThird"};
        this.setMappedNames(methods);
        return super.matches(method, targetClass);
    }
}

6、定义业务类IndexServiceImpl

public class IndexServiceImpl implements IndexService{

    public void doFirst(){
        System.out.println("执行了doFirst方法。。。");
    }

    public void doSecond(){
        System.out.println("执行了doSecond方法。。。");
    }

    public void doThird(){
        System.out.println("执行了doThird方法。。。");
    }

}
7、配置XML文件
  • 配置方式一:spring AOP API方式配置

      <bean id="indexService" class="com.charge.aopdemo.jdkaop.service.IndexServiceImpl"></bean>
      <!--注册advice-->
      <bean id="homeBeforeAdvice" class="com.charge.aopdemo.jdkaop.advice.HomeBeforeAdvice"></bean>
      <bean id="homeAfterAdvice" class="com.charge.aopdemo.jdkaop.advice.HomeAfterReturningAdvice"></bean>
      <bean id="homeInterceptorAdvice" class="com.charge.aopdemo.jdkaop.advice.HomeInterceptorAdvice"></bean>
      <bean id="homeThrowingAdvice" class="com.charge.aopdemo.jdkaop.advice.HomeThrowingAdvice"></bean>

      <!--配置切入点-->
      <bean id="pointcutBean" class="org.springframework.aop.support.NameMatchMethodPointcut" >
          <property name="mappedNames">
              <list>
                  <value>do*</value>
              </list>
          </property>
      </bean>

      <!--配置advisor-->
      <bean id="homeAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor" >
          <property name="advice" ref="homeBeforeAdvice"></property>
          <property name="pointcut" ref="pointcutBean"></property>
      </bean>
  • 配置方式二:配置proxy代理bean
 <!--配置proxy代理bean-->
      <bean id="homeProxyBean" class="org.springframework.aop.framework.ProxyFactoryBean" >

          <property name="target"  ref="indexService"></property>

          <property name="interfaces" value="com.charge.aopdemo.jdkaop.service.IndexService"></property>
          <!--配置要执行的切面-->
          <property name="interceptorNames">
              <list>
                  <value>homeBeforeAdvice</value>
                  <value>homeAfterAdvice</value>
                  <value>homeInterceptorAdvice</value>
                  <value>homeThrowingAdvice</value>
              </list>
          </property>
      </bean>

  • 测试结果
我是前置通知,拦截的方法名是: doFirst  目标对象是:  com.charge.aopdemo.jdkaop.service.IndexServiceImpl
执行环绕通知  ====  HomeInterceptorAdvice 01,拦截的方法:doFirst
执行了doFirst方法。。。
执行环绕通知  ====  HomeInterceptorAdvice 02
我是后置通知,执行了  doFirst方法的目标对象是:  com.charge.aopdemo.jdkaop.service.IndexServiceImpl
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值