类加载器

Java虚拟机中可以安装多个类加载器,系统默认三个主要类加载器,每个类负责加载特定位置的类:BootStrap,ExtClassLoader,AppClassLoader

类加载器也是Java类,因为其他是java类的类加载器本身也要被类加载器加载,显然必须有第一个类加载器不是不是java类,这正是BootStrap。

Java虚拟机中的所有类装载器采用具有父子关系的树形结构进行组织,在实例化每个类装载器对象时,需要为其指定一个父级类装载器对象或者默认采用系统类装载器为其父级类加载。 





类加载器也是一个具体的对象。

委托机制:最先找到上级(JRE/lib/rt.jar).然后逐步往下,也可以写一个加载器,然后让它指定去找。

当Java虚拟机要加载一个类时,到底派出哪个类加载器去加载呢?

首先当前线程的类加载器去加载线程中的第一个类。

如果类A中引用了类B,Java虚拟机将使用加载类A的类装载器来加载类B。 

还可以直接调用ClassLoader.loadClass()方法来指定某个类加载器去加载某个类。

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

当所有祖宗类加载器没有加载到类,回到发起者类加载器,还加载不了,则抛ClassNotFoundException,不是再去找发起者类加载器的儿子,因为没有getChild方法,即使有,那有多个儿子,找哪一个呢?

对着类加载器的层次结构图和委托加载原理,解释先前将ClassLoaderTest输出成jre/lib/ext目录下的.jar包中后,运行结果为ExtClassLoader的原因。

编写自己的类加载器

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;

public class MyClassLoader extends ClassLoader {

	public static void main(String[] args)throws Exception {
		String srcPath=args[0];
		String destDir=args[1];
		FileInputStream fis=new FileInputStream(srcPath);
		String destFileName=srcPath.substring(srcPath.lastIndexOf('/')+1);//路径的File,加1是说明从盘符下面开始
		String destPath=destDir+"\\"+destFileName;
		FileOutputStream fos=new FileOutputStream(destPath);
		cypher(fis,fos);
		fis.close();
		fos.close();
	}
	private static void cypher(InputStream ips,OutputStream ops) throws Exception{
		int b=-1;
		while(ips.read()!=-1){
			ops.write(b);
			ops.write(b^0xff);
		}

	}
	private String classDir;

	@Override//类加载器
	protected Class<?> findClass(String name) throws ClassNotFoundException {
		// TODO Auto-generated method stub
		String classFileName=classDir+"\\"+name+".class";//通过类找出硬盘上的文件。

		try {
			FileInputStream fis=new FileInputStream(classFileName);

			ByteArrayOutputStream bos=new ByteArrayOutputStream();//定义一个字节数据流
			cypher(fis,bos);//解密
			fis.close();
			byte[] bytes=bos.toByteArray();
			return defineClass(bytes, 0, bytes.length);

		} catch (Exception e) {//子类不能被父类抛出

			e.printStackTrace();
		}//加载这个文件
		return super.findClass(name);//调用父类的class
	}
	//去哪个目录下寻找那份文件
	public MyClassLoader(){

	}
	public MyClassLoader(String clasPath){

		this.classDir=classDir;
	}

}

类加载器不能加载这种非public的类

/*
Exception in thread "main" java.lang.IllegalAccessException: Class MyClassLoader
 can not access a member of class MyTest with modifiers ""
*/
/*
class MyTest
{
	public void test()
	{
		System.out.println("hello,www.it315.org");
	}
}
*/

AOP:

系统中存在交叉业务,一个交叉业务就是要切入到系统中的一个方面,如下所示:

                              安全       事务         日志

StudentService  ------|----------|------------|-------------

CourseService   ------|----------|------------|-------------

MiscService       ------|----------|------------|-------------

用具体的程序代码描述交叉业务:

method1         method2          method3

{                      {                       { 

------------------------------------------------------切面

....            ....              ......

------------------------------------------------------切面

}                       }                       }

交叉业务的编程问题即为面向方面的编程(Aspect oriented program ,简称AOP),AOP的目标就是要使交叉业务模块化。可以采用将切面代码移动到原始方法的周围,这与直接在方法中编写切面代码的运行效果是一样的,如下所示:

------------------------------------------------------切面

func1         func2            func3

