黑马程序员-高新技术-内省、类加载器、代理

----------------------ASP.Net+Android+IOS开发.Net培训、期待与您交流! ---------------------- 

内省

 IntroSpector

主要是对JavaBean进行操作

什么是JavaBeanJavaBean

是一种特殊的java类,主要是用于传递数据信息,这种java类中的方法主要用于访问私有字段,且方法名符合某种规则

在多个模块中传递信息的时候,可以将这些信息封装到一个JavaBean中,这种JavaBean的实例对象通常称之为值对象(Value Object ,VO)

JavaBean类的书写规则

1.JavaBean类必须是具体的和公共的,不能是抽象的

2.类中必须有一个无参的构造方法,为了实现序列化Serializable

3.类中的属性应该声明为私有权限,定义getter和setter方法对属性进行操作

package cn.itcast.day1;

public class PersonBean {
	private String name;
	private int age;
        
        public PersonBean(){}
	public PersonBean(String name, int age) {
		super();
		this.name = name;
		this.age = age;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

}


 

 下面用PropertyDescriptor来获取和设置PersonBean中的name值


package cn.itcast.day1;

import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class IntroSepctorTest {

	/**
	 * @param args
	 */
	public static void main(String[] args) throws Exception {
		// TODO Auto-generated method stub
		PersonBean personBean = new PersonBean("lisi", 18);
		String propertyName = "name";
		PropertyDescriptor pd = new PropertyDescriptor(propertyName,
				personBean.getClass());
		Object retVal = getProperty(personBean, pd);
		System.out.println(retVal);

		Object value = "zxx";
		setProperty(personBean, pd, value);
		System.out.println(getProperty(personBean, pd));
	}

	private static void setProperty(Object bean, PropertyDescriptor pd,
			Object value) throws IllegalAccessException,
			InvocationTargetException {
		Method methodSetName = pd.getWriteMethod();
		methodSetName.invoke(bean, value);
	}

	private static Object getProperty(Object bean, PropertyDescriptor pd)
			throws IllegalAccessException, InvocationTargetException {
		Method methodGetName = pd.getReadMethod();
		Object retVal = methodGetName.invoke(bean);
		return retVal;
	}

}

这是内省的一种简单的应用方式
 还有一种复杂的应用方式


package cn.itcast.day1;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class IntroSepctorTest {

	/**
	 * @param args
	 */
	public static void main(String[] args) throws Exception {
		// TODO Auto-generated method stub
		PersonBean personBean = new PersonBean("lisi", 18);
		String propertyName = "name";

		Object retVal = getProperty(personBean, propertyName);
		System.out.println(retVal);

		Object value = "zxx";
		setProperty(personBean, propertyName, value);
		System.out.println(getProperty(personBean, propertyName));
	}

	private static void setProperty(Object bean, String propertyName,
			Object value) throws IllegalAccessException,
			InvocationTargetException, IntrospectionException {
		/*
		 * PropertyDescriptor pd = new PropertyDescriptor(propertyName,
		 * bean.getClass()); Method methodSetName = pd.getWriteMethod();
		 * methodSetName.invoke(bean, value);
		 */
		BeanInfo beanInfo = Introspector.getBeanInfo(bean.getClass());
		PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
		for (PropertyDescriptor pd : pds) {
			if (pd.getName().equals(propertyName)) {
				Method methodSetName = pd.getWriteMethod();
				methodSetName.invoke(bean, value);
				break;
			}
		}
	}

	private static Object getProperty(Object bean, String propertyName)
			throws IllegalAccessException, InvocationTargetException,
			IntrospectionException {
		/*
		 * PropertyDescriptor pd = new PropertyDescriptor(propertyName,
		 * bean.getClass()); Method methodGetName = pd.getReadMethod(); Object
		 * retVal = methodGetName.invoke(bean);
		 */
		BeanInfo beanInfo = Introspector.getBeanInfo(bean.getClass());// BeanInfo是把一个java类当作javabean看的结果
		PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
		Object retVal = null;
		for (PropertyDescriptor pd : pds) {
			if (pd.getName().equals(propertyName)) {//当找到这个属性就对它进行操作
				Method methodGetName = pd.getReadMethod();
				retVal = methodGetName.invoke(bean);
				break;//操作完成直接跳出循环
			}
		}
		return retVal;
	}

}


