黑马程序员_高新技术四(Java动态代理、AOP简单框架)

----------- android培训java培训、java博客、java学习型技术博客、期待与您交流! --------------


本章主要的知识点总结:1、动态代理的简介及AOP的概念

                                        2、如何创建动态类代理类实例,分析InvocationHandler对象的运行原理

                                        3、实现一个简单的可配置的AOP框架(类似Spring)


一.代理类

1.代理简介
       要为已存在的多个具体相同接口的目标类的各个方法增加一些系统功能,例如:异常处理,日志,计算机方法的运行时间,事务管理,等等,那么就用到了代理类。 
     代理类使用过程 :让代理类的每个方法调用目标类的相同方法,并在调用方法时加上系统功能的代码,代理是实现AOP编程的核心和关键技术。
2.动态代理
        要为系统中的各种接口的类增加代理功能,那将需要太多的代理类,全部采用静态代理方式,将会非常麻烦,要写百上千个代理类。JVM可以在运行期可以自动的动态生成出类的字节码,这种动态生成的类被用作代理类。JVM生成的动态类必须实现一个或多个接口,所 以JVM生成的动态类只能用作具有相同接口的目标类的代理。如果要为一个没有实现接口的类生成动态代理类,那么可以使用CGLIB库动态生成一个类的子类,这个子类可以作用该类的代理。
 3.AOP的概念
  (1)系统中存在交叉业务,一个交叉业务就是要切入到系统中的一个方面,如下所示:
      安全     事务    日志
  StudentService----|--------|-------|------
  CourseService ----|--------|-------|------
  MiscService   ----|--------|-------|------
  (2)用具体的程序代码描述交叉业务:
  method1  method2  method3
  {   {   {
  -----------------------------------------------切面
   ......   ......   ......
  -----------------------------------------------切面
  }   }   }
 ( 3)交叉业务的编程问题即为面向方面的编程(Aspect oriented program ,简称AOP),AOP的目标就是要使交叉业务模块化。可以采用将切面代码移动到原始方法的周围,这与直接在方法中编写切面代码的运行效果是一样的,如下所示:
  ----------------------------------------------切面
  func1  func2  func3
  {   [   {
   .....   ....   ....
  }   }   }
  ----------------------------------------------切面
  (4)安全,事务,日志等功能要贯穿到好多个模块中,所以,它们是交叉业务。
  使用代理技术正好可以解决这种问题,代理是实现AOP功能的核心和关键技术。
  
    创建动态类
