java类加载,反射,动态代理入门理解

1、java的类加载机制,参考文档

http://blog.csdn.net/gjanyanlig/article/details/6818655

JVM的类加载是通过ClassLoader及其子类来完成的,类的层次关系和加载顺序可以由下图来描述:


1)Bootstrap ClassLoader

负责加载$JAVA_HOME中jre/lib/rt.jar里所有的class,由C++实现,不是ClassLoader子类

2)Extension ClassLoader

负责加载java平台中扩展功能的一些jar包,包括$JAVA_HOME中jre/lib/*.jar或-Djava.ext.dirs指定目录下的jar包

3)App ClassLoader

负责记载classpath中指定的jar包及目录中class

4)Custom ClassLoader

属于应用程序根据自身需要自定义的ClassLoader,如tomcat、jboss都会根据j2ee规范自行实现ClassLoader

加载过程中会先检查类是否被已加载,检查顺序是自底向上,从Custom ClassLoader到BootStrap ClassLoader逐层检查,只要某个classloader已加载就视为已加载此类,保证此类只所有ClassLoader加载一次。而加载的顺序是自顶向下,也就是由上层来逐层尝试加载此类。


2、反射

       JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
      要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象.


反射:就是通过class文件对象,去使用该文件中的成员变量,构造方法,成员方法。

下面我们通过一个小案例来了解下class对象的一些特性

/*
 * 反射:就是通过class文件对象,去使用该文件中的成员变量,构造方法,成员方法。
 * 
 * Person p = new Person();
 * p.使用
 * 
 * 要想这样使用,首先你必须得到class文件对象,其实也就是得到Class类的对象。
 * Class类:
 * 		成员变量	Field
 * 		构造方法	Constructor
 * 		成员方法	Method
 * 
 * 获取class文件对象的方式:
 * A:Object类的getClass()方法
 * B:数据类型的静态属性class
 * C:Class类中的静态方法
 * 		public static Class forName(String className)
 * 
 * 一般我们到底使用谁呢?
 * 		A:自己玩	任选一种,第二种比较方便
 * 		B:开发	第三种
 * 			为什么呢?因为第三种是一个字符串,而不是一个具体的类名。这样我们就可以把这样的字符串配置到配置文件中。
 */
public class ReflectDemo {
	public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
		// 方式1
		Person p = new Person();
		Class c = p.getClass();

		Person p2 = new Person();
		Class c2 = p2.getClass();

		System.out.println(p == p2);// false
		System.out.println(c == c2);// true

		// 方式2
		Class c3 = Person.class;
		// int.class;
		// String.class;
		System.out.println(c == c3);

		// 方式3
		// ClassNotFoundException
		Class c4 = Class.forName("cn.itcast_01.Person");
		Person person=(Person) c2.newInstance();
		System.out.println(c == c4);
	}
}
输出结果false
true
true
true

我们可以这么吧,获取class对象的方式有三种方式,且class对象是单例模式。


(2)下面介绍下class对象的主要方法:

Class c = Class.forName("cn.itcast_01.Person");

Constructor con = c.getConstructor(String.class, int.class,String.class);// 返回的是构造方法对象(可以带有参数,有重载方法)

Person obj = (Person) con.newInstance();//创建该类的对象(可以带有参数,有重载方法)

getConstructors() //返回一个集合,所有构造方法

输出结果

public cn.itcast_01.Person()
Person [name=null, age=0, address=null]

反射可以创建私有构造方法的对象

/*
 * 需求:通过反射获取私有构造方法并使用
 * private Person(String name){}
 * 
 * Person p = new Person("风清扬");
 * System.out.println(p);
 */
public class ReflectDemo3 {
	public static void main(String[] args) throws Exception {
		// 获取字节码文件对象
		Class c = Class.forName("cn.itcast_01.Person");

		// 获取私有构造方法对象
		// NoSuchMethodException:每个这个方法异常
		// 原因是一开始我们使用的方法只能获取公共的,下面这种方式就可以了。
		Constructor con = c.getDeclaredConstructor(String.class);

		// 用该私有构造方法创建对象
		// IllegalAccessException:非法的访问异常。
		// 暴力访问
		con.setAccessible(true);// 值为true则指示反射的对象在使用时应该取消Java语言访问检查。
		Object obj = con.newInstance("风清扬");

		System.out.println(obj);
	}
}

(3)、获取字段对象,并进行赋值

// 获取单个的成员变量
		// 获取address并对其赋值
		Field addressField = c.getField("address");
		// public void set(Object obj,Object value)
		// 将指定对象变量上此 Field 对象表示的字段设置为指定的新值。
		addressField.set(obj, "北京"); // 给obj对象的addressField字段设置值为"北京"
		System.out.println(obj);
(4)通过方法名,得到方法Method,并调用该方法(这种模式下,可以调用私有方法)