{             {                { 

....            ....              ......

}             }                }

------------------------------------------------------切面

使用代理技术正好可以解决这种问题,代理是实现AOP功能的核心和关键技术。








动态代理技术

要为系统中的各种接口的类增加代理机制,那就将需要太多的代理类,全部采用静态代理方法,将是一件非常麻烦事,写成百上千个代理类,是不是太累!

代理类的各种方法中通常除了要调用目标和相应方法和对外返回目标返回的结果外,还可以在代理中的如下四个

1.在调用目标方法之前

2.在调用目标方法之后

3.在调用目标方法前后

4.在处理目标方法异常的catch块中

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;

import javax.xml.ws.spi.Invoker;

public class ProxyTest {

	public static void main(String[] args) throws Exception{
		Class clazzProxy1=Proxy.getProxyClass(Collection.class.getClassLoader(),Collection.class);
		//对于clazz,我们通常认为它是字节码
		System.out.println(clazzProxy1.getName());

		System.out.println("begin constructors list-----:");
		Constructor[] constructors=clazzProxy1.getConstructors();//得到它的构造方法
		for(Constructor constructor:constructors){
			String name=constructor.getName();
			StringBuilder sBuilder=new StringBuilder();//用 StringBuilder效率更高一点
			sBuilder.append('(');

			Class [] clazzParams=constructor.getParameterTypes();//得到参数的类型,返回的是一个class的数组。
			for(Class clazzParam: clazzParams){//取出每个参数的名字
				sBuilder.append(clazzParam.getName()).append(',');
			}
			if(clazzParams!=null&&clazzParams.length!=0)
			sBuilder.deleteCharAt(sBuilder.length()-1);//去掉最后一个参数
			sBuilder.append(')');

			System.out.println(sBuilder.toString());
		}
		//StringBuilder与StringBuffered的区别:
		//在动态上,都是往字符串中添加字符,在单线程下,用StringBuilder效率要高一点,在多线程下StringBufferd要高点

		System.out.println("----------begin methods list----------");
		/*$Proxy0()
		$Proxy0(InvocationHandler,int)*/
		Method[] methods = clazzProxy1.getMethods();
		for(Method method : methods){
			String name = method.getName();
			StringBuilder sBuilder = new StringBuilder(name);
			sBuilder.append('(');
			Class[] clazzParams = method.getParameterTypes();
			for(Class clazzParam : clazzParams){
				sBuilder.append(clazzParam.getName()).append(',');
			}
			if(clazzParams!=null && clazzParams.length != 0)
				sBuilder.deleteCharAt(sBuilder.length()-1);
			sBuilder.append(')');
			System.out.println(sBuilder.toString());
		}

		//创建动态类的实例对象用调用方法
		System.out.println("-----begin create instance-----");
		//Object obj=clazzProxy1.newInstance();//不能这能调用构造参数的实例化方法。
		//构造方法接受一个参数,然后再去调用构造方法。
		Constructor constructor=clazzProxy1.getConstructor(InvocationHandler.class);
		class MyInvocationHander1 implements InvocationHandler{

			@Override
			public Object invoke(Object proxy, Method method, Object[] args)
					throws Throwable {

				return null;
			}

		}

		Collection proxy1=(Collection) constructor.newInstance(new MyInvocationHander1());
		System.out.println(proxy1);

		proxy1.clear();//如果不报空指针异常,就说明这个对象是有的。
		//proxy1.size();//出错了,那么就判定size方法出问题了,因为size方法有返回值,clear方法没有。

		Collection proxy2= (Collection) constructor.newInstance(new InvocationHandler(){

			@Override
			public Object invoke(Object proxy, Method method, Object[] args)
					throws Throwable {

				return null;
			}

		});

		//代理对象
		Collection proxy3=(Collection) Proxy.newProxyInstance(
				Collection.class.getClassLoader(),
				new Class[] {Collection.class},
				new InvocationHandler(){
					public Object invoke(Object proxy,Method method,Object[] args) throws Throwable{

						ArrayList target=new ArrayList();
						long beginTime=System.currentTimeMillis();
						Object retVal=method.invoke(target, args);
						long endTime=System.currentTimeMillis();
						System.out.println(method.getName()+" running time of: "+(endTime-beginTime)+"ms");
						return retVal;
					}
				}
				);
		proxy3.add("zxx");//每调用一个add方法,invoke就被执行
		proxy3.add("lhm");
		proxy3.add("hjl");

		System.out.println(proxy3.size());

	}

}



