欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Proxy實現(xiàn)AOP切面編程案例

 更新時間:2020年08月20日 09:51:45   作者:三朵耳朵  
這篇文章主要介紹了Proxy實現(xiàn)AOP切面編程案例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧

通過JDK的Proxy代理實現(xiàn)對業(yè)務類做簡單的AOP實現(xiàn)

接口:UserService 包含的方法為切入點,會被代理攔截

類:UserServiceImpl 實現(xiàn)UserService接口

類:UserServiceFactory 工廠模式生成動態(tài)代理

類:MyAspect 切面類,實現(xiàn)對切入點的操作

UserService

public interface UserService {
  //切面: 需要被攔截的方法
  public void addUser();
  public void updateUser();
  public int deleteUser(int id);
}

UserServiceImpl

public class UserServiceImpl implements UserService {
  public void add() {
    System.out.println("UserServiceImpl.add()");
  }
 
  public void add(User user) {
    System.out.println("UserServiceImpl.add(" + user + ")");
  }
 
  //下面繼承自UserService接口的方法會被攔截
  @Override
  public void addUser() {
    System.out.println("UserServiceImpl.addUser()");
  }
 
  @Override
  public void updateUser() {
    System.out.println("UserServiceImpl.updateUser()");
  }
 
  @Override
  public int deleteUser(int id) {
    System.out.println("UserServiceImpl.deleteUser(" + id + ")");
    return 1;
  }
}

UserServiceFactory

public class UserServiceFactory {
  public static UserService createUserService() {
    //1、創(chuàng)建目標對象target
    final UserService userService = new UserServiceImpl();
    //2、聲明切面類對象
    final MyAspect myAspect = new MyAspect();
    //3、將切面類before()與after()方法應用到目標類
    //3.1、創(chuàng)建JDK代理(返回一個接口)
    /*
     newProxyInstance(
        ClassLoader loader,   //類加載器,寫當前類
        Class<?>[] interfaces, //接口,接口中包含的方法執(zhí)行時會被攔截
        InvocationHandler h)  //處理 調(diào)用切面類中的處理如:deforre()、after()
     */
    UserService serviceProxy = (UserService) Proxy.newProxyInstance(
        UserServiceFactory.class.getClassLoader(),
        userService.getClass().getInterfaces(),
        new InvocationHandler() {
          @Override
          public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            //開啟事務
            myAspect.before();
            
            //返回值是調(diào)用的業(yè)務方法的返回值
            Object obj = method.invoke(userService, args);
 
            //提交事務
            myAspect.after();
            
            return obj;
          }
        });
    return serviceProxy;
  }
}

MyAspect :(就是一些具體操作,如記錄日志等)

public class MyAspect {
  public void before() {
    System.out.println("MyAspect.before()開啟事務...");
  }
 
  public void after() {
    System.out.println("MyAspect.after()提交事務...");
  }
}

單元測試:

  @Test
  public void aop_test() {
    UserService userService = UserServiceFactory.createUserService();
    userService.addUser();
    userService.deleteUser(10);
    userService.updateUser();
  }

輸出:

MyAspect.before()開啟事務...

UserServiceImpl.addUser()

MyAspect.after()提交事務...

MyAspect.before()開啟事務...

UserServiceImpl.deleteUser(10)

MyAspect.after()提交事務...

MyAspect.before()開啟事務...

UserServiceImpl.updateUser()

MyAspect.after()提交事務...

補充知識:結合動態(tài)代理技術學習SpringAop實現(xiàn)切面編程

結合一個例子利用動態(tài)代理技術和SpringAop實現(xiàn)需求

需求:為我的UserService類中的每一個方法加上一個計時器

最初的實現(xiàn)是為每一個類添加一段代碼,這樣看起來代碼的冗余度特別大

靜態(tài)代理實現(xiàn)

在使用JDK提供動態(tài)代理之前我們先利用靜態(tài)代理技術實現(xiàn)這個需求

靜態(tài)代理需要我們自己創(chuàng)建代理類具體代碼如下:

創(chuàng)建UserService接口以及他的實現(xiàn)類及目標類UserServiceTarget

public interface UserService {
 
  public void insert();
  public void update();
  public void delete();
}
 
// 目標類
public class UserServiceTarget implements UserService {
 
  @Time
  public void insert() {
    System.out.println("插入用戶");
  }
 
  public void update() {
    System.out.println("修改用戶");
  }
 
  public void delete() {
    System.out.println("刪除用戶");
  }
}

創(chuàng)建TimeHandler類,將重復的計時器代碼邏輯寫入TimeHandler類中

