亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

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

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

通過JDK的Proxy代理實(shí)現(xiàn)對(duì)業(yè)務(wù)類做簡(jiǎn)單的AOP實(shí)現(xiàn)

接口:UserService 包含的方法為切入點(diǎn),會(huì)被代理攔截

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

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

類:MyAspect 切面類,實(shí)現(xiàn)對(duì)切入點(diǎ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接口的方法會(huì)被攔截
  @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)建目標(biāo)對(duì)象target
    final UserService userService = new UserServiceImpl();
    //2、聲明切面類對(duì)象
    final MyAspect myAspect = new MyAspect();
    //3、將切面類before()與after()方法應(yīng)用到目標(biāo)類
    //3.1、創(chuàng)建JDK代理(返回一個(gè)接口)
    /*
     newProxyInstance(
        ClassLoader loader,   //類加載器,寫當(dāng)前類
        Class<?>[] interfaces, //接口,接口中包含的方法執(zhí)行時(shí)會(huì)被攔截
        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 {
            //開啟事務(wù)
            myAspect.before();
            
            //返回值是調(diào)用的業(yè)務(wù)方法的返回值
            Object obj = method.invoke(userService, args);
 
            //提交事務(wù)
            myAspect.after();
            
            return obj;
          }
        });
    return serviceProxy;
  }
}

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

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

單元測(cè)試:

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

輸出:

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

UserServiceImpl.addUser()

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

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

UserServiceImpl.deleteUser(10)

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

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

UserServiceImpl.updateUser()

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

補(bǔ)充知識(shí):結(jié)合動(dòng)態(tài)代理技術(shù)學(xué)習(xí)SpringAop實(shí)現(xiàn)切面編程

結(jié)合一個(gè)例子利用動(dòng)態(tài)代理技術(shù)和SpringAop實(shí)現(xiàn)需求

需求:為我的UserService類中的每一個(gè)方法加上一個(gè)計(jì)時(shí)器

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

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

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

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

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

public interface UserService {
 
  public void insert();
  public void update();
  public void delete();
}
 
// 目標(biāo)類
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類,將重復(fù)的計(jì)時(shí)器代碼邏輯寫入TimeHandler類中

public class TimeHandler {
  private UserServiceTarget userService = new UserServiceTarget();
  //需要加計(jì)時(shí)器的方法對(duì)應(yīng)的對(duì)象 -- method
  public void invoke(Method method) {
    long start = System.nanoTime();
    // 反射調(diào)用: 方法.invoke(對(duì)象, 參數(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("花費(fèi)了: " + (end - start));
    }
  }
}

最后一步就是自己實(shí)現(xiàn)代理類UserServiceProxy,自己實(shí)現(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類和其實(shí)現(xiàn)類的情況下增加了代碼的擴(kuò)展性,降低了代碼間的耦合度。

動(dòng)態(tài)代理實(shí)現(xiàn)

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

動(dòng)態(tài)代理的三個(gè)步驟

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

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

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

方式一

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

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

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

//這里第一個(gè)參數(shù)是自己為代理類起的類名,第二個(gè)參數(shù)是需要?jiǎng)?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"); // 進(jìn)行類加載, 獲得了 UserServiceProxy 類對(duì)象

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

 // 獲取代理類的構(gòu)造方法
    Constructor constructor = c.getConstructor(InvocationHandler.class);
 
    UserServiceTarget target = new UserServiceTarget();
    // 創(chuàng)建實(shí)例對(duì)象, 強(qiáng)制轉(zhuǎn)換為它的接口類型
    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("花費(fèi)了:" + (end - start));
        return null;
      }
    });

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

//使用代理對(duì)象

proxy.insert();

方式二

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

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

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

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

IOC總結(jié)見我的博客SpringIOC總結(jié)

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

切面 aspect = 通知 adivce + 切點(diǎn) pointcut

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

切點(diǎn):是一種匹配條件, 與條件相符合的目標(biāo)方法,才會(huì)應(yīng)用通知方法,需要配合切點(diǎn)表達(dá)式

再來類比一下之前的圖

圖中的UserService就是SpringAOP技術(shù)中的代理,TimeHandler就是切面,UserServiceTarget在SpringAOP技術(shù)中被稱作目標(biāo)。

SpringAOP實(shí)現(xiàn)

首先需要添加maven依賴

<!--spring核心依賴-->
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-context</artifactId>
  <version>4.3.22.RELEASE</version>
</dependency>
<!--切面相關(guān)依賴-->
<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 注解表示該類是一個(gè)切面類
//切面 = 通知 + 切點(diǎn)
public class UserAspect {
  //配置切點(diǎn) @Around注解和切點(diǎn)表達(dá)式
  @Around("within(service.impl.*)")
 
  //配置通知方法
  //ProceedingJoinPoint參數(shù)用來調(diào)用目標(biāo)方法
  public Object time(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
    long start = System.nanoTime();
    Object proceed = proceedingJoinPoint.proceed();//調(diào)用目標(biāo)方法返回結(jié)果
    long end = System.nanoTime();
    System.out.println("springaop 方法耗時(shí)" + (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容器進(jìn)行包掃描 配有@Componet @Service @Controller @Repository會(huì)交由spring容器管理-->
  <context:component-scan base-package="service,aspect"/>
 
  <!-- 啟用切面編程的相關(guān)注解,例如: @Aspect, @Around, 還提供了自動(dòng)產(chǎn)生代理類的功能-->
  <aop:aspectj-autoproxy/>
</beans>

編寫測(cè)試類

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();
  }
}

彩蛋

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

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

自定義注解

自定義注解需要添加兩個(gè)注解 @Target @Retention

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

ElementType.TYPE 表示能夠加在 類上

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

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

@Retention 表示注解的作用范圍

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

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

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

自定義注解類Time

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

這個(gè)時(shí)候只需要在指定的方法上加@Time注解,然后在代理對(duì)象進(jìn)行判斷即可,示例代碼如下:

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實(shí)現(xiàn)AOP切面編程案例就是小編分享給大家的全部?jī)?nèi)容了,希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • SpringBoot配置文件、多環(huán)境配置、讀取配置的4種實(shí)現(xiàn)方式

    SpringBoot配置文件、多環(huán)境配置、讀取配置的4種實(shí)現(xiàn)方式

    SpringBoot支持多種配置文件位置和格式,其中application.properties和application.yml是默認(rèn)加載的文件,配置文件可以根據(jù)環(huán)境通過spring.profiles.active屬性進(jìn)行區(qū)分,命令行參數(shù)具有最高優(yōu)先級(jí),可覆蓋其他所有配置
    2024-09-09
  • Java實(shí)戰(zhàn)之實(shí)現(xiàn)一個(gè)好用的MybatisPlus代碼生成器

    Java實(shí)戰(zhàn)之實(shí)現(xiàn)一個(gè)好用的MybatisPlus代碼生成器

    這篇文章主要介紹了Java實(shí)戰(zhàn)之實(shí)現(xiàn)一個(gè)好用的MybatisPlus代碼生成器,文中有非常詳細(xì)的代碼示例,對(duì)正在學(xué)習(xí)java的小伙伴們有非常好的幫助,需要的朋友可以參考下
    2021-04-04
  • Java實(shí)現(xiàn)等待所有子線程結(jié)束后再執(zhí)行一段代碼的方法

    Java實(shí)現(xiàn)等待所有子線程結(jié)束后再執(zhí)行一段代碼的方法

    這篇文章主要介紹了Java實(shí)現(xiàn)等待所有子線程結(jié)束后再執(zhí)行一段代碼的方法,涉及java多線程的線程等待與執(zhí)行等相關(guān)操作技巧,需要的朋友可以參考下
    2017-08-08
  • Java實(shí)現(xiàn)經(jīng)典游戲Flappy Bird的示例代碼

    Java實(shí)現(xiàn)經(jīng)典游戲Flappy Bird的示例代碼

    Flappy?Bird是13年紅極一時(shí)的小游戲,即摁上鍵控制鳥的位置穿過管道間的縫隙。本文將用Java語言實(shí)現(xiàn)這一經(jīng)典的游戲,需要的可以參考一下
    2022-02-02
  • SpringBoot實(shí)現(xiàn)PDF添加水印的三種方法

    SpringBoot實(shí)現(xiàn)PDF添加水印的三種方法

    本文主要介紹了SpringBoot實(shí)現(xiàn)PDF添加水印的三種方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-07-07
  • idea中怎樣創(chuàng)建并運(yùn)行第一個(gè)java程序

    idea中怎樣創(chuàng)建并運(yùn)行第一個(gè)java程序

    這篇文章主要介紹了idea中怎樣創(chuàng)建并運(yùn)行第一個(gè)java程序問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-08-08
  • 最新評(píng)論