1、java的类加载机制,参考文档
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);// 返回的是构造方法对象(可以带有参数,有重载方法)
(可以带有参数,有重载方法)
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解决,这里不介绍了,也是个代理方法
而且这里我们必须联系到注解
首先我们谈下如何定义注解,请阅读如下文章
那么我们如何读取类上,方法上 等的注解呢?
毫无疑问也是通过class对象,那么我介绍下class对象的一些关于注解的方法,具体参考api
isAnnotation()
如果此 Class
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()
返回直接存在于此元素上的所有注释。