您的位置:首页 > 编程语言 > Java开发

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

2016-10-07 17:26 423 查看
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()
 

          返回直接存在于此元素上的所有注释。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