meleto 2019-07-01
面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP(面向对象编程)的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。代理方式向目标类织入增强代码。特殊的通知,在不修改类代码的前提下,Introduction 可以在运行期为类动态地添加一些方法或Field。
一个线是一个特殊的面。
 一个切入点和一个通知,组成成一个特殊的面。
 详解如图01:

详解如图02:

动态代理
代理对象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("删除用户");
    }
}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();
        //权限校验==============
        //保存用户
        //查找用户
        //删除用户
        //更新用户
    }
}
目标类:没有实现接口
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("删除客户");
    }
}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();
          //权限校验========
          //保存客户
          //查找客户
          //更新客户
          //删除客户
      }
  }Spring AOP的底层是通过JDK动态代理或者Cglib动态代理技术为目标bean执行横向织入的。
Spring两种开发方式
除去基本的6个包,在web项目中添加aop开发的相关jar包
<?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: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/aop 
        http://www.springframework.org/schema/aop/spring-aop.xsd">
</beans>目标接口
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("删除商品");
    }
}配置目标对象
<!-- 配置目标对象:被增强的对象 --> <bean id="productDao" class="com.itzhouq.spring.demo3.ProductDaoImpl"></bean>
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();
//        保存商品
//        更新商品
//        查找商品
//        删除商品
    }
}package com.itzhouq.spring.demo3;
          /*
           * 切面类
           */
          public class MyAspectXML {
              public void checkPri() {
                  System.out.println("权限校验。。。");
              }
          }<!-- 将切面类交给Spring管理 -->
      <bean id="myAspect" class="com.itzhouq.spring.demo3.MyAspectXML"></bean><!-- 通过AOP的配置完成对目标类产生代理 -->
    <aop:config>
        <!-- 表达式配置哪些类的哪些方法需要进行增强 -->
        <aop:pointcut expression="execution(* com.itzhouq.spring.demo3.ProductDaoImpl.save(..))" id="pointcut1"/>
        <!-- 配置切面 -->
        <aop:aspect ref="myAspect">
            <aop:before method="checkPri" pointcut-ref="pointcust1"/>
        </aop:aspect>
    </aop:config>@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();
//        权限校验。。。
//        保存商品
//        更新商品
//        查找商品
//        删除商品
    }
}配置
<!-- 配置切面 -->
<aop:aspect ref="myAspect">
    <aop:before method="checkPri" pointcut-ref="pointcut1"/>
</aop:aspect>比如在切面类中加入参数JoinPoint
public class MyAspectXML {
    
    public void checkPri(JoinPoint joinPoint) {
        System.out.println("权限校验。。。"+joinPoint);
    }
}测试打印的效果
// 权限校验。。。execution(void com.itzhouq.spring.demo3.ProductDao.save()) // 保存商品 // 更新商品 // 查找商品 // 删除商品
配置后置通知
<!-- 通过AOP的配置完成对目标类产生代理 -->
    <aop:config>
        <!-- 表达式配置哪些类的哪些方法需要进行增强 -->
        <aop:pointcut expression="execution(* com.itzhouq.spring.demo3.ProductDaoImpl.save(..))" id="pointcut1"/>
        <aop:pointcut expression="execution(* com.itzhouq.spring.demo3.ProductDaoImpl.delete(..))" id="pointcut2"/>
        <!-- 配置切面 -->
        <aop:aspect ref="myAspect">
            <!-- 前置通知============= -->
            <aop:before method="checkPri" pointcut-ref="pointcut1"/>
            <!-- 后置通知============= -->
            <aop:after-returning method="writeLog" pointcut-ref="pointcut2" returning="result"/>
        </aop:aspect>
    </aop:config>/*
       * 后置通知演示
       */
      public void writeLog(Object result) {
          System.out.println("日志记录======="+result);
      }测试
