Spring系列-Spring Aop基本用法

导读:本篇文章讲解 Spring系列-Spring Aop基本用法,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

  

一、AOP

相对AOP(面向切面编程)而言大家一定更了解OOP(面向对象程序设计)

面向对象设计是一种模块化方法:把相关的数据(相关的对象)及处理方法放在一起。

面向切面编程也是一种模块化机制:核心思想是从关注点中分离出横切关注点。(这样关注的问题从代码的业务逻辑独立出来,解耦)

  个人理解:面向切面编程,可以把任意的两个切面随意的组合成一个整体,这样可以在不影响原有业务逻辑的情况下进行功能的增强

先来了解一下 AOP 的相关概念 

 Spring系列-Spring Aop基本用法

 

 

 

1、横切关注点

  对哪些方法进行拦截,拦截后怎么处理,这些关注点称之为横切关注点。

2、切面(aspect)

  类是对物体特征的抽象,切面就是对横切关注点的抽象,面向切面编程,就是指 对很多功能都有的重复的代码抽取,再在运行的时候往业务方法上动态植入“切面类代码”。

3、连接点(joinpoint)

  被拦截到的点,因为Spring只支持方法类型的连接点,所以在Spring中连接点指的就是被拦截到的方法,实际上连接点还可以是字段或者构造器。

4、切入点(pointcut)

  切入点在AOP中的通知和切入点表达式关联,指定拦截哪些类的哪些方法, 给指定的类在运行的时候动态的植入切面类代码。

5、通知(advice)

  所谓通知指的就是指拦截到连接点之后要执行的代码,

Spring切面可以应用5种类型的通知:

前置通知(Before):在目标方法被调用之前调用通知功能;

后置通知(After):在目标方法完成之后调用通知,此时不会关心方法的输出是什么;

返回通知(After-returning):在目标方法成功执行之后调用通知;

异常通知(After-throwing):在目标方法抛出异常后调用通知;

环绕通知(Around):通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为。

  注:可以将多个通知应用到一个目标对象上,即可以将多个切面织入到同一目标对象。

6、目标对象

  被一个或者多个切面所通知的对象。

7、织入(weave)

  将切面应用到目标对象并导致代理对象创建的过程

8、引入(introduction)

  在不修改代码的前提下,引入可以在运行期为类动态地添加一些方法或字段

9、AOP代理(AOP Proxy)

  在Spring AOP中有两种代理方式,JDK动态代理和CGLIB代理。

  使用 Spring AOP 可以基于两种方式,一种是比较方便和强大的注解方式,另一种则是中规中矩的 xml配置方式。

二、注解方式Spring + AspectJ

  对切面类添加 @Aspect 注解,将切面类和目标类放入到 IOC 容器中,可以通过 <context:component-scan base-package=””/> 进行扫描。

  加增强方法(包括增强类型和切点表达式,以及连接点)。

1.在 Spring 配置文件 文件中添加 <aop:aspectj-autoproxy proxy-target-class=”true”/>, proxy-target-class属性,false 只能代理接口(JDK动态代理),true 代理类(CGLIB动态代理)

<?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:context="http://www.springframework.org/schema/context"
       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/context
       http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
   <!-- 注解驱动加-->
   <aop:aspectj-autoproxy proxy-target-class="true"/>
   <context:component-scan base-package="springinaction3.wiringbean.qiyi"></context:component-scan>
</beans>

2.定义切面类(包括了5中通知)

//声明这是一个组件
@Component
//声明这是一个切面Bean,AnnotaionAspect是一个切面,由框架实现的
@Aspect
public class AnnotaionAspect {
   private final static Logger log = Logger.getLogger(AnnotaionAspect.class);
   //配置切入点,该方法无方法体,主要为方便同类中其他方法使用此处配置的切入点
   //切点的集合,这个表达式所描述的是一个虚拟面(规则)
   //就是为了Annotation扫描时能够拿到注解中的内容
   @Pointcut("execution(* springinaction3.wiringbean.qiyi..*(..))")
   public void aspect(){}
  
