• Spring中的AOP


    一:为什么要有AOP?

     上图的案例是使用MyBatis完成用户添加的经典案例,红色方框中的是具体实现功能的业务代码。那么大家可以看到,仅仅只占整个代码片段的三分之一,那么其余的三分之二呢?

    充斥着许多功能代码。如:日志记录、事务提交、事务回滚。。。。这些功能代码和业务代码耦合在一起,分散精力,不得尝失。

     AOP的目标:让我们可以“专心做事”,将所有功能代码(日志记录、事务处理)从程序代码中剥离,让用户专心与一点在所要实现的业务上。

     7.2 AOP原理

    • 将复杂的需求分解出不同方面,将散布在系统中的公共功能集中解决
    • 采用代理机制组装起来运行,在不改变原程序的基础上对代码段进行增强处理,增加新的功能。

           

    SpringAOP主要围绕以下概念展开:

    • 切面(Aspect)一个关注点的模块化,这个关注点可能会横切多个对象
    • 连接点(Joinpoint)程序执行过程中某个特定的连接点
    • 通知(Advice) 在切面的某个特的连接点上执行的动作
    • 切入点(Pointcut)匹配连接的断言,在Aop中通知和一个切入点表达式关联
    • 引入(Intruduction) 在不修改类代码的前提下,为类添加新的方法和属性
    • 目标对象(Target Object) 被一个或者多个切面所通知的对象
    • Aop代理(AOP Proxy) AOP框架创建的对象,用来实现切面契约(aspect contract)(包括方法执行等)
    • 织入(Weaving)把切面连接到其他的应用程序类型或者对象上,并创建一个被通知的对象,

    通知类型Advice:

    • 前置通知(before advice) 在某个连接点(jion point)之前执行的通知,但不能阻止连接点前的执行(除非抛出一个异常)
    • 返回后通知(after returning advice)在某个连接点(jion point)正常执行完后执行通知
    • 抛出异常通知(after throwing advice) 在方法异常退出时执行的通知
    • 后通知(after(finally) advice)在方法抛出异常退出时候的执行通知(不管正常返回还是异常退出)
    • 环绕通知(around advice) 包围一个连接点(jion point)的通知

    3. 切点表达式之execution

      由于Spring切面粒度最小是达到方法级别,而execution表达式可以用于明确指定方法返回类型,类名,方法名和参数名等与方法相关的部件,并且在Spring中,大部分需要使用AOP的业务场景也只需要达到方法级别即可,因而execution表达式的使用是最为广泛的。如下是execution表达式的语法:

    execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern) throws-pattern?)

       这里问号表示当前项可以有也可以没有,其中各项的语义如下:

    • modifiers-pattern:方法的可见性,如public,protected;
    • ret-type-pattern:方法的返回值类型,如int,void等;
    • declaring-type-pattern:方法所在类的全路径名,如com.spring.Aspect;
    • name-pattern:方法名类型,如buisinessService();
    • param-pattern:方法的参数类型,如java.lang.String;
    • throws-pattern:方法抛出的异常类型,如java.lang.Exception;
    • *通配符,该通配符主要用于匹配单个单词,或者是以某个词为前缀或后缀的单词。
    • ..通配符,该通配符表示0个或多个项,主要用于declaring-type-pattern和param-pattern中,如果用于declaring-type-pattern中,则表示匹配当前包及其子包,如果用于param-pattern中,则表示匹配0个或多个参数。

    其他切点表达式见  https://www.cnblogs.com/zhangxufeng/p/9160869.html

    接下来通过一个简单的AOP例子来了解一下AOP的使用方式

    现在有一个doSome方法的执行前增加一些功能,类似于(声明日志),方法执行后(提交事务,释放资源等等)

    package com.yjc.before;
    
    public class IDoSomeImpl implements IDoSome{
        @Override
        public void doSome() {
            System.out.println("---------我是需要被增强的doSome方法-------------");
        }
    }

    接下来创建我们的增强类,给doSome方法分别添加前置和后置增强

    package com.yjc.before;
    
    import org.springframework.aop.AfterReturningAdvice;
    import org.springframework.aop.MethodBeforeAdvice;
    
    import java.lang.reflect.Method;
    
    public class BeforeAdvice implements MethodBeforeAdvice, AfterReturningAdvice {
        @Override
        public void before(Method method, Object[] args, Object target) throws Throwable {
            System.out.println("前置==================================");
        }
    
        @Override
        public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
            System.out.println("后置==================================");
        }
    }

    前置和后置增强方法需要分别实现MethodBeforeAdvice, AfterReturningAdvice这两个接口,还有重写它们的方法

    增强类有了之后就需要在我们的Spring的核心配置文件中配置了,使用我们的aop也需要引用依赖

    <dependency>
          <groupId>org.aspectj</groupId>
          <artifactId>aspectjweaver</artifactId>
          <version>1.7.0</version>
        </dependency>
    <?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:con="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/aop
           http://www.springframework.org/schema/aop/spring-aop.xsd">
       <!--把增强类和目标类放入到spring容器中-->
        <bean id="beforeAdvice" class="com.yjc.before.BeforeAdvice"/>
        <bean id="iDoSomeImpl" class="com.yjc.before.IDoSomeImpl"/>
        <con:config>
    <!--声明切点表达式,代表before包下的所有方法--> <con:pointcut id="pointcut" expression="execution(* *..before.*.*(..))"/>
       
          <!--织入--> <con:advisor advice-ref="beforeAdvice" pointcut-ref="pointcut"/> </con:config> </beans>

    编写测试类

    package com.yjc.before;
    
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class BeforeTest {
    
        public static void main(String[] args) {
            ApplicationContext applicationContext=new ClassPathXmlApplicationContext("com/yjc/before/applicationContext.xml");
        //我的
    applicationContext.xml文件是放在bao下的,所以需要指向全路径
      IDoSome iDoSomeImpl = applicationContext.getBean("iDoSomeImpl", IDoSome.class); iDoSomeImpl.doSome(); } }

     测试成功

  • 相关阅读:
    web端
    vue 键盘事件keyup/keydoen
    APiCloud
    APiCloud
    对于HTML和XML的理解
    JS 的三种定义变量 var let const
    jQuery
    NodeJS 阻塞/非阻塞
    NodeJs REPL交互式解析器常用命令
    用NodeJS创建一个聊天服务器
  • 原文地址:https://www.cnblogs.com/yjc1605961523/p/11748089.html
Copyright © 2020-2023  润新知