代理类实例

 代码示例:

  1. public class ProxyTest {  
  2. public static void main(String[] args) throws Exception{     
  3. //得到实现了Collection接口的动态代理类的字节码
  4. Class clazzProxy1 = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class);  
  5.   
  6. //得到代理类的构造方法  
  7. Constructor[] constructors  = clazzProxy1.getConstructors();  
  8. for(Constructor constructor : constructors){  
  9. String name = constructor.getName();  
  10. StringBuilder sb = new StringBuilder(name);  
  11. sb.append("(");  
  12. //得到构造方法的参数列表 
  13. Class[] claszzParams  = constructor.getParameterTypes();  
  14. for(Class classparam : claszzParams){  
  15. sb.append(classparam.getName()+",");  
  16. }  
  17. if(claszzParams!= null && claszzParams.length!=0)  
  18. sb.deleteCharAt(sb.length()-1);  
  19. sb.append(")");  
  20. System.out.println(sb.toString());  
  21. }  
  22.   
  23. //得到代理类中的方法列表  
  24. Method[] methods = clazzProxy1.getMethods();  
  25. for(Method method : methods){  
  26. String methodname = method.getName();  
  27. StringBuilder sb = new StringBuilder(methodname);  
  28. sb.append("(");  
  29. Class[] clazzParams = method.getParameterTypes();  
  30. for(Class clazzParam : clazzParams){  
  31. sb.append(clazzParam.getName()+",");  
  32.   
  33. }  
  34. if(clazzParams!= null && clazzParams.length!=  0)  
  35. sb.deleteCharAt(sb.length()-1);  
  36. sb.append(")");  
  37. System.out.println(sb.toString());  
  38. }


  1. //先得到代理类的构造函数再实例化对象。
  2. Constructor constructor = clazzProxy1.getConstructor(InvocationHandler.class);  
  3. class MyInvocationHandler implements InvocationHandler{  
  4. @Override  
  5. public Object invoke(Object proxy, Method method, Object[] args)  
  6. throws Throwable {   
  7. return null;  
  8. }  
  9. }  
  10. Collection col = (Collection)constructor.newInstance(new MyInvocationHandler());  
  11. System.out.println(col);  
  12. col.clear();    
  13. //用匿名内部类来实现InvocationHandler接口传递给newInstance的参数。只有有参数的构造方法 
  14. Collection col2 = (Collection)constructor.newInstance(new InvocationHandler(){  
  15.  
  16. @Override  
  17. public Object invoke(Object proxy, Method method, Object[] args)  
  18. throws Throwable {  
  19. // TODO Auto-generated method stub  
  20. return null;  
  21. }   
  22. });   
  23.  //直接调用代理类的 newProxyInstance方法,来创建动态代理类的实例对象
  24.  Collection proxy3 = (Collection)Proxy.newProxyInstance(   
  25. Collection.class.getClassLoader(),   
  26. new Class[]{Collection.class},  
  27. new InvocationHandler(){  
  28. public Object invoke(Object proxy, Method method, Object[] args)throws Throwable  
  29. Object retVal = method.invoke(target, args);  
  30. return retVal;    
  31. }  
  32. }   


4.分析InvocationHandler对象的运行原理
      InvocationHandler 是代理实例的调用处理程序 实现的接口。 每个代理实例都具有一个关联的调用处理程序。对代理实例调用方法时,将对方法调用进行编码并将其指派到它的调用处理程序的  invoke 方法。 

      猜想分析动态生成的类的内部代码:
 (1)动态生成的类实现了Collection接口(可以实现若干接口),生成的类有Collection接口中的所有方法和
   一个如下接受InvocationHandler参数的构造方法,构造方法接收一个invocationHandler对象
 (2)实现的Collection接口中的各个方法的代码又是怎样的呢?
  //生成的Collection接口中的方法的运行原理
   int size()
   { return handler.invoke(this,this.getClass().getMethod("size"),null);}
   void clear()
   {   handler.invoke(this,this.getClass().getMethod("clear"),null); }


 (3)..InvocationHandler接口中定义的invoke方法接收的三个参数又是什么意思?图解说明如下:
  Client程序调用objProxy.add("abc")方法时,涉及三要素:objProxy对象,add方法,"abc"参数
   Class Proxy$
   {  add(Object object)
    { return handler.invoke(Object proxy,Method method,Object[] args);  }
   }

(4)分析为什么动态类的实例对象的getClass()方法返回了正确结果? 
         调用代理实例对像从Object类继承的hashCode、equals、或toString这几个方法时,代理对象将调用
  请求转发给InvocationHandler对象,对于其它方法,自己实现。

编写可生成代理和插入代码的通用方法
代码示例:

  1. ArrayList target = new ArrayList();//目标类   
  2. Collection proxy3 = (Collection)getProxy(target,new MyAdvice());//代理类  
  3.   //每调用一次方法就会去调用InvocationHandler的invoke方法。
  4. proxy3.add("zxx");   
  5. proxy3.add("lxc");  
  6. proxy3.add("lxm");   
  7. proxy3.getClass().getName();
  8. //在Object类上继承的方法,只有hashCode,equals,toString()会委托InvocationHandler。  
  9. System.out.println(proxy3.size());  
  10. }  
  11. private static Object getProxy(final Object target,final Advice advice) {  
  12. Object proxy3 = Proxy.newProxyInstance(   
  13. target.getClass().getClassLoader(),   
  14. target.getClass().getInterfaces(),  
  15. new InvocationHandler(){  
  16. public Object invoke(Object proxy, Method method, Object[] args)throws Throwable {  
  17. advice.beforeMethod(method);//插入代码 
  18. Object retVal = method.invoke(target, args);//调用目标类方法。  
  19. advice.afterMethod(method);//插入代码 
  20. return retVal;  
  21. }  
  22. }  
  23. );  
  24. return proxy3;  
  25. }  
  26. }


