能够想到的方式起码有两种继承和聚合。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(织入):组装方面来建立一个被通知对象。