天天看點

尚矽谷學習筆記 AOP尚矽谷AOP

尚矽谷AOP

AOP(概念)

1.什麼是AOP

(1)面向切面程式設計(方面),利用 AOP 可以對業務邏輯的各個部分進行隔離,進而使得 業務邏輯各部分之間的耦合度降低,提高程式的可重用性,同時提高了開發的效率。

(2)通俗描述:不通過修改源代碼方式,在主幹功能裡面添加新功能

(3)使用登入例子說明 AOP

AOP(底層原理)

  1. AOP底層使用動态代理

    (1)有兩種情況動态代理

    第一種 有接口情況,使用JDK動态代理

    • 建立接口實作類的代理對象,增強類的方法
    第二種 沒有接口情況,使用CGLIB動态代理
    • 建立目前類子類代理對象,增強類的方法
  2. AOP(JDK動态代理)

    第一步 使用JDK動态代理,使用Proxy類方法建立對象

    (1)調用newProxyInstance方法

    • class類 Proxy() 中 (查api)

      static Object

      newProxyInstance(ClassLoader loader, 類<?>[] interfaces, InvocationHandler h)

      傳回指定接口的代理類的執行個體,該接口将方法調用分派給指定的調用處理程式。
    • 第一個參數,類加載器
    • 第一個參數,增強方法所在的類,這個類實作的接口,支援多個接口
    • 第一個參數,實作這個接口InvocationHandler,建立代理對象,寫增強的方法;

    第二步:編寫JDK動态代碼

    (1)建立接口,定義方法

    package com.atguigu.spring5.Dao;
    
    public interface UserDao {
    
        public  int add(int a, int b);
        public String  update(String id);
    }
    
               
    (2)建立接口實作類,實作方法
    package com.atguigu.spring5.Dao.impl;
    
    import com.atguigu.spring5.Dao.UserDao;
    
    public class UserDaoImpl implements UserDao {
        @Override
        public int add(int a, int b) {
            return a+b;
        }
    
        @Override
        public String update(String id) {
            return id;
        }
    }
    
               
    (3)使用Proxy類建立接口的代理對象
    • 建立代理對象的兩種方式
      //匿名内部類
      Class[] interfaces={UserDao.class};
              Proxy.newProxyInstance(JDKProxy.class.getClassLoader()
                      , interfaces, new InvocationHandler() {
                          @Override
                          public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                              return null;
                          }
                      });
                 
      //建立對象實作接口
      package com.atguigu.spring5.test;
      
      import com.atguigu.spring5.Dao.UserDao;
      
      import java.lang.reflect.InvocationHandler;
      import java.lang.reflect.Method;
      import java.lang.reflect.Proxy;
      
      public class JDKProxy {
          public static void main(String[] args) {
              //建立是實作類類的代理對象
              Class[] interfaces={UserDao.class};
              Proxy.newProxyInstance(JDKProxy.class.getClassLoader()
                      , interfaces, new UserDaoProxy() );
          }
      }
      //建立代理對象代碼
      class UserDaoProxy implements InvocationHandler{
          //寫增強的邏輯
          @Override
          public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
              return null;
          }
      }
      
                 
    • 代碼
      package com.atguigu.spring5.test;
      
      import com.atguigu.spring5.Dao.UserDao;
      import com.atguigu.spring5.Dao.impl.UserDaoImpl;
      
      import java.lang.reflect.InvocationHandler;
      import java.lang.reflect.Method;
      import java.lang.reflect.Proxy;
      import java.util.Arrays;
      
      public class JDKProxy {
          public static void main(String[] args) {
              //建立是實作類類的代理對象
              Class[] interfaces={UserDao.class};
              UserDaoImpl userDao=new UserDaoImpl();
              UserDao dao =(UserDao) Proxy.newProxyInstance(JDKProxy.class.getClassLoader()
                      , interfaces, new UserDaoProxy(userDao));
              int result=dao.add(1,2);
              System.out.println("result:"+result);
          }
      }
      //建立代理對象代碼
      class UserDaoProxy implements InvocationHandler{
          //1 把建立的是誰的代理對象,把誰傳遞過來
          private Object obj;
          //有參數構造傳遞
          public UserDaoProxy(Object obj){
              this.obj=obj;
          }
      
      
      
      
          //寫增強的邏輯
          @Override
          public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
              //方法之前做一個處理
              System.out.println("方法之前執行..."+method.getName()+":傳遞的參數.."+ Arrays.toString(args));
              //被增強的方法執行
              Object res=method.invoke(obj,args);
              //方法之後做一個處理
              System.out.println("方法之後行..."+obj);
      
              return res;
          }
      }
      
                 
  3. AOP術語

    (1)連接配接點

    • 類的那些方法可以被增強,這些方法為連接配接點
    (2)切入點
    • 實際被真正增強的方法,稱為切入點
    (3)通知(增強):
    • 實際增強的邏輯部分稱為通知(增強)
    • 通知有多種類型

      (1)前置通知

      (2)後置通知

      (3)環繞通知

      (4)異常通知

      (5)最終通知

    (4)切面
    • 把通知應用到切入點過程

    AOP操作(準備)

    1. spring架構一般都是基于AspectJ實作AOP操作

      (1)什麼是AspectJ

      *AspectJ不是Spring組成部分,獨立AOP架構,一般吧AspectJ和Spring架構一起使用,進行AOP操作

    2. 基于AspectJ實作AOP操作

      (1)基于xml配置檔案實作

      (2)基于注解方式實作(使用)

    3. 在項目工程裡面引入AOP相關依賴
    4. 切入點表達式

      (1)切入點表達式的作用:知道對哪個類裡面的哪個方法進行增強

      (2)文法結構:

      • execution([權限修飾符] [ 傳回類型] [ 類的全路徑] [方法名稱] ([參數清單]) )
      • 舉例1:對com.atguigu.dao.BookDao類裡面的add進行增強

        execution(*com.atguigu.dao.BookDao.add(…) )

      • 舉例2:對com.atguigu.dao.BookDao類裡面的所有方法進行增強

        execution(com.atguigu.dao.BookDao.(…) )

      • 舉例三:對 com.atguigu.dao 包裡面所有類,類裡面所有方法進行增強

        execution(* com.atguigu.dao.* .* (…))

    AOP操作(AspectJ注解)

    1. 建立類,在類裡面定義方法
      package com.atguigu.spring5.aopanno;
      //被增強類
      public class User {
          public void  add(){
              System.out.println("add.........");
          }
      
      }
      
                 
    2. 建立增強類(編寫你增強的邏輯)

      (1)在增強類裡面,建立方法,讓不同的方法代表不同的通知類型

      //增強類
      public class UserProxy {
          //前置通知
          public  void  before(){
              System.out.println("before.......");
          }
      }
                 
    3. 進行通知的配置
      1. (1)在spring 配置檔案中,開啟注解掃描
        <?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:p="http://www.springframework.org/schema/p"
               xmlns:context="http://www.springframework.org/schema/context"
               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-beans.xsd
                                    http://www.springframework.org/schema/context
                                    http://www.springframework.org/schema/context/spring-aop.xsd">
        <!--開啟注解的掃描-->
            <context:component-scan base-package="com.atguigu.spring5.aopanno"></context:component-scan>
        </beans>
                   
        (2)使用注解建立User和UserProxy對象
        package com.atguigu.spring5.aopanno;
        
        import org.springframework.stereotype.Component;
        
        //被增強類
        @Component
        public class User {
            public void  add(){
                System.out.println("add.........");
            }
        
        }
        
                   
        package com.atguigu.spring5.aopanno;
        
        import org.springframework.stereotype.Component;
        
        //增強類
        @Component
        public class UserProxy {
            //前置通知
            public  void  before(){
                System.out.println("before.......");
            }
        }
        
                   
        (3)在增強類上面添加注解@Aspect
        package com.atguigu.spring5.aopanno;
        
        import org.aspectj.lang.annotation.Aspect;
        import org.springframework.stereotype.Component;
        
        //增強類
        @Component
        @Aspect //生成代理對象
        public class UserProxy {
            //前置通知
            public  void  before(){
                System.out.println("before.......");
            }
        }
        
                   
        (4)在spring配置檔案開啟生成代理對象
        <?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:p="http://www.springframework.org/schema/p"
               xmlns:context="http://www.springframework.org/schema/context"
               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-beans.xsd
                                    http://www.springframework.org/schema/context
                                    http://www.springframework.org/schema/context/spring-aop.xsd">
        <!--開啟注解的掃描-->
            <context:component-scan base-package="com.atguigu.spring5.aopanno"></context:component-scan>
        <!--開啟Aspectj代理對象-->
            <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
        </beans>
                   
    4. 配置不同類型的通知

      (1)在增強類的裡面,在作為通知方法上添加通知類型注解,使用切入點表達式

      package com.atguigu.spring5.aopanno;
      
      import org.aspectj.lang.ProceedingJoinPoint;
      import org.aspectj.lang.annotation.*;
      import org.springframework.stereotype.Component;
      
      
      //增強類
      @Component
      @Aspect //生成代理對象
      public class UserProxy {
          //前置通知
          //before注解作為表示為前置通知
          @Before(value="execution(* com.atguigu.spring5.aopanno.User.add(..))")
          public  void  before(){
              System.out.println("before.......");
          }
          @After(value="execution(* com.atguigu.spring5.aopanno.User.add(..))")
          public  void  after(){
              System.out.println("after.......");
          }
          @AfterReturning(value="execution(* com.atguigu.spring5.aopanno.User.add(..))")
          public  void  afterReturning(){
              System.out.println("afterReturning.......");
          }
          //異常通知
          @AfterThrowing(value="execution(* com.atguigu.spring5.aopanno.User.add(..))")
          public  void  afterThrowing(){
              System.out.println("AfterThrowing.......");
          }
          //環繞通知.
          @Around(value="execution(* com.atguigu.spring5.aopanno.User.add(..))")
          public  void  around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
              System.out.println("環繞之前.......");
              //被增強的方法執行
              proceedingJoinPoint.proceed();
              System.out.println("環繞之後.......");
          }
      }
      
                 
    5. 相同的切入點抽取
      package com.atguigu.spring5.aopanno;
      
      import org.aspectj.lang.ProceedingJoinPoint;
      import org.aspectj.lang.annotation.*;
      import org.springframework.stereotype.Component;
      
      
      //增強類
      @Component
      @Aspect //生成代理對象
      public class UserProxy {
          //相同的切入點進行抽取
          @Pointcut(value="execution(* com.atguigu.spring5.aopanno.User.add(..))" )
          public void pointdemo(){
      
          }
      
      
          //前置通知
          //before注解作為表示為前置通知
          @Before(value="pointdemo()")
          public  void  before(){
              System.out.println("before.......");
          }
          @After(value="pointdemo()")
          public  void  after(){
              System.out.println("after.......");
          }
          @AfterReturning(value="pointdemo()")
          public  void  afterReturning(){
              System.out.println("afterReturning.......");
          }
          //異常通知
          @AfterThrowing(value="pointdemo()")
          public  void  afterThrowing(){
              System.out.println("AfterThrowing.......");
          }
          //環繞通知.
          @Around(value="pointdemo()")
          public  void  around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
              System.out.println("環繞之前.......");
              //被增強的方法執行
              proceedingJoinPoint.proceed();
              System.out.println("環繞之後.......");
          }
      }
      
                 
    6. 有多個增強類對同一個方法進行增強,可以設定增強類的優先級

      (1)在增強類上面添加注解@Order(數字類型值),數字類型越小,優先級越高

      @Component
      @Aspect
      @Order(1)
      public class PersonProxy {
                 
    7. 完全注解開發

      (1)建立配置類,不需要建立xml配置檔案

      @Configuration
      @ComponentScan(basePackages = {"com.atguigu"})
      @EnableAspectJAutoProxy(proxyTargetClass = true)
      public class configAop {
                 

    AOP操作(AspectJ配置檔案)

    1. 建立兩個類,增強類和被增強類,建立方法
    2. 在spring配置檔案中建立兩個類對象
      <!--建立兩個類的對象-->
          <bean id="book" class="com.atguigu.spring5.aopxml.Book"></bean>
          <bean id="bookProxy" class="com.atguigu.spring5.aopxml.BookProxy"></bean>
                 
    3. 在spring配置檔案中配置切入點
    <!--配置 aop 增強-->
    <aop:config>
     <!--切入點-->
     <aop:pointcut id="p" expression="execution(*
    com.atguigu.spring5.aopxml.Book.buy(..))"/>
     <!--配置切面-->
     <aop:aspect ref="bookProxy">
     <!--增強作用在具體的方法上-->
     <aop:before method="before" pointcut-ref="p"/>
     </aop:aspect>