三.实现简单的可配置的AOP框架(类似Spring)


 1.实现AOP功能的封装与配置
  (1)工厂类BeanFactory负责创建目标类或代理类的实例对象,并通过配置文件实现切换。其getBean方法
  根据参数字符串返回一个相应的实例对象,如果参数字符串在配置文件中对应的类名不是ProxyFactoryBean,
  则直接返回该类的实例对象,否则,返回该类实例对象的getProxy方法返回的对象。
  (2)BeanFactory的构造方法接收代表配置文件的输入流对象,配置文件格式如下:
 
 #xxx=java.util.ArrayList
  xxx=cn.itcast.ProxyFactoryBean
  xxx.target=java.util.ArrayList
  xxx.advice=cn.itcast.MyAdvice

 (3)ProxyFactoryBean充
当封装生成动态代理的工厂,需要为工厂类提供哪些配置参数信息?

       目标、通知

(4)编写客户端应用:

      编写实现Advice接口的类和在配置文件中进行配置
       调用BeanFactory获取对象

代码示例:

  1. /Bean工厂 专用于生产Bean  
  2. public class BeanFactory {  
  3. Properties props = new Properties();  
  4. public BeanFactory(InputStream ips){  
  5. try {  
  6. //从输入流中读取属性列表(键和元素对)
  7. props.load(ips);  
  8. catch (IOException e) {  
  9. e.printStackTrace();  
  10. }  
  11. }  
  12. public Object getBean(String name){  
  13. // 用指定的键在此属性列表中搜索属性
  14. String className = props.getProperty(name);  
  15. Object bean= null;  
  16. try {  
  17. Class clazz = Class.forName(className);  
  18. //JavaBean必须有一个不带参数的构造方法
  19. bean = clazz.newInstance();  
  20. catch (Exception e) {  
  21. e.printStackTrace();  
  22. }   
  23. if(bean instanceof ProxyFactoryBean)  
  24. {  
  25. Object proxy = null;  
  26. try {  
  27. //判断Bean为ProxyFactoryBean,返回代理
  28. ProxyFactoryBean proxyFactoryBean = (ProxyFactoryBean)bean;  
  29. Advice advice = (Advice)Class.forName(props.getProperty(name+".advice")).newInstance(); 
  30. Object target = Class.forName( props.getProperty(name+".target")).newInstance();  
  31. proxyFactoryBean.setAdvice(advice);  
  32. proxyFactoryBean.setTarget(target);  
  33. proxy = ((ProxyFactoryBean)bean).getProxy();  
  34. catch (Exception e) {  
  35. e.printStackTrace();  
  36. }  
  37. return proxy;  
  38. }   
  39. return bean;  
  40. }  
  41. }  
  42.   
  43. --------------------------------------  
  44. //专用于操作代理类  
  45. public class ProxyFactoryBean {  
  46. private Object target;  
  47. private Advice advice;  
  48. public void setTarget(Object target) {  
  49. this.target = target;  
  50. }   
  51. public Object getTarget() {  
  52. return target;  
  53. }  
  54. public void setTarget(String target) {  
  55. this.target = target;  
  56. }  
  57. public Advice getAdvice() {  
  58. return advice;  
  59. }  
  60. public void setAdvice(Advice advice) {  
  61. this.advice = advice;  
  62. }  
  63. public Object getProxy() {  
  64. Object proxy = Proxy.newProxyInstance(target.getClass()  
  65. .getClassLoader(), target.getClass().getInterfaces(),  
  66. new InvocationHandler() {  
  67. public Object invoke(Object proxy, Method method,  
  68. Object[] args) throws Throwable {  
  69. advice.beforeMethod(method);  
  70. Object retVal = method.invoke(target, args);  
  71. advice.afterMethod(method);  
  72.  
  73. return retVal;  
  74. }  
  75. });  
  76. return proxy;  
  77. }  
  78. }   
  79. -----------------------  
  80. 测试类  
  81. public class AopFrameworkTest {
  82. public static void main(String[] args) { 
  83. // 把指定文件加载到输入流.
  84. InputStream ips =   
  85. AopFrameworkTest.class.getResourceAsStream("config.properties");  
  86. Object bean = new BeanFactory(ips).getBean("xxx");  
  87. System.out.println(bean.getClass().getName());  
  88. ((Collection)bean).clear();  
  89.   
  90. }  
  91. }  

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值