0
点赞
收藏
分享

微信扫一扫

Spring03--AOP 面向切面编程

萧萧雨潇潇 2022-01-20 阅读 65

Spring03--AOP 面向切面编程

1.1 AOP 简介

AOP(Aspect Orient Programming),面向切面编程。面向切面编程是从动态角度考虑程序运行过程。
AOP 底层,就是采用动态代理模式实现的。采用了两种代理:JDK 的动态代理,与 CGLIB的动态代理

面向切面编程,就是将交叉业务逻辑封装成切面,利用 AOP 容器的功能将切面织入到主业务逻辑中。所谓交叉业务逻辑是指通用的、与主业务逻辑无关的代码,如安全检查、事务、日志、缓存等。若不使用 AOP,则会出现代码纠缠,即交叉业务逻辑与主业务逻辑混合在一起。这样,会使主业务逻辑变的混杂不清。

1.2 面向切面编程的好处

1.减少重复;
2.专注业务;

示例:pandas 是基于NumPy 的一种工具,该工具是为了解决数据分析任务而创建的。

1.3 AOP 编程术语

切面(Aspect)

连接点(JoinPoint)

切入点(Pointcut)

目标对象(Target)

通知(Advice)

1.4 AspectJ 对 AOP 的实现

对于 AOP 这种编程思想,很多框架都进行了实现。Spring 就是其中之一,可以完成面向切面编程。然而,AspectJ 也实现了 AOP 的功能,且其实现方式更为简捷,使用更为方便,而且还支持注解式开发。所以,Spring 又将 AspectJ 的对于 AOP 的实现也引入到了自己的框架中。

AspectJ 简介

1.4.1 AspectJ 的通知类型

AspectJ 中常用的通知有五种类型:
(1)前置通知
(2)后置通知
(3)环绕通知
(4)异常通知
(5)最终通知

1.4.2 AspectJ 的切入点表达式

AspectJ 定义了专门的表达式用于指定切入点。表达式的原型是:

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

以上表达式共 4 个部分。
execution(访问权限 方法返回值 方法声明(参数) 异常类型)
切入点表达式要匹配的对象就是目标方法的方法名。所以,execution 表达式中明显就是方法的签名。注意,表达式中黑色不加粗文字表示可省略部分,各部分间用空格分开。在其中可以使用以下符号:

符号意义
*0至多个任意字符
用在参数方法中,表示任意多个参数;用在包名后,表示当前包及其子包路径。
+用在类名后,表示当前类及其子类;用在接口后,表示当前接口及其实现类。

1.4.3 AspectJ 的开发环境

1)maven依赖

<dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
    <!--spring依赖-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.2.5.RELEASE</version>
    </dependency>
    <!--aspectj依赖-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aspects</artifactId>
      <version>5.2.5.RELEASE</version>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.1</version>
        <configuration>
          <source>1.8</source>
          <target>1.8</target>
        </configuration>
      </plugin>
    </plugins>
  </build>

2)引入 AOP 约束

在 AspectJ 实现 AOP 时,要引入 AOP 的约束。配置文件中使用的 AOP 约束中的标签,均是 AspectJ 框架使用的,而非 Spring 框架本身在实现 AOP 时使用的。
AspectJ 对于 AOP 的实现有注解和配置文件两种方式,常用是注解方式。

1.4.4 AspectJ 基于注解的 AOP 实现

1)前置通知

测试接口:

public interface SomeService {
    void doSome(String name,Integer age);
}

接口实现类:

public class SomeServiceImpl implements SomeService {
    @Override
    public void doSome(String name,Integer age) {
        //给doSome方法增加一个功能,在doSome()执行之前, 输出方法的执行时间
        System.out.println("====目标方法doSome()====");
    }
}

切面类:

/**
 *  @Aspect : 是aspectj框架中的注解。
 *     作用:表示当前类是切面类。
 *     切面类:是用来给业务方法增加功能的类,在这个类中有切面的功能代码
 *     位置:在类定义的上面
 */
@Aspect
public class MyAspect {
    /**
     * @Before: 前置通知注解
     *   属性:value ,是切入点表达式,表示切面的功能执行的位置。
     *   位置:在方法的上面
     * 特点:
     *  1.在目标方法之前先执行的
     *  2.不会改变目标方法的执行结果
     *  3.不会影响目标方法的执行。
     */
   @Before(value = "execution(public void com.suyv.spring.ba01.SomeServiceImpl.doSome(String,Integer))")
    public void myBefore(){
        //就是你切面要执行的功能代码
        System.out.println("前置通知, 切面功能:在目标方法之前输出执行时间:"+ new Date());
    }

spring配置文件:

	<!--声明目标对象-->
    <bean id="someService" class="com.suyv.spring.ba01.SomeServiceImpl" />
    <!--声明切面类对象-->
    <bean id="myAspect" class="com.suyv.spring.ba01.MyAspect" />
    <!--声明自动代理生成器:使用aspectj框架内部的功能,创建目标对象的代理对象。
        创建代理对象是在内存中实现的, 修改目标对象的内存中的结构。 
        创建为代理对象所以目标对象就是被修改后的代理对象.
        aspectj-autoproxy:会把spring容器中的所有的目标对象,一次性都生成代理对象。
    -->
    <aop:aspectj-autoproxy />

测试方法:

	@Test
    public void test01(){
        String config="applicationContext.xml";
        ApplicationContext ctx = new ClassPathXmlApplicationContext(config);
        //从容器中获取目标对象
        SomeService proxy = (SomeService) ctx.getBean("someService");
        System.out.println("proxy:"+proxy.getClass().getName());
        //通过代理的对象执行方法,实现目标方法执行时,增强了功能
        proxy.doSome("lisi",20);
    }

测试结果:
在这里插入图片描述

2)后置通知

测试接口:

public interface SomeService {
    void doSome(String name, Integer age);
    String doOther(String name,Integer age);
}

接口实现类:

public class SomeServiceImpl implements SomeService {

    @Override
    public void doSome(String name,Integer age) {
        //给doSome方法增加一个功能,在doSome()执行之前, 输出方法的执行时间
        System.out.println("====目标方法doSome()====");
    }

    @Override
    public String doOther(String name, Integer age) {
        System.out.println("====目标方法doOther()====");
        return "abcd";
    }

}

切面类:

@Aspect
public class MyAspect {
    /**
     * @AfterReturning:后置通知
     *    属性:1.value 切入点表达式
     *         2.returning 自定义的变量,表示目标方法的返回值的。
     *          自定义变量名必须和通知方法的形参名一样。
     *    位置:在方法定义的上面
     * 特点:
     *  1。在目标方法之后执行的。
     *  2. 能够获取到目标方法的返回值,可以根据这个返回值做不同的处理功能
     *      Object res = doOther();
     *  3. 可以修改这个返回值
     */
    @AfterReturning(value = "execution(* *..SomeServiceImpl.doOther(..))",
                    returning = "res")
    public void myAfterReturing(  JoinPoint jp  ,Object res ){
        // Object res:是目标方法执行后的返回值,根据返回值做你的切面的功能处理
        System.out.println("后置通知:方法的定义"+ jp.getSignature());
        System.out.println("后置通知:在目标方法之后执行的,获取的返回值是:"+res);
        if(res.equals("abcd")){
            //做一些功能
        } else{
            //做其它功能
        }
        //修改目标方法的返回值, 看一下是否会影响 最后的方法调用结果
        if( res != null){
            res = "Hello Aspectj";
        }

    }
    
}

spring配置文件:

    <!--声明目标对象-->
    <bean id="someService" class="com.suyv.spring.ba02.SomeServiceImpl" />
    <!--声明切面类对象-->
    <bean id="myAspect" class="com.suyv.spring.ba02.MyAspect" />
    <!--声明自动代理生成器-->
    <aop:aspectj-autoproxy />

测试方法:

    @Test
    public void test01(){
        String config="applicationContext.xml";
        ApplicationContext ctx = new ClassPathXmlApplicationContext(config);
        //从容器中获取目标对象
        SomeService proxy = (SomeService) ctx.getBean("someService");
        //通过代理的对象执行方法,实现目标方法执行时,增强了功能
        String str = proxy.doOther("zs",28);
        System.out.println("str===="+str);

    }

测试结果:
在这里插入图片描述

3)环绕通知

测试接口:

public interface SomeService {
    void doSome(String name, Integer age);
    String doOther(String name, Integer age);
    String doFirst(String name,Integer age);
}

接口实现类:

public class SomeServiceImpl implements SomeService{
    @Override
    public void doSome(String name,Integer age) {
        //给doSome方法增加一个功能,在doSome()执行之前, 输出方法的执行时间
        System.out.println("====目标方法doSome()====");
    }

