黑马程序员-Java基础加强(3)
2013-05-20 22:40
453 查看
------- android培训、java培训、期待与您交流! ----------
代理的概念与作用
为已存在的多个具有相同接口的目标类的各个方法增加一些系统功能,例如,异常处理、日志、计算方法的运行时间、事务管理、等等,你准备如何做?
编写一个与目标类具有相同接口的代理类,代理类的每个方法调用目标类的相同方法,并在调用方法时加上系统功能的代码。 (参看下页的原理图)
如果采用工厂模式和配置文件的方式进行管理,则不需要修改客户端程序,在配置文件中配置是使用目标类、还是代理类,这样以后很容易切换,譬如,想要日志功能时就配置代理类,否则配置目标类,这样,增加系统功能很容易,以后运行一段时间后,又想去掉系统功能也很容易。
代理架构图
AOP
交叉业务的编程问题即为面向方面的编程(Aspect oriented program ,简称AOP),AOP的目标就是要使交叉业务模块化。可以采用将切面代码移动到原始方法的周围,这与直接在方法中编写切面代码的运行效果是一样的。
使用代理技术正好可以解决这种问题,代理是实现AOP功能的核心和关键技术。
动态代理技术
要为系统中的各种接口的类增加代理功能,那将需要太多的代理类,全部采用静态代理方式,将是一件非常麻烦的事情!写成百上千个代理类,是不是太累!
JVM可以在运行期动态生成出类的字节码,这种动态生成的类往往被用作代理类,即动态代理类。
JVM生成的动态类必须实现一个或多个接口,所以,JVM生成的动态类只能用作具有相同接口的目标类的代理。
CGLIB库可以动态生成一个类的子类,一个类的子类也可以用作该类的代理,所以,如果要为一个没有实现接口的类生成动态代理类,那么可以使用CGLIB库。
代理类的各个方法中通常除了要调用目标的相应方法和对外返回目标返回的结果外,还可以在代理方法中的如下四个位置加上系统功能代码:
1.在调用目标方法之前
2.在调用目标方法之后
3.在调用目标方法前后
4.在处理目标方法异常的catch块中
动态生成的类实现了Collection接口(可以实现若干接口),生成的类有Collection接口中的所有方法和一个如下接受InvocationHandler参数的构造方法。
构造方法接受一个InvocationHandler对象,接受对象了要干什么用呢?该方法内部的代码会是怎样的呢?
实现Collection接口的动态类中的各个方法的代码又是怎样的呢?InvocationHandler接口中定义的invoke方法接受的三个参数又是什么意思?图解说明如下:
分析为什么动态类的实例对象的getClass()方法返回了正确结果呢?
调用代理对象的从Object类继承的hashCode, equals, 或toString这几个方法时,代理对象将调用请求转发给InvocationHandler对象,对于其他方法,则不转发调用请求。
怎样将目标类传进去?
直接在InvocationHandler实现类中创建目标类的实例对象,可以看运行效果和加入日志代码,但没有实际意义。
为InvocationHandler实现类注入目标类的实例对象,不能采用匿名内部类的形式了。
让匿名的InvocationHandler实现类访问外面方法中的目标类实例对象的final类型的引用变量。
将创建代理的过程改为一种更优雅的方式,eclipse重构出一个getProxy方法绑定接收目标同时返回代理对象,让调用者更懒惰,更方便,调用者甚至不用接触任何代理的API。
将系统功能代码模块化,即将切面代码也改为通过参数形式提供,怎样把要执行的系统功能代码以参数形式提供?
把要执行的代码装到一个对象的某个方法里,然后把这个对象作为参数传递,接收者只要调用这个对象的方法,即等于执行了外界提供的代码!
为bind方法增加一个Advice参数。
用于为某个对象生成和返回其代理对象,源对象必须实现接口,生成的代理对象会实现与源对象相同的接口,注意,
源对象的类必须自己定义时就实现接口,从该类的祖辈类上继承的接口是无效的。
该方法接口两个参数:一个是目标对象,另一个是封装了用户系统功能代码的Advice对象,
该对象必须实现Advice接口。
*/
getProxy
由此联想到spring的一个问题:spring无法将通知应用到目标类的父类的方法上,例如,我们写的Action继承了DispatachAction,那么spring无法将advice应用到execute方法上。
动态代理的工作原理图:
实现AOP功能的封装与配置
工厂类BeanFactory负责创建目标类或代理类的实例对象,并通过配置文件实现切换。其getBean方法根据参数字符串返回一个相应的实例对象,如果参数字符串在配置文件中对应的类名不是ProxyFactoryBean,则直接返回该类的实例对象,否则,返回该类实例对象的getProxy方法返回的对象。
BeanFactory的构造方法接收代表配置文件的输入流对象,配置文件格式如下:
#xxx=java.util.ArrayList
xxx=cn.itcast.ProxyFactoryBean
xxx.target=java.util.ArrayList
xxx.advice=cn.itcast.MyAdvice
ProxyFacotryBean充当封装生成动态代理的工厂,需要为工厂类提供哪些配置参数信息?
目标
通知
编写客户端应用:
编写实现Advice接口的类和在配置文件中进行配置
调用BeanFactory获取对象
代理的概念与作用
为已存在的多个具有相同接口的目标类的各个方法增加一些系统功能,例如,异常处理、日志、计算方法的运行时间、事务管理、等等,你准备如何做?
编写一个与目标类具有相同接口的代理类,代理类的每个方法调用目标类的相同方法,并在调用方法时加上系统功能的代码。 (参看下页的原理图)
如果采用工厂模式和配置文件的方式进行管理,则不需要修改客户端程序,在配置文件中配置是使用目标类、还是代理类,这样以后很容易切换,譬如,想要日志功能时就配置代理类,否则配置目标类,这样,增加系统功能很容易,以后运行一段时间后,又想去掉系统功能也很容易。
代理架构图
AOP
交叉业务的编程问题即为面向方面的编程(Aspect oriented program ,简称AOP),AOP的目标就是要使交叉业务模块化。可以采用将切面代码移动到原始方法的周围,这与直接在方法中编写切面代码的运行效果是一样的。
使用代理技术正好可以解决这种问题,代理是实现AOP功能的核心和关键技术。
动态代理技术
要为系统中的各种接口的类增加代理功能,那将需要太多的代理类,全部采用静态代理方式,将是一件非常麻烦的事情!写成百上千个代理类,是不是太累!
JVM可以在运行期动态生成出类的字节码,这种动态生成的类往往被用作代理类,即动态代理类。
JVM生成的动态类必须实现一个或多个接口,所以,JVM生成的动态类只能用作具有相同接口的目标类的代理。
CGLIB库可以动态生成一个类的子类,一个类的子类也可以用作该类的代理,所以,如果要为一个没有实现接口的类生成动态代理类,那么可以使用CGLIB库。
代理类的各个方法中通常除了要调用目标的相应方法和对外返回目标返回的结果外,还可以在代理方法中的如下四个位置加上系统功能代码:
1.在调用目标方法之前
2.在调用目标方法之后
3.在调用目标方法前后
4.在处理目标方法异常的catch块中
动态生成的类实现了Collection接口(可以实现若干接口),生成的类有Collection接口中的所有方法和一个如下接受InvocationHandler参数的构造方法。
构造方法接受一个InvocationHandler对象,接受对象了要干什么用呢?该方法内部的代码会是怎样的呢?
实现Collection接口的动态类中的各个方法的代码又是怎样的呢?InvocationHandler接口中定义的invoke方法接受的三个参数又是什么意思?图解说明如下:
分析为什么动态类的实例对象的getClass()方法返回了正确结果呢?
调用代理对象的从Object类继承的hashCode, equals, 或toString这几个方法时,代理对象将调用请求转发给InvocationHandler对象,对于其他方法,则不转发调用请求。
怎样将目标类传进去?
直接在InvocationHandler实现类中创建目标类的实例对象,可以看运行效果和加入日志代码,但没有实际意义。
为InvocationHandler实现类注入目标类的实例对象,不能采用匿名内部类的形式了。
让匿名的InvocationHandler实现类访问外面方法中的目标类实例对象的final类型的引用变量。
将创建代理的过程改为一种更优雅的方式,eclipse重构出一个getProxy方法绑定接收目标同时返回代理对象,让调用者更懒惰,更方便,调用者甚至不用接触任何代理的API。
将系统功能代码模块化,即将切面代码也改为通过参数形式提供,怎样把要执行的系统功能代码以参数形式提供?
把要执行的代码装到一个对象的某个方法里,然后把这个对象作为参数传递,接收者只要调用这个对象的方法,即等于执行了外界提供的代码!
为bind方法增加一个Advice参数。
用于为某个对象生成和返回其代理对象,源对象必须实现接口,生成的代理对象会实现与源对象相同的接口,注意,
源对象的类必须自己定义时就实现接口,从该类的祖辈类上继承的接口是无效的。
该方法接口两个参数:一个是目标对象,另一个是封装了用户系统功能代码的Advice对象,
该对象必须实现Advice接口。
*/
getProxy
由此联想到spring的一个问题:spring无法将通知应用到目标类的父类的方法上,例如,我们写的Action继承了DispatachAction,那么spring无法将advice应用到execute方法上。
动态代理的工作原理图:
package org.javainhance03; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.ArrayList; import java.util.Collection; public class ProxyTest { /** * @param args * @throws NoSuchMethodException * @throws SecurityException * @throws InvocationTargetException * @throws IllegalAccessException * @throws InstantiationException * @throws IllegalArgumentException */ public static void main(String[] args) throws SecurityException, NoSuchMethodException, IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException { // TODO Auto-generated method stub Class classProxy1 = Proxy.getProxyClass( Collection.class.getClassLoader(), Collection.class);// 生成Collection类的字节码 System.out.println(classProxy1.getName());// 得到这个代理类的名字 System.out.println("--------constructors list ------------"); Constructor[] constructors = classProxy1.getConstructors();// 得到代理类中的构造方法 for (Constructor constructor : constructors) {// 打印出这些构造方法 String name = constructor.getName(); StringBuilder sBuilder = new StringBuilder(name); sBuilder.append("("); Class[] classParams = constructor.getParameterTypes(); for (int i = 0; i < classParams.length; i++) {// 得到构造方法中的参数 sBuilder.append(classParams[i].getName()).append(" args" + i) .append(","); } if (classParams.length != 0) { sBuilder.deleteCharAt(sBuilder.length() - 1); } sBuilder.append(")"); System.out.println(sBuilder);// 打印构造方法 } System.out.println("--------------methods list -----------------"); Method[] methods = classProxy1.getMethods();// 得到代理类中的方法 for (Method method : methods) {// 打印出这些构造方法 String name = method.getName(); StringBuilder sBuilder = new StringBuilder(name); sBuilder.append("("); Class[] classParams = method.getParameterTypes(); for (int i = 0; i < classParams.length; i++) {// 得到方法中的参数 sBuilder.append(classParams[i].getName()).append(" args" + i) .append(","); } if (classParams.length != 0) { sBuilder.deleteCharAt(sBuilder.length() - 1); } sBuilder.append(")"); System.out.println(sBuilder);// 打印成员方法 } Constructor constructor = classProxy1 .getConstructor(InvocationHandler.class);// 获取具体某一的构造方法 class MyInvocationHandler1 implements InvocationHandler {// 实现InvocationHandler接口 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // TODO Auto-generated method stub return null; } } Collection proxy1 = (Collection) constructor .newInstance(n 4000 ew MyInvocationHandler1());// 用MyInvocationHandler1创建动态类的实例对象 System.out.println(proxy1);// 打印出这个实例对象 proxy1.clear();// 调用 动态类中的方法 // proxy1.size(); //第二种方法实例化动态类对象 Collection proxy2 = (Collection) constructor .newInstance(new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // TODO Auto-generated method stub return null; } }); //第三种创建动态类代理对象的方法 Collection proxy = (Collection)Proxy.newProxyInstance(Collection.class.getClassLoader(), new Class[]{Collection.class}, new InvocationHandler(){ ArrayList target = new ArrayList();//用于实现动态Collection接口对象 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { long beginTime = System.currentTimeMillis();//测量method方法运行时间 //ArrayList target = new ArrayList(); Object reVal = method.invoke(target, args); long endTime = System.currentTimeMillis(); System.out.println(method.getName() + "方法运行时间:" + (endTime - beginTime)); return reVal; } }); proxy.add("123"); proxy.add("abc"); System.out.println(proxy.size()); final ArrayList target = new ArrayList(); final Advice myAdvice = new MyAdvice(); // 根据目标和建议生成系统功能 Collection proxy3 = (Collection) getProxy(target, myAdvice);//动态代理工作原理 proxy3.add("String"); proxy3.add("String"); proxy3.add("String"); System.out.println(proxy3.size()); } private static Object getProxy(final Object target, final Advice advice) { Object proxy3 = Proxy.newProxyInstance(target.getClass() .getClassLoader(), new Class[] { Collection.class }, new InvocationHandler() { @Override // 动态调用方法 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { /* * long beginTime = System.currentTimeMillis(); Object * reVal = method.invoke(target, args); long endTime = * System.currentTimeMillis(); * System.out.println(method.getName() + " run time:" + * (beginTime - endTime)); return reVal; // 返回方法的返回信息 */ // long beginTime = System.currentTimeMillis(); advice.beforeMethod(method); Object reVal = method.invoke(target, args); advice.afterMethod(method); // long endTime = System.currentTimeMillis(); return reVal; } }); return proxy3; } }
package org.javainhance03; import java.lang.reflect.Method; public interface Advice {//建议 void beforeMethod(Method method); void afterMethod(Method method); }
实现AOP功能的封装与配置
工厂类BeanFactory负责创建目标类或代理类的实例对象,并通过配置文件实现切换。其getBean方法根据参数字符串返回一个相应的实例对象,如果参数字符串在配置文件中对应的类名不是ProxyFactoryBean,则直接返回该类的实例对象,否则,返回该类实例对象的getProxy方法返回的对象。
BeanFactory的构造方法接收代表配置文件的输入流对象,配置文件格式如下:
#xxx=java.util.ArrayList
xxx=cn.itcast.ProxyFactoryBean
xxx.target=java.util.ArrayList
xxx.advice=cn.itcast.MyAdvice
ProxyFacotryBean充当封装生成动态代理的工厂,需要为工厂类提供哪些配置参数信息?
目标
通知
编写客户端应用:
编写实现Advice接口的类和在配置文件中进行配置
调用BeanFactory获取对象
package org.javainhance03.aopframework; import java.io.InputStream; import java.util.Properties; import org.javainhance03.Advice; public class BeanFactory { Properties props = new Properties(); public BeanFactory(InputStream ips){//接收一个配置文件 try { props.load(ips); } catch (Exception e) { e.printStackTrace(); } } public Object getBean(String name){ String className = props.getProperty(name); Object bean = null; try { Class clazz = Class.forName(className); bean = clazz.newInstance(); } catch (Exception e) { e.printStackTrace(); } if(bean instanceof ProxyFactoryBean){//如果 是ProxyFactoryBean对象则创建代理对象 Object proxy = null; ProxyFactoryBean proxyFactoryBean = (ProxyFactoryBean)bean; Advice advice = null; Object target = null; try { advice = (Advice) Class.forName(props.getProperty(name + ".advice")).newInstance();//设置adive target = Class.forName(props.getProperty(name + ".target")).newInstance(); //创建target目标 } catch (InstantiationException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } proxyFactoryBean.setAdvice(advice);//工厂代理类接收建议 proxyFactoryBean.setTarget(target);//工厂代理类接收目标 proxy = proxyFactoryBean.getProxy(); return proxy; } return bean; } }
package org.javainhance03.aopframework; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.Collection; import org.javainhance03.Advice; public class ProxyFactoryBean { public Advice getAdvice() { return advice; } public void setAdvice(Advice advice) { this.advice = advice; } public Object getTarget() { return target; } public void setTarget(Object target) { this.target = target; } private Advice advice; private Object target; public Object getProxy() { Object proxy3 = Proxy.newProxyInstance(target.getClass() .getClassLoader(), new Class[] { Collection.class }, new InvocationHandler() { @Override // 动态调用方法 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { advice.beforeMethod(method); Object reVal = method.invoke(target, args); advice.afterMethod(method); return reVal; } }); return proxy3; } }
package org.javainhance03.aopframework; import java.io.InputStream; public class AopFrameworkTest { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub InputStream ips = AopFrameworkTest.class.getResourceAsStream("config.properties");//测试文件要和代码在同一个文件下 Object bean = new BeanFactory(ips).getBean("xxx"); System.out.println(bean.getClass().getName());//查看是否创建代理还是目标 } }
相关文章推荐
- 黑马程序员_java基础加强学习之枚举
- 黑马程序员--Java基础加强(1)
- 黑马程序员--Java基础加强1
- 黑马程序员Java基础加强成员方法的反射
- 黑马程序员_java基础加强6_多线程加强
- 黑马程序员——张孝祥Java基础加强笔记
- 黑马程序员_java基础加强之JavaBean
- 黑马程序员-JAVA基础加强-新特性简介
- 黑马程序员__JAVA基础加强__高新技术(一)
- 黑马程序员_java基础加强12_空中网题解_老师解答
- 黑马程序员-Java基础加强之反射
- 黑马程序员-Java基础加强之代理
- 【黑马程序员】 java笔记——基础加强
- 黑马程序员--Java基础加强学习笔记之重载和覆写、可变参数、枚举
- 黑马程序员--第三阶段--Java基础加强--第27天
- 黑马程序员-----java基础加强<一>
- 《黑马程序员》基础加强---JavaBean
- 黑马程序员_Java基础加强3
- 黑马程序员—Java基础加强学习笔记之泛型
- 黑马程序员Java基础加强33-40学习日记