动态代理与类加载器

l  Java提供了一个Proxy类,调用它的newInstance方法可以生成某个对象的代理对象,使用该方法生成代理对象时,需要三个参数:

•   1.生成代理对象使用哪个类装载器

•   2.生成哪个对象的代理对象,通过接口指定

•   3.生成的代理对象的方法里干什么事,由开发人员编写handler接口的实现来指定。

备注:

•   Proxy类负责创建代理对象时,如果指定了handler(处理器),那么不管用户调用代理对象的什么方法,该方法都是调用处理器的invoke方法。(这相当于invoke方法拦截到了代理对象的方法调用)。

•   由于invoke方法被调用需要三个参数:代理对象、方法、方法的参数,因此不管代理对象哪个方法调用处理器的invoke方法,都必须把自己所在的对象、自己(调用invoke方法的方法)、方法的参数传递进来。

用途:

•   开发人员通过invoke方法的参数,还可以在拦截的同时,知道用户调用的是什么方法,因此利用这两个特性,就可以实现一些特殊需求,例如:拦截用户的访问请求,以检查用户是否有访问权限、动态为某个对象添加额外的功能。

静态代理

         包装设计模式的写法可以实现代理, 我们称之为静态代理

方法: 写一个类去代理某个对象

     1. 代理类要和被代理对象实现同样的接口

     2. 代理类一定要持有一个被代理对象

     3. 代理类在实现接口所有的方法时,大部分方法都依靠被代理对象实现,有些功能,需要懂一些手脚

动态代理

         分析静态代理缺点:由于实现同样的接口,要实现所有不需要代理的方法,很麻烦,这个时候我们就想能不能自己生成个类,jdk提供了一种机制,能够动态地生成一个类,不需要写一行代码,要求我们说明这个类要实现哪些接口,目的就是为了让其知道要覆盖什么方法。

         用法:Proxy.getProxyClass(loader,interfaces);

         这个方法就能够动态生成一个类,传入类加载器与共同实现的接口就能返回一个类,这个类没有无参构造函数,因此不能够用生成的class文件.newInstance方法来获取实例对象,由于newInstance方法默认会调用无参构造函数,但是实际上动态生成的类中存在一个有一个参数的构造函数,这个参数的类型为InvocationHandler(接口),实例这个接口要覆盖一个invoke方法。

例:
public class DaoProxy {
	
	// 代理谁   被代理对象
	private Dao dao;	
	