    @Override
    public String doOther(String name, Integer age) {
        System.out.println("====目标方法doOther()====");
        return "abcd";
    }

    @Override
    public String doFirst(String name, Integer age) {
        System.out.println("====业务方法doFirst()====");

        return "doFirst";
    }
}

切面类:

@Aspect
public class MyAspect {
    /**
     * @Around: 环绕通知
     *    属性:value 切入点表达式
     *    位置:在方法的定义什么
     * 特点:
     *   1.它是功能最强的通知
     *   2.在目标方法的前和后都能增强功能。
     *   3.控制目标方法是否被调用执行
     *   4.修改原来的目标方法的执行结果。 影响最后的调用结果
     */
    @Around(value = "execution(* *..SomeServiceImpl.doFirst(..))")
    public Object myAround(ProceedingJoinPoint pjp) throws Throwable {
        String name = "";
        //获取第一个参数值
        Object args [] = pjp.getArgs();
        if( args!= null && args.length > 1){
            Object arg=  args[0];
            name =(String)arg;
        }
        //实现环绕通知
        Object result = null;
        System.out.println("环绕通知:在目标方法之前,输出时间:"+ new Date());
        //1.目标方法调用
        if( "zhangsan".equals(name)){
            //符合条件,调用目标方法
            result = pjp.proceed(); //method.invoke(); Object result = doFirst();
        }
        System.out.println("环绕通知:在目标方法之后,提交事务");
        //2.在目标方法的前或者后加入功能
        //修改目标方法的执行结果, 影响方法最后的调用结果
        if( result != null){
            result = "Hello AspectJ AOP";
        }
        //返回目标方法的执行结果
        return result;
    }
}

spring配置文件:

	<!--声明目标对象-->
    <bean id="someService" class="com.suyv.spring.ba03.SomeServiceImpl" />
    <!--声明切面类对象-->
    <bean id="myAspect" class="com.suyv.spring.ba03.MyAspect" />
    <!--声明自动代理生成器-->
    <aop:aspectj-autoproxy />

测试方法:

@Test
    public void test01(){
        String config="applicationContext.xml";
        ApplicationContext ctx = new ClassPathXmlApplicationContext(config);
        //从容器中获取目标对象
        SomeService proxy = (SomeService) ctx.getBean("someService");
        //通过代理的对象执行方法,实现目标方法执行时,增强了功能
        String str = proxy.doFirst("zhangsan",20); /// myAround()
    }

测试结果:
在这里插入图片描述

4)异常通知

测试接口:

public interface SomeService {
    void doSome(String name, Integer age);
    String doOther(String name, Integer age);
    String doFirst(String name, Integer age);
    void doSecond();
}

接口实现类:

//目标类
public class SomeServiceImpl implements SomeService {
    @Override
    public void doSome(String name,Integer age) {
        //给doSome方法增加一个功能,在doSome()执行之前, 输出方法的执行时间
        System.out.println("====目标方法doSome()====");
    }

    @Override
    public String doOther(String name, Integer age) {
        System.out.println("====目标方法doOther()====");
        return "abcd";
    }

    @Override
    public String doFirst(String name, Integer age) {
        System.out.println("====业务方法doFirst()====");
        return "doFirst";
    }

    @Override
    public void doSecond() {
        System.out.println("执行业务方法doSecond()" + (10/0));
    }
}

切面类:

@Aspect
public class MyAspect {
    /**
     * @AfterThrowing:异常通知
     *     属性:1. value 切入点表达式
     *          2. throwinng 自定义的变量,表示目标方法抛出的异常对象。
     *             变量名必须和方法的参数名一样
     * 特点:
     *   1. 在目标方法抛出异常时执行的
     *   2. 可以做异常的监控程序, 监控目标方法执行时是不是有异常。
     *      如果有异常,可以发送邮件,短信进行通知
     */
    @AfterThrowing(value = "execution(* *..SomeServiceImpl.doSecond(..))",
            throwing = "ex")
    public void myAfterThrowing(Exception ex) {
        System.out.println("异常通知:方法发生异常时,执行:"+ex.getMessage());
    }
}

spring配置文件:

	<!--声明目标对象-->
    <bean id="someService" class="com.suyv.spring.ba04.SomeServiceImpl" />
    <!--声明切面类对象-->
    <bean id="myAspect" class="com.suyv.spring.ba04.MyAspect" />
    <!--声明自动代理生成器-->
    <aop:aspectj-autoproxy />