public class TimeHandler {
  private UserServiceTarget userService = new UserServiceTarget();
  //需要加計時器的方法對應的對象 -- method
  public void invoke(Method method) {
    long start = System.nanoTime();
    // 反射調(diào)用: 方法.invoke(對象, 參數(shù));
    try {
      method.invoke(userService);
    } catch (Exception e) {
      e.printStackTrace();
    }
    long end = System.nanoTime();
    Time time = method.getAnnotation(Time.class);
    if(time != null) {
      System.out.println("花費了: " + (end - start));
    }
  }
}

最后一步就是自己實現(xiàn)代理類UserServiceProxy,自己實現(xiàn)代理類被稱作靜態(tài)代理

public class UserServiceProxy implements UserService {
 
  public void insert() {
    try {
      TimeHandler timeHandler = new TimeHandler();
      Method a = UserServiceTarget.class.getMethod("insert");
      timeHandler.invoke(a);
    } catch (NoSuchMethodException e) {
      e.printStackTrace();
    }
  }
 
  public void update() {
    try {
      TimeHandler timeHandler = new TimeHandler();
      Method b = UserServiceTarget.class.getMethod("update");
      timeHandler.invoke(b);
    } catch (NoSuchMethodException e) {
      e.printStackTrace();
    }
  }
 
  public void delete() {
    try {
      TimeHandler timeHandler = new TimeHandler();
      Method c = UserServiceTarget.class.getMethod("delete");
      timeHandler.invoke(c);
    } catch (NoSuchMethodException e) {
      e.printStackTrace();
    }
  }
}

這樣在無需改變UserService類和其實現(xiàn)類的情況下增加了代碼的擴展性,降低了代碼間的耦合度。

動態(tài)代理實現(xiàn)

動態(tài)代理就是不需要我們自己創(chuàng)建代理類和代理對象,JDK會在程序運行中為我們自動生成代理對象

動態(tài)代理的三個步驟

1、生成代理類的字節(jié)碼

2、執(zhí)行類加載將字節(jié)碼加載進入JVM

3、創(chuàng)建代理類的實例對象

方式一

自己寫代碼生成需要代理類的字節(jié)碼

1、獲取代理類的字節(jié)碼

byte[] bytes = ProxyGenerator.generateProxyClass("UserServiceProxy", new Class[]{UserService.class});

//這里第一個參數(shù)是自己為代理類起的類名,第二個參數(shù)是需要創(chuàng)建代理類的字節(jié)碼數(shù)組

2、執(zhí)行類加載

 ClassLoader cl = new ClassLoader() {
      @Override
      protected Class<?> findClass(String name) throws ClassNotFoundException {
        return defineClass(name, bytes, 0, bytes.length);
      }
    };
    Class c = cl.loadClass("UserServiceProxy"); // 進行類加載, 獲得了 UserServiceProxy 類對象

3、 創(chuàng)建代理類實例對象--通過反射

 // 獲取代理類的構造方法
    Constructor constructor = c.getConstructor(InvocationHandler.class);
 
    UserServiceTarget target = new UserServiceTarget();
    // 創(chuàng)建實例對象, 強制轉換為它的接口類型
    UserService proxy = (UserService)constructor.newInstance(new InvocationHandler() {
      @Override
      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        long start = System.nanoTime();
        method.invoke(target, args);
        long end = System.nanoTime();
        System.out.println("花費了:" + (end - start));
        return null;
      }
    });

這里的InvocationHandler接口匿名實現(xiàn)類似于我們之前的TimeHandler類,只需要將重復代碼邏輯寫入其中在通過方法對象反射調(diào)用該方法即可實現(xiàn)動態(tài)代理。

//使用代理對象

proxy.insert();

方式二

利用Proxy類的newProxyInstance()方法實現(xiàn)動態(tài)代理,具體代碼如下

public static void main(String[] args) {
    // 直接創(chuàng)建代理類的實例
    // 1. 獲取類加載器
    ClassLoader cl = UserService.class.getClassLoader();
    // 2. 規(guī)定代理類要實現(xiàn)的接口
    Class[] interfaces = new Class[] {UserService.class};
    // 3. 給一個 InvocationHandler 對象, 包含要執(zhí)行的重復邏輯
    UserServiceTarget target = new UserServiceTarget();
    InvocationHandler h = new InvocationHandler() {
      @Override
      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        long start = System.nanoTime();
 
        // 方法.invoke(目標, 參數(shù));
        method.invoke(target, args);
 
        long end = System.nanoTime();
        System.out.println("花費了:" + (end - start));
        return null;
      }
    };
    UserService proxy = (UserService) Proxy.newProxyInstance(cl, interfaces, h);
    //4. 使用代理對象
    proxy.update();
  }
}

使用Spring框架AOP(面向切面編程)完成需求

Spring框架最最主要的兩大特性就是IOC(控制反轉)和AOP(面向切面編程)

IOC總結見我的博客SpringIOC總結

AOP (aspect oriented programming ) 即面向切面編程

