`

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

    博客分类:
  • J2SE
阅读更多
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、创建动态类及查看其方法列表信息:
示例代码:
public static void main(String[] args) {
		Class clazz = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class);
		String clazzName = clazz.getName();	
		
		//打印所以的构造方法以及参数
		Constructor[] constructs = clazz.getConstructors();
		for(Constructor constructor : constructs) {
			String constructName = constructor.getName();
			StringBuilder sBuilder = new StringBuilder(constructName);
			sBuilder.append('(');
			Class[] clazzTypes = constructor.getParameterTypes();
			for(Class clazzType : clazzTypes) {
				sBuilder.append(clazzType.getName()).append(',');
			}
			if(clazzTypes != null && clazzTypes.length >0) {
				sBuilder.deleteCharAt(sBuilder.length() - 1);
			}
			sBuilder.append(')');
			System.out.println(sBuilder);
		}
		
		//打印这个接口所拥有的所有的方法以及他们的参数
		Method[] clazzMethods = clazz.getMethods();
		for(Method clazzMethod : clazzMethods) {
			String methodName = clazzMethod.getName();
			StringBuilder sBilder = new StringBuilder(methodName);
			Class[] methodTypes = clazzMethod.getParameterTypes();
			sBilder.append('(');
			for(Class methodType : methodTypes) {
				sBilder.append(methodType.getName()).append(',');
			}
			if(methodTypes != null && methodTypes.length >0) {
				sBilder.deleteCharAt(sBilder.length() - 1);
			}
			sBilder.append(')');
			System.out.println(sBilder);
		}
	}
	
	结果:
	$Proxy0(java.lang.reflect.InvocationHandler)
	add(java.lang.Object)
	hashCode()
	clear()
	equals(java.lang.Object)
	toString()
	contains(java.lang.Object)
	isEmpty()
	addAll(java.util.Collection)
	iterator()
	size()
	toArray([Ljava.lang.Object;)
	toArray()
	remove(java.lang.Object)
	containsAll(java.util.Collection)
	removeAll(java.util.Collection)
	retainAll(java.util.Collection)
	isProxyClass(java.lang.Class)
	getProxyClass(java.lang.ClassLoader,[Ljava.lang.Class;)
	getInvocationHandler(java.lang.Object)
	newProxyInstance(java.lang.ClassLoader,[Ljava.lang.Class;,java.lang.reflect.InvocationHandler)
	wait()
	wait(long,int)
	wait(long)
	getClass()
	notify()
	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();

//根据你传递的参数类型,返回相应的构造方法
Constructor construct = clazz.getConstructor(InvocationHandler.class);
	class myInvocationHandler implements InvocationHandler {
		public Object invoke(Object proxy, Method method, Object[] args)
				throws Throwable {
			return null;
		}
	}
//要返回代理对象,必须要传递一个实现了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中创建代理对象的二个步骤合成一个 (用到内部类)
Collection proxy2 = (Collection)construct.newInstance(new InvocationHandler() {

			public Object invoke(Object proxy, Method method, Object[] args)
					throws Throwable {
				return null;
			}
		});
		
在51 中我们创建一个代理对象是 得到字节码------>得到构造方法----->创建对象。我们能不能一步到位?这要利用Proxy
类提供的另一个静态方法:newProxyInstance
Collection proxy3 = (Collection)Proxy.newProxyInstance(
						//类加载器
						Collection.class.getClassLoader(), 
						//传入接口可能有多个,所以是数组
						new Class[]{Collection.class}, 
						
						new InvocationHandler() {
							//target就是我们要创建代理对象的那个真实的对象。
							ArrayList target = new ArrayList();
							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() + "方法运行时间为" + (beginTime -endTime));
								return retVal;
							}   
						}
		);
		
		proxy3.add("abc");
		proxy3.add("cde");
		System.out.println(proxy3.size());
		
总结:当调用代理对象的方法的时候,其实都会去调用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,以便让他更有通用性
(1)切面问题接口
	public interface Advice {
		public void beforeMethod(Method method);
		public void afterMethod(Method method);
	}
	(2)切面问题实现
	public class MyAdvice implements Advice {
		long beginTime;
		public void afterMethod(Method method) {
			System.out.println("方法执行之后:");
			long endTime = System.currentTimeMillis();
			System.out.println(method.getName() + "方法运行时间为" + (beginTime - endTime));
			
		}

		public void beforeMethod(Method method) {
			System.out.println("方法执行之前:");
			beginTime = System.currentTimeMillis();
		}
	}
	(3)动态代理方法的封装
	private static Object getProxy(final Object target, final Advice advice) {
		Object proxy = Proxy.newProxyInstance(
						target.getClass().getClassLoader(), 
						target.getClass().getInterfaces(),
						new InvocationHandler() {
							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 proxy;
	}
	(4)测试运行
		ArrayList target = new ArrayList();
		Collection proxy = (Collection)getProxy(target, new MyAdvice());
		proxy.add("abc");

56、实现类似Spring可配置的Aop框架:
模拟目标:根据我传递的方法的名称来确认返回的是真实的一个对象,还是一个代理对象,
如果我传递的这个类是个ProxyFactory类型的一个类(instanceof)则返回该类的一个代理对象
否则直接返回所指定类的一个真实的对象。
(1)BeanFactory
	public class BeanFactory {
	
		static Properties props = new Properties();
		
		public BeanFactory(InputStream ips) {
			try {
				props.load(ips);
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		
		public static Object getBean(String name) {
			Object bean = null;
			try {
				//得到传递对象的字节码
				Class clazz = Class.forName(props.getProperty(name));
				//创建一个实例对象
				bean = clazz.newInstance();
				//如果创建的这个对象是一个ProxyFactoryBean类型的,就返回代理对象,否则返回真实的对象
			} catch (Exception e) {
				e.printStackTrace();
			}
			if(bean instanceof ProxyFactoryBean) {
				ProxyFactoryBean proxyFactoryBean = (ProxyFactoryBean)bean;
				Object proxy = null;
			
				try {
					//得到目标类的对象
					Object target = Class.forName(props.getProperty(name + ".target")).newInstance();
					//得到解决“切面问题”类的对象
					Advice advice = (Advice)Class.forName(props.getProperty(name + ".advice")).newInstance();
					proxyFactoryBean.setAdvice(advice);
					proxyFactoryBean.setTarget(target);
					proxy = proxyFactoryBean.getProxy();
				} catch (Exception e){
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				//将上述两个对象设置到代理对象中
				//返回代理对象
				return proxy;
			}
			//返回真实的对象
			return bean;
		}
	}
	
	(2)ProxyFactoryBean
	public class ProxyFactoryBean {
	
		private Object target;
		
		private Advice advice;
		
		public Object getTarget() {
			return target;
		}

		public void setTarget(Object target) {
			this.target = target;
		}

		public Advice getAdvice() {
			return advice;
		}

		public void setAdvice(Advice advice) {
			this.advice = advice;
		}

		public Object getProxy() {
			Object proxy = Proxy.newProxyInstance(
					target.getClass().getClassLoader(), 
					target.getClass().getInterfaces(),
					new InvocationHandler() {
						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 proxy;
		}
	}
	
	(3)所用到的配置文件:config.properties 注意:要与AopTest测试类位于同一个包下面(xxx.target所指定的类要与返回真实类对象所指定的类一样,否则就不一致了)
	#xxx=java.util.ArrayList
	xxx=cn.itcast.aop.framework.ProxyFactoryBean
	xxx.advice=cn.itcast.day2.MyAdvice
	xxx.target=java.util.ArrayList
	
	(4)AopTest测试类
	public class AopTest {

		public static void main(String[] args) {
			InputStream ips = AopTest.class.getResourceAsStream("config.properties");
			Object bean = new BeanFactory(ips).getBean("xxx");
			System.out.println(bean.getClass().getName());
			((Collection)bean).clear();
		}
	}
  • 大小: 57.9 KB
  • 大小: 77.9 KB
  • 大小: 32 KB
  • 大小: 106.7 KB
  • 大小: 43.6 KB
分享到:
评论

相关推荐

    JAVA\张孝祥IT课堂-Java03-面向对象(上).ppt

    3. 多态(Polymorphism):多态是指同一个接口可以有不同的实现方式,即子类可以重写父类的方法,使得相同的操作可以作用于不同的对象,增强了代码的灵活性。Java中的方法重写(Override)和接口实现是多态的重要...

    张孝祥教授Javascript网页开发教程-02.pdf

    详细解释了html语言、css、javascript语法、dom对象编程、正则表达式,介绍了网页脚本编程的其他技术和知识。(由于上传限制,故将本教程分为6个部分上传,请全部下载后解压)

    张孝祥_高级j2se代码

    【张孝祥_高级j2se代码】这个资源包含了张孝祥老师精心编写的J2SE(Java 2 Standard Edition)高级代码示例,旨在帮助开发者深入理解和掌握Java核心技术。J2SE是Java平台的核心部分,它提供了丰富的类库和API,支持...

    张孝祥JAVA学习笔记

    张孝祥课程学习笔记,包括JAVA基础,JAVA高级,JavaScript全部的笔记,适合初学者。

    《张孝祥JAVA视频教程》完整版[RMVB]

    ed2k://|file|[张孝祥JAVA视频教程!(1-5课)].lesson01.[XiDong.Net].rmvb|555980477|1954514FA6C5757740064027D85826C1|h=EDKWEPSDDPS42DLGMONDGZPTQ7VF5BWX|/ ………………省略部分………… 第二部分 ed2k://|...

    张孝祥教授Javascript网页开发教程-01.pdf

    详细解释了html语言、css、javascript语法、dom对象编程、正则表达式,介绍了网页脚本不编程的其他技术和知识。(由于上传限制,故将本教程分为6个部分上传,请全部下载后解压)

    张孝祥教授Javascript网页开发教程-05.pdf

    详细解释了html语言、css、javascript语法、dom对象编程、正则表达式,介绍了网页脚本编程的其他技术和知识。(由于上传限制,故将本教程分为6个部分上传,请全部下载后解压)

    张孝祥Java邮件开发详解-高清扫描版-part1

    张孝祥Java邮件开发详解-高清扫描版-part1 绝对是精品 张孝祥的好书 。很大,分为2部分。

    j2se基础加强_张孝祥ppt

    j2se基础加强_张孝祥版ppt,对于java的基础提升是很有帮助的。

    传智播客张孝祥java高新技术 笔记ppt

    【Java高新技术笔记】这篇PPT主要涵盖了传智播客张孝祥教授的Java基础增强课程内容,其中重点讲解了Eclipse开发工具的快捷键使用,单元测试,静态导入,以及可变参数等Java编程中的重要知识点。 首先,Eclipse作为...

    张孝祥邮件开发ppt

    传智播客-------张孝祥老师--------------- javaMail开发PPT

    张孝祥java基础加强视频教程笔记

    【Java基础加强】张孝祥的Java教学视频涵盖了Java编程语言的基础到进阶知识,旨在帮助学习者巩固和提升Java编程技能。以下是根据教程笔记整理的一些关键知识点: 1. **Java简介**:Java是一种跨平台的面向对象的...

    张孝祥Java邮件开发详解-高清扫描版-part2

    张孝祥Java邮件开发详解-高清扫描版-part2

    张孝祥笔记

    jAVA基础+高新+7K知识点总结.chm 需要的可以下载

    Java基础加强--张孝祥版.ppt

    Java基础加强--张孝祥版 传智播客

    -张孝祥java视频教程56集

    java 教程 下载 -张孝祥java视频教程56集

    张孝祥Java多线程与并发库高级应用笔记

    ### 张孝祥Java多线程与并发库高级应用笔记概览 #### 一、Java多线程技术的重要性与挑战 Java线程技术是软件工程领域不可或缺的一部分,尤其在底层编程、Android应用开发以及游戏开发中,其重要性不言而喻。然而,...

    张孝祥老师讲课笔记

    ### 张孝祥老师讲课笔记:深入理解Windows程序运行机制 #### Windows程序设计与传统DOS方式的区别 张孝祥老师的讲课笔记强调了Windows程序设计与传统的DOS方式有着本质的不同,尤其体现在程序运行机制上。Windows...

    张孝祥交通灯管理系统笔记

    张孝祥交通灯管理系统笔记 面试题视频教程笔记

Global site tag (gtag.js) - Google Analytics