 在开发中最常用的方式是用apache开发的开源的BeanUtils工具包

System.out.println(BeanUtils.getProperty(personBean, "name"));
		BeanUtils.setProperty(personBean, "age", "38");// 参数类型为object
		BeanUtils.setProperty(personBean, "name", "lhm");
		System.out.println(BeanUtils.getProperty(personBean, "name").getClass()
				.getName());
		System.out.println(BeanUtils.getProperty(personBean, "age").getClass()
				.getName());// 返回值类型为String
		BeanUtils.setProperty(personBean, "birthday.time",
				System.currentTimeMillis());
		System.out.println(BeanUtils.getProperty(personBean, "birthday"));
		
		/*java7的新特性 
		Map map={name:"zxx",age:18};
		BeanUtils.setProperty(map, "name", "lhm");
		BeanUtils.getProperty(map, "name");*/

		PropertyUtils.setProperty(personBean, "age", 38);// 参数类型是javabean类中属性的类型
		System.out.println(PropertyUtils.getProperty(personBean, "age")
				.getClass().getName());// 返回值类型为javabean类中属性的类型


 


 

 

 

 类加载器

什么是类加载器

就是加载类的工具

类加载器的作用

在源程序中用到的类,首先JVM要在硬盘上找到这个类的class文件,加载到内存中再进行一些处理

java虚拟机中有多个类加载器,系统默认提供了三个主要的类加载器

每个类加载器负责加载特定位置的类

BootStrap,ExtClassLoader,AppClassLoader

类加载器本身也是java类,因为其他java类的类加载器也要被类加载器加载,所以要有第一个不是java类的类加载器,来加载这些类加载器,就是BootStrap

BootStrap不是java类,它不需要被类加载器加载,是嵌套在java虚拟机内核里面的,是用C++写的

 

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

 

 

类加载器的委托机制

当java虚拟机需要加载一个类时,到底要用哪个类加载器去加载呢

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

如果类A中引用了类B,java虚拟机将用加载类A的类加载器去加载类B

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

 

第个类加载器加载时,都先委托给其上级类加载器

当所有上级类加载器没有加载到类,回到发起者类加载器,如果还加载不了,则抛ClassNotFoundException,不是再去找发起者类加载器的下级,因为没有getChild方法,即使有,那有多个下级,找哪一个呢

 

有一首面试题,能不能自己写一个类叫java.lang.System

能写,但写出之后,java虚拟机永远不会调用这个System类

因为当我用到System类时,java虚拟机会AppClassLoader ,而这时就会委托给它的上级ExtClassLoader,又委托给BootStrap类加载器去加载java类库中的System,这样就不会用到自己写的System类

如果自己写一个类加载器,而不指定它的上级类加载器,就可以用到了

 

编写自己的类加载器

 

package cn.itcast.day2;

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 {

 /**
  * @param args
  */
 public static void main(String[] args) {
  // TODO Auto-generated method stub
  String srcPath=args[0];
  String destDir=args[1];
  
  String destFileName=srcPath.substring(srcPath.lastIndexOf("\\")+1);
  String destPath=destDir+"\\"+destFileName;
  
  InputStream in=null;
  try {
   in=new FileInputStream(srcPath);
  } catch (FileNotFoundException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
  OutputStream out=null;
  try {
    out=new FileOutputStream(destPath);
   cipher(in,out);
  } catch (FileNotFoundException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
  finally{
   if(in!=null)
    try {
     in.close();
    } catch (IOException e) {
     // TODO Auto-generated catch block
     e.printStackTrace();
    }
   if(out!=null)
    try {
     out.close();
    } catch (IOException e) {
     // TODO Auto-generated catch block
     e.printStackTrace();
    }
  }
 }
 private static void cipher(InputStream ips,OutputStream ops){
  int b=-1;
  try {
   while((b=ips.read())!=-1){
    ops.write(b ^ 0xff);
   }
  } catch (IOException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
 }
 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();
   cipher(fis, bos);
   fis.close();
   byte[] buf = bos.toByteArray();
   return defineClass(buf, 0, buf.length);
  } catch (Exception e) {
   // TODO: handle exception
  }
  return super.findClass(name);
  
 }
 public MyClassLoader(){}
 public MyClassLoader(String classDir){
  this.classDir=classDir;
 }
}


 代理

当我们想在原有的类上增加一些功能,比如测试程序运行时间,我们不用修改原有的代码

而是创建一个代理类,在这个类中定义一个相同的方法,在方法中调用目标类的方法

这样不但保持了代码的封装性,也控制了对目标类的对象的访问


这个被代理的类一般称为目标类或委托类

一般情况下,代理类和目标类有同样的接口


代理类为目标类提供了额外的处理和操作:日志记录,性能统计,安全控制,事务处理,异常处理等等。代理类的对象并不实现真正的服务,而是通过调用目标类的对象的相关方法提供服务

采用代理是为了通过不修改源代码的情况下给程序动态统一添加功能,利用代理技术可以将业务逻辑中一些非业务逻辑的代码分离出来,把他们独立到业务逻辑类外,比如日志记录,性能统计,安全控制,事务处理,异常处理等。这样做,不仅降低了业务逻辑和非业务逻辑的耦合性,提高程序的可重用性,同时提高了开发的效率

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




系统中存在着交叉业务,安全、事务、日志等功能要贯穿于好多个模块中,所以他们就是交叉业务一个交叉业务就是要切入到系统中的一个方面


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


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


动态代理技术

静态代理:由程序员创建或特定工具自动生成源代码,再对其编译。在程序运行前,代理类的文件就已经存在了
动态代理:在程序运行时,运用反射机制动态创建而成

要为系统中的各种接口的类增加代理功能,那将需要太多的代理类,全部采用静态代理方式,将是一件非常麻烦的事情!写成百上千个代理类,太繁琐了
JVM可以在运行期动态生成出类的字节码,这种动态生成的类往往被用作代理类,即动态代理类
JVM生成的动态类必须实现一个或多个接口,所以,生成的动态类只能用作具有相同接口的目标类的代理
CGLIB(Code Generation Library)库可以动态生成一个类的子类,一个类的子类也可以用作该类的代理,所以,如果要为一个没有实现接口的类生成动态代理类,那么可以使用CGLIB库
代理类的各个方法中通常除了要调用目标的相应方法和对外返回目标返回的结果外,还可以在代理方法中的如下四个位置加上系统功能代码:
1,在调用的目标方法之前
2,在调用目标方法之后
3,在调用目标方法前后
4,在处理目标方法异常的catch块中


创建动态代理类对象

package cn.itcast.day3;

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;

public class ProxyTest {

	/**
	 * @param args
	 */
	public static void main(String[] args) throws Exception {
		// TODO Auto-generated method stub
		Class<?> clazzProxy1 = Proxy.getProxyClass(
				Collection.class.getClassLoader(), Collection.class);
		// Proxy.getProxyClass(loader, interfaces)这里的loader通常用后面interface的类加载器
		System.out.println(clazzProxy1.getName());

		System.out
				.println("-------------------begin constructor list----------------------");
		Constructor<?>[] costructors = clazzProxy1.getConstructors();
		for (Constructor<?> constructor : costructors) {
			String name = constructor.getName();
			StringBuilder sBuilder = new StringBuilder();
			sBuilder.append(name + "(");
			Class<?>[] clazzParams = constructor.getParameterTypes();
			if (clazzParams.length != 0) {
				for (Class<?> clazzParam : clazzParams) {
					sBuilder.append(clazzParam.getName() + ", ");
				}
				sBuilder.delete(sBuilder.length() - 2, sBuilder.length());
			}
			sBuilder.append(")");
			System.out.println(sBuilder);
		}
		System.out
				.println("-------------------begin method list----------------------");
		Method[] methods = clazzProxy1.getMethods();
		for (Method method : methods) {
			String name = method.getName();
			StringBuilder sBuilder = new StringBuilder();
			sBuilder.append(name + "(");
			Class<?>[] clazzParams = method.getParameterTypes();
			if (clazzParams.length != 0) {
				for (Class<?> clazzParam : clazzParams) {
					sBuilder.append(clazzParam.getName() + ", ");
				}
				sBuilder.delete(sBuilder.length() - 2, sBuilder.length());
			}
			sBuilder.append(")");
			System.out.println(sBuilder);
		}
		System.out
				.println("-------------------begin create instance object list----------------------");
		// 创建动态代理类对象方法1
		Constructor<?> constructor = clazzProxy1
				.getConstructor(InvocationHandler.class);
		class MyInvocationHandler implements InvocationHandler {

			@Override
			public Object invoke(Object proxy, Method method, Object[] args)
					throws Throwable {
				// TODO Auto-generated method stub
				return null;
			}
		}
		Collection<?> proxy1 = (Collection<?>) constructor
				.newInstance(new MyInvocationHandler());
		System.out.println(proxy1);
		// 返回了null,说明它的toString方法返回的是null,如果对象为null那么此时应该报空指针异常,也可以调用代理类的方法来测试
		proxy1.clear();// 这个方法没有返回值,可以调用
		//proxy1.size();// 这个方法有返回值,报告了NullPointerException

		// 创建动态代理类的方法2
		Collection<?> proxy2 = (Collection<?>) constructor
				.newInstance(new InvocationHandler() {
					@Override
					public Object invoke(Object proxy, Method method,
							Object[] args) throws Throwable {
						// TODO Auto-generated method stub
						return null;
					}
				});
		System.out.println(proxy2);
		/*
		 * 刚才让JVM创建了动态类及其实例对象 都做了三件事
		 * 1.为Proxy的getProxyClass传递了目标类的接口,即Collection,告诉代理类生成的类中有哪些方法
		 * 2.指定了这个接口的ClassLoader,产生类的字节码必须有一个关联的类加载器对象
		 * 3.生成的为中的方法的代码是怎样的,也要由我们提供,
		 * 把我们的代码写在一个约定好了的接口对象的方法中,把对象传递给它,它调用了我的方法,就相当于插入了我的代码,
		 * 提供执行代码的对象就是那个InvocationHandler对象
		 * ,它是在创建动态类的实例对象的构造方法时传递进去的。在上面的InvocationHandler对象的invoke
		 * 方法中加一点代码,就可以看到这些代码被调用并运行了
		 */
		
		@SuppressWarnings("unchecked")
		Collection<String> proxy3=(Collection<String>)Proxy.newProxyInstance(
				Collection.class.getClassLoader(), 
				new Class[]{Collection.class}, 
				new InvocationHandler(){
					ArrayList<String> target=new ArrayList<String>();
			@Override
			public Object invoke(Object proxy, Method method, Object[] args)
					throws Throwable {
				// TODO Auto-generated method stub
				//ArrayList<String> target=new ArrayList<String>();
				//每调用一个方法都会去调用InvocationHandler的invoke方法,第调用一次就会有一个目标,ArrayList对象,所以size打印为0
				long beginTime=System.currentTimeMillis();
				Object retVal=method.invoke(target, args);
				long endTime=System.currentTimeMillis();
				System.out.println("running time is "+(endTime-beginTime)+" of "+method.getName());
				return retVal;
			}});
		proxy3.add("zxx");
		proxy3.add("zxx");
		proxy3.add("zxx");
		System.out.println(proxy3.size());
	}

}

分析InvocationHandler内部原理

编写可生成代理和插入通告的通用方法

package cn.itcast.day3;

import java.lang.reflect.Method;

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

package cn.itcast.day3;

import java.lang.reflect.Method;

public class MyAdvice implements Advice {

	private long endTime;
	private long beginTime;

	@Override
	public void beforeMethod(Method method) {
		// TODO Auto-generated method stub
		beginTime = System.currentTimeMillis();
		System.out.println("到传志播客来学习了");
	}

	@Override
	public void afterMethod(Method method) {
		// TODO Auto-generated method stub
		System.out.println("从传志播客毕业上班了");
		endTime = System.currentTimeMillis();
		System.out.println(method.getName()+" running time is " + (endTime - beginTime));
	}

}

private static Object getProxy(final Object target, final Advice advice) {
		Object proxy = Proxy.newProxyInstance(target.getClass()
				.getClassLoader(), target.getClass().getInterfaces(),
				new InvocationHandler() {
					@Override
					public Object invoke(Object proxy, Method method,
							Object[] args) throws Throwable {
						// TODO Auto-generated method stub
						advice.beforeMethod(method);
						Object retVal = method.invoke(target, args);
						advice.afterMethod(method);

						return retVal;
					}
				});
		return proxy;
	}

final ArrayList<String> target = new ArrayList<String>();
@SuppressWarnings("unchecked")
Collection<String> proxy3 = (Collection<String>) getProxy(target,new MyAdvice());
proxy3.add("zxx");
proxy3.add("zxx");
proxy3.add("zxx");
System.out.println(proxy3.size());



----------------------ASP.Net+Android+IOS开发.Net培训、期待与您交流! ---------------------- 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值