切面 aspect = 通知 adivce + 切點 pointcut

通知:是一個方法,其中包含了重復的邏輯(例如我們今天需要實現(xiàn)的計時器需求,以及Spring事務管理的底層實現(xiàn))

切點:是一種匹配條件, 與條件相符合的目標方法,才會應用通知方法,需要配合切點表達式

再來類比一下之前的圖

圖中的UserService就是SpringAOP技術中的代理,TimeHandler就是切面,UserServiceTarget在SpringAOP技術中被稱作目標。

SpringAOP實現(xiàn)

首先需要添加maven依賴

<!--spring核心依賴-->
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-context</artifactId>
  <version>4.3.22.RELEASE</version>
</dependency>
<!--切面相關依賴-->
<dependency>
  <groupId>org.aspectj</groupId>
  <artifactId>aspectjrt</artifactId>
  <version>1.8.13</version>
</dependency>
<dependency>
  <groupId>org.aspectj</groupId>
  <artifactId>aspectjweaver</artifactId>
  <version>1.8.13</version>
</dependency>

第二步,編寫切面類

@Component
//將切面交給spring容器管理
@Aspect
//@Aspect 注解表示該類是一個切面類
//切面 = 通知 + 切點
public class UserAspect {
  //配置切點 @Around注解和切點表達式
  @Around("within(service.impl.*)")
 
  //配置通知方法
  //ProceedingJoinPoint參數(shù)用來調(diào)用目標方法
  public Object time(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
    long start = System.nanoTime();
    Object proceed = proceedingJoinPoint.proceed();//調(diào)用目標方法返回結果
    long end = System.nanoTime();
    System.out.println("springaop 方法耗時" + (end - start) + "納秒");
    return proceed;
  }
}

UserService和UserServiceImpl代碼如下

public interface UserService {
  void insert(); 
  void update(); 
  void delete();
}
 
@Service
public class UserServiceImpl implements UserService {
  @Override
  public void insert() {
    System.out.println("UserServiceImpl 增加用戶信息");
}
 
  @Override
  public void update() {
    System.out.println("UserServiceImpl 修改用戶信息");
  }
 
  @Override
  public void delete() {
    System.out.println("UserServiceImpl 刪除用戶信息");
  }
}

最后一步,配置Spring配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
    http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
    http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"
>
  <!-- spring容器進行包掃描 配有@Componet @Service @Controller @Repository會交由spring容器管理-->
  <context:component-scan base-package="service,aspect"/>
 
  <!-- 啟用切面編程的相關注解,例如: @Aspect, @Around, 還提供了自動產(chǎn)生代理類的功能-->
  <aop:aspectj-autoproxy/>
</beans>

編寫測試類

public class TestSpringAopProgramming {
  public static void main(String[] args) {
    ClassPathXmlApplicationContext context =
        new ClassPathXmlApplicationContext("spring.xml");
    UserService userService = context.getBean(UserService.class);
    userService.insert();
    userService.update();
    userService.delete();
  }
}

彩蛋

這個時候上面的需求又發(fā)生了變法,不是給UserService中所有的方法加計時器,而是給指定方法加計時器,又該如何實現(xiàn)?

如果我們給需要加計時器的方法加上一個注解,當反射調(diào)用該方法的時候判斷如果有該注解在通過動態(tài)代理的方式為其加計時器不就可以解決問題了。

自定義注解

自定義注解需要添加兩個注解 @Target @Retention

@Target 表示能夠加在哪些位置

ElementType.TYPE 表示能夠加在 類上

ElementType.METHOD 表示能夠加在 方法上

ElementType.FIELD 表示能夠加在 屬性上

@Retention 表示注解的作用范圍

Source 表示注解僅在 *.java 源碼中有效

Class 表示注解在 *.java 源碼 和 *.class 字節(jié)碼中有效

Runtime 表示注解在 *.java 源碼 和 *.class 字節(jié)碼 和 運行期間都中有效

自定義注解類Time

@Target({ ElementType.METHOD } ) //該只需要加載方法上
@Retention(RetentionPolicy.RUNTIME)//需要在源碼,字節(jié)碼,以及運行中都有效
public @interface Time {
}

這個時候只需要在指定的方法上加@Time注解,然后在代理對象進行判斷即可,示例代碼如下:

public void insert() {
    try {
      TimeHandler timeHandler = new TimeHandler();
      Method a = UserServiceTarget.class.getMethod("insert");
      //通過getAnnotation()方法判斷是否存在@Time注解
      if(method.getAnnotation(Time.class) !=null) {
        timeHandler.invoke(a);
      }
    } catch (NoSuchMethodException e) {
      e.printStackTrace();
    }
  }

以上這篇Proxy實現(xiàn)AOP切面編程案例就是小編分享給大家的全部內(nèi)容了,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關文章

最新評論