资讯专栏INFORMATION COLUMN

Spring笔记03_AOP

blair / 1965人阅读

摘要:介绍什么是在软件业,为的缩写,意为面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。切面是切入点和通知引介的结合。切面类权限校验。。。

1. AOP 1.1 AOP介绍 1.1.1 什么是AOP

在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP(面向对象编程)的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

AOP采取横向抽取机制,取代了传统纵向继承体系重复性代码。如下图所示:

经典应用:事务管理、性能监视、安全检查、缓存 、日志等。

Spring AOP使用纯Java实现,不需要专门的编译过程和类加载器,在运行期通过代理方式向目标类织入增强代码。

AspectJ是一个基于Java语言的AOP框架,从Spring2.0开始,Spring AOP引入对Aspect的支持,AspectJ扩展了Java语言,提供了一个专门的编译器,在编译时提供横向代码的织入。

1.1.2 AOP实现原理

aop底层将采用代理机制进行实现

接口+实现类时:Spring采用JDK的动态代理Proxy

只有实现类时:Spring采用cglib字节码增强。这种底层属于继承增强。

1.1.3 AOP术语【掌握】

Target :目标类,需要被代理的类。本例中如:UserDao

Joinpoint(连接点) :所谓连接点是指那些可能被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点。本例中如:UserDao的所有的方法

PointCut 切入点 :所谓切入点是指我们要对哪些Joinpoint进行拦截,即已经被增强的连接点。例如:save()

Advice :通知/增强,增强的代码。例如:checkPri()
所谓通知是指拦截到Joinpoint之后所要做的事情就是通知,通知分为前置通知、后置通知、异常通知、最终通知、环绕通知(即切面要完成的功能)。

Weaving(织入) :是指把通知/增强advice应用到目标对象target来创建新的代理对象proxy的过程。
spring采用动态代理织入,而AspectJ采用编译期织入和类装在期织入。

Proxy :代理类,一个类被AOP织入增强后,就产生一个结果代理类。

Aspect(切面) : 是切入点Pointcut和通知Advice(引介)的结合。

Introduction(引介) :引介是一种特殊的通知,在不修改类代码的前提下,Introduction 可以在运行期为类动态地添加一些方法或Field。

小结:

一个线是一个特殊的面。
一个切入点和一个通知,组成成一个特殊的面。
详解如图01:

详解如图02:

1.2 AOP的底层实现(了解)

动态代理

JDK动态代理:只能对实现了接口的类产生代理

Cglib动态代理(第三方代理技术):对没有实现接口的类产生代理对象,生成子类对象。

代理知识点参考:https://www.cnblogs.com/itzho...

1.2.1 JDK动态代理

准备工作:新建web项目,不需要导包

代理对象UserDao

package com.itzhouq.spring.demo1;

public interface UserDao {
    public void save();
    public void update();
    public void find();
    public void delete();
}

实现类

package com.itzhouq.spring.demo1;

public class UserDaoImpl implements UserDao {

    @Override
    public void save() {
        System.out.println("保存用户");
    }

    @Override
    public void update() {
        System.out.println("更新用户");
    }

    @Override
    public void find() {
        System.out.println("查找用户");
    }

    @Override
    public void delete() {
        System.out.println("删除用户");
    }

}

使用JDK产生UserDao的代理类

  package com.itzhouq.spring.demo1;
  
  import java.lang.reflect.InvocationHandler;
  import java.lang.reflect.Method;
  import java.lang.reflect.Proxy;
  
  /*
   * 使用JDK动态代理对UserDao产生代理
   */
  public class JDKProxy implements InvocationHandler {
      // 将被增强的对象传递到代理总
      private UserDao userDao;
      public JDKProxy(UserDao userDao) {
          this.userDao = userDao;
      }
      /*
       * 产生UserDao代理的方法
       */
      public UserDao createProxy() {
          UserDao userDaoProxy = (UserDao) Proxy.newProxyInstance(
                  userDao.getClass().getClassLoader(),
                  userDao.getClass().getInterfaces(),
                  this);
          return userDaoProxy;
      }
      @Override
      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
          // 判断方法名是不是save
          if("save".equals(method.getName())) {
              // 增强
              System.out.println("权限校验==============");
              return method.invoke(userDao, args);
              
          }
          return method.invoke(userDao, args);
      }
  }

