能夠想到的方式起碼有兩種繼承和聚合。java
建立一個接口spring
package com.jyd.proxy; /** * 定義一個可以工做的接口。定義一系列操做方法 * @author hadoop * */ public interface Workable { void renting(); }
建立一個類繼承這個接口編程
package com.jyd.proxy; import java.util.Random; /** * 定義一箇中介人來實現可以工做的接口 * @author hadoop * */ public class Middleman implements Workable { private Random random = new Random(); @Override public void renting() { //中介操做 try { Thread.sleep(random.nextInt(10000)); System.out.println("中介開始租房..."); } catch (InterruptedException e) { e.printStackTrace(); } } }
建立client類來調用此方法安全
package com.jyd.proxy; public class Client { public static void main(String[] args) { Workable workable = new Middleman(); workable.renting(); } }
假設我想知道一個類裏的某一個方法一共運行多長時間。怎樣實現了?前提是咱們並無這個類的源代碼框架
建立一個類來繼承中間類dom
package com.jyd.proxy; public class MiddlemanTimeProxy extends Middleman { @Override public void renting() { long startTime = System.currentTimeMillis(); System.out.println("開始計時: " + startTime); super.renting();//一個類繼承了父類,super會調用本身父類 long endTime = System.currentTimeMillis(); System.out.println("這種方法一共使用多長時間:" + (endTime - startTime)); } }
建立一個client類來調用此代理方法ide
package com.jyd.proxy; public class TimeProxyClient { public static void main(String[] args) { Workable workable = new MiddlemanTimeProxy(); workable.renting(); } }
依次來類推:假設現在我想實現一個記錄日誌的功能,那麼的話又一次建立一個類來繼承中間人的方法在super. renting()方法的先後來處理日誌的操做;現在我又像實現一個權限驗證的模塊化
功能,那麼需要再次建立一個類裏繼承中間人的方法在super.renting()方法的先後來處理權限驗證操做;函數
重點:oop
再想假設我想實現一個先記錄日誌再實現權限驗證的功能,那麼是否是再建立一個
類來繼承中間人的方法在super. renting()方法的先後來處理日誌的操做在驗證權限,假設這麼作的話,就會出現無窮無盡的類繼承到最後會出現一個類爆炸的狀況。這裏咱們得出一個結論:假設經過類繼承來實現靜態代理會出現類爆炸。
怎麼辦???
假設我想知道一個類裏的某一個方法一共運行多長時間。怎樣實現了?前提是咱們並無這個類的源代碼
建立一個記錄日誌聚合的代理類
package com.jyd.proxy; public class MiddlemanTimeProxy implements Workable{ //聚合就是一個類包括還有一類的引用 private Workable workable; public MiddlemanTimeProxy(Workable workable) { super(); this.workable = workable; } @Override public void renting() { long startTime = System.currentTimeMillis(); System.out.println("開始計時: " + startTime); workable.renting(); long endTime = System.currentTimeMillis(); System.out.println("這種方法一共使用多長時間:" + (endTime - startTime)); } }
建立一個client類來調用此代理方法
package com.jyd.proxy; public class TimeProxyClient { public static void main(String[] args) { Middleman m = new Middleman(); MiddlemanTimeProxy mt = new MiddlemanTimeProxy(m); mt.renting(); } }
建立一個權限檢查聚合的代理類
package com.jyd.proxy; public class MiddlemanPermissionProxy implements Workable{ private Workable workable; public MiddlemanPermissionProxy(Workable workable) { super(); this.workable = workable; } @Override public void renting(){ System.out.println("開始檢查權限..."); workable.renting(); System.out.println("結束檢查權限..."); } }
比方說:現在需要實現一個計算時間的功能在檢查權限的代理功能。那麼的話現在我就不需要又一次一個代理類,直接使用以前實現的代理類組合一下就能夠,這樣可以有效的下降代理類的實現
package com.jyd.proxy; public class ProxyClient { public static void main(String[] args) { Workable workable = new Middleman(); MiddlemanTimeProxy mt = new MiddlemanTimeProxy(workable); MiddlemanPermissionProxy mp = new MiddlemanPermissionProxy(mt); mp.renting(); } }
結論:聚合實現靜態代理類可以有效下降代理類的建立,因此集合實現靜態代理要比繼承實現要好。
那麼問題來了。假設一個項目中有很是多類要實現各個方法都實現計算方法的運行時間怎麼處理???
爲每個類都建立一個代理對象嗎?顯然這樣作的方式是很不可取的,那麼咱們來分析以前實現的靜態代理類,咱們發現一個問題一個代理類中包括被代理的對象,被代理對象去調用需要運行的方法,--這種話基本上這個代理類是一個模板式的代碼—>發揮咱們想象,這個代理類可不可以動態的去建立???
可以把代理對象模板類寫成一個字符串,經過java的JavaCompile編譯器動態來編譯此文件,而後經過URLClassLoader載入器來載入對象,而後經過反射來動態調用需要被代理的方法
package com.jyd.proxy; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.lang.reflect.Constructor; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; import java.util.ArrayList; import java.util.List; import javax.tools.JavaCompiler; import javax.tools.JavaCompiler.CompilationTask; import javax.tools.StandardJavaFileManager; import javax.tools.ToolProvider; public class Proxy { private static final String BASE_TARGET_PATH = "d:/src/"; private static final String TARGET_PATH = BASE_TARGET_PATH + "com/jyd/proxy"; private static final String RT = "\r\n"; private static final String PROXY_CLASS_NAME = "$Proxy"; private static URLClassLoader classLoader; /** * 一、分析所有靜態代理類,模式都是固定的如 * 開始以前處理邏輯 * target.method()目標對象調用方法 * 目標對象調用以後處理邏輯 * * 處於這樣的狀況的話,那麼我經過代碼本身主動生成模式固定的代理類 * @return */ public static Object newInstance(){ // 一、建立一個段代碼的字符經過代碼編譯。生成對象 String proxySource = "package com.jyd.proxy;"+RT+ "public class "+PROXY_CLASS_NAME+" implements Workable{"+RT+ " private Workable workable;"+RT+ " public "+PROXY_CLASS_NAME+"(Workable workable) {"+RT+ " super();"+RT+ " this.workable = workable;"+RT+ " }"+RT+ " @Override"+RT+ " public void renting() {"+RT+ " long startTime = System.currentTimeMillis();"+RT+ " System.out.println(\"開始計時: \" + startTime);"+RT+ " workable.renting();"+RT+ " long endTime = System.currentTimeMillis();"+RT+ " System.out.println(\"這種方法一共使用多長時間:\" + (endTime - startTime));"+RT+ " }"+RT+ "}"; //推斷目標路徑是否存在 File targetDir = new File(TARGET_PATH); if(!targetDir.exists())targetDir.mkdirs(); //將源代碼文件生成到磁盤上 File file = new File(TARGET_PATH, PROXY_CLASS_NAME+".java"); try { FileWriter fw = new FileWriter(file); fw.write(new String(proxySource.getBytes(), "UTF-8")); fw.close(); } catch (Exception e) { e.printStackTrace(); } //使用代碼動態編譯此java文件 JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); StandardJavaFileManager standardJavaFileManager = compiler.getStandardFileManager(null, null, null); List<File> files = new ArrayList<File>(); files.add(file); Iterable compilationUnits = standardJavaFileManager.getJavaFileObjectsFromFiles(files); CompilationTask task = compiler.getTask(null, standardJavaFileManager, null, null, null, compilationUnits); task.call(); //經過URLClassLoader來載入編譯好的字節碼文件 URL[] urls = null;; try { urls = new URL[]{new URL("file:/" + BASE_TARGET_PATH)}; } catch (MalformedURLException e) { e.printStackTrace(); } URLClassLoader classLoader = new URLClassLoader(urls); //此處載入class類名需要類的全路徑 Class<?> clazz = null; try { clazz = classLoader.loadClass("com.jyd.proxy."+PROXY_CLASS_NAME); } catch (ClassNotFoundException e) { e.printStackTrace(); } Object proxyObject = null; try { Constructor<?
> constructor = clazz.getConstructor(new Class[]{Workable.class}); proxyObject = constructor.newInstance(new Middleman()); } catch (Exception e) { e.printStackTrace(); } return proxyObject; } public static void main(String[] args) { Workable workable = (Workable)Proxy.newInstance(); workable.renting(); } }
2.1中的動態編譯的java對象眼下所實現的接口仍是固定這樣不符合個人需求,思考:怎樣實現可以代理不論什麼接口的代理類???
package com.jyd.proxy; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; import java.util.ArrayList; import java.util.List; import javax.tools.JavaCompiler; import javax.tools.JavaCompiler.CompilationTask; import javax.tools.StandardJavaFileManager; import javax.tools.ToolProvider; public class Proxy { private static final String BASE_TARGET_PATH = "d:/src/"; private static final String TARGET_PATH = BASE_TARGET_PATH + "com/jyd/proxy"; private static final String RT = "\r\n"; private static final String PROXY_CLASS_NAME = "$Proxy"; private static URLClassLoader classLoader; /** * 一、分析所有靜態代理類。模式都是固定的如 * 開始以前處理邏輯 * target.method()目標對象調用方法 * 目標對象調用以後處理邏輯 * * 處於這樣的狀況的話,那麼我經過代碼本身主動生成模式固定的代理類 * @return */ public static Object newInstance(Class<?> interf){ // 一、建立一個段代碼的字符經過代碼編譯,生成對象 // 1.二、怎樣將代理對象實現爲動態的? // 可以在建立Proxy的newInstance方法的時候將接口對象傳入再經過反射來獲取此接口中所有的方法 // " @Override"+RT+ // " public void renting() {"+RT+ // " long startTime = System.currentTimeMillis();"+RT+ // " System.out.println(\"開始計時: \" + startTime);"+RT+ // " workable.renting();"+RT+ // " long endTime = System.currentTimeMillis();"+RT+ // " System.out.println(\"這種方法一共使用多長時間:\" + (endTime - startTime));"+RT+ // " }"+RT+ // 1.三、一個接口中有N個方法需要代理實現那麼僅僅能循環獲取該類中所有的方法 String methodSources = ""; Method[] methods = interf.getMethods(); if(null != methods && methods.length > 0){ for(Method m : methods){ methodSources += " @Override"+RT+ " public void "+m.getName()+"() {"+RT+ " long startTime = System.currentTimeMillis();"+RT+ " System.out.println(\"開始計時: \" + startTime);"+RT+ " target."+m.getName()+"();"+RT+ " long endTime = System.currentTimeMillis();"+RT+ " System.out.println(\"這種方法一共使用多長時間:\" + (endTime - startTime));"+RT+ " }"+RT; } } String proxySource = "package com.jyd.proxy;"+RT+ "public class "+PROXY_CLASS_NAME+" implements "+interf.getName()+"{"+RT+ " private "+interf.getName()+" target;"+RT+ " public "+PROXY_CLASS_NAME+"("+interf.getName()+" target) {"+RT+ " super();"+RT+ " this.target = target;"+RT+ " }"+RT+ methodSources +RT+ "}"; //推斷目標路徑是否存在 File targetDir = new File(TARGET_PATH); if(!targetDir.exists())targetDir.mkdirs(); //將源代碼文件生成到磁盤上 File file = new File(TARGET_PATH, PROXY_CLASS_NAME+".java"); try { FileWriter fw = new FileWriter(file); fw.write(new String(proxySource.getBytes(), "UTF-8")); fw.close(); } catch (Exception e) { e.printStackTrace(); } //使用代碼動態編譯此java文件 JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); StandardJavaFileManager standardJavaFileManager = compiler.getStandardFileManager(null, null, null); List<File> files = new ArrayList<File>(); files.add(file); Iterable compilationUnits = standardJavaFileManager.getJavaFileObjectsFromFiles(files); CompilationTask task = compiler.getTask(null, standardJavaFileManager, null, null, null, compilationUnits); task.call(); //經過URLClassLoader來載入編譯好的字節碼文件 URL[] urls = null;; try { urls = new URL[]{new URL("file:/" + BASE_TARGET_PATH)}; } catch (MalformedURLException e) { e.printStackTrace(); } URLClassLoader classLoader = new URLClassLoader(urls); //此處載入class類名需要類的全路徑 Class<?> clazz = null; try { clazz = classLoader.loadClass("com.jyd.proxy."+PROXY_CLASS_NAME); } catch (ClassNotFoundException e) { e.printStackTrace(); } Object proxyObject = null; try { Constructor<?
> constructor = clazz.getConstructor(new Class[]{Workable.class}); proxyObject = constructor.newInstance(new Middleman()); } catch (Exception e) { e.printStackTrace(); } return proxyObject; } public static void main(String[] args) { Workable workable = (Workable)Proxy.newInstance(Workable.class); workable.renting(); } }
經過2.2的處理,proxy類可以動態生成代理不論什麼接口代理類,但是美中不足的是代理類中所有作的事情是固定。這樣則沒有靈活性。思考???
此處能不能由子類本身實現需要處理的邏輯了?可以定義一個規範。由子類本身去實現,由於多態方式,當程序執行的時候那個子類來實現的則調用子類的方法
那麼此處需要定義一個接口:
package com.jyd.proxy; import java.lang.reflect.Method; /** * 定義一個出來規範的邏輯,讓子類去實現本身的業務邏輯 * @author hadoop * */ public interface InvocationHandler { /** * * @param o 當前實例的對象 * @param method 須要被代理的方法 */ public void invok(Object o, Method method); }
建立一個子類處理類來實現記錄方法時間的類
package com.jyd.proxy; import java.lang.reflect.Method; public class TimeInvocationHandler implements InvocationHandler { private Object target; public TimeInvocationHandler(Object target) { super(); this.target = target; } @Override public void invok(Object o, Method method) { //開始處理的業務邏輯 long startTime = System.currentTimeMillis(); System.out.println("開始計時: " + startTime); try { //被代理的對象的方法需要調用,針對本身的目標對象,此處沒有目標對象。需要將目標對象經過構成函數傳入就能夠 method.invoke(target, new Object[]{}); } catch (Exception e) { e.printStackTrace(); } long endTime = System.currentTimeMillis(); System.out.println("這種方法一共使用多長時間:" + (endTime - startTime)); } }
經過代理類動態建立代理對象
package com.jyd.proxy; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; import java.util.ArrayList; import java.util.List; import javax.tools.JavaCompiler; import javax.tools.JavaCompiler.CompilationTask; import javax.tools.StandardJavaFileManager; import javax.tools.ToolProvider; public class Proxy { private static final String BASE_TARGET_PATH = "d:/src/"; private static final String TARGET_PATH = BASE_TARGET_PATH + "com/jyd/proxy"; private static final String RT = "\r\n"; private static final String PROXY_CLASS_NAME = "$Proxy"; /** * 一、分析所有靜態代理類,模式都是固定的如 * 開始以前處理邏輯 * target.method()目標對象調用方法 * 目標對象調用以後處理邏輯 * * 處於這樣的狀況的話,那麼我經過代碼本身主動生成模式固定的代理類 * @return */ public static Object newInstance(Class<?> interf, InvocationHandler h){ // 一、建立一個段代碼的字符經過代碼編譯。生成對象 // 1.二、怎樣將代理對象實現爲動態的? // 可以在建立Proxy的newInstance方法的時候將接口對象傳入再經過反射來獲取此接口中所有的方法 // " @Override"+RT+ // " public void renting() {"+RT+ // " long startTime = System.currentTimeMillis();"+RT+ // " System.out.println(\"開始計時: \" + startTime);"+RT+ // " workable.renting();"+RT+ // " long endTime = System.currentTimeMillis();"+RT+ // " System.out.println(\"這種方法一共使用多長時間:\" + (endTime - startTime));"+RT+ // " }"+RT+ // 1.三、一個接口中有N個方法需要代理實現那麼僅僅能循環獲取該類中所有的方法 // String methodSources = ""; // Method[] methods = interf.getMethods(); // if(null != methods && methods.length > 0){ // for(Method m : methods){ // methodSources += " @Override"+RT+ // " public void "+m.getName()+"() {"+RT+ // " long startTime = System.currentTimeMillis();"+RT+ // " System.out.println(\"開始計時: \" + startTime);"+RT+ // " target."+m.getName()+"();"+RT+ // " long endTime = System.currentTimeMillis();"+RT+ // " System.out.println(\"這種方法一共使用多長時間:\" + (endTime - startTime));"+RT+ // " }"+RT; // } // } // // 現在需要使用一個接口來規範此業務操做邏輯。需要改寫現在處理業務,那麼需要處理邏輯的類傳入到代理對象中 // String methodSources = ""; Method[] methods = interf.getMethods(); if(null != methods && methods.length > 0){ for(Method m : methods){ methodSources += " @Override"+RT+ " public void "+m.getName()+"() {"+RT+ " try {"+RT+ " java.lang.reflect.Method md = " + interf.getName() +".class.getMethod(\""+m.getName()+"\", new Class[]{});"+RT+ " h.invok(this, md);"+RT+ " }catch(Exception e){e.printStackTrace();}"+RT+ " }"+RT; } } // 此處需要把InvocationHandler對象傳入到代理類中,經過InvocationHandler中經過反射來調用被代理的對象的方法 // String proxySource = "package com.jyd.proxy;"+RT+ // "public class "+PROXY_CLASS_NAME+" implements "+interf.getName()+"{"+RT+ // " private "+interf.getName()+" target;"+RT+ // " public "+PROXY_CLASS_NAME+"("+interf.getName()+" target) {"+RT+ // " super();"+RT+ // " this.target = target;"+RT+ // " }"+RT+ // methodSources +RT+ // "}"; String proxySource = "package com.jyd.proxy;"+RT+ "public class "+PROXY_CLASS_NAME+" implements "+interf.getName()+"{"+RT+ " private com.jyd.proxy.InvocationHandler h;"+RT+ " public "+PROXY_CLASS_NAME+"(com.jyd.proxy.InvocationHandler h) {"+RT+ " super();"+RT+ " this.h = h;"+RT+ " }"+RT+ methodSources +RT+ "}"; //推斷目標路徑是否存在 File targetDir = new File(TARGET_PATH); if(!targetDir.exists())targetDir.mkdirs(); //將源代碼文件生成到磁盤上 File file = new File(TARGET_PATH, PROXY_CLASS_NAME+".java"); try { FileWriter fw = new FileWriter(file); fw.write(new String(proxySource.getBytes(), "UTF-8")); fw.close(); } catch (Exception e) { e.printStackTrace(); } //使用代碼動態編譯此java文件 // 獲取java中JDK中java編譯器,此編譯器僅僅在jdk1.6以後提供。假設要作這個實驗jdk版本號大於1.6 JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); StandardJavaFileManager standardJavaFileManager = compiler.getStandardFileManager(null, null, null); List<File> files = new ArrayList<File>(); files.add(file); Iterable compilationUnits = standardJavaFileManager.getJavaFileObjectsFromFiles(files); CompilationTask task = compiler.getTask(null, standardJavaFileManager, null, null, null, compilationUnits); task.call(); //經過URLClassLoader來載入編譯好的字節碼文件 URL[] urls = null;; try { urls = new URL[]{new URL("file:/" + BASE_TARGET_PATH)}; } catch (MalformedURLException e) { e.printStackTrace(); } URLClassLoader classLoader = new URLClassLoader(urls); //此處載入class類名需要類的全路徑 Class<?> clazz = null; try { clazz = classLoader.loadClass("com.jyd.proxy."+PROXY_CLASS_NAME); } catch (ClassNotFoundException e) { e.printStackTrace(); } Object proxyObject = null; try { // Constructor<?> constructor = clazz.getConstructor(new Class[]{Workable.class}); // proxyObject = constructor.newInstance(new Middleman()); // 此時proxy類中就不能是接口對象而InvocationHandler Constructor<?> constructor = clazz.getConstructor(InvocationHandler.class); proxyObject = constructor.newInstance(h); } catch (Exception e) { e.printStackTrace(); } try { classLoader.close(); } catch (IOException e) { e.printStackTrace(); } return proxyObject; } public static void main(String[] args) { Workable workable = (Workable)Proxy.newInstance(Workable.class, new TimeInvocationHandler(new Middleman())); workable.renting(); } }
package com.jyd.proxy; /** * 定義一個可以工做的接口,定義一系列操做方法 * @author hadoop * */ public interface Workable { void renting(); }
package com.jyd.proxy; import java.util.Random; /** * 定義一箇中介人來實現可以工做的接口 * @author hadoop * */ public class Middleman implements Workable { private Random random = new Random(); @Override public void renting() { //中介操做 try { Thread.sleep(random.nextInt(1000)); System.out.println("中介開始租房..."); } catch (InterruptedException e) { e.printStackTrace(); } } }
package com.jyd.proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class TimeInvocationHandler implements InvocationHandler { private Object target; public TimeInvocationHandler(Object target) { super(); this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //開始處理的業務邏輯 long startTime = System.currentTimeMillis(); System.out.println("開始計時: " + startTime); Object result = null; try { //被代理的對象的方法需要調用,針對本身的目標對象。此處沒有目標對象。需要將目標對象經過構成函數傳入就能夠 result = method.invoke(target, args); } catch (Exception e) { e.printStackTrace(); } long endTime = System.currentTimeMillis(); System.out.println("這種方法一共使用多長時間:" + (endTime - startTime)); return result; } }
package com.jyd.proxy; import java.lang.reflect.Proxy; public class Client { public static void main(String[] args) { Workable workable = (Workable) Proxy.newProxyInstance(Workable.class.getClassLoader(), new Class[]{Workable.class}, new TimeInvocationHandler(new Middleman())); workable.renting(); } }
AOP(Aspect-OrientedProgramming。面向方面編程),可以說是OOP(Object-OrientedPrograming,面向對象編程)的補充和無缺。OOP引入封裝、繼承和多態性等概念來創建一種對象層次結構,用以模擬公共行爲的一個集合。當咱們需要爲分散的對象引入公共行爲的時候。OOP則顯得無能爲力。
也就是說,OOP贊成你定義從上到下的關係。但並不適合定義從左到右的關係。好比日誌功能。日誌代碼每每水平地散佈在所有對象層次中。而與它所散佈到的對象的核心功能毫無關係。對於其它類型的代碼。如安全性、異常處理和透明的持續性也是如此。
這樣的散佈在各處的無關的代碼被稱爲橫切(cross-cutting)代碼,在OOP設計中。它致使了大量代碼的反覆。而不利於各個模塊的重用。
Aspect(方面):一個關注點的模塊化,這個關注點實現可能另外橫切多個對象。
事務管理是J2EE應用中一個很是好的橫切關注點樣例。方面用spring的Advisor或攔截器實現。
Joinpoint(鏈接點):程序運行過程當中明白的點,如方法的調用或特定的異常被拋出。
Advice(通知):在特定的鏈接點,AOP框架運行的動做。
各類類型的通知包含「around」、「before」、「throws」通知。
Pointcut(切入點):指定一個通知將被引起的一系列鏈接點的集合。
weaving(織入):組裝方面來建立一個被通知對象。