<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Java 動態(tài)代理的兩種方式及其優(yōu)缺點(diǎn)

          共 8552字,需瀏覽 18分鐘

           ·

          2022-10-15 15:30

          點(diǎn)擊關(guān)注上方“Stephen”,

          設(shè)為“置頂或星標(biāo)”,第一時(shí)間送達(dá)干貨

          來源:blog.csdn.net/hongtaolong/articple/details/88688634

          前言

          動態(tài)代理應(yīng)用非常的廣泛,在各種開源的框架中都能看到他們的身影,比如spring中的aop使用動態(tài)代理增強(qiáng),mybatis中使用動態(tài)代理生成mapper,動態(tài)代理主要有JDK和CGLIB兩種方式,今天來學(xué)習(xí)下這兩種方式的實(shí)現(xiàn),以及它們的優(yōu)缺點(diǎn)
          動態(tài)代理:是使用反射和字節(jié)碼的技術(shù),在運(yùn)行期創(chuàng)建指定接口或類的子類,以及其實(shí)例對象的技術(shù),通過這個(gè)技術(shù)可以無侵入的為代碼進(jìn)行增強(qiáng)

          一、JDK實(shí)現(xiàn)的動態(tài)代理

          1、解析

          jdk實(shí)現(xiàn)的動態(tài)代理由兩個(gè)重要的成員組成,分別是ProxyInvocationHandler
          • Proxy: 是所有動態(tài)代理的父類,它提供了一個(gè)靜態(tài)方法來創(chuàng)建動態(tài)代理的class對象和實(shí)例
          • InvocationHandler: 每個(gè)動態(tài)代理實(shí)例都有一個(gè)關(guān)聯(lián)的InvocationHandler,在代理實(shí)例上調(diào)用方法是,方法調(diào)用將被轉(zhuǎn)發(fā)到InvocationHandler的invoke方法

          2、簡單看下jdk的動態(tài)代理的原理圖

          圖片

          3、代碼實(shí)現(xiàn)

          現(xiàn)在模擬一個(gè)用戶注冊的功能,動態(tài)代理對用戶的注冊功能進(jìn)行增強(qiáng),會判斷用戶名和密碼的長度,如果用戶名<=1和密碼<6則會拋出異常
          User.java
          package com.taolong;
           
          public class User {
           
           private String name;
           
           private Integer age;
           
           private String password;
           
           public String getName() {
            return name;
           }
           
           public void setName(String name) {
            this.name = name;
           }
           
           
           public Integer getAge() {
            return age;
           }
           
           public void setAge(Integer age) {
            this.age = age;
           }
           
           public String getPassword() {
            return password;
           }
           
           public void setPassword(String password) {
            this.password = password;
           }
           
           @Override
           public String toString() {
            return "User [name=" + name + ", age=" + age + ", password=" + password + "]";
           }
           
          }
          UserService.java
          package com.taolong.jdk;
           
          import com.taolong.User;
           
          public interface UserService {
           
           void addUser(User user);
          }
          UserServiceImpl.java
          package com.taolong.jdk;

          import com.taolong.User;

          public class UserServiceImpl implements UserService {

             @Override
             public void addUser(User user) {
              System.out.println("jdk...正在注冊用戶,用戶信息為:"+user);
             }

          }
          UserServiceInterceptor.java
          package com.taolong.jdk;
           
          import java.lang.reflect.InvocationHandler;
          import java.lang.reflect.Method;
           
          import com.taolong.User;
           
          public class UserServiceInterceptor implements InvocationHandler {
           
           private Object realObj;
           
           public UserServiceInterceptor(Object realObject) {
            super();
            this.realObj = realObject;
           }
           
           @Override
           public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            if (args!=null && args.length > 0 && args[0] instanceof User) {
             User user = (User)args[0];
             //進(jìn)行增強(qiáng)判斷
             if (user.getName().length() <= 1) {
              throw new RuntimeException("用戶名長度必須大于1");
             }
             if (user.getPassword().length() <= 6) {
              throw new RuntimeException("密碼長度必須大于6");
             }
            }
            Object result = method.invoke(realObj, args);
            System.out.println("用戶注冊成功...");
            return result;
           }
           
           public Object getRealObj() {
            return realObj;
           }
           
           public void setRealObj(Object realObj) {
            this.realObj = realObj;
           }
           
          }
          ClientTest.java
          package com.taolong.jdk;
           
          import java.lang.reflect.InvocationHandler;
          import java.lang.reflect.Proxy;
           
          import com.taolong.User;
           
          public class ClientTest {
           
           public static void main(String[] args) {
            User user = new User();
            user.setName("hongtaolong");
            user.setPassword("hong");
            user.setAge(23);
            //被代理類
            UserService delegate = new UserServiceImpl();
            InvocationHandler userServiceInterceptor = new UserServiceInterceptor(delegate);
            //動態(tài)代理類
            UserService userServiceProxy = (UserService)Proxy.newProxyInstance(delegate.getClass().getClassLoader(),
              delegate.getClass().getInterfaces(), userServiceInterceptor);
            System.out.println("動態(tài)代理類:"+userServiceProxy.getClass());
            userServiceProxy.addUser(user);
           }
          }
          運(yùn)行結(jié)果:當(dāng)密碼的長度小于6時(shí)
          圖片
          這里就起到了動態(tài)增強(qiáng)的作用,mybatis的使用中我們知道不需要?jiǎng)?chuàng)建dao中的mapper接口的子類,也能調(diào)用到相應(yīng)的方法,其實(shí)就是生成的實(shí)現(xiàn)了mapper接口的動態(tài)的代理類,我們可以去看看它的這個(gè)方法
          圖片
          接下來我們看下cglib的使用

          二、CGLIB動態(tài)代理

          1、解析

          CGLIB(Code Generation Library)是一個(gè)基于ASM的字節(jié)碼生成庫,它允許我們在運(yùn)行時(shí)對字節(jié)碼進(jìn)行修改和動態(tài)生成。CGLIB通過繼承的方式實(shí)現(xiàn)代理(最后這部分我們深思一下,它可能有哪些局限,final方法是不能夠被重寫,所以它不能增強(qiáng)被final修飾的方法,這個(gè)等下我們來驗(yàn)證)
          CGLIB的實(shí)現(xiàn)也有兩個(gè)重要的成員組成,Enhancer、MethodInterceptor,其實(shí)這兩個(gè)的使用和jdk實(shí)現(xiàn)的動態(tài)代理的Proxy、InvocationHandler非常相似
          • Enhancer: 來指定要代理的目標(biāo)對象,實(shí)際處理代理邏輯的對象,最終通過調(diào)用create()方法得到代理對象、對這個(gè)對象所有的非final方法的調(diào)用都會轉(zhuǎn)發(fā)給MethodInterceptor
          • MethodInterceptor: 動態(tài)代理對象的方法調(diào)用都會轉(zhuǎn)發(fā)到intercept方法進(jìn)行增強(qiáng)

          2、圖解

          圖片

          3、代碼的實(shí)現(xiàn)

          還是上面的場景,注冊用戶進(jìn)行攔截增強(qiáng),部分代碼如下
          UserServiceCglibInterceptor.java
          package com.taolong.cglib;
           
          import java.lang.reflect.Method;
           
          import com.taolong.User;
           
          import net.sf.cglib.proxy.MethodInterceptor;
          import net.sf.cglib.proxy.MethodProxy;
           
          public class UserServiceCglibInterceptor implements MethodInterceptor {
           
           private Object realObject;
           
           public UserServiceCglibInterceptor(Object realObject) {
            super();
            this.realObject = realObject;
           }
           
           @Override
           public Object intercept(Object object, Method method, Object[] args, MethodProxy proxy) throws Throwable {
            if (args!=null && args.length > 0 && args[0] instanceof User) {
             User user = (User)args[0];
             //進(jìn)行增強(qiáng)判斷
             if (user.getName().length() <= 1) {
              throw new RuntimeException("用戶名長度必須大于1");
             }
             if (user.getPassword().length() <= 6) {
              throw new RuntimeException("密碼長度必須大于6");
             }
            }
            Object result = method.invoke(realObject, args);
            System.out.println("用戶注冊成功...");
            return result;
           }
           
          }
          ClientTest.java
          package com.taolong.cglib;
           
          import com.taolong.User;
           
          import net.sf.cglib.proxy.Enhancer;
           
          public class ClientTest {
           
           public static void main(String[] args) {
            User user = new User();
            user.setName("hongtaolong");
            user.setPassword("hong");
            user.setAge(23);
            //被代理的對象
            UserServiceImplCglib delegate = new UserServiceImplCglib();
            UserServiceCglibInterceptor serviceInterceptor = new UserServiceCglibInterceptor(delegate);
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(delegate.getClass());
            enhancer.setCallback(serviceInterceptor);
            //動態(tài)代理類
            UserServiceImplCglib cglibProxy = (UserServiceImplCglib)enhancer.create();
            System.out.println("動態(tài)代理類父類:"+cglibProxy.getClass().getSuperclass());
            cglibProxy.addUser(user);
           }
          }
          運(yùn)行結(jié)果:
          圖片
          這里順便打印了動態(tài)代理類的父類,接下來我們將它的父類UserServiceImplCglib的addUser方法用final修飾,看看是否會被增強(qiáng)
          UserServiceImplCglib.java
          package com.taolong.cglib;
           
          import com.taolong.User;
           
          public class UserServiceImplCglib {
           
           final void addUser(User  user) {
            System.out.println("cglib...正在注冊用戶,用戶信息為:"+user);
           }
          }
          運(yùn)行結(jié)果:
          圖片

          總結(jié)一下

          1、JDK原聲動態(tài)代理時(shí)java原聲支持的、不需要任何外部依賴、但是它只能基于接口進(jìn)行代理(因?yàn)樗呀?jīng)繼承了proxy了,java不支持多繼承)
          2、CGLIB通過繼承的方式進(jìn)行代理、無論目標(biāo)對象沒有沒實(shí)現(xiàn)接口都可以代理,但是無法處理final的情況(final修飾的方法不能被覆寫)

          END


          關(guān)注 Stephen,一起學(xué)習(xí),一起成長。


          點(diǎn)“在看”支持下吧


          點(diǎn) 閱讀原文 可優(yōu)惠充值話費(fèi),流量,視頻會員等。

          瀏覽 33
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  欧洲熟妇的性久久久久久 | 青草青青精品视频在线观看 | 成人H精品动漫在线无码播放 | 欧美系列在线 | 91久久爽无码人妻AⅤ精品蜜桃 |