今天开始复习的学习spring AOP的应用

AOP 面向切面编程,主要解决的是针对一批组件的通用逻辑编程的问题 ,它采用了公共类+配置的方式,实现这个逻辑,可以降低公共类和组件之间的耦合度。

 

Spring AOP包括

 

方面组件、目标组件、切入点和通知

 

方面组件:方面组件是封装公共处理的组件,该组件被作用到其他目标组件的方法上

目标组件:一个或多个方面所作用的对象

切入点:用以指定那些组件和方法使用方面功能在Spring中使用一个表达式来指定切入目标

1、常用的切入点表达式

         1)匹配类型

                   用于匹配哪些类型的方法启用方面组件,语法格式如下

                   within(类型)

                   --匹配容器中HibernateCostDaoImpl的所有方法

                            within(com.tarena.dao.HibernateCostDaoImpl)

                   --匹配com.tarena包下所有类的所有方法

                            within(com.tarena.*)

                   --匹配com.tarena包及子包的所有类的所有方法

                            within(com.tarena..*)

         2)匹配方法

                   用于匹配哪些方法启用方面组件,语法格式如下

                   execution(修饰符? 返回类型 方法名(参数列表) throws异常?)

                   --匹配所有对象的delete方法

                            execution(* delete(..))

                   --匹配HibernateCostDaoImpl的delete方法

                            execution(* com.tarena.dao.HibernateCostDaoImpl.delete(..))

                   --匹配HibernateCostDaoImpl的所有方法

                            execution(* com.tarena.dao.HibernateCostDaoImpl.*(..))

                   --匹配com.tarena包下所有类的所有方法

                            execution(* com.tarena.*.*(..))

                   --匹配com.tarena包及子包下所有类的所有方法

                                     execution(* com.tarena..*.*(..))

         3)匹配bean名称

                   用于匹配bean的id属性,语法格式如下

                   bean(id属性值)

                   --匹配id="costDao"的组件的所有方法

                            bean(costDao)

                   --匹配所有id以Dao结尾的组件的所有方法

                            bean(*Dao)

         4)匹配参数

                   用于匹配参数类型和个数,代码格式如下

                   args(参数列表)

                   --匹配有一个参数并且为String类型的所有方法

                            args(java.lang.String)

 

通知:通知时用于指定方面组件和目标组件的作用时机,例如方面组件是在目标组件的方法之前执行还是在 目标组件方法之后执行。

         通知包括:前置通知 ,后置通知,最终通知,环绕通知,和异常通知

 

未使用annotation进行声明组件的方法之前

 

开发步骤

  1. 创建一个方面组件

创建一个类,充当方面组件,实现通用业务逻辑

  1. 声明方面组件

可以使用非annotation的方式 ,

也可以使用

annotation(注解)方式进行声明:

在applicationContext.xml文件中开启aop注解扫描

<aop:aspect-autoproxy proxy-target-class=”true” />

使用@Component标注这个类 ,将其声明为组件

使用@Aspect标注这个类,将其声明为方面组件

  1. 使用方面组件

在组件的方法上,使用注解将方面组件作用到目标组件的方法上,并设置通知类型以确认方面组件调用的时机。

前置通知:使用前置通知,在方面组件方法上增加注释:

@Before(“within(controller..*)”)

Public void log(){

}

后置通知,最终通知 使用方式与前置通知一致,只需要将注释改为@AfterReturning 、@After即可

 

环绕通知:使用环绕通知,在方面组件方法上增加注释

@Around(“within(controller..*)”)

Public Object log(ProceedingJoinPoint p) throws Throwable{

         //此处代码在目标组件前执行

         Object obj = p.proceed();//执行目标组件的方法

         //此处代码在目标组件后执行

 

}

 

异常通知:使用异常通知,在方面组件方法上增加注释

@AfterThrowing(pointcut=”within(controller..*)”,throwing=”e”)

Public void log(Exception e){

}

 

 

 

 

 

通过配置方式声明SpringAOP:

package com.tarena.aspect;

 

import java.text.SimpleDateFormat;

import java.util.Date;

 

import org.aspectj.lang.ProceedingJoinPoint;

public class LoggerBean {

    public void log1() {

       //模拟记录日志

       System.out.println("记录日志...");

    }

    public Object log2(ProceedingJoinPoint p )throws Throwable{

       String className = p.getTarget().getClass().getName();

       String method = p.getSignature().getName();

       String date = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date());

       String msg = "-->用户在"+date+",执行了"+className +"." +method+"()";

       System.out.println(msg);

       Object obj = p.proceed();

       System.out.println("-->调用目标组件业务方法之后...");

       return obj;

    }

    public void log3(Exception e){

       StackTraceElement[] elems = e.getStackTrace();

       System.out.println("-->"+elems[0].toString());

    }

}

 

package com.tarena.controller;

 

import org.springframework.stereotype.Controller;

import org.springframework.ui.Model;

import org.springframework.web.bind.annotation.RequestMapping;

 

 

@Controller

@RequestMapping("/emp")

public class EmpController {

  @RequestMapping("/findEmp.do")

  public String find(Model model) {

      System.out.println("查询全部员工数据...");

      return "emp/emp_list";

  }

}

 

 

<!-- 注册组件 -->

    <bean id="logger"

       class="com.tarena.aspect.LoggerBean"/>

      

    <!-- AOP配置 -->

    <aop:config>

       <!-- 声明方面组件 -->

       <aop:aspect ref="logger">

           <!--

              aop:before,是前置通知;

              aop:after-returning,是后置通知;

              aop:after,是最终通知;

              method,是方面组件中的方法;

              pointcut,是切入点。

            -->

           <aop:after-returning method="log1"

              pointcut="within(com.tarena.controller..*)"/>

       </aop:aspect>

    </aop:config>

   

    <aop:config>

       <aop:aspect ref="logger">

           <aop:before method="log1" pointcut="within(com.tarena.controller..*)"/>

           <aop:around method="log2" pointcut="within(com.tarena.controller..*)"/>

           <aop:after-throwing method="log3" throwing="e" pointcut="within(com.tarena.controller..*)" />

       </aop:aspect>

</aop:config>