JAVA技术发展——你不知道的J2SE(五)
2016-05-02 17:05
633 查看
一、Proxy—程序中的代理使用
试想如何为已经存在的类添加一些新的功能,例如日志、事务处理;这些类已经编写好,数量庞大,且实现相同接口;如果工程允许可能比较粗暴的就是直接向类中添加需要新增的代码逻辑,但现实时:添加这些功能要修改的不是一个两个类。
机智的java君采用代理解决这类繁琐的问题:
1、创建一个与目标类实现相同接口的代理类,代理类的每个方法调用目标类的相同方法
2、在调用目标类的相同方法前后,添加需要新增的功能代码。
3、客户端直接调用代理类。
这样的做的好处是,既不用对原目标类进行修改便满足了添加新功能的需求,更重要的是,它解决的是一个量级的困扰。
二、动态代理技术
那么问题又来了,是不是没出现一次这样的需求,我就要编写一个代理类呢??久而久之,我的写多少代理类来应对这种情况?你遇到的问题java君都给你出主意了。动态代理就是应对这种情况,JVM可以在运行期动态生成类的字节码,使用类加载器生成一个类用作代理类,这就是动态代理。 说着有点玄乎,其实就几行代码如下:
注:使用JVM动态创建类的步骤不外乎这么几步:
1、传入类的全称或者使用类加载器获取类的字节码
2、根据字节码获取构造器方法,调用newInstance方法创建对象。下面代码同此思路
1、构造一个InvocationHandler对象作为参数传入构造器,再调用newInstance方法创建对象
另一种写法:
和afterMethod方法,中间执行目标类方法,实现在调用目标对象前后均添加新的功能实现,且根据传入的目标类和接口的不同,放之四海皆为准,任何类的需要这两个方法(如日志、事务处理功能),直接调用getProxy方法,传入目标类和实现接口即可享用beforeMethod 和afterMethod方法。这就是典型的AOP编程思想。
下面是advice接口类代码。
1、创建一个BeanFactory,根据beanName创建对应java对象;(Spring通过配置文件加载创建具体对象)提供getBean方法,如果为普通java类则直接创建,如果为代理类,则返回该代理类。
试想如何为已经存在的类添加一些新的功能,例如日志、事务处理;这些类已经编写好,数量庞大,且实现相同接口;如果工程允许可能比较粗暴的就是直接向类中添加需要新增的代码逻辑,但现实时:添加这些功能要修改的不是一个两个类。
机智的java君采用代理解决这类繁琐的问题:
1、创建一个与目标类实现相同接口的代理类,代理类的每个方法调用目标类的相同方法
2、在调用目标类的相同方法前后,添加需要新增的功能代码。
3、客户端直接调用代理类。
这样的做的好处是,既不用对原目标类进行修改便满足了添加新功能的需求,更重要的是,它解决的是一个量级的困扰。
二、动态代理技术
那么问题又来了,是不是没出现一次这样的需求,我就要编写一个代理类呢??久而久之,我的写多少代理类来应对这种情况?你遇到的问题java君都给你出主意了。动态代理就是应对这种情况,JVM可以在运行期动态生成类的字节码,使用类加载器生成一个类用作代理类,这就是动态代理。 说着有点玄乎,其实就几行代码如下:
注:使用JVM动态创建类的步骤不外乎这么几步:
1、传入类的全称或者使用类加载器获取类的字节码
2、根据字节码获取构造器方法,调用newInstance方法创建对象。下面代码同此思路
public class ProxyTest { public static void main(String[] args) throws Exception { // 根据Proxy类获取类加载器、创建动态类 Class clazzProxy1 = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class); System.out.println(clazzProxy1.getName()); // com.sun.proxy.$Proxy0 // 查看该类的所有构造方法 System.out.println("-----------------Constructor para--------------"); Constructor[] cs = clazzProxy1.getConstructors(); for (Constructor c : cs) { String name = c.getName(); StringBuilder sBuilder = new StringBuilder(name); sBuilder.append('('); // 获取构造方法的参数类型 Class[] classTypes = c.getParameterTypes(); for (Class clazzPara : classTypes) { sBuilder.append(clazzPara.getName()).append(','); } if (classTypes.length != 0 && classTypes != null) sBuilder.deleteCharAt(sBuilder.length() - 1);// 去掉最后一个‘,’ sBuilder.append(')'); System.out.println(sBuilder.toString()); // com.sun.proxy.$Proxy0(java.lang.reflect.InvocationHandler) // 只有一个构造方法,接收一个InvocationHandler参数 } // 获取方法 System.out.println("-----------------Method para--------------"); Method[] methods = clazzProxy1.getMethods(); for (Method c : methods) { String name = c.getName(); StringBuilder sBuilder = new StringBuilder(name); sBuilder.append('('); // 获取构造方法的参数类型 Class[] classTypes = c.getParameterTypes(); for (Class clazzPara : classTypes) { sBuilder.append(clazzPara.getName()).append(','); } if (classTypes.length != 0 && classTypes != null) sBuilder.deleteCharAt(sBuilder.length() - 1);// 去掉最后一个‘,’ sBuilder.append(')'); System.out.println(sBuilder.toString()); }获取到Collection类的构造方法发现,该构造方法是带有参数的,形如:com.sun.proxy.$Proxy0(java.lang.reflect.InvocationHandler),直接使用newInstance方法无法创建对象。这里介绍两种方式创建
1、构造一个InvocationHandler对象作为参数传入构造器,再调用newInstance方法创建对象
Constructor constructor = clazzProxy1.getConstructor(InvocationHandler.class);// 参数类型 // 构造一个InvocationHandler参数 class MyInvocationHandler implements InvocationHandler { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { return null; } } Collection myProxy = (Collection) constructor .newInstance(new MyInvocationHandler());// 传入一个具体的InvocationHandler参数 System.out.println(myProxy.toString());
另一种写法:
// 跟先new一个对象再传入是一个效果。 Collection myProxy2 = (Collection) constructor .newInstance(new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { return null; } });2、直接调用newProxyInstance方法,传入目标对象创建
// 第三种直接一步到位,newProxyInstance final ArrayList target = new ArrayList(); Collection myProxy3 = (Collection) getProxy(target,new AdviseImpl());//这里调用的target 传入,就很专业了,不仅针对集合,任何对象传入都ok myProxy3.add("df"); //分别调用新的invokehandler对象 ,又new一个array 所以最后就是0 myProxy3.add("kd"); myProxy3.add("dkj"); System.out.println(myProxy3.size()); } private static Object getProxy(final Object target,final Advice advise) { Collection myProxy3 = (Collection) Proxy.newProxyInstance( target.getClass().getClassLoader(),//目标类的构造器,传什么目标类,就实现什么接口 target.getClass().getInterfaces(),//改成实现目标的接口 new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { advise.beforeMethod(method); Object retVal=method.invoke(target, args); advise.afterMethod(method); return retVal; } }); return myProxy3; }这里的newProxyInstance分别传入目标类的构造器、实现接口和InvocationHandler三个参数,同时在调用invoke方法,调用新增的beforeMethod
和afterMethod方法,中间执行目标类方法,实现在调用目标对象前后均添加新的功能实现,且根据传入的目标类和接口的不同,放之四海皆为准,任何类的需要这两个方法(如日志、事务处理功能),直接调用getProxy方法,传入目标类和实现接口即可享用beforeMethod 和afterMethod方法。这就是典型的AOP编程思想。
下面是advice接口类代码。
public interface Advice { public void beforeMethod(Method method); public void afterMethod(Method method); }三、实现Spring AOP的封装和配置
1、创建一个BeanFactory,根据beanName创建对应java对象;(Spring通过配置文件加载创建具体对象)提供getBean方法,如果为普通java类则直接创建,如果为代理类,则返回该代理类。
public class BeanFactory { //加载配置文件,获取props属性 Properties props=new Properties(); public BeanFactory(InputStream input) throws IOException { props.load(input); } // public Object getBean(String name) throws InstantiationException, IllegalAccessException, ClassNotFoundException { String className=props.getProperty(name); Object bean=new Object(); try { bean=Class.forName(className).newInstance(); } catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) { e.printStackTrace(); } if(bean instanceof ProxyFactoryBean)//如果是代理类-领导--弄到代理类 { ProxyFactoryBean proxyFactoryBean=(ProxyFactoryBean)bean; Advice advice=(Advice) Class.forName(props.get(name)+".advise").newInstance(); Object target=Class.forName(props.get(name)+".target").newInstance(); proxyFactoryBean.setAdvise(advice); proxyFactoryBean.setTarget(target); Object proxy=proxyFactoryBean.getProxy(); return proxy; } return bean; }配置文件:
#xxx=java.util.ArrayList xxx=it.max.ProxyFactoryBean xxx.advise=it.max.Advice xxx.target=java.util.ArrayList2、创建代理beanFactory,用于接收目标类和目标接口,实现调用目标类方法前后调用新增系统功能。
public class ProxyFactoryBean { private Advice advise; private Object target; public Object getProxy() {//不传参了,直接顶一个一私有变量 Collection myProxy3 = (Collection) Proxy.newProxyInstance( target.getClass().getClassLoader(),//目标类的构造器,传什么目标类,就实现什么就扣 target.getClass().getInterfaces(),//改成实现目标的接口 new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { advise.beforeMethod(method); Object retVal=method.invoke(target, args); advise.afterMethod(method); return retVal; } }); return myProxy3; } }3、客户端测试类,用于加载配置文件,调用beanFactory创建对象实例,输出实例名。
public class AOPtest { public static void main(String[] args) throws InstantiationException, IllegalAccessException, ClassNotFoundException, IOException { InputStream input=AOPtest.class.getResourceAsStream("config.properties");//相对路径 Object bean=new BeanFactory(input).getBean("xxx"); System.out.println(bean.getClass().getName()); } }麻雀虽小,五脏俱全。这篇博客需要结合代码体会Proxy.
相关文章推荐
- JavaSE入门学习35:Java集合框架之List接口及其实现类ArrayList和LinkedList
- 深入理解java异常处理机制
- JDK源码学习(2)-TreeMap源码分析
- java并发编程(7)--Lock
- IT忍者神龟之Spring Task动态添加任务
- Java实现文件复制的四种方式
- JAVA SE 8 学习笔记(二)Stream API
- Java链接sql server并对它进行查询、增删改
- 《Java多线程编程核心技术》读书笔记
- Java InputStream、String、File相互转化
- java中Static&Final的使用方法,&&&Java中两个主要的抽象Abstract&&Implement
- Google Java编码规范
- maven+springmvc+easyui+fastjson+pagehelper
- RandomAccessFile类
- spring boot 异常处理
- 华为Java编程规范
- Java 多线程编程学习(一)
- Maven——Java项目构建工具
- 对象流
- 安卓开发之java基础笔记【6】