测试方法:

	@Test
    public void test01(){
        String config="applicationContext.xml";
        ApplicationContext ctx = new ClassPathXmlApplicationContext(config);
        //从容器中获取目标对象
        SomeService proxy = (SomeService) ctx.getBean("someService");
        //通过代理的对象执行方法,实现目标方法执行时,增强了功能
        proxy.doSecond();
    }

测试结果:
在这里插入图片描述

5)最终通知

测试接口:

public interface SomeService {
    void doSome(String name, Integer age);
    String doOther(String name, Integer age);
    String doFirst(String name, Integer age);
    void doSecond();
    void doThird();
}

接口实现类:

public class SomeServiceImpl implements SomeService {
    @Override
    public void doSome(String name,Integer age) {
        //给doSome方法增加一个功能,在doSome()执行之前, 输出方法的执行时间
        System.out.println("====目标方法doSome()====");
    }

    @Override
    public String doOther(String name, Integer age) {
        System.out.println("====目标方法doOther()====");
        return "abcd";
    }
    
    @Override
    public String doFirst(String name, Integer age) {
        System.out.println("====业务方法doFirst()====");
        return "doFirst";
    }

    @Override
    public void doSecond() {
        System.out.println("执行业务方法doSecond()" + (10/0));
    }

    @Override
    public void doThird() {
        System.out.println("执行业务方法doThird()"+ (10/0));
    }

}

切面类:

@Aspect
public class MyAspect {
    /**
     * @After :最终通知
     *    属性: value 切入点表达式
     *    位置: 在方法的上面
     * 特点:
     *  1.总是会执行
     *  2.在目标方法之后执行的
     */
    @After(value = "execution(* *..SomeServiceImpl.doThird(..))")
    public  void  myAfter(){
        System.out.println("执行最终通知,总是会被执行的代码");
     }
}

spring配置文件:

	<!--声明目标对象-->
    <bean id="someService" class="com.suyv.spring.ba05.SomeServiceImpl" />
    <!--声明切面类对象-->
    <bean id="myAspect" class="com.suyv.spring.ba05.MyAspect" />
    <!--声明自动代理生成器-->
    <aop:aspectj-autoproxy />

测试方法:

	@Test
    public void test01(){
        String config="applicationContext.xml";
        ApplicationContext ctx = new ClassPathXmlApplicationContext(config);
        //从容器中获取目标对象
        SomeService proxy = (SomeService) ctx.getBean("someService");
        //通过代理的对象执行方法,实现目标方法执行时,增强了功能
        proxy.doThird();
    }

测试结果:
在这里插入图片描述

1.5 CGLIB对AOP的实现

测试接口:

public interface SomeService {
    void doSome(String name,Integer age);
}

接口实现类:

public class SomeServiceImpl implements SomeService {
    @Override
    public void doSome(String name,Integer age) {
        //给doSome方法增加一个功能,在doSome()执行之前, 输出方法的执行时间
        System.out.println("====目标方法doSome()====");
    }
}

切面类:

@Aspect
public class MyAspect {
   @Before(value = "execution(public void com.suyv.spring.ba01.SomeServiceImpl.doSome(String,Integer))")
    public void myBefore(){
        //就是你切面要执行的功能代码
        System.out.println("前置通知, 切面功能:在目标方法之前输出执行时间:"+ new Date());
    }

spring配置文件:

	<!--声明目标对象-->
    <bean id="someService" class="com.suyv.spring.ba01.SomeServiceImpl" />
    <!--声明切面类对象-->
    <bean id="myAspect" class="com.suyv.spring.ba01.MyAspect" />
    <!--
       如果你期望目标类有接口,使用cglib代理
       proxy-target-class="true":告诉框架,要使用cglib动态代理
    -->
    <aop:aspectj-autoproxy proxy-target-class="true"/>

测试方法:

	@Test
    public void test01(){
        String config="applicationContext.xml";
        ApplicationContext ctx = new ClassPathXmlApplicationContext(config);
        //从容器中获取目标对象
        SomeService proxy = (SomeService) ctx.getBean("someService");
        System.out.println("proxy:"+proxy.getClass().getName());
        //通过代理的对象执行方法,实现目标方法执行时,增强了功能
        proxy.doSome("lisi",20);
    }

测试结果:
在这里插入图片描述

举报

相关推荐

0 条评论