基础加强 类加载器 代理

--------------------android培训java培训、期待与您交流! ----------------------

类加载器:

一、类加载器:
    1,类加载器就是加载类的工具。
          当出现一个类,用到此类的时候,Java虚拟机首先将类字节码加载进内存,通常字节码的原始信息放在硬盘上的classpath指定的目录下。
    2,类加载器作用:将.class文件中的内容从硬盘上加载进内存进行处理,处理完后的结果就是字节码。
    3,类加载器:
          1)Java虚拟机中可安装多个类加载器,系统默认的有三个主要的,每个类负责加载特定位置的类:
                         BootStrap、ExtClassLoader、AppClassLoader
          2)BootStrap--顶级类加载器:类加载器也是Java类,因为其他是java类的类加载器本身也要被类加载器加载,显然必须有第一个类加载器不是java类,这正是BootStrap。它是嵌套在Java虚拟机内核中的。
    4,java虚拟机中的所有类装载器采用具有父子关系的树形结构进行组织,在实例化每个类装载器对象时,需要为其指定一个父级类装载器对象或者默认采用系统类装载器为其父级类加载
    5. 类加载器功能体系结构图:

                        

简单ClassLoader演示:

public class ClassLoaderTest {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		
		//打印ClassLoaderTest是由哪个类加载器加载出来的。
		System.out.println(ClassLoaderTest.class.getClassLoader().getClass().getName());
		System.out.println(System.class.getClassLoader());//打印结果为null,这说明System类没有类加载器
		
		ClassLoader loader = ClassLoaderTest.class.getClassLoader();
		while(loader!=null){
			System.out.println(loader.getClass().getName());
			loader = loader.getParent();//获取loader的父类。
		}
		System.out.println(loader);
		
	}

}


 

二,类加载器的委托机制:

   1,加载类的方式
        当Java虚拟机要加载一个类时,到底要用哪个类加载器加载呢?
        1)首先,当前线程的类加载器去加载线程中的第一个类。
        2)若A引用类B(继承或者使用了B),Java虚拟机将使用加载类的类加载器来加载类B。
        3)还可直接调用ClassLoader的LoaderClass()方法,来制定某个类加载器去加载某个类。

   2,委托机制(参看类加载器功能体系结构图)

        每个类加载器加载类时,会先委托给其上级类加载器。

        每个ClassLoader都有其管辖范围,当需要加载某个类时,它们先委托它们的父类加载器去加载,这就是类加载器的委托模式,类加载器先一级级委托到BootStrap类加载器,当BootStrap在指定目录中没有找到要加载的类时,无法加载当前所要加载的类,就会一级级返回子孙类加载器,进行加载,每级都会先到自己相应指定的目录中去找,看有没有当前的类,如果找到了就直接加载。 直到退回到最初的类装载器的发起者时,如果它自身还未找到,就不能完成类的加载,那就报告ClassNoFoundException的异常。
    简单说,就是先由发起者将类一级级委托为BootStrap,从父级开始找,找到了直接返回,没找到再返回给其子级找,直到发起者,再没找到就报异常。

思考:能否自己写一个java.lang.System类?
     通常不可以,因为根据类加载器的委托机制,每次都会先委托给上级寻找,先找到的是Jdk中的java.lang.System,而不是自己写的 java.lang.System 类;但可以自己写一个类加载器,撇开器委托机制,不给它指定上级,来加载自己的类
   

 三、自定义类加载器
    1、自定义的类加载器必须继承抽象类ClassLoader,并覆写其中的findClass(String name)方法,而不用覆写loadClass()方法。
    2、覆写findClass(String name)方法而不覆盖loadClass()方法的原因:
       1)是要保留loadClass()方法中的流程,因为loadClass()中调用了findClass(String name)这个方法,
       此方法返回的就是去寻找父级的类加载器。
       2)在loadClass()内部是会先委托给父级,当父级找到后就会调用findClass(String name)方法,
       而找不到时就会用子级的类加载器,再找不到就报异常了,所以只需要覆写findClass方法,
       那么就具有了实现用自定义的类加载器加载类的目的。
    流程:父级-->loadClass-->findClass-->得到Class文件后转化成字节码-->defind()。
    3、编程步骤:
    1)编写一个对文件内容进行简单加盟的程序
    2)编写好了一个自己的类加载器,可实现对加密过来的类进行装载和解密。
    3)编写一个程序,调用类加载器加载类,在源程序中不能用该类名定义引用变量,因为编译器无法识别这个类,
    程序中除了可使用ClassLoader的load方法外,还能使用放置线程的上线文类加载器加载或系统类加载器,
    然后在使用forName得到字节码文件。

 

 

 

 代理:   