让动态生成的类成为目标类的代理:

import java.lang.reflect.Method;

public class MyAdvice implements Advice {
	long beginTime = 0;
	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));

	}

	public void beforeMethod(Method method) {
		// TODO Auto-generated method stub
		System.out.println("结束啦!");
		beginTime = System.currentTimeMillis();
	}

}
import java.lang.reflect.Method;

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

object 中有三个方法交给handler,分别是hashcode,equals,toString.

其他的不委托,都有自己的实现方法。



实现类似spring的可配置的AOP框架:

import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

import cn.itcast.day3.Advice;

public class BeanFactory {
	Properties props = new Properties();
	public BeanFactory(InputStream ips){
		try {
			props.load(ips);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			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) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		if(bean instanceof ProxyFactoryBean){
			Object proxy = null;
			ProxyFactoryBean proxyFactoryBean = (ProxyFactoryBean)bean;
			try {
				Advice advice = (Advice)Class.forName(props.getProperty(name + ".advice")).newInstance();
				Object target = Class.forName(props.getProperty(name + ".target")).newInstance();
				proxyFactoryBean.setAdvice(advice);
				proxyFactoryBean.setTarget(target);
				proxy = proxyFactoryBean.getProxy();
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			return proxy;
		}
		return bean;
	}
}
import java.io.InputStream;
import java.util.Collection;

public class AopFrameworkTest {

	/**
	 * @param args
	 */
	public static void main(String[] args) throws Exception {
		// 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());
		((Collection)bean).clear();
	}

}
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class ProxyFactoryBean {

	private Advice advice;
	private Object target;

	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;
	}

	public Object getProxy() {
		// TODO Auto-generated method stub
		Object proxy3 = Proxy.newProxyInstance(
				target.getClass().getClassLoader(),
				/*new Class[]{Collection.class},*/
				target.getClass().getInterfaces(),
				new InvocationHandler(){

					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;*/

						advice.beforeMethod(method);
						Object retVal = method.invoke(target, args);
						advice.afterMethod(method);
						return retVal;						

					}
				}
				);
		return proxy3;
	}

}



工厂类BeanFactory负责创建目标类或代理类的实例对象,并通过配置文件实现切换。其getBean方法根据参数字符串返回一个相应的实例对象,如果参数字符串在配置文件中对应的类名不是ProxyFactoryBean,则直接返回该类的实例对象,否则,返回该类实例对象的getProxy方法返回的对象。

BeanFactory的构造方法接收代表配置文件的输入流对象,配置文件格式如下:

#xxx=java.util.ArrayList

xxx=cn.day3.aopframework.ProxyFactoryBean //代理

xxx.advice=cn.day3.MyAdvice

xxx.target=java.util.ArrayList//目标

