<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代理簡(jiǎn)述

          共 32839字,需瀏覽 66分鐘

           ·

          2021-05-02 07:00

          點(diǎn)擊上方藍(lán)色字體,選擇“標(biāo)星公眾號(hào)”

          優(yōu)質(zhì)文章,第一時(shí)間送達(dá)

            作者 |  碼猿手

          來(lái)源 |  urlify.cn/qQBvMf

           1.什么是代理?

            對(duì)類或?qū)ο螅繕?biāo)對(duì)象)進(jìn)行增強(qiáng)功能,最終形成一個(gè)新的代理對(duì)象,(Spring Framework中)當(dāng)應(yīng)用調(diào)用該對(duì)象(目標(biāo)對(duì)象)的方法時(shí),實(shí)際調(diào)用的是代理對(duì)象增強(qiáng)后的方法,比如對(duì)功能方法login實(shí)現(xiàn)日志記錄,可以通過(guò)代理實(shí)現(xiàn);

            PS:

          目標(biāo)對(duì)象--被增強(qiáng)的對(duì)象;

          代理對(duì)象--增強(qiáng)后的對(duì)象;

            2.為什么需要代理?

            一些類里面的方法有相同的代碼或類中有相同的功能,可以將這些相同抽取出來(lái)形成一個(gè)公共的方法或功能,但Java有兩個(gè)重要的原則:

          單一職責(zé)(對(duì)類來(lái)說(shuō)的,即一個(gè)類應(yīng)該只負(fù)責(zé)一項(xiàng)職責(zé))和開(kāi)閉原則(開(kāi)放擴(kuò)展,修改關(guān)閉),如果每個(gè)類的每個(gè)功能都調(diào)用了公共功能,就破壞了單一職責(zé),如下圖;

          如果這個(gè)類是別人已經(jīng)寫(xiě)好的,你動(dòng)了這個(gè)代碼,同時(shí)也破壞了開(kāi)閉原則(同時(shí)改動(dòng)代碼很麻煩,里面可能涉及其他很多的調(diào)用,可能帶出無(wú)數(shù)的bug,改代碼比新開(kāi)發(fā)功能還難/(ㄒoㄒ)/~~);

            由于有上面的問(wèn)題存在,使用代理來(lái)實(shí)現(xiàn)是最好的解決辦法;

            3.Java實(shí)現(xiàn)代理有哪些?

           ?。?)靜態(tài)代理:通過(guò)對(duì)目標(biāo)方法進(jìn)行繼承或聚合(接口)實(shí)現(xiàn);

          (會(huì)產(chǎn)生類爆炸,因此在不確定的情況下,盡量不要使用靜態(tài)代理,避免產(chǎn)生類爆炸)   1)繼承:代理對(duì)象繼承目標(biāo)對(duì)象,重寫(xiě)需要增強(qiáng)的方法;

          //業(yè)務(wù)類(目標(biāo)對(duì)象)
          public class UserServiceImpl {
              public void query(){
                  System.out.println("業(yè)務(wù)操作查詢數(shù)據(jù)庫(kù)....");
              }
          }
          //日志功能類
          public class Log {
              public static void info(){
                  System.out.println("日志功能");
              }
          }
          //繼承實(shí)現(xiàn)代理(代理對(duì)象)
          public class UserServiceLogImpl extends UserServiceImpl {
              public void query(){
                  Log.info();
                  super.query();
              }
          }

              從上面代碼可以看出,每種增強(qiáng)方法會(huì)產(chǎn)生一個(gè)代理類,如果現(xiàn)在增強(qiáng)方法有日志和權(quán)限,單個(gè)方法增強(qiáng)那需要兩個(gè)代理類(日志代理類和權(quán)限代理類),如果代理類要同時(shí)擁有日志和權(quán)限功能,那又會(huì)產(chǎn)生一個(gè)代理類,同時(shí)由于順序的不同,可能會(huì)產(chǎn)生多個(gè)類,比如先日志后權(quán)限是一個(gè)代理類,先權(quán)限后日志又是另外一個(gè)代理類。

              由此可以看出代理使用繼承的缺陷:

          產(chǎn)生的代理類過(guò)多(產(chǎn)生類爆炸),非常復(fù)雜,維護(hù)難;

              2)聚合:

          代理對(duì)象和目標(biāo)對(duì)象都實(shí)現(xiàn)同一接口,使用裝飾者模式,提供一個(gè)代理類構(gòu)造方法(代理對(duì)象當(dāng)中要包含目標(biāo)對(duì)象),參數(shù)是接口,重寫(xiě)目標(biāo)方法;

          //接口
          public interface UserDao {
              void query();
          }
          //接口實(shí)現(xiàn)類:目標(biāo)對(duì)象
          public class UserDaoImpl implements  UserDao {
              @Override
              public void query() {
                  System.out.println("query......");
              }
          }
          //日志功能類
          public class Log {
              public static void info(){
                  System.out.println("日志功能");
              }
          }
          //聚合實(shí)現(xiàn)代理:同樣實(shí)現(xiàn)接口,使用裝飾者模式;(代理對(duì)象)
          public class UserDaoLogProxy implements UserDao {
              UserDao userDao;
              public UserDaoLogProxy(UserDao userDao){//代理對(duì)象包含目標(biāo)對(duì)象
                  this.userDao = userDao;
              }
              @Override
              public void query() {
                  Log.info();
                  userDao.query();
              }
          }
          //測(cè)試
          public static void main(String[] args) {
              UserDaoImpl target = new UserDaoImpl();
              UserDaoLogProxy proxy = new UserDaoLogProxy(target);
              proxy.query();
          }


              聚合由于利用了面向接口編程的特性,產(chǎn)生的代理類相對(duì)繼承要少一點(diǎn)(雖然也是會(huì)產(chǎn)生類爆炸,假設(shè)有多個(gè)Dao,每個(gè)Dao產(chǎn)生一個(gè)代理類,所以還是會(huì)產(chǎn)生類爆炸),如下案例:

          //時(shí)間記錄功能
          public class Timer {
              public static void timer(){
                  System.out.println("時(shí)間記錄功能");
              }
          }
          //代理類:時(shí)間功能+業(yè)務(wù)
          public class UserDaoTimerProxy implements UserDao {

              UserDao userDao;
              public UserDaoTimerProxy(UserDao userDao){
                  this.userDao = userDao;
              }
              @Override
              public void query() {
                  Timer.timer();
                  userDao.query();
              }
          }
          //測(cè)試
          public static void main(String[] args) {
              //timer+query
              UserDao target = new UserDaoTimerProxy(new UserDaoImpl());
              //log+timer+query
              UserDao proxy = new UserDaoLogProxy(target);
              proxy.query();
          }
          public static void main(String[] args) {
              //log+query
              UserDao target = new UserDaoLogProxy(new UserDaoImpl());
              //timer+log+query
              UserDao proxy = new UserDaoTimerProxy(target);
              proxy.query();
          }

            PS:

              裝飾和代理的區(qū)別:

          代理不需要指定目標(biāo)對(duì)象,可以對(duì)任何對(duì)象進(jìn)行代理;

          裝飾需要指定目標(biāo)對(duì)象,所以需要構(gòu)造方法參數(shù)或set方法指定目標(biāo)對(duì)象;

              幾個(gè)io的Buffer流(BufferedInputStream、BufferedOutputStream、BufferedReader、BufferedWriter)就是使用了裝飾模式的靜態(tài)代理;

          public class BufferedReader extends Reader {
              private Reader in;
              ........
              public BufferedReader(Reader in, int sz) {
                  super(in);
                  if (sz <= 0)
                      throw new IllegalArgumentException("Buffer size <= 0");
                  this.in = in;
                  cb = new char[sz];
                  nextChar = nChars = 0;
              }

              public BufferedReader(Reader in) {
                  this(in, defaultCharBufferSize);
              }
          }

           ?。?)動(dòng)態(tài)代理:Java有JDK動(dòng)態(tài)代理和CGLIB代理;

          (Spring Framework通過(guò)Spring AOP實(shí)現(xiàn)代理,底層還是使用JDK代理和CGLIB代理來(lái)實(shí)現(xiàn))    

          模擬動(dòng)態(tài)代理:不需要手動(dòng)創(chuàng)建類文件(因?yàn)橐坏┦謩?dòng)創(chuàng)建類文件,會(huì)產(chǎn)生類爆炸),通過(guò)接口反射生成一個(gè)類文件,然后調(diào)用第三方的編譯技術(shù),動(dòng)態(tài)編譯這個(gè)產(chǎn)生的類文件成class文件,然后利用URLclassLoader把這個(gè)動(dòng)態(tài)編譯的類加載到j(luò)vm中,然后通過(guò)反射把這個(gè)類實(shí)例化。

          (通過(guò)字符串產(chǎn)生一個(gè)對(duì)象實(shí)現(xiàn)代理);

              PS:

          Java文件 -> class文件 -> byte字節(jié)(JVM)-> class對(duì)象(類對(duì)象)-> new(對(duì)象);

              所以步驟如下:

                1)代碼實(shí)現(xiàn)一個(gè)內(nèi)容(完整的Java文件內(nèi)容,包含包名、變量、構(gòu)造方法、增強(qiáng)后的方法等),使其通過(guò)IO產(chǎn)生一個(gè)Java文件;

                2)通過(guò)第三方編譯技術(shù)產(chǎn)生一個(gè)class文件;

                3)加載class文件利用反射實(shí)例化一個(gè)代理對(duì)象出來(lái);

          public class ProxyUtil {
              /**
               *  content --->string
               *     |
               *     |生成
               *     v
               *  .java   <-----通過(guò)io產(chǎn)生
               *  .class  <-----Java文件編程產(chǎn)生
               *
               *  .new    <-----class文件反射產(chǎn)生實(shí)例對(duì)象
               * @return
               */
              public static Object newInstance(Object target){
                  Object proxy=null;
                  //根據(jù)對(duì)象獲取接口
                  Class targetInf =target.getClass().getInterfaces()[0];
                  //獲取接口的所有方法
                  //getMethods(),該方法是獲取本類以及父類或者父接口中所有的公共方法(public修飾符修飾的)
                  //getDeclaredMethods(),該方法是獲取本類中的所有方法,包括私有的(private、protected、默認(rèn)以及public)的方法
                  Method[] declaredMethods = targetInf.getDeclaredMethods();
                  String line="\n";
                  String tab ="\t";
                  //接口名稱
                  String targetInfName = targetInf.getSimpleName();
                  String content ="";
                  //包位置
                  String packageContent = "package com;"+line;
                  //接口
                  String importContent = "import "+targetInf.getName()+";"+line;
                  String clazzFirstLineContent = "public class $Proxy implements "+targetInfName+"{"+line;
                  //屬性
                  String filedContent  =tab+"private "+targetInfName+" target;"+line;
                  //構(gòu)造方法
                  String constructorContent =tab+"public $Proxy ("+targetInfName+" target){" +line
                          +tab+tab+"this.target =target;"
                          +line+tab+"}"+line;
                  //方法
                  String methodContent = "";
                  for(Method method:declaredMethods){
                      //返回值
                      String returnTypeName = method.getReturnType().getSimpleName();
                      //方法名
                      String methodName = method.getName();
                      // Sting.class String.class 參數(shù)類型
                      Class<?>[] parameterTypes = method.getParameterTypes();
                      String argsContent = "";
                      String paramsContent="";
                      int flag = 0;
                      for(Class args :parameterTypes){
                          //String,參數(shù)類型
                          String simpleName = args.getSimpleName();
                          //String p0,Sting p1,
                          argsContent+=simpleName+" p"+flag+",";
                          paramsContent+="p"+flag+",";
                          flag++;
                      }
                      if (argsContent.length()>0){
                          argsContent=argsContent.substring(0,argsContent.lastIndexOf(",")-1);
                          paramsContent=paramsContent.substring(0,paramsContent.lastIndexOf(",")-1);
                      }
                      methodContent+=tab+"public "+returnTypeName+" "+methodName+"("+argsContent+") {"+line
                              //增強(qiáng)方法先寫(xiě)死
                              +tab+tab+"System.out.println(\"log\");"+line;
                      if(returnTypeName.equals("void")){
                          methodContent+=tab+tab+"target."+methodName+"("+paramsContent+");"+line
                                  +tab+"}"+line;
                      }else{
                          methodContent+=tab+tab+"return target."+methodName+"("+paramsContent+");"+line
                                  +tab+"}"+line;
                      }
                  }
                  content+=packageContent+importContent+clazzFirstLineContent+filedContent
                          +constructorContent+methodContent+"}";
                  //生成Java文件
                  File file = new File("D:\\com\\$Proxy.java");
                  try{
                      if(!file.exists()){
                          file.createNewFile();
                      }
                      //創(chuàng)建
                      FileWriter wr = new FileWriter(file);
                      wr.write(content);
                      wr.flush();
                      wr.close();
                      //編譯Java文件
                      JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();

                      StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);
                      Iterable units = fileMgr.getJavaFileObjects(file);

                      JavaCompiler.CompilationTask t = compiler.getTask(null, fileMgr, null, null, null, units);
                      t.call();
                      fileMgr.close();
                      //new --> 反射
                      URL[] urls = new URL[]{new URL("file:D:\\\\")};
                      //加載外部文件
                      URLClassLoader classLoader = new URLClassLoader(urls);
                      Class<?> proxyClass = classLoader.loadClass("com.$Proxy");
                      Constructor constructor = proxyClass.getConstructor(targetInf);
                      //構(gòu)造方法創(chuàng)建實(shí)例
                      proxy = constructor.newInstance(target);
                      //clazz.newInstance();//根據(jù)默認(rèn)構(gòu)造方法創(chuàng)建對(duì)象
                      //Class.forName()
                  }catch (Exception e){
                      e.printStackTrace();
                  }
                  return proxy;
              }
          }

            自定義代理的缺點(diǎn):

          生成Java文件;

          動(dòng)態(tài)編譯文件;

          需要一個(gè)URLClassLoader;

          涉及到了IO操作,軟件的最終性能體現(xiàn)到了IO操作,即IO操作影響到軟件的性能;

          案例:

            1)接口:

          public interface UserDao {
               void query();
               void query(String name);
               String getName(String id);
          }

           2)實(shí)現(xiàn)類:

          public class UserService implements UserDao {
              @Override
              public void query() {
                  System.out.println("query");
              }

              @Override
              public void query(String name) {
                  System.out.println(name);
              }

              @Override
              public String getName(String id) {
                  System.out.println("id:"+id);
                  return "李四";
              }
          }

            3)測(cè)試:

          public static void main(String[] args) {
              UserDao dao = (UserDao) ProxyUtil.newInstance(new UserService());
              dao.query();
              dao.query("張三");
          }
          --------結(jié)果--------
          log
          query
          log
          張三

          package com;
          import com.hrh.dynamicProxy.dao.UserDao;
          public class $Proxy implements UserDao{//自定義動(dòng)態(tài)代理生成的文件
              private UserDao target;
              public $Proxy (UserDao target){
                  this.target =target;
              }
              public String getName(String p) {
                  System.out.println("log");
                  return target.getName(p);
              }
              public void query(String p) {
                  System.out.println("log");
                  target.query(p);
              }
              public void query() {
                  System.out.println("log");
                  target.query();
              }
          }

            JDK動(dòng)態(tài)代理:

          基于反射實(shí)現(xiàn),通過(guò)接口反射得到字節(jié)碼,然后將字節(jié)碼轉(zhuǎn)成class,通過(guò)一個(gè)native(JVM實(shí)現(xiàn))方法來(lái)執(zhí)行;

            案例實(shí)現(xiàn):

          實(shí)現(xiàn) InvocationHandler 接口重寫(xiě) invoke 方法,其中包含一個(gè)對(duì)象變量和提供一個(gè)包含對(duì)象的構(gòu)造方法;

          public class MyInvocationHandler implements InvocationHandler {
              Object target;//目標(biāo)對(duì)象
              public MyInvocationHandler(Object target){
                  this.target=target;
              }
              /**
               * @param proxy 代理對(duì)象
               * @param method 目標(biāo)對(duì)象的目標(biāo)方法
               * @param args    目標(biāo)方法的參數(shù)
               */
              @Override
              public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                  System.out.println("log");
                  return method.invoke(target,args);
              }
          }
          public class MyInvocationHandlerTest {
              public static void main(String[] args) {
                  //參數(shù): 當(dāng)前類的classLoader(保證MyInvocationHandlerTest當(dāng)前類可用)
                  //         接口數(shù)組:通過(guò)接口反射得到接口里面的方法,對(duì)接口里面的所有方法都進(jìn)行代理
                  //         實(shí)現(xiàn)的InvocationHandler:參數(shù)是目標(biāo)對(duì)象
                  UserDao jdkproxy = (UserDao) Proxy.newProxyInstance(MyInvocationHandlerTest.class.getClassLoader(),
                          new Class[]{UserDao.class},new MyInvocationHandler(new UserService()));
                  jdkproxy.query("query");
                  //-----結(jié)果------
                  //log
                  //query
              }
          }

            下面查看底層JDK生成的代理類class:

          byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy18", new Class[]{UserDao.class});

                  try {
                      FileOutputStream fileOutputStream = new FileOutputStream("xxx本地路徑\\$Proxy18.class");
                      fileOutputStream.write(bytes);
                      fileOutputStream.flush();
                  } catch (FileNotFoundException e) {
                      e.printStackTrace();
                  } catch (IOException e) {
                      e.printStackTrace();
                  }

            或在代碼的最前面添加下面代碼:

          System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "xxx本地路徑");

          代理類class反編譯后的內(nèi)容:

          下面的內(nèi)容驗(yàn)證了JDK動(dòng)態(tài)代理為什么基于聚合(接口)來(lái)的,而不能基于繼承來(lái)的?

          因?yàn)镴DK動(dòng)態(tài)代理底層已經(jīng)繼承了Proxy,而Java是單繼承,不支持多繼承,所以以接口來(lái)實(shí)現(xiàn);

          //
          // Source code recreated from a .class file by IntelliJ IDEA
          // (powered by Fernflower decompiler)
          //

          import com.hrh.dao.UserDao;
          import java.lang.reflect.InvocationHandler;
          import java.lang.reflect.Method;
          import java.lang.reflect.Proxy;
          import java.lang.reflect.UndeclaredThrowableException;

          public final class $Proxy18 extends Proxy implements UserDao {
              private static Method m1;
              private static Method m4;
              private static Method m5;
              private static Method m2;
              private static Method m0;
              private static Method m3;

              public $Proxy18(InvocationHandler var1) throws  {
                  super(var1);
              }

              public final boolean equals(Object var1) throws  {
                  try {
                      return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
                  } catch (RuntimeException | Error var3) {
                      throw var3;
                  } catch (Throwable var4) {
                      throw new UndeclaredThrowableException(var4);
                  }
              }

              public final void query(String var1) throws  {
                  try {
                      super.h.invoke(this, m4, new Object[]{var1});
                  } catch (RuntimeException | Error var3) {
                      throw var3;
                  } catch (Throwable var4) {
                      throw new UndeclaredThrowableException(var4);
                  }
              }

              public final void query() throws  {
                  try {
                      super.h.invoke(this, m5, (Object[])null);
                  } catch (RuntimeException | Error var2) {
                      throw var2;
                  } catch (Throwable var3) {
                      throw new UndeclaredThrowableException(var3);
                  }
              }

              public final String toString() throws  {
                  try {
                      return (String)super.h.invoke(this, m2, (Object[])null);
                  } catch (RuntimeException | Error var2) {
                      throw var2;
                  } catch (Throwable var3) {
                      throw new UndeclaredThrowableException(var3);
                  }
              }

              public final int hashCode() throws  {
                  try {
                      return (Integer)super.h.invoke(this, m0, (Object[])null);
                  } catch (RuntimeException | Error var2) {
                      throw var2;
                  } catch (Throwable var3) {
                      throw new UndeclaredThrowableException(var3);
                  }
              }

              public final String getName(String var1) throws  {
                  try {
                      return (String)super.h.invoke(this, m3, new Object[]{var1});
                  } catch (RuntimeException | Error var3) {
                      throw var3;
                  } catch (Throwable var4) {
                      throw new UndeclaredThrowableException(var4);
                  }
              }

              static {
                  try {
                      m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
                      m4 = Class.forName("com.hrh.dao.UserDao").getMethod("query", Class.forName("java.lang.String"));
                      m5 = Class.forName("com.hrh.dao.UserDao").getMethod("query");
                      m2 = Class.forName("java.lang.Object").getMethod("toString");
                      m0 = Class.forName("java.lang.Object").getMethod("hashCode");
                      m3 = Class.forName("com.hrh.dao.UserDao").getMethod("getName", Class.forName("java.lang.String"));
                  } catch (NoSuchMethodException var2) {
                      throw new NoSuchMethodError(var2.getMessage());
                  } catch (ClassNotFoundException var3) {
                      throw new NoClassDefFoundError(var3.getMessage());
                  }
              }
          }

            CGLIB代理:

          借助asm(一個(gè)操作字節(jié)碼的框架)實(shí)現(xiàn)代理操作;

          CGLIB基于繼承來(lái)的(前文有CGLIB代理類class的反編譯可以看出);

          public class UserService {
              public void query(){
                  System.out.println("query");
              }

              public static void main(String[] args) {
                  // 通過(guò)CGLIB動(dòng)態(tài)代理獲取代理對(duì)象的過(guò)程
                  Enhancer enhancer = new Enhancer();
                  // 設(shè)置enhancer對(duì)象的父類
                  enhancer.setSuperclass(UserService.class);
                  // 設(shè)置enhancer的回調(diào)對(duì)象
                  enhancer.setCallback(new MethodInterceptor() {
                      @Override
                      public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
                          System.out.println("before method run...");
                          Object result = proxy.invokeSuper(obj, args);
                          System.out.println("after method run...");
                          return result;
                      }
                  });
                  //創(chuàng)建代理對(duì)象
                  UserService bean = (UserService) enhancer.create();
                  bean.query();
              }
          }//-----------結(jié)果------before method run...queryafter method run...

            PS:如果只是對(duì)項(xiàng)目中一個(gè)類進(jìn)行代理,可以使用靜態(tài)代理,如果是多個(gè)則使用動(dòng)態(tài)代理;





          粉絲福利:Java從入門(mén)到入土學(xué)習(xí)路線圖

          ??????

          ??長(zhǎng)按上方微信二維碼 2 秒


          感謝點(diǎn)贊支持下哈 

          瀏覽 41
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

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

          手機(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>
                  天天操天天拍天天干 | 亚洲无码电影一 | 日皮精品视频免费 | 欧美精品成人a在线观看hd | 三级五月天 |