1,代理的概念与作用:
     1)生活中的代理
 就是我们日常生活中相当于代理商,比如现在很多人在淘宝上买东西,但是淘宝上的东西大都不是卖家自己的,
 他们是在买家下完订单之后,然后再把订单给真正的商家,通过商家发货。这淘宝网就是一个代理。
     2)程序中的代理
            为已存在的多个具有相同接口的目标类的各个方法增加一些系统功能,例如,异常处理、日志、计算方法的运行时间、事务管理、等等。  

            编写一个与目标类具有相同接口的代理类,代理类的每个方法调用目标类的相同方法,并在调用方法时加上系统功能的代码。 (参看的原理图)

                                          
           如果采用工厂模式和配置文件的方式进行管理,则不需要修改客户端程序,在配置文件中配置是使用目标类、还是代理类,这样以后很容易切换,譬如,想要日志功能时就配置代理类,否则配置目标类,这样,增加系统功能很容易,以后运行一段时间后,又想去掉系统功能也很容易。


    2,AOP面向方面的编程:
 系统中存在交叉业务,一个交叉业务就是要切入到系统中的一个方面,如下所示:
          安全       事务         日志
 StudentService  ------|----------|------------|-------------
 CourseService   ------|----------|------------|-------------
 MiscService       ------|----------|------------|-------------
 用具体的程序代码描述交叉业务:
 method1         method2          method3
 {                      {                       {
 ------------------------------------------------------切面
 ....            ....              ......
 ------------------------------------------------------切面
 }                       }                       }
 交叉业务的编程问题即为面向方面的编程(Aspect oriented program ,简称AOP),AOP的目标就是要使交叉业务
 模块化。可以采用将切面代码移动到原始方法的周围,这与直接在方法中编写切面代码的运行效果是一样的,
 如下所示:
 ------------------------------------------------------切面
 func1         func2            func3
 {             {                {
 ....            ....              ......
 }             }                }
 ------------------------------------------------------切面
 使用代理技术正好可以解决这种问题,代理是实现AOP功能的核心和关键技术。

 

import cn.itcast.day03.Advice;

public interface Advice {
	void beforeMethod(Method method);
	void afterMethod(Method method);
}

public class MyAdvice implements Advice {
	long beginTime = 0 ;
	@Override
	public void beforeMethod(Method method) {
		// TODO Auto-generated method stub
		System.out.println("到传智播客来学习了");
		beginTime = System.currentTimeMillis(); 
	}

	@Override
	public void afterMethod(Method method) {
		// TODO Auto-generated method stub		
		System.out.println("从传智播客毕业了");
		long endTime = System.currentTimeMillis(); 
		System.out.println(method.getName()+"running time of "+(endTime - beginTime)); 
	}

}

 

动态代理技术
一、概述:
    1,如果要为系统中的各种接口的类增加代理功能,那将需要太多的代理类,而且全部采用静态代理方式,
    将是一件非常麻烦的事情,这时就要用动态代理技术。
    2,动态代理类:JVM可在运行时,动态生成类的字节码,这种动态生成的类往往被用作代理类,
    即动态代理类(不是代理,只是拿出来作为代理类)。
    3,JVM生成的动态类必须实现一或多个接口,所以JVM生成的动态代理类只能用作具有相同接口的目标类代理。
    4,CGLIB库可以动态生成一个类的子类,一个类的子类也可以作为该类的代理,所以,如果要为一个没有
    实现接口的类生成动态代理,那么可以使用CGLIB库。
    5,代理类各个方法通常除了调用目标相应方法和对外返回目标返回的结果外,
       还可以再代理方法中的如下位置上加上系统功能代码:
       1)在调用目标方法之前
       2)在调用目标方法之后
       3)在调用目标方法前后
       4)在处理目标方法异常的catch块中。

二、分析JVM动态生成的类
    1,创建动态类的实例对象:
       1)用反射获得构造方法
       2)编写一个最简单的InvocationHandler的类
       3)调用构造方法创建动态类的实例对象,并将编写的InvocationHandler类的实例对象传进去。
    代码演示:

public static void createInstence(Class clazzProxy1) throws Exception {
	
	Class clazzProxy1 = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class);
	//上面类加载器通常使用接口相同的那个类加载器。
	System.out.println(clazzProxy1.getName());
	System.out.println("-------create instence--------");
	//用为class的newInstance方法构造的是无参的构造方法。而Proxy的构造方法是有参数的,所以不能直接用newInstance创建新实例。
	//只能先获得Proxy的构造方法,然后通过它的构造方法的字节码在进行newInstance
	
	Constructor constructor = clazzProxy1.getConstructor(InvocationHandler.class);
	class MyInvocationHander1 implements InvocationHandler{
		public Object invoke(Object proxy, Method method, Object[] args)
				throws Throwable {
			return null;
		}
	}
	Collection proxy1 =
			(Collection)constructor.newInstance(new MyInvocationHander1());
	System.out.println(proxy1);
	
	//也可以通过匿名内部类来创建实例对象
	Collection proxy2 = (Collection)constructor.newInstance(new InvocationHandler(){
		public Object invoke(Object proxy, Method method, Object[] args)
				throws Throwable {
			return null;
		}		
	});
	System.out.println(proxy2);
}



    2,让JVM创建动态类需要提供的信息:
       1)生成类中的哪些方法,通过让其实现哪些接口的方式进行告知。
       2)产生的类字节码必须有一个关联的类加载器对象
       3)生成的类中的方法的代码是怎么样的,也得由我们自己提供,把我们的代码写在一个约定好的子接口对象的方法中,
    把对象传给它,它调用我们的方法,即相当于插入了我们自己的代码。
    提供执行代码的对象就是InvocationHandler对象,它是在创建动态类的实例对象的构造方法时传递进去的,
    在上面的InvocationHandler对象的invoke方法中,加一点代码就可以看到这些代码被调用运行了。

 