	//这个仅仅是个例子,user应该在session中,这里就在实例化的时候让调用者传过来就得了
	private User user;
	public DaoProxy(Dao dao, User user) {
		super();
		this.dao = dao;
		this.user = user;
	}
	//返回值为Dao,因为我们要创建的这个类实现了这个接口,所以就是他的子类
	public Dao createDaoProxy() {
		try {
			// 通过 工具类  Proxy的getProxyClass方法 动态生成代理类
			//this.dao为被代理对象.getClass().getClassLoader()获取类加载器
			//this.dao.getClass().getInterfaces()获取被代理对象所实现的接口
			Class proxyClass = Proxy.getProxyClass(this.dao.getClass()
					.getClassLoader(), this.dao.getClass().getInterfaces());
			// 代理生成一个类后要实例化对象
		// 看一下有什么构造函数,实验结论为有一个有参的构造函数  参数类型是InvocationHandler
	Constructor constructor = proxyClass.getConstructor(InvocationHandler.class);
	//由于实现了dao接口,所以返回值一定是个Dao,这里也应用了策略模式,我帮你生成一个类,你要给我	  实现一个接口
		Dao dao = (Dao) constructor.newInstance(new InvocationHandler() {
				@Override//实例InvocationHandler接口需要覆盖invoke方法
				public Object invoke(Object proxy, Method method, Object[] args)
						throws Throwable {
					// 当前代理对象的任何一个方法被调用了   都会触发invoke方法
					// 例子:判断一下被调用的方法名是什么,如果是delete 就检查权限
					if("delete".equals(method.getName())) {
						//这里就简单做了,如果用户名不为manager就告诉其没有权限
						if(!"manager".equals(user.getUsername())) {
							System.out.println("没有删除的权限");
							return null;
						}
					}
					// 对于大部分方法  不需要包装就调用被代理对象同样的方法
		// 用方法参数的method的invoke方法执行该方法,DaoProxy.this.dao意思为被代理对象			  (DaoProxy的dao)
		//args为方法的参数,我们不去管代理代理对象的方法参数是什么,还用这个参数就行了,将方法的		  返回值进行返回就行了
					Object value = method.invoke(DaoProxy.this.dao, args);
					return value;
				}
			});
			return dao;
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}
}

例:由于拦截器的统一全站乱码用到了代理,这里就进行演示
Proxy.newProxyInstance(loader, interfaces, InvocationHandler);方法直接就将动态生成的类进行实例化

//这里直接在这个类上实现InvocationHandler,直接在类里覆盖接口的方法,这样写看着清楚
class RequestProxy implements InvocationHandler {
	// 被代理对象
	private HttpServletRequest request; 	
	public RequestProxy(HttpServletRequest request) {
		super();
		this.request = request;
	}
	// 实现一个方法用于创建代理
	public HttpServletRequest createRequestProxy() {
	HttpServletRequest requestProxy = (HttpServletRequest) Proxy.newProxyInstance(
											this.request.getClass().getClassLoader()
											, this.request.getClass().getInterfaces()
											, this);
		// this指向RequestProxy类对象,RequestProxy类又实现了InvocationHandler接口,所以this就是InvocationHandler
		return requestProxy;
	}

	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		// 处理程序
		// 代理 getParameter
		if("getParameter".equals(method.getName())) {
			String value = (String) method.invoke(this.request, args);
			if(value==null || "post".equals(this.request.getMethod())) {
				return value;
			}
			// 是get方式提交
			return new String(value.getBytes("iso-8859-1"),"utf-8");
		}
		//HttpServletRequest的其他方法不变
		return method.invoke(this.request, args);
	}
}

类加载器

         类加载器负责将 .class 文件(可能在磁盘上, 也可能在网络上) 加载到内存中, 并为之生成对应的        java.lang.Class 对象当JVM 启动时,会形成由三个类加载器组成的初始类加载器层次结构

类加载器之间的父子关系和管辖范围图

 

bootstrap classloader

l  bootstrap classloader:引导(也称为原始)类加载器它负责加载Java的核心类。这个加载器的是非常特殊的,它实际上不是java.lang.ClassLoader的子类,而是由JVM自身实现的。可以通过执行以下代码来获得bootstrapclassloader加载了那些核心类库:

           URL[]urls=sun.misc.Launcher.getBootstrapClassPath().getURLs();

           for (int i = 0; i < urls.length; i++) {

                    System.out.println(urls[i].toExternalForm());

           }

 

l  因为JVM在启动的时候就自动加载它们,所以不需要在系统属性CLASSPATH中指定这些类库

 

extension classloader

l  extension classloader -扩展类加载器,它负责加载JRE的扩展目录(JAVA_HOME/jre/lib/ext或者由java.ext.dirs系统属性指定的)中的JAR包。这为引入除Java核心类以外的新功能提供了一个标准机制。因为默认的扩展目录对所有从同一个JRE中启动的JVM都是通用的,所以放入这个目录的JAR类包对所有的JVM和system classloader都是可见的。

 

system classloader

l  system classloader - 系统(也称为应用)类加载器,它负责在JVM被启动时,加载来自在命令java中的-classpath或者java.class.path系统属性或者CLASSPATH操作系统属性所指定的JAR类包和类路径。

l  可以通过静态方法ClassLoader.getSystemClassLoader()找到该类加载器。如果没有特别指定,则用户自定义的任何类加载器都将该类加载器作为它的父加载器。

 

父类委托机制

l  classloader 加载类用的是父类委托机制

父类委托机制:先让parent(父)类加载器 寻找,只有在parent找不到的时候才从自己的类路径中去寻找

类加载还采用了cache机制:如果cache中保存了这个Class就直接返回它,如果没有才从文件中读取和转换成Class,并存入cache,这就是为什么修改了Class但是必须重新启动JVM才能生效,并且类只加载一次的原因。



  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值