• 跟着刚哥学习Spring框架--AOP(五)


    AOP

    AOP(Aspect Oriented Programming),即面向切面编程,可以说是OOP(Object Oriented Programming,面向对象编程)的补充和完善。OOP引入封装、继承、多态等概念来建立一种对象层次结构,用于模拟公共行为的一个集合。不过OOP允许开发者定义纵向的关系,但并不适合定义横向的关系,例如日志功能。日志代码往往横向地散布在所有对象层次中,而与它对应的对象的核心功能毫无关系对于其他类型的代码,如安全性、异常处理和透明的持续性也都是如此,这种散布在各处的无关的代码被称为横切(cross cutting),在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。

    AOP技术恰恰相反,它利用一种称为"横切"的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其命名为"Aspect",即切面。所谓"切面",简单说就是那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可操作性和可维护性。

    使用"横切"技术,AOP把软件系统分为两个部分:核心关注点横切关注点。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。横切关注点的一个特点是,他们经常发生在核心关注点的多处,而各处基本相似,比如权限认证、日志、事物。AOP的作用在于分离系统中的各种关注点,将核心关注点和横切关注点分离开来。

     基于Spring的AOP简单实现

    1、通过配置文件实现AOP

    首先需要额外上网下载两个jar包:

      ① aspectjrt.jar
      ② aspectjweaver.jar

    先定义一个接口IHelloWorld:

    1 public interface IHelloWorld {
    2     void printHelloWorld(String name);
    3 }

    定义一个实现类HelloWorld

    1 public class HelloWorld implements IHelloWorld {
    2 
    3     @Override
    4     public void printHelloWorld(String name) {
    5         System.out.println("Print HelloWorld :" + name);
    6     }
    7 }

    实现了两个切面(一个日志切面和一个时间切面)

    1 public class LogAspect {
    2     public void beforeLog(JoinPoint joinPoint){
    3         System.out.println("log before method " + joinPoint.getSignature().getName() + "参数 :" + Arrays.asList(joinPoint.getArgs()));
    4     }
    5     public void afterLog(JoinPoint joinPoint){
    6         System.out.println("log after method " + joinPoint.getSignature().getName() + "参数 :" + Arrays.asList(joinPoint.getArgs()));
    7     }
    8 }
    1 public class TimeAspect {
    2     public void beforeTime(JoinPoint joinPoint){
    3         System.out.println("time before method " + joinPoint.getSignature().getName() + "参数 :" + Arrays.asList(joinPoint.getArgs()));
    4     }
    5     public void afterTime(JoinPoint joinPoint){
    6         System.out.println("time after method " + joinPoint.getSignature().getName() + "参数 :" + Arrays.asList(joinPoint.getArgs()));
    7     }
    8 }

    定义一个配置文件

     1 <?xml version="1.0" encoding="UTF-8"?>
     2 <beans xmlns="http://www.springframework.org/schema/beans"
     3        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     4        xmlns:context="http://www.springframework.org/schema/context"
     5        xmlns:aop="http://www.springframework.org/schema/aop"
     6        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">
     7 
     8     <!-- 核心业务Bean -->
     9     <bean id="helloworld" class="com.hzg.HelloWorld"></bean>
    10 
    11     <!-- 切面Bean(日志切面) -->
    12     <bean id="logAspect" class="com.hzg.LogAspect"></bean>
    13 
    14     <!-- 切面Bean(时间切面) -->
    15     <bean id="timeAspect" class="com.hzg.TimeAspect"></bean>
    16 
    17     <!-- AOP配置 -->
    18     <aop:config>
    19         <!-- 配置切点 -->
    20         <aop:pointcut id="pointcut" expression="execution(* com.hzg.IHelloWorld.*(..))"></aop:pointcut>
    21         <!-- 配置切面(日志切面)切面的优先级需要通过order定义 -->
    22         <aop:aspect id="log" ref="logAspect" order="1">
    23             <!-- 配置通知(前置通知、后置通知、返回通知、异常通知、环绕通知) -->
    24             <aop:before method="beforeLog" pointcut-ref="pointcut"></aop:before>
    25             <aop:after method="afterLog" pointcut-ref="pointcut"></aop:after>
    26         </aop:aspect>
    27         <!-- 配置切面(时间切面)切面的优先级需要通过order定义 -->
    28         <aop:aspect id="time" ref="timeAspect" order="2">
    29             <!-- 配置通知(前置通知、后置通知、后置返回通知、异常通知、环绕通知) -->
    30             <aop:before method="beforeTime" pointcut-ref="pointcut"></aop:before>
    31             <aop:after method="afterTime" pointcut-ref="pointcut"></aop:after>
    32         </aop:aspect>
    33     </aop:config>
    34 
    35 </beans>

    定义Main主方法

    1 public static void main(String[] args) {
    2         //由于ApplicationContext没有close方法,所以要使用它下面接口ConfigurableApplicationContext
    3         ApplicationContext ctx = new ClassPathXmlApplicationContext("configaop.xml");
    4         IHelloWorld helloworld = (IHelloWorld) ctx.getBean("helloworld");
    5         helloworld.printHelloWorld("gangge");
    6     }

    输出结果:

    log before method printHelloWorld参数 :[gangge]
    time before method printHelloWorld参数 :[gangge]
    Print HelloWorld :gangge
    time after method printHelloWorld参数 :[gangge]
    log after method printHelloWorld参数 :[gangge]

    2、通过注解实现AOP

    实现配置文件方式同样的功能

    先定义一个接口文件(没区别)

    1 public interface IHelloWorld {
    2     void printHelloWorld(String name);
    3 }

    定义实现类(区别:加了一个注解@Component

    1 @Component
    2 public class HelloWorld implements IHelloWorld {
    3 
    4     @Override
    5     public void printHelloWorld(String name) {
    6         System.out.println("Print HelloWorld :" + name);
    7     }
    8 }

    定义两个切面(一个日志切面和一个时间切面 区别:增加了@Component、@Aspect和@Order注解

     1 @Component
     2 @Aspect
     3 @Order(1)
     4 public class LogAspect {
     5     public void beforeLog(JoinPoint joinPoint){
     6         System.out.println("log before method " + joinPoint.getSignature().getName() + "参数 :" + Arrays.asList(joinPoint.getArgs()));
     7     }
     8     public void afterLog(JoinPoint joinPoint){
     9         System.out.println("log after method " + joinPoint.getSignature().getName() + "参数 :" + Arrays.asList(joinPoint.getArgs()));
    10     }
    11 }
     1 @Component
     2 @Aspect
     3 @Order(2)
     4 public class TimeAspect {
     5     @Before("execution(* com.hzg.HelloWorld.*(..))")
     6     public void beforeTime(JoinPoint joinPoint){
     7         System.out.println("time before method " + joinPoint.getSignature().getName() + "参数 :" + Arrays.asList(joinPoint.getArgs()));
     8     }
     9     @After("execution(* com.hzg.HelloWorld.*(..))")
    10     public void afterTime(JoinPoint joinPoint){
    11         System.out.println("time after method " + joinPoint.getSignature().getName() + "参数 :" + Arrays.asList(joinPoint.getArgs()));
    12     }
    13 }

    定义配置文件(区别:Spring自动扫描包和aop自动代理

     1 <?xml version="1.0" encoding="UTF-8"?>
     2 <beans xmlns="http://www.springframework.org/schema/beans"
     3        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     4        xmlns:context="http://www.springframework.org/schema/context"
     5        xmlns:aop="http://www.springframework.org/schema/aop"
     6        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">
     7 
     8     <context:component-scan base-package="com.hzg"></context:component-scan>
     9     <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
    10 
    11 </beans>

    定义Main主方法(无区别)

    1 public static void main(String[] args) {
    2         //由于ApplicationContext没有close方法,所以要使用它下面接口ConfigurableApplicationContext
    3         ApplicationContext ctx = new ClassPathXmlApplicationContext("configaop.xml");
    4         IHelloWorld helloworld = (IHelloWorld) ctx.getBean("helloworld");
    5         helloworld.printHelloWorld("gangge");
    6     }

    输出结果和配置文件一样

    五种通知

    1、前置通知

      上面已经有了,不再说明

    2、后置通知

      上面已经有了,不再说明

    3、返回通知

     1 /**
     2      * 返回通知: 方法正常执行完后调用,如果有异常,则不调用
     3      *          可以访问到方法的返回值
     4      * @param joinPoint
     5      * @param result   方法的返回值
     6      */
     7     @AfterReturning(value="execution(* com.hzg.*(..))",returning="result")
     8     public void afterReturning(JoinPoint joinPoint,Object result){
     9         //方法名
    10         String methodName = joinPoint.getSignature().getName();
    11         System.out.println("method "+methodName+" end:"+result);
    12     }

    4、异常通知

     1 /**
     2      * 异常通知: 当方法出现异常时
     3      *          可以指定出现哪些异常时才执行: 如何指定?通过入参指定,如:
     4      *          如果入参为NullPointException ex,那么只有在发生空指针异常时才执行 
     5      * @param joinPoint
     6      * @param ex
     7      */
     8     @AfterThrowing(value="execution(* com.hzg.*(..))",throwing="ex")
     9     public void afterThrowing(JoinPoint joinPoint,Exception ex){
    10         //方法名
    11         String methodName = joinPoint.getSignature().getName();
    12         System.out.println("method "+methodName+" occurs:"+ex);
    13     }

    5、环绕通知

      环绕通知类似于动态代理的全过程,可以使用环绕通知实现前置通知,后置通知,返回通知,异常通知的功能,十分强大,但并不常用

     1 /**
     2      * 环绕通知需要携带ProceedingJoinPoint类型的参数
     3      * 环绕通知类似于动态代理的全过程: ProceedingJoinPoint这个类型的参数可以决定是否执行目标方法
     4      * 且环绕通知必须有返回值,返回值即为目标方法的返回值
     5      * @param pjd
     6      */
     7     @Around("execution(* com.hzg.*(..))")
     8     public Object around(ProceedingJoinPoint pjd){
     9         Object result = null;
    10         String methodName = pjd.getSignature().getName();
    11 
    12         try {
    13             //前置通知
    14             System.out.println("method:"+methodName+" begins with "+Arrays.asList(pjd.getArgs()));
    15             //执行目标方法
    16             result = pjd.proceed();
    17             //返回通知
    18             System.out.println("method:"+methodName+" end with "+result);
    19         } catch (Throwable e) {
    20             // 异常通知
    21             System.out.println("method:"+methodName+" occurs exception "+e);
    22         }
    23         //后置通知
    24         System.out.println("method ends");
    25         return result;
    26     }

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

    跟着刚哥学习Spring框架系列:

    跟着刚哥学习Spring框架--创建HelloWorld项目(一)

    跟着刚哥学习Spring框架--Spring容器(二)

    跟着刚哥学习Spring框架--通过XML方式配置Bean(三)

    跟着刚哥学习Spring框架--通过注解方式配置Bean(四)

    跟着刚哥学习Spring框架--AOP(五)

    跟着刚哥学习Spring框架--JDBC(六)

    跟着刚哥学习Spring框架--事务配置(七)

  • 相关阅读:
    [JLOI2010] 冠军调查
    [ZJOI2009] 狼和羊的故事
    [CF1451D] Circle Game
    [CF1451E1] Bitwise Queries (Easy Version)
    [CF343D] Water Tree
    [CF1344B] Monopole Magnets
    [CF191C] Fools and Roads
    [CF1370D] Odd-Even Subsequence
    [CF1366D] Two Divisors
    [CF1359D] Yet Another Yet Another Task
  • 原文地址:https://www.cnblogs.com/hzg110/p/6794465.html
Copyright © 2020-2023  润新知