   /*
    * 配置前置通知,使用在方法aspect()上注册的切入点
    * 同时接受JoinPoint切入点对象,可以没有该参数
    */
   @Before("aspect()")
   public void before(JoinPoint joinPoint){
      log.info("before " + joinPoint);
   }
  
   //配置后置通知,使用在方法aspect()上注册的切入点
   @After("aspect()")
   public void after(JoinPoint joinPoint){
      log.info("after " + joinPoint);
   }
  
   //配置环绕通知,使用在方法aspect()上注册的切入点
   @Around("aspect()")
   public void around(JoinPoint joinPoint){
      long start = System.currentTimeMillis();
      try {
         ((ProceedingJoinPoint) joinPoint).proceed();
         long end = System.currentTimeMillis();
         log.info("around " + joinPoint + "\tUse time : " + (end - start) + " ms!");
      } catch (Throwable e) {
         long end = System.currentTimeMillis();
         log.info("around " + joinPoint + "\tUse time : " + (end - start) + " ms with exception : " + e.getMessage());
      }
   }
  
   //配置后置返回通知,使用在方法aspect()上注册的切入点
   @AfterReturning("aspect()")
   public void afterReturn(JoinPoint joinPoint){
      log.info("afterReturn " + joinPoint);
   }
  
   //配置抛出异常后通知,使用在方法aspect()上注册的切入点
   @AfterThrowing(pointcut="aspect()", throwing="ex")
   public void afterThrow(JoinPoint joinPoint, Exception ex){
      log.info("afterThrow " + joinPoint + "\t" + ex.getMessage());
   }
  
}

注意:配置切入点需要额外注意:@Pointcut

3.配置log4j.properties

### set log levels ###
log4j.rootLogger = INFO , console , debug , error 
### console ###
log4j.appender.console = org.apache.log4j.ConsoleAppender 
log4j.appender.console.Target = System.out 
log4j.appender.console.layout = org.apache.log4j.PatternLayout 
log4j.appender.console.layout.ConversionPattern = %-d{yyyy-MM-dd HH\:mm\:ss} [%p]-[%c] %m%n 
### log file ###
log4j.appender.debug = org.apache.log4j.DailyRollingFileAppender 
log4j.appender.debug.File = ../logs/debug.log 
log4j.appender.debug.Append = true 
log4j.appender.debug.Threshold = INFO 
log4j.appender.debug.layout = org.apache.log4j.PatternLayout 
log4j.appender.debug.layout.ConversionPattern =%-d{yyyy-MM-dd HH\:mm\:ss} [%p]-[%c] %m%n 
### exception ###
log4j.appender.error = org.apache.log4j.DailyRollingFileAppender 
log4j.appender.error.File = ../logs/error.log 
log4j.appender.error.Append = true 
log4j.appender.error.Threshold = ERROR 
log4j.appender.error.layout = org.apache.log4j.PatternLayout 
log4j.appender.error.layout.ConversionPattern =%-d{yyyy-MM-dd HH\:mm\:ss} [%p]-[%c] %m%n 
log4j.appender.stdout=org.apache.log4j.ConsoleAppender 
log4j.appender.stdout.Target=System.out 
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout 
log4j.appender.stdout.layout.ConversionPattern=%d{ISO8601} %l %c%n%p: %m%n

三、Xml方式:

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:context="http://www.springframework.org/schema/context"
       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/context
       http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--
   <aop:aspectj-autoproxy proxy-target-class="true"/>