测试类

package com.itzhouq.spring.demo1;

import org.junit.Test;

public class SpringDemo1 {
    
    @Test
    //JDK动态代理
    public void test1() {
        UserDao userDao = new UserDaoImpl();
        // 创建代理
        UserDao proxy = new JDKProxy(userDao).createProxy();
        proxy.save();
        proxy.find();
        proxy.delete();
        proxy.update();
        //权限校验==============
        //保存用户
        //查找用户
        //删除用户
        //更新用户
    }
}

1.2.2 Cglib动态代理

Cglib是第三方开源代码生成类库,动态添加类的属性和方法

cglib的运行原理:在运行时创建目标类的子类从而对目标类进行增强。

因为Spring核心包中包含了cglib的包,所以引入Spring的4+2必备包就可以使用Cglib了

目标类:没有实现接口

package com.itzhouq.spring.demo2;

public class CustomerDao {
    public void save() {
        System.out.println("保存客户");
    }
    public void update() {
        System.out.println("更新客户");
    }
    public void find() {
        System.out.println("查找客户");
    }
    public void delete() {
        System.out.println("删除客户");
    }
}

Cglib代理类

 package com.itzhouq.spring.demo2;
 
 import java.lang.reflect.Method;
 
 import org.springframework.cglib.proxy.Enhancer;
 import org.springframework.cglib.proxy.MethodInterceptor;
 import org.springframework.cglib.proxy.MethodProxy;
 
 /*
  * Cglib动态代理
  */
 public class CglibProxy implements MethodInterceptor {
     
     private CustomerDao customerDao;
 
     public CglibProxy(CustomerDao customerDao) {
         this.customerDao = customerDao;
     }
     
     // 使用Cglib产生代理的方法
     public CustomerDao createProxy() {
         // 1. 创建cglib的核心类对象
         Enhancer enhancer = new Enhancer();
         // 2. 设置父类
         enhancer.setSuperclass(customerDao.getClass());
         // 3. 设置回调(类似于InvocationHandler对象)
         enhancer.setCallback(this);
         // 4. 创建代理对象
         CustomerDao proxy = (CustomerDao) enhancer.create();
         return proxy;
     }
 
     @Override
     public Object intercept(Object proxy, Method method, Object[] arg, MethodProxy methodProxy) 
             throws Throwable {
         // 判断方法是否为save
         if("save".equals(method.getName())) {
             // 增强
             System.out.println("权限校验========");
             return methodProxy.invokeSuper(proxy, arg);
         }
         return methodProxy.invokeSuper(proxy, arg);
     }
 }

测试

  package com.itzhouq.spring.demo2;
  
  import org.junit.Test;
  
  public class SpringDemo2 {
      /*
       * cglib的测试
       */
      
      @Test
      public void test1() {
          CustomerDao customerDao = new CustomerDao();
          CustomerDao proxy = new CglibProxy(customerDao).createProxy();
          proxy.save();
          proxy.find();
          proxy.update();
          proxy.delete();
          //权限校验========
          //保存客户
          //查找客户
          //更新客户
          //删除客户
      }
  }
1.2.3 总结

Spring在运行期,生成动态代理对象,不需要特殊的编译器。

Spring AOP的底层是通过JDK动态代理或者Cglib动态代理技术为目标bean执行横向织入的。

Spring会优先使用Spring使用JDK代理方式进行代理

若目标对象没有实现任何接口,Spring容器会使用Cglib动态代理

标记为final的方法不能被代理,因为无法进行覆盖

Cglib动态代理,是针对的目标类产生子类,所以目标类不能被final修饰。

Spring只支持方法连接点,不提供属性连接。