public static void createInstence(Class clazzProxy1) throws Exception {
	Collection proxy3 = (Collection)Proxy.newProxyInstance( 
	          Collection.class.getClassLoader(),
	          new Class[]{Collection.class}, 
	          new InvocationHandler() { 
		        	  ArrayList target = new ArrayList(); 
		        	  //invoke()方法的3个参数分别是:调用的代理对象,调用代理对象的方法,方法的参数列表 
		           
			          @Override 
			          public Object invoke(Object proxy, Method method, Object[] 
			          args) throws Throwable {
			        	  long beginTime = System.currentTimeMillis(); 
				          //让目标去执行代理身上的方法 
			        	  Object retVal = method.invoke(target, args);
			        	  long endTime = System.currentTimeMillis(); 
				          System.out.println(method.getName()+"running time of "+(endTime - beginTime)); 
				          return retVal;
				       // return method.invoke(proxy, args); //如果返回此方法会出现代码死循环
			          } 
	          	}
	          ); 
	  //handler的返回结果给了add()方法,而handler的结果又来自于目标返回invoke() 
	  proxy3.add("zxx");//代理的add()方法会去调用handler对象的invoke()方法 
	  proxy3.add("lhm");
	  proxy3.add("bxd");
	  System.out.println(proxy3.size()); 
	  //调用代理的getClass()方法,会转手交给目标去了,那么目标应该返回的是ArrayList 
	  //但是,虽然getClass()方法是从Object继承过来的,但却不是Object派发给handler的 
	  //3个方法:hashCode(),equals().toString()之一,那么它自己有实现,没有交给handler 
	  System.out.println(proxy3.getClass().getName());//$Proxy0 
}

 

实现类似spring的可配置的AOP框架
    1,工厂类BeanFactory负责创建目标类或代理类的实例对象,并通过配置文件实现切换。
    2,getBean方法根据参数字符串返回一个相应的实例对象,
       如果参数字符串在配置文件中对应的类名不是ProxyFactoryBean,则直接返回该类的实例对象,
       否则返回该类示例对象的getProxy方法返回的对象。
    3,BeanFactory的构造方法接收代表配置文件的输入流对象的配置文件格式如下:
       #xxx=java.util.ArrayList
       xxx=cn.itcast.test3.aopframework.ProxyFactoryBean
       xxx.advice=cn.itcast.test3.MyAdvice
       xxx.target=java.util. ArrayList
       注意:其中的#代表注释当前行。
    4,ProxyFactoryBean充当封装成动态的工厂,需为工厂提供的配置参数信息包括:
       目标(target)通告(advice)
    5,BeanFactory和ProxyFactoryBean:
       1)BeanFactory是一个纯粹的bean工程,就是创建bean即相应的对象的工厂。
       2)ProxyfactoryBean是BeanFactory中的一个特殊的Bean,是创建代理的工厂。

package cn.itcast.day03;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Collection;

public class ProxyTest {

	/**
	 * @param args
	 */
	public static void main(String[] args) throws Exception {
		// TODO Auto-generated method stub
		Class clazzProxy1 = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class);
		//上面类加载器通常使用接口相同的那个类加载器。
	
		createInstence(clazzProxy1);
		
	}

	public static void createInstence(Class clazzProxy1) throws Exception {				
		  final ArrayList target = new ArrayList(); 
		  Collection proxy3 = (Collection)getProxy(target,new MyAdvice()); 
		  proxy3.add("zxx");
		  proxy3.add("lhm");
		  proxy3.add("bxd");
		  System.out.println(proxy3.size());  
		  System.out.println(proxy3.getClass().getName());
	}


	private static Object getProxy(final Object target,final Advice advice) {
		Object proxy3 = Proxy.newProxyInstance( 
				target.getClass().getClassLoader(),
		        /*new Class[]{Collection.class}, */
				target.getClass().getInterfaces(),
		        new InvocationHandler() { 
				          @Override 
				          public Object invoke(Object proxy, Method method, Object[] 
				          args) throws Throwable {				    				        	  
				        	  advice.beforeMethod(method);
				        	  Object retVal = method.invoke(target, args);
				        	  advice.afterMethod(method);				        
					          
					          return retVal;
				          } 
		          	}
		          );
		return proxy3;
	}
}



 

 

 

---------------------android培训java培训、期待与您交流! -----------------------

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值