-->
   <context:component-scan base-package="springinaction3.wiringbean.qiyi"></context:component-scan>
   <bean id="xmlAspect" class="springinaction3.wiringbean.qiyi.aspect.XmlAspect"></bean>
    <!--AOP配置 -->
   <aop:config>
    <!--声明一个切面,并注入切面Bean,相当于@Aspect -->
   <aop:aspect ref="xmlAspect">
    <!--配置一个切入点,相当于@Pointcut -->
   <aop:pointcut expression="execution(* springinaction3.wiringbean.qiyi..*(..))" id="simplePointcut"/>
    <!--配置通知,相当于@Before、@After、@AfterReturn、@Around、@AfterThrowing -->
   <aop:before pointcut-ref="simplePointcut" method="before"/>
   <aop:after pointcut-ref="simplePointcut" method="after"/>
   <aop:after-returning pointcut-ref="simplePointcut" method="afterReturn"/>
   <aop:after-throwing pointcut-ref="simplePointcut" method="afterThrow" throwing="ex"/>
   </aop:aspect>
   </aop:config>
</beans>
 

切面类

public class XmlAspect {
    private final static Logger log = Logger.getLogger(XmlAspect.class);
    /*
     * 配置前置通知,使用在方法aspect()上注册的切入点
     * 同时接受JoinPoint切入点对象,可以没有该参数
     */
    public void before(JoinPoint joinPoint){
//    System.out.println(joinPoint.getArgs()); //获取实参列表
//    System.out.println(joinPoint.getKind());   //连接点类型,如method-execution
//    System.out.println(joinPoint.getSignature()); //获取被调用的切点
//    System.out.println(joinPoint.getTarget()); //获取目标对象
//    System.out.println(joinPoint.getThis());   //获取this的值
        log.info("before " + joinPoint);
    }
    //配置后置通知,使用在方法aspect()上注册的切入点
    public void after(JoinPoint joinPoint){
        log.info("after " + joinPoint);
    }
    //配置环绕通知,使用在方法aspect()上注册的切入点
    public void around(JoinPoint joinPoint){
        long start = System.currentTimeMillis();
        try {
            ((ProceedingJoinPoint) joinPoint).proceed();
            long end = System.currentTimeMillis();
            log.info("around " + joinPoint + "\ttime : " + (end - start) + " ms!");
        } catch (Throwable e) {
            long end = System.currentTimeMillis();
            log.info("around " + joinPoint + "\ttime : " + (end - start) + " ms with exception : " + e.getMessage());
        }
    }
    //配置后置返回通知,使用在方法aspect()上注册的切入点
    public void afterReturn(JoinPoint joinPoint){
        log.info("afterReturn " + joinPoint);
    }
    //配置抛出异常后通知,使用在方法aspect()上注册的切入点
    public void afterThrow(JoinPoint joinPoint, Exception ex){
        log.info("afterThrow " + joinPoint + "\t" + ex.getMessage());
    }
}

学习难点

掌握和使用切入点表达式。

切入点表达式的配置规则。

通常情况下,表达式中使用”execution“就可以满足大部分的要求。

表达式格式如下:

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:方法名

parm-pattern:参数名

throws-pattern:异常

  其中,除 ret-type-pattern 和 name-pattern 之外,其他都是可选的。

  上例中,execution(* com.spring.service.*.*(..))表示 com.spring.service 包下,返回值为任意类型;方法名任意; 参数不作限制的所有方法。

  最后说一下通知参数 可以通过 args 来绑定参数,这样就可以在通知(Advice)中访问具体参数了。

  例如, 配置如下: 上面的代码 args(msg,..)是指将切入点方法上的第一个 String 类型参数添加到参数名为 msg 的通知 的入参上,这样就可以直接使用该参数啦。

  访问当前的连接点 在上面的 Aspect 切面 Bean 中已经看到了,每个通知方法第一个参数都是 JoinPoint。其实,在 Spring 中,任何通知(Advice)方法都可以将第一个参数定义为 org.aspectj.lang.JoinPoint 类型用以 接受当前连接点对象。JoinPoint 接口提供了一系列有用的方法, 比如 getArgs() (返回

方法参数)、getThis() (返回代理对象)、getTarget() (返回目标)、getSignature() (返回正在被通知的 方法相关信息)和 toString() (打印出正在被通知的方法的有用信息)。

 

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/70226.html

(0)
小半的头像小半

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!