2. Spring的AOP的开发(AspectJ的XML方式) 2.1 Spring的AOP简介

AOP思想最早是由AOP联盟组织提出。Spring是使用这种思想最好的框架。

Spring的AOP有自己的实现方式(非常繁琐)。AspectJ是一个AOP框架,Spring引入AspectJ作为自身AOP的开发

Spring两种开发方式

Spring传统方式(弃用)。

Spring基于AspectJ的AOP开发方式(使用)。

2.2 AOP入门开发 2.2.1 准备工程和jar包

除去基本的6个包,在web项目中添加aop开发的相关jar包

AOP联盟规范包:..spring相关依赖包spring-framework-3.0.2.RELEASE-dependenciesorg.aopalliancecom.springsource.org.aopalliance1.0.0com.springsource.org.aopalliance-1.0.0.jar

AOP包:..spring-framework-4.2.4.RELEASE-distspring-framework-4.2.4.RELEASElibsspring-aop-4.2.4.RELEASE.jar

AspectJ包:..spring相关依赖包spring-framework-3.0.2.RELEASE-dependenciesorg.aspectjcom.springsource.org.aspectj.weaver1.6.8.RELEASEcom.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar

Spring和ASpectJ整合包:..spring-framework-4.2.4.RELEASE-distspring-framework-4.2.4.RELEASElibsspring-aspects-4.2.4.RELEASE.jar

Spring整合JUnit单元测试包:..spring-framework-4.2.4.RELEASE-distspring-framework-4.2.4.RELEASElibsspring-test-4.2.4.RELEASE.jar

2.2.2 引入Spring的配置文件

引入aop的约束

约束的位置:../spring-framework-4.2.4.RELEASE-dist/spring-framework-4.2.4.RELEASE/docs/spring-framework-reference/html/xsd-configuration.html 40.2.7 the aop schema



2.2.3 编写目标类并完成配置

目标接口

package com.itzhouq.spring.demo3;

public interface ProductDao {
    public void save();
    public void update();
    public void find();
    public void delete();
}

目标类

package com.itzhouq.spring.demo3;

public class ProductDaoImpl implements ProductDao {

    @Override
    public void save() {
        System.out.println("保存商品");
    }

    @Override
    public void update() {
        System.out.println("更新商品");
    }

    @Override
    public void find() {
        System.out.println("查找商品");
    }

    @Override
    public void delete() {
        System.out.println("删除商品");
    }

}

配置目标对象


2.2.4 测试类
package com.itzhouq.spring.demo3;

import javax.annotation.Resource;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

/*
 * AOP入门
 */
@RunWith(SpringJUnit4ClassRunner.class)    //这两个注释就是Spring整合了JUnit单元测试
@ContextConfiguration("classpath:applicationContext.xml")
public class SpringDemo3 {
    
    //注入productDao
    @Resource(name="productDao")
    private ProductDao productDao;
    
    @Test
    public void test1() {
        productDao.save();
        productDao.update();
        productDao.find();
        productDao.delete();
//        保存商品
//        更新商品
//        查找商品
//        删除商品
    }
}

下面需要对save()方法增强

2.2.5 编写一个切面类

切面:多个通知和多个切入点的组合。

 package com.itzhouq.spring.demo3;
          /*
           * 切面类
           */
          public class MyAspectXML {
              public void checkPri() {
                  System.out.println("权限校验。。。");
              }
          }
2.2.6 将切面类交给Spring

    
      

2.2.7 通过AOP的配置实现动态代理

    
        
        
        
        
            
        
    
2.2.8 测试类中运行结果,save实现了增强
@RunWith(SpringJUnit4ClassRunner.class)    //这两个注释就是Spring整合了JUnit单元测试
@ContextConfiguration("classpath:applicationContext.xml")
public class SpringDemo3 {
    
    //注入productDao
    @Resource(name="productDao")
    private ProductDao productDao;
    
