Java基础加强_类加载器、代理

Java基础加强day03

八、类加载器

    类加载器:专门把类加载进内存的工具,用到一个类,首先应该把它加载进内存,那么这些工作就由加载器完成。

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

    类加载器本身也是Java类,类加载器也要被其他的类加载器加载,但BootStrap不是一个类,所以不需要被别的类加载,它是一个嵌套在JVM内核中的,JVM一启动它就加载进内存、用C++编写的一段二进制代码。

    Java虚拟机中的所有类加载器都是采用父子关系的树形结构进行组织,在实例化每个类装载器对象时,需要为其指定一个父级类装载器对象或者默认采用系统类装载器为其父级类加载。类加载器的关系以及管辖范围如下图所示:

   

    类加载器的委托机制:每个类加载器加载类时,先委托给其上级类加载器;当所有的祖宗类加载器都没有加载到类时,回到发起者加载器,如果还是加载不了,则抛出ClassNotFoundException,而不会去找发起者的子类加载器。

    每个ClassLoader本身只能分别加载特定位置和目录中的类,但它们可以委托其他的类装载器去加载类,这就是类加载器的委托模式。类装载器一级级委托到BootStrap类加载器,当BootStrap无法加载当前所要加载的类时,然后才一级级回退到子孙类装载器去进行真正的加载。当回退到最初的类装载器时,如果它自己也不能完成类的装载,那就应报告ClassNotFoundException异常。

    模板方法设计模式总的流程已经在父类中确定好了,而流程中的一些细节父类无法确定,留给子类去完成。那么子类只要复写父类的方法,给出具体的操作细节即可,这就是模板方法设计模式。

    自定义一个类加载器的步骤:

    1、自定义的类加载器必须继承抽象类ClassLoader;

    2、loadClass方法,把类名传给该函数,它就会去加载这个类,并且返回一个class字节码。loadClass方法会先去找父类加载器,接着找findClass方法。

    3、所以只要复写findClass方法就可以了,loadClass方法中封装的加载器的委托流程,我们要做的就是在findClass方法中给出具体实现。

    4、defineClass方法,可以通过该方法将得到的class文件的内容转换成为字节码。

下面代码体现:

1、演示基本的类加载器

package itheima.day03;

import java.util.Date;

public class ClassLoaderTest {
	
	public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
		System.out.println(
				ClassLoaderTest.class.getClassLoader().getClass().getName()
				);
//		sun.misc.Launcher$AppClassLoader
		System.out.println(
				System.class.getClassLoader()
				);
		
		ClassLoader loader = ClassLoaderTest.class.getClassLoader();
//		sun.misc.Launcher$AppClassLoader
//		sun.misc.Launcher$ExtClassLoader
		while(loader != null){
			System.out.println(loader.getClass().getName());
			loader = loader.getParent();
		}
//		null
		System.out.println(loader);
		
//		System.out.println(new ClassLoaderAttachment().toString());//hello itheima
		Class clazz = 	new MyClassLoader("itheimalib").loadClass("ClassLoaderAttachment");
		Date d1 =(Date)clazz.newInstance();
		System.out.println(d1);
	}
}

2、自定义的类加载器
package itheima.day03;

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

public class MyClassLoader	 extends ClassLoader {

	public static void main(String[] args) throws IOException {
		
		String srcPath =args[0];
		String destDir = args[1];
		FileInputStream fis = new FileInputStream(srcPath);
		String destFileName = srcPath.substring(srcPath.lastIndexOf("\\")+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 IOException{
		int b = -1;
		while((b = ips.read())!=-1){
			ops.write(b ^ 0xff);
		}
	}
	@Override
	protected Class<?> findClass(String name) throws ClassNotFoundException {
		
		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) {
			System.out.println("nononno");
		}
		return null;
	}
	
	private String classDir;
	public MyClassLoader(){	}
	public MyClassLoader(String classDir){
		this.classDir = classDir;
	}
}
3、用自定义类加载器加密的类文件
package itheima.day03;

import java.util.Date;

public class ClassLoaderAttachment extends Date{
	@Override
	public String toString(){
		return "hello itheima";
	}
}

九、代理

    代理:要为已经存在的多个具有相同接口的目标类的各个方法增加一些系统功能,则编写一个与目标类具有相同接口的代理类;代理类的每个方法调用目标类的相同方法,并在调用方法时加上系统功能的代码;这就是代理的基本体现

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

    代理的架构图如下所示:


    AOP:交叉业务编程的问题即为面向方面的编程(AOP),AOP的目标就是要使交叉业务模块化。可以将切面代码移动到原始方法的周围,这与直接在方法中编写切面代码的运行效果是一样的。代理是实现AOP功能的核心和关键技术

    动态代理技术:JVM可以在运行期动态生成类的字节码,这个动态生成的类往往被用作代理类,即动态代理类,API为类Proxy。

    JVM生成的动态代理类必须实现一个或者多个接口,所以,JVM生成的动态类只能用作具有相同接口的目标类的代理。

    代理类的各个方法中通常除了要调用目标的相应方法对外返回目标返回的结果外,还可以在代理的方法中添加上一些系统的代码。

