张孝祥J2SE加强自学笔记(48-56)(转载)

J2SEJavaBeanAOPSpring

48、类加载器的一个高级问题的实验分析:
这次我们新建一个web项目然后新建一个servlet,在servet的doGet方法中我们循环的遍历出所有的类加载
器分别为:
WebappClassLoader
StandardClassLoader
AppClassLoader
ExtClassLoader

这样我们可以正常的访问该Servlet, 然后我们把这个servlet打成.Jar包放到jre/lib/ext/下面去让
ExtClassLoader去加载他,然后当我们再次的访问这个程序的时候就报错了:
原因:当ExtClassLoader加载改程序的时候,会首先让他的父亲去加载,由于父亲没有找到,所以就又交给他来加载
当他加载这个servlet的时候,他发现这个类extends HttpServlet所以就又去加载他,因为找不到所以就报错了,因为这个
jar包是由tomcat提供的,把tomcat lib中的servlet-api.jar页拷贝到ext目录下面就可以解决这个问题了。

web程序下类所使用的不同的类加载器





49、分析代理类的作用与原理及AOP概念
(1)AOP的概念:



(2)动态代理:



50、创建动态类及查看其方法列表信息:
示例代码:

Java代码  收藏代码

  1. public static void main(String[] args) {  
  2.         Class clazz = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class);  
  3.         String clazzName = clazz.getName();   
  4.           
  5.         //打印所以的构造方法以及参数  
  6.         Constructor[] constructs = clazz.getConstructors();  
  7.         for(Constructor constructor : constructs) {  
  8.             String constructName = constructor.getName();  
  9.             StringBuilder sBuilder = new StringBuilder(constructName);  
  10.             sBuilder.append('(');  
  11.             Class[] clazzTypes = constructor.getParameterTypes();  
  12.             for(Class clazzType : clazzTypes) {  
  13.                 sBuilder.append(clazzType.getName()).append(',');  
  14.             }  
  15.             if(clazzTypes != null && clazzTypes.length >0) {  
  16.                 sBuilder.deleteCharAt(sBuilder.length() - 1);  
  17.             }  
  18.             sBuilder.append(')');  
  19.             System.out.println(sBuilder);  
  20.         }  
  21.           
  22.         //打印这个接口所拥有的所有的方法以及他们的参数  
  23.         Method[] clazzMethods = clazz.getMethods();  
  24.         for(Method clazzMethod : clazzMethods) {  
  25.             String methodName = clazzMethod.getName();  
  26.             StringBuilder sBilder = new StringBuilder(methodName);  
  27.             Class[] methodTypes = clazzMethod.getParameterTypes();  
  28.             sBilder.append('(');  
  29.             for(Class methodType : methodTypes) {  
  30.                 sBilder.append(methodType.getName()).append(',');  
  31.             }  
  32.             if(methodTypes != null && methodTypes.length >0) {  
  33.                 sBilder.deleteCharAt(sBilder.length() - 1);  
  34.             }  
  35.             sBilder.append(')');  
  36.             System.out.println(sBilder);  
  37.         }  
  38.     }  
  39.       
  40.     结果:  
  41.     $Proxy0(java.lang.reflect.InvocationHandler)  
  42.     add(java.lang.Object)  
  43.     hashCode()  
  44.     clear()  
  45.     equals(java.lang.Object)  
  46.     toString()  
  47.     contains(java.lang.Object)  
  48.     isEmpty()  
  49.     addAll(java.util.Collection)  
  50.     iterator()  
  51.     size()  
  52.     toArray([Ljava.lang.Object;)  
  53.     toArray()  
  54.     remove(java.lang.Object)  
  55.     containsAll(java.util.Collection)  
  56.     removeAll(java.util.Collection)  
  57.     retainAll(java.util.Collection)  
  58.     isProxyClass(java.lang.Class)  
  59.     getProxyClass(java.lang.ClassLoader,[Ljava.lang.Class;)  
  60.     getInvocationHandler(java.lang.Object)  
  61.     newProxyInstance(java.lang.ClassLoader,[Ljava.lang.Class;,java.lang.reflect.InvocationHandler)  
  62.     wait()  
  63.     wait(long,int)  
  64.     wait(long)  
  65.     getClass()  
  66.     notify()  
  67.     notifyAll()  

 


1、细节注意:在上述例子中,Proxy的静态方法getProxyClass的第二个参数需要传递一个interfaces的Class的数组。而在上述程序中应该写成Collection.class.getInterfaces(),为什么写成Collection.class也行呢? 那是因为Collection本身就是一个接口,如果Collection不是一个接口而是一个实现了数组的类,则必须写成Collection.class.getInterfaces(),否则程序将报错。
2、生成的代理对象是没有参数为空的构造方法的,只有一个参数为InvocationHandler的构造方法,所以不能直接调用newInstance()方法直接创建一个实例,因为该方法会调用相关类的无参的构造方法。所以只能得到参数为InvocationHandler的构造方法,然后传递一个实现了InvocationHandler接口的类来创建一个对象,从而引出我们下面要讲解的动态代理内容。



51、创建动态类的实例对象及调用其方法
在上面类的基础上添加如下代码:
//无法用下面的方法创建对象,因为newInstance()默认调用的是无参的构造方法,而我们上面获得的是Proxy类的一份字节码,而这个类
//没有无参的构造方法,必须取得他的构造方法来构造对象
//Object object = clazz.newInstance();

//根据你传递的参数类型,返回相应的构造方法

 

 

Java代码  收藏代码

  1. Constructor construct = clazz.getConstructor(InvocationHandler.class);  
  2.     class myInvocationHandler implements InvocationHandler {  
  3.         public Object invoke(Object proxy, Method method, Object[] args)  
  4.                 throws Throwable {  
  5.             return null;  
  6.         }  
  7.     }  

//要返回代理对象,必须要传递一个实现了InvocationHandler接口的类的一个对象
Collection proxy1 = (Collection)construct.newInstance(new myInvocationHandler());
//可以
proxy1.clear();
//报错
proxy1.size();

打印proxy1的结果是null,那并不代表proxy1就是null 是他的toString()方法是null,如果你调用它的clear()方法,不会报
NullPointException,但是返回的这个代理对象为什么只能调用没有返回值的方法,而如果调用有返回值的方法就会报空指针异常呢
因为每当你调用代理类的一个方法的时候,他就会去调用InvocationHandler中的Invoke方法,size的返回值是int,而return的是null

52、完成InvocationHandler对象的内部功能
我们可以把51中创建代理对象的二个步骤合成一个 (用到内部类)

 

Java代码  收藏代码

  1. Collection proxy2 = (Collection)construct.newInstance(new InvocationHandler() {  
  2.   
  3.             public Object invoke(Object proxy, Method method, Object[] args)  
  4.                     throws Throwable {  
  5.                 return null;  
  6.             }  
  7.         });  
  8.           

在51 中我们创建一个代理对象是 得到字节码------>得到构造方法----->创建对象。我们能不能一步到位?这要利用Proxy
类提供的另一个静态方法:newProxyInstance

 

Java代码  收藏代码

  1. Collection proxy3 = (Collection)Proxy.newProxyInstance(  
  2.                         //类加载器  
  3.                         Collection.class.getClassLoader(),   
  4.                         //传入接口可能有多个,所以是数组  
  5.                         new Class[]{Collection.class},   
  6.                           
  7.                         new InvocationHandler() {  
  8.                             //target就是我们要创建代理对象的那个真实的对象。  
  9.                             ArrayList target = new ArrayList();  
  10.                             public Object invoke(Object proxy, Method method, Object[] args)  
  11.                                     throws Throwable {  
  12.                                 long beginTime = System.currentTimeMillis();  
  13.                                 Object retVal = method.invoke(target, args);  
  14.                                 long endTime = System.currentTimeMillis();  
  15.                                 System.out.println(method.getName() + "方法运行时间为" + (beginTime -endTime));  
  16.                                 return retVal;  
  17.                             }     
  18.                         }  
  19.         );  
  20.           
  21.         proxy3.add("abc");  
  22.         proxy3.add("cde");  
  23.         System.out.println(proxy3.size());  
  24.           

总结:当调用代理对象的方法的时候,其实都会去调用InvocationHandler类的method.invoke()方法。invoke方法中有两个参数:(1)要调用的这个方法所属的对象。(2)这个方法所用到的参数。
例子中:传递的对象是target --new的一个真实ArrayLIst的对象,所以在程序后调用proxy2.add方法时,调用的是target的add方法。(每当调用一个代理对象的xx方法的时候,程序会自动将调用过程转交给InvocationHandler的invoke方法)按照这个规律:如果第一个参数传proxy3时,将会出现程序的死循环。
在上面的代码中我把AraryList target = new ArrayList()放到了Invoke方法前面,也就是成员变量,这个时候,
你用代理对象的时候操作的是同一个对象,所以上面的size()方法打印结果是2,但是如果你把target对象放到invoke方法
的内部,也就是局部变量的时候,在操作代理对象的时候,每调用代理对象的一个方法其实操作的是完全不同的对象
所以这个时候的结果是0

53、分析InvocationHandler对象的运行原理:
InvocationHandler原理分析



我们说如果我们调用代理类的一个方法,他会交给InvocationHandler的invoke方法去执行 返回的结果也是目标对象调用方法后的返回结果,那对于代理对象:
System.out.println(proxy3.getClass().getName()); 按照我们上面的理论,调用代理对象方法的时候会返回真实对象的返回结果,那应该是java.util.ArrayList
为什么会是$proxy0呢?
答案:调用代理对象的从Object继承的hashCode()  equals()  toString()这几个方法的时候才会把调用请求转发给InvocationHandler对象,而对于其他的方法
有自己的实现,所以getClass().getName()返回的是$proxy0

54、总结分析动态代理类的设计原理与结构
动态代理的工作原理图




55、编写可生成代理和插入通告的通用方法
模拟spring,将动态代理中的“切面”问题封装到一个类中,而不要硬编码到动态代理类中,动态代理类中的target
要改成Object,以便让他更有通用性

 

Java代码  收藏代码

  1. (1)切面问题接口  
  2.     public interface Advice {  
  3.         public void beforeMethod(Method method);  
  4.         public void afterMethod(Method method);  
  5.     }  
  6.     (2)切面问题实现  
  7.     public class MyAdvice implements Advice {  
  8.         long beginTime;  
  9.         public void afterMethod(Method method) {  
  10.             System.out.println("方法执行之后:");  
  11.             long endTime = System.currentTimeMillis();  
  12.             System.out.println(method.getName() + "方法运行时间为" + (beginTime - endTime));  
  13.               
  14.         }  
  15.   
  16.         public void beforeMethod(Method method) {  
  17.             System.out.println("方法执行之前:");  
  18.             beginTime = System.currentTimeMillis();  
  19.         }  
  20.     }  
  21.     (3)动态代理方法的封装  
  22.     private static Object getProxy(final Object target, final Advice advice) {  
  23.         Object proxy = Proxy.newProxyInstance(  
  24.                         target.getClass().getClassLoader(),   
  25.                         target.getClass().getInterfaces(),  
  26.                         new InvocationHandler() {  
  27.                             public Object invoke(Object proxy, Method method, Object[] args)  
  28.                                     throws Throwable {  
  29.                                   
  30.                                 advice.beforeMethod(method);  
  31.                                 Object retVal = method.invoke(target, args);  
  32.                                 advice.afterMethod(method);  
  33.                                 return retVal;  
  34.                             }  
  35.                         }  
  36.         );  
  37.         return proxy;  
  38.     }  
  39.     (4)测试运行  
  40.         ArrayList target = new ArrayList();  
  41.         Collection proxy = (Collection)getProxy(target, new MyAdvice());  
  42.         proxy.add("abc");  


56、实现类似Spring可配置的Aop框架:
模拟目标:根据我传递的方法的名称来确认返回的是真实的一个对象,还是一个代理对象,
如果我传递的这个类是个ProxyFactory类型的一个类(instanceof)则返回该类的一个代理对象
否则直接返回所指定类的一个真实的对象。

 

Java代码  收藏代码

  1. (1)BeanFactory  
  2.     public class BeanFactory {  
  3.       
  4.         static Properties props = new Properties();  
  5.           
  6.         public BeanFactory(InputStream ips) {  
  7.             try {  
  8.                 props.load(ips);  
  9.             } catch (IOException e) {  
  10.                 e.printStackTrace();  
  11.             }  
  12.         }  
  13.           
  14.         public static Object getBean(String name) {  
  15.             Object bean = null;  
  16.             try {  
  17.                 //得到传递对象的字节码  
  18.                 Class clazz = Class.forName(props.getProperty(name));  
  19.                 //创建一个实例对象  
  20.                 bean = clazz.newInstance();  
  21.                 //如果创建的这个对象是一个ProxyFactoryBean类型的,就返回代理对象,否则返回真实的对象  
  22.             } catch (Exception e) {  
  23.                 e.printStackTrace();  
  24.             }  
  25.             if(bean instanceof ProxyFactoryBean) {  
  26.                 ProxyFactoryBean proxyFactoryBean = (ProxyFactoryBean)bean;  
  27.                 Object proxy = null;  
  28.               
  29.                 try {  
  30.                     //得到目标类的对象  
  31.                     Object target = Class.forName(props.getProperty(name + ".target")).newInstance();  
  32.                     //得到解决“切面问题”类的对象  
  33.                     Advice advice = (Advice)Class.forName(props.getProperty(name + ".advice")).newInstance();  
  34.                     proxyFactoryBean.setAdvice(advice);  
  35.                     proxyFactoryBean.setTarget(target);  
  36.                     proxy = proxyFactoryBean.getProxy();  
  37.                 } catch (Exception e){  
  38.                     // TODO Auto-generated catch block  
  39.                     e.printStackTrace();  
  40.                 }  
  41.                 //将上述两个对象设置到代理对象中  
  42.                 //返回代理对象  
  43.                 return proxy;  
  44.             }  
  45.             //返回真实的对象  
  46.             return bean;  
  47.         }  
  48.     }  
  49.       
  50.     (2)ProxyFactoryBean  
  51.     public class ProxyFactoryBean {  
  52.       
  53.         private Object target;  
  54.           
  55.         private Advice advice;  
  56.           
  57.         public Object getTarget() {  
  58.             return target;  
  59.         }  
  60.   
  61.         public void setTarget(Object target) {  
  62.             this.target = target;  
  63.         }  
  64.   
  65.         public Advice getAdvice() {  
  66.             return advice;  
  67.         }  
  68.   
  69.         public void setAdvice(Advice advice) {  
  70.             this.advice = advice;  
  71.         }  
  72.   
  73.         public Object getProxy() {  
  74.             Object proxy = Proxy.newProxyInstance(  
  75.                     target.getClass().getClassLoader(),   
  76.                     target.getClass().getInterfaces(),  
  77.                     new InvocationHandler() {  
  78.                         public Object invoke(Object proxy, Method method, Object[] args)  
  79.                                 throws Throwable {  
  80.                             advice.beforeMethod(method);  
  81.                             Object retVal = method.invoke(target, args);  
  82.                             advice.afterMethod(method);  
  83.                             return retVal;  
  84.                         }  
  85.                     }  
  86.             );  
  87.             return proxy;  
  88.         }  
  89.     }  
  90.       
  91.     (3)所用到的配置文件:config.properties 注意:要与AopTest测试类位于同一个包下面(xxx.target所指定的类要与返回真实类对象所指定的类一样,否则就不一致了)  
  92.     #xxx=java.util.ArrayList  
  93.     xxx=cn.itcast.aop.framework.ProxyFactoryBean  
  94.     xxx.advice=cn.itcast.day2.MyAdvice  
  95.     xxx.target=java.util.ArrayList  
  96.       
  97.     (4)AopTest测试类  
  98.     public class AopTest {  
  99.   
  100.         public static void main(String[] args) {  
  101.             InputStream ips = AopTest.class.getResourceAsStream("config.properties");  
  102.             Object bean = new BeanFactory(ips).getBean("xxx");  
  103.             System.out.println(bean.getClass().getName());  
  104.             ((Collection)bean).clear();  
  105.         }  
  106.     } 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值