    @Test
    public void test1() {
        productDao.save();
        productDao.update();
        productDao.find();
        productDao.delete();
//        权限校验。。。
//        保存商品
//        更新商品
//        查找商品
//        删除商品

    }
}
2.3 Spring中的通知类型 2.3.1 前置通知

配置



    

在目标方法执行之前进行操作

可以获得切入点的信息

比如在切面类中加入参数JoinPoint

public class MyAspectXML {
    
    public void checkPri(JoinPoint joinPoint) {
        System.out.println("权限校验。。。"+joinPoint);
    }
}

测试打印的效果

//        权限校验。。。execution(void com.itzhouq.spring.demo3.ProductDao.save())
//        保存商品
//        更新商品
//        查找商品
//        删除商品

2.3.2 后置通知:

在目标方法之后操作,可以获得返回值

修改接口ProductDao和实现类ProductDaoImpl类的delete方法的返回值为String

配置后置通知


    
        
        
        
        
        
            
            
            
            
        
    

在后置切面类中添加记录日志的方法

 /*
       * 后置通知演示
       */
      public void writeLog(Object result) {
          System.out.println("日志记录======="+result);
      }

测试

权限校验。。。execution(void com.itzhouq.spring.demo3.ProductDao.save())
//        保存商品
//        更新商品
//        查找商品
//        删除商品
//        日志记录=======kkk

2.3.3 环绕通知:

在目标方法执行之前 和之后进行操作

环绕通知可以组织目标方法的执行

举例:需求---在修改方法update前后添加性能监控

在切面类中添加一个方法用来测试环绕通知

/**
       * 性能监控
       * @throws Throwable 
       */
      public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
          System.out.println("环绕前通知===========");
          Object obj = joinPoint.proceed();//这一步相当于执行目标程序
          System.out.println("环绕后通知=========");
          return obj;
      }

配置环绕通知


        
        
        
        
        
        
            
            
            
            
            
            
        
    

测试

//        权限校验。。。execution(void com.itzhouq.spring.demo3.ProductDao.save())
//        保存商品
//        环绕前通知===========
//        更新商品
//        环绕后通知=========
//        查找商品
//        删除商品
//        日志记录=======kkk

2.3.4 异常抛出通知:

在程序抛出异常时候进行操作,可以得到异常信息

在find方法上模拟一个异常

@Override
    public void find() {
        System.out.println("查找商品");
        int i = 1 / 0;
    }

切面类中添加一个方法用于测试异常抛出通知

 /*
       * 异常抛出通知
       */
      public void afterThrowing(Throwable ex) {
          System.out.println("异常抛出通知=======" + ex);
      }

配置异常通知


        
        
        
        
        
        
        
            
            
            
            
            
            
            
            
        
    

测试

//        权限校验。。。execution(void com.itzhouq.spring.demo3.ProductDao.save())
//        保存商品
//        环绕前通知===========
//        更新商品
//        环绕后通知=========
//        查找商品
//        异常抛出通知=======java.lang.ArithmeticException: / by zero

2.3.5 最终通知:

在切面类中添加方法测试最终通知

/*
       * 最终通知:相当于finally代码块中的内容
       */
      public void after() {
          System.out.println("最终通知=========");
      }

配置最终通知


            

测试

无论是否有异常,最终通知都会执行。

2.3.6 引介通知(不用会) 2.4 切入点表达式【掌握】
1.execution()  用于描述方法【掌握】
    语法:execution(修饰符  返回值  包.类.方法名(参数) throws异常)
        修饰符,一般省略
            public      公共方法
            *           任意
        返回值,不能省略
            void        返回没有值
            String      返回值字符串
            *           任意
        包,[可以省略]
            com.itheima.crm                 固定的包
            com.itheima.crm.*.service       crm包下面的任意子包,固定目录service(例如:com.itheima.crm.staff.service)
            com.itheima.crm..               crm包下面的所有子包(含自己)
            com.itheima.crm.*.service..     crm包下面的任意子包,固定目录service,service目录任意包(含自己)
        类,[可以省略]
            UserServiceImpl                 指定的类
            *Impl                           以Impl结尾的类
            User*                           以User开头的类
            *                               任意的类
        方法名,不能省略
            addUser                         固定的方法名
            add*                            以add开头的方法名
            *Do                             以Do结尾的方法名
            *                               任意的方法名
        (参数)
            ()                              无参
            (int)                           一个整型
            (int, int)                      两个整型
            (..)                            参数任意
        throws,[可以省略],一般省略。

    综合案例1:
        execution(* com.itheima.crm.*.service..*.*(..))
    综合案例2:
        