    位置:1、在调用目标方法之前;2、在调用目标方法之后;3、在调用目标方法前后;

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

    创建动态类及其实例对象:1、类加载器;2、接口;3、生成的类中的方法怎样,由我们提供,把我们的代码写在一个约定好的接口对象的方法中(类InvocationHandler中的invoke方法),把对象传给它,它调用我们的方法,相当于插入了我们的代码,提供执行代码的对象就是那个InvocationHandler对象,它是在创建动态类的实例对象的构造方法时传递进去的。在上面的InvocationHandler对象的invoke方法中加一点代码,就可以看到这些代码被调用运行了。

    动态代理的工作原理:客户端调用代理,代理的构造方法接收一个InvocationHandler对象;然后客户端调用代理的各个方法时,代理会把各个请求转发给成员变量InvocationHandler,然后InvocationHandler对象又把请求分发给目标的相应方法,并在InvocationHandler的invoke方法中可以增加额外的功能。

   增加的功能不能写死,要灵活使用。可以把额外的功能封装成为一个类,这个类中的方法封装了额外的功能,并把这个类的对象传递到InvocationHandler的invoke方法中,调用这个类中的方法,那么就相当于增加了额外功能。传对象,在代理中调用该对象的方法,这就是面向切面的编程。执行了对象的方法,就相当于执行了切面的方法。

原理图如下:

 

    动态代理类的构造方法接收的是一个InvocationHandler对象,说明代理类内部一定有一个InvocationHandler的成员变量!

    Client程序调用objProxy.add(“abc”)方法时,涉及到三个要素:objProxy对象、add方法、“abc”参数;分别对应InvocationHandler类中invoke方法中的Object proxy, Method method, Object[] args。

有关代理的更多分析,代码体现:

 4、代理的基本演示:

package itheima.day03;

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 {

	public static void main(String[] args) throws SecurityException, NoSuchMethodException, IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException {

		Class clazzProxy1 = 
				Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class);
//		$Proxy0
		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(name);
			sBuilder.append('(');
			Class[] clazzParams = constructor.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());
//			$Proxy0(java.lang.reflect.InvocationHandler)		}
		
		System.out.println("------------begin methods list----------");
		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----");
		
		Constructor constructor = clazzProxy1.getConstructor(InvocationHandler.class);
		Collection proxy2 = (Collection) constructor.newInstance(new InvocationHandler(){
			
			@Override
			public Object invoke(Object proxy, Method method, Object[] args)
					throws Throwable {
				return null;
//				return "123";
			}	
		});
		
		System.out.println(proxy2);
//		proxy2.clear();
//		proxy2.size();
		
		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());
//		$Proxy1
	}
	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;
	}
}

5、用在代理中的契约接口
package itheima.day03;

import java.lang.reflect.Method;

public interface Advice {//建议
	
	void beforeMethod(Method method);
	void afterMethod(Method method);
}
6、实现契约接口的类(建议类):面向切面
package itheima.day03;

import java.lang.reflect.Method;

public class MyAdvice	implements Advice {
	long beginTime = 0;
	@Override
	public void beforeMethod(Method method) {
		System.out.println("开始啦....");
		beginTime = System.currentTimeMillis();
	}

	@Override
	public void afterMethod(Method method) {
		System.out.println("结束啦.....");
		
		long endTime = System.currentTimeMillis();
		
		System.out.println(method.getName()+" running time of "+(endTime - beginTime));
	}
}

    做一个类似于Spring的AOP框架:

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

    ProxyFactoryBean充当封装生成动态代理的工厂,需要为工厂提供目标,建议等参数信息。

   想要代理,可以在配置文件中配;不想要代理,也可以在配置文件中配,这就是Spring的精髓!

下面代码体现:

7、生成动态代理的工厂:

package itheima.day03.aopframework;

import itheima.day03.Advice;

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() {
		
		Object proxy3 = Proxy.newProxyInstance(
				target.getClass().getClassLoader(),
				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;
	}
}

8、生成类的工厂:
package itheima.day03.aopframework;

import itheima.day03.Advice;

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

public class BeanFactory {
	
	Properties props = new Properties();
	
	public BeanFactory(InputStream ips){
		try {
			props.load(ips);
		} catch (IOException 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){
			Object proxy = null;
			try {
				
				ProxyFactoryBean proxyFactoryBean = (ProxyFactoryBean)bean;
				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) bean).getProxy();
			} catch (Exception e) {
				e.printStackTrace();
			}
			 return proxy;
		}
		return bean;
	}
}

配置文件:

xxx=java.util.ArrayList
#xxx=itheima.day03.aopframework.ProxyFactoryBean
xxx.advice=itheima.day03.MyAdvice
xxx.target =java.util.ArrayList

9、演示:

package itheima.day03.aopframework;

import java.io.InputStream;

public class AopFrameworkTest {
	
	public static void main(String[] args) {
		InputStream ips = AopFrameworkTest.class.getResourceAsStream("config.properties");
		Object bean = new BeanFactory(ips).getBean("xxx");
		System.out.println(bean.getClass().getName());
	}
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章