Java基础---Java---基础加强---类加载器、委托机制、AOP、 动态代理技术、让动态生成的类成为目标类的代理、实现Spring可配置的AOP框架的更多相关文章

  1. JAVA基础知识之JVM-——自定义类加载器

    JVM中除了根加载器之外其他加载器都是ClassLoader的子类实例, 可以通过扩展ClassLoader的子类,通过重写方法来实现自定义的类加载器. ClassLoader中有两个关键的方法如下, ...

  2. Java虚拟机JVM学习05 类加载器的父委托机制

    Java虚拟机JVM学习05 类加载器的父委托机制 类加载器 类加载器用来把类加载到Java虚拟机中. 类加载器的类型 有两种类型的类加载器: 1.JVM自带的加载器: 根类加载器(Bootstrap ...

  3. Java内存管理-掌握自定义类加载器的实现(七)

    勿在流沙筑高台,出来混迟早要还的. 做一个积极的人 编码.改bug.提升自己 我有一个乐园,面向编程,春暖花开! 上一篇分析了ClassLoader的类加载相关的核心源码,也简单介绍了ClassLoa ...

  4. [转载] Java高新技术第一篇:类加载器详解

    本文转载自: http://blog.csdn.net/jiangwei0910410003/article/details/17733153 首先来了解一下字节码和class文件的区别: 我们知道, ...

  5. Java高新技术第一篇:类加载器详解

    首先来了解一下字节码和class文件的区别: 我们知道,新建一个Java对象的时候,JVM要将这个对象对应的字节码加载到内存中,这个字节码的原始信息存放在classpath(就是我们新建Java工程的 ...

  6. 【java虚拟机系列】JVM类加载器与ClassNotFoundException和NoClassDefFoundError

    在我们日常的项目开发中,会经常碰到ClassNotFoundException和NoClassDefFoundError这两种异常,对于经验足够的工程师而言,可能很轻松的就可以解决,但是却不一定明白为 ...

  7. java 类加载器的委托机制

    l 当Java虚拟机要加载一个类时,到底派出哪个类加载器去加载呢? 1.首先当前线程的类加载器去加载线程中的第一个类. 2.如果类A中引用了类B,Java虚拟机将使用加载类A的类装载器来加载类B. 3 ...

  8. Java内存管理-掌握虚拟机类加载器(五)

    勿在流沙筑高台,出来混迟早要还的. 做一个积极的人 编码.改bug.提升自己 我有一个乐园,面向编程,春暖花开! 上一篇介绍虚拟机类加载机制,讲解了类加载机制中的三个阶段,分别是:加载.连接(验证.准 ...

  9. 【Java虚拟机8】自定义类加载器、类加载器命名空间、类的卸载

    前言 学习类加载器就一定要自己实现一个类加载器,今天就从一个简单的自定义类加载器说起. 自定义类加载器 例1 一个简单的类加载器,从一个给定的二进制名字读取一个字节码文件的内容,然后生成对应的clas ...

随机推荐

  1. Java Servlet 笔记1

    1. 什么是Servlet. Java Servlet 是运行在 Web 服务器或应用服务器上的程序,它是作为来自 Web 浏览器或其他 HTTP 客户端的请求和 HTTP 服务器上的数据库或应用程序 ...

  2. 上传本地项目到Github

    进入要上传的本地文件夹,右键打开Git Bash Here,然后进行以下步骤: 1.在命令行中,输入"git init",使Test文件夹加入git管理: 2.输入"gi ...

  3. python day3_liaoxuefeng

    1.Python的循环有两种,一种是for...in循环,依次把list或tuple中的每个元素迭代出来,看例子: names = ['Michael', 'Bob', 'Tracy'] for na ...

  4. python中的缩进问题

    python中没有{}来表示代码块,而是用缩进来表示,刚开始写python代码,没有注意缩进,结果各种报错(( ╯□╰ )). 在python中的原则就是同一层次的代码一定要有相同的缩进!!! 从上图 ...

  5. JSSDK实现微信自定义分享---java 后端获取签名信息

    一.首先说下关于微信Access_token的问题,微信Access_token分为2中: 1.授权token获取方式: 这种token需要code值(如何获取code值查看官方文档) "h ...

  6. java中什么是序列化和反序列化

    序列化:能够把一个对象用二进制的表示出来. 类似我第一个字节表示什么属性名词,第二个字节表示什么属性值,第几个字段表示有几个属性等. 而且这个二进制可以写到硬盘或者在网络上传输但不会破坏他的结构.一般 ...

  7. Big Christmas Tree(poj-3013)最短路

    Big Christmas Tree Time Limit: 3000MS   Memory Limit: 131072K Total Submissions: 25823   Accepted: 5 ...

  8. Socket网络编程详解

    一,socket的起源 socket一词的起源 在组网领域的首次使用是在1970年2月12日发布的文献IETF RFC33中发现的, 撰写者为Stephen Carr.Steve Crocker和Vi ...

  9. OpenCV3.1.0中调用MHI(Motion History Images, 运动历史图像)

    写在前边: OpenCV3.0+要想使用MHI,就要现安装扩展模块opencv_contrib.安装方法见:ubuntu 14.04 64位 安装Opencv3.1.0 (包含opencv_contr ...

  10. 02Vue2.0+生命周期

    Vue生命周期是Vue对象从无到有再到无的一个过程,我们又是不仅要明白一个对象的使用, 同时也要知道一个对象怎么创建了,就比如Spring的生命周期,往往不只是面试官的考点, 同时在项目中也也可能常常 ...