2.within:匹配包或子包中的方法(了解)
    within(com.itheima.aop..*)
3.this:匹配实现了接口的代理对象中的方法(了解)
    this(com.itheima.aop.user.UserDAO)
4.target:匹配实现了接口的目标对象中的方法(了解)
    target(com.itheima.aop.user.UserDAO)
5.args:匹配参数格式符合标准的方法(了解)
    args(int, int)
6.bean(id):对指定的bean所有的方法(了解)
    bean("userServiceId")

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/74401.html

相关文章

  • Spring笔记04_AOP注解开发_模板_事务

    摘要:后置增强周杰伦环绕通知在切面类中添加以下方法环绕通知环绕前增强环绕前增强测试前置增强保存订单。。。不使用事务管理。 1. Spring基于AspectJ的注解的AOP开发 1. 1 SpringAOP的注解入门 创建项目,导入jar包 需要导入Spring基础包4+2 需要导入AOP联盟包、AspectJ包、Spring整合Aspect包Spring-aop包 Spring整合单...

    youkede 评论0 收藏0
  • 慕课网_《探秘Spring AOP》学习总结

    时间:2017年09月03日星期日说明:本文部分内容均来自慕课网。@慕课网:http://www.imooc.com 教学源码:https://github.com/zccodere/s...学习源码:https://github.com/zccodere/s... 第一章:课程介绍 1-1 面向切面 课程章节 概览 AOP使用 AOP原理 AOP开源运用 课程实战 课程总结 面向切面编程是一种...

    Tony_Zby 评论0 收藏0
  • Spring框架学习笔记(一):官方文档介绍,IoC与AOP概念学习

    摘要:构造函数注入通过调用类的构造函数,将接口实现类通过构造函数变量传入。而在中,其使用横切技术,将这类代码从原属的封装对象中提取出来,封装到一个可重用模块中,称为。 最近实习用到Spring的开发框架,但是之前没有接触过,因此希望利用网上的资源来学习以下。 Spring官方给出了非常全面的介绍,非常适合我这种完全的小白……在这一系列学习中,我阅读的主要资源是5.1.2 Reference ...

    mindwind 评论0 收藏0
  • Spring Boot 2.x(十一):AOP实战--打印接口日志

    摘要:接口日志有啥用在我们日常的开发过程中,我们可以通过接口日志去查看这个接口的一些详细信息。在切入点返回内容之后切入内容可以用来对处理返回值做一些加工处理。 接口日志有啥用 在我们日常的开发过程中,我们可以通过接口日志去查看这个接口的一些详细信息。比如客户端的IP,客户端的类型,响应的时间,请求的类型,请求的接口方法等等,我们可以对这些数据进行统计分析,提取出我们想要的信息。 怎么拿到接口...

    Youngdze 评论0 收藏0
  • Spring入门IOC和AOP学习笔记

    摘要:入门和学习笔记概述框架的核心有两个容器作为超级大工厂,负责管理创建所有的对象,这些对象被称为。中的一些术语切面切面组织多个,放在切面中定义。 Spring入门IOC和AOP学习笔记 概述 Spring框架的核心有两个: Spring容器作为超级大工厂,负责管理、创建所有的Java对象,这些Java对象被称为Bean。 Spring容器管理容器中Bean之间的依赖关系,使用一种叫做依赖...

    wenyiweb 评论0 收藏0

发表评论

0条评论

blair

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<