public class ReflectDemo {
	public static void main(String[] args) throws Exception {
		// 获取字节码文件对象
		Class c = Class.forName("cn.itcast_01.Person");

		// 获取所有的方法
		// Method[] methods = c.getMethods(); // 获取自己的包括父亲的公共方法
		// Method[] methods = c.getDeclaredMethods(); // 获取自己的所有的方法
		// for (Method method : methods) {
		// System.out.println(method);
		// }

		Constructor con = c.getConstructor();
		Object obj = con.newInstance();
		

		/*
		 * Person p = new Person(); p.show();
		 */

		// 获取单个方法并使用
		// public void show()
		// public Method getMethod(String name,Class<?>... parameterTypes)
		// 第一个参数表示的方法名,第二个参数表示的是方法的参数的class类型
		Method m1 = c.getMethod("show");
		// obj.m1(); // 错误
		// public Object invoke(Object obj,Object... args)
		// 返回值是Object接收,第一个参数表示对象是谁,第二参数表示调用该方法的实际参数
		m1.invoke(obj); // 调用obj对象的m1方法

		System.out.println("----------");
		// public void method(String s)
		Method m2 = c.getMethod("method", String.class);
		m2.invoke(obj, "hello");
		System.out.println("----------");

		// public String getString(String s, int i)
		Method m3 = c.getMethod("getString", String.class, int.class);
		Object objString = m3.invoke(obj, "hello", 100);
		System.out.println(objString);
		// String s = (String)m3.invoke(obj, "hello",100);
		// System.out.println(s);
		System.out.println("----------");

		// private void function()
		Method m4 = c.getDeclaredMethod("function");
		m4.setAccessible(true);
		m4.invoke(obj);
	}
}
总结:上面我们说了如果通过反射获取对象,获取字段,获取方法、如果如何给字段赋值,如何调用方法,并且

我们发现获取方法,字段都是重构的,并且可以一次获取所有方法和字段。具体方法可以参考api


下面我们接触动态代理,我们会发现动态代理中通过反射调用目标方法

案例

/**
 * 拦截器
 *    1、目标类导入进来
 *    2、事务导入进来
 *    3、invoke完成
 *        1、开启事务
 *        2、调用目标对象的方法
 *        3、事务的提交
 * @author zd
 *
 */
public class MyInterceptor implements InvocationHandler{
	private Object target;//目标类
	private Transaction transaction;
	
	
	public MyInterceptor(Object target, Transaction transaction) {
		super();
		this.target = target;
		this.transaction = transaction;
	}
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		String methodName = method.getName();
		if("savePerson".equals(methodName)||"updatePerson".equals(methodName)
				||"deletePerson".equals(methodName)){
			this.transaction.beginTransaction();//开启事务
			method.invoke(target);//调用目标方法
			this.transaction.commit();//事务的提交
		}else{
			method.invoke(target);
		}
		return null;
	}
}

public class JDKProxyTest {
	@Test
	public void testJDKProxy(){
		/**
		 * 1、创建一个目标对象
		 * 2、创建一个事务
		 * 3、创建一个拦截器
		 * 4、动态产生一个代理对象
		 */
		Object target = new PersonDaoImpl();
		Transaction transaction = new Transaction();
		MyInterceptor interceptor = new MyInterceptor(target, transaction);
		/**
		 * 1、目标类的类加载器
		 * 2、目标类实现的所有的接口
		 * 3、拦截器
		 */
		PersonDao personDao = (PersonDao)Proxy.newProxyInstance(target.getClass().getClassLoader(), 
				target.getClass().getInterfaces(), interceptor);
		//personDao.savePerson();
		personDao.updatePerson();
	}
}
这里有个注意事项,我们发现代理类,必须实现接口,那么解决方法可以通过cglib解决,这里不介绍了,也是个代理方法

而且这里我们必须联系到注解

首先我们谈下如何定义注解,请阅读如下文章

http://blog.csdn.net/u012316953/article/details/52749496


那么我们如何读取类上,方法上 等的注解呢?
毫无疑问也是通过class对象,那么我介绍下class对象的一些关于注解的方法,具体参考api

isAnnotation()
          如果此 Class 对象表示一个注释类型则返回 true。

getAnnotation(Class<A> annotationClass)
          如果存在该元素的指定类型的注释,则返回这些注释,否则返回 null。

getAnnotations()
          返回此元素上存在的所有注释。

 字段Field 关于注解的方法

getAnnotation(Class<T> annotationClass)
          如果存在该元素的指定类型的注释,则返回这些注释,否则返回 null。

方法Method关于注解的方法

getAnnotation(Class<T> annotationClass)
          如果存在该元素的指定类型的注释,则返回这些注释,否则返回 null。

getDeclaredAnnotations()
          返回直接存在于此元素上的所有注释。

构造函数construct上的注解方法

getAnnotation(Class<T> annotationClass) 
          如果存在该元素的指定类型的注释,则返回这些注释,否则返回 null。

getDeclaredAnnotations() 
          返回直接存在于此元素上的所有注释。








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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值