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


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