权限校验。。。execution(void com.itzhouq.spring.demo3.ProductDao.save()) // 保存商品 // 更新商品 // 查找商品 // 删除商品 // 日志记录=======kkk
/**
       * 性能监控
       * @throws Throwable 
       */
      public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
          System.out.println("环绕前通知===========");
          Object obj = joinPoint.proceed();//这一步相当于执行目标程序
          System.out.println("环绕后通知=========");
          return obj;
      }配置环绕通知
<aop:config>
        <!-- 表达式配置哪些类的哪些方法需要进行增强 -->
        <aop:pointcut expression="execution(* com.itzhouq.spring.demo3.ProductDaoImpl.save(..))" id="pointcut1"/>
        <aop:pointcut expression="execution(* com.itzhouq.spring.demo3.ProductDaoImpl.delete(..))" id="pointcut2"/>
        <aop:pointcut expression="execution(* com.itzhouq.spring.demo3.ProductDaoImpl.update(..))" id="pointcut3"/>
        <!-- 配置切面 -->
        <aop:aspect ref="myAspect">
            <!-- 前置通知============= -->
            <aop:before method="checkPri" pointcut-ref="pointcut1"/>
            <!-- 后置通知============= -->
            <aop:after-returning method="writeLog" pointcut-ref="pointcut2" returning="result"/>
            <!-- 环绕通知 -->
            <aop:around method="around" pointcut-ref="pointcut3"/>
        </aop:aspect>
    </aop:config>测试
// 权限校验。。。execution(void com.itzhouq.spring.demo3.ProductDao.save()) // 保存商品 // 环绕前通知=========== // 更新商品 // 环绕后通知========= // 查找商品 // 删除商品 // 日志记录=======kkk
在find方法上模拟一个异常
@Override
    public void find() {
        System.out.println("查找商品");
        int i = 1 / 0;
    }/*
       * 异常抛出通知
       */
      public void afterThrowing(Throwable ex) {
          System.out.println("异常抛出通知=======" + ex);
      }配置异常通知
<aop:config>
        <!-- 表达式配置哪些类的哪些方法需要进行增强 -->
        <aop:pointcut expression="execution(* com.itzhouq.spring.demo3.ProductDaoImpl.save(..))" id="pointcut1"/>
        <aop:pointcut expression="execution(* com.itzhouq.spring.demo3.ProductDaoImpl.delete(..))" id="pointcut2"/>
        <aop:pointcut expression="execution(* com.itzhouq.spring.demo3.ProductDaoImpl.update(..))" id="pointcut3"/>
        <aop:pointcut expression="execution(* com.itzhouq.spring.demo3.ProductDaoImpl.find(..))" id="pointcut4"/>
        <!-- 配置切面 -->
        <aop:aspect ref="myAspect">
            <!-- 前置通知============= -->
            <aop:before method="checkPri" pointcut-ref="pointcut1"/>
            <!-- 后置通知============= -->
            <aop:after-returning method="writeLog" pointcut-ref="pointcut2" returning="result"/>
            <!-- 环绕通知 -->
            <aop:around method="around" pointcut-ref="pointcut3"/>
            <!-- 异常抛出通知 -->
            <aop:after-throwing method="afterThrowing" pointcut-ref="pointcut4" throwing="ex"/>
        </aop:aspect>
    </aop:config>测试
// 权限校验。。。execution(void com.itzhouq.spring.demo3.ProductDao.save()) // 保存商品 // 环绕前通知=========== // 更新商品 // 环绕后通知========= // 查找商品 // 异常抛出通知=======java.lang.ArithmeticException: / by zero
/*
       * 最终通知:相当于finally代码块中的内容
       */
      public void after() {
          System.out.println("最终通知=========");
      }配置最终通知
<!-- 配置最终通知 -->
            <aop:after method="after" pointcut-ref="pointcut4"/>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:
        <aop:pointcut expression="execution(* com.itheima.*WithCommit.*(..)) || 
                                  execution(* com.itheima.*Service.*(..))" id="myPointCut"/>
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')