Java反射和动态代理

在运行状态中,对于任意一个类,都能够获取到这个类的所有属性和方法,对于任意一个对象,都能够调用它的任意一个方法和属性(包括私有的方法和属性),这种动态获取的信息以及动态调用对象的方法的功能就称为java语言的反射机制。通俗点讲,通过反射,该类对我们来说是完全透明的,想要获取任何东西都可以。
想要使用反射机制,就必须要先获取到该类的字节码文件对象(.class),通过字节码文件对象,就能够通过该类中的方法获取到我们想要的所有信息(方法,属性,类名,父类名,实现的所有接口等等),每一个类对应着一个字节码文件也就对应着一个Class类型的对象,也就是字节码文件对象。

获取字节码文件对象的三种方式:

// 通过Class类中的静态方法forName获取类的字节码文件对象,此时该类还是源文件阶段,并没有变为字节码文件。
1、Class clazz1 = Class.forName("全限定类名"); 
// 当类被加载成.class文件时,此时Person类变成了.class,在获取该字节码文件对象,也就是获取自己,该类处于字节码阶段。
2、Class clazz2  = Person.class; 
// 通过类的实例获取该类的字节码文件对象,该类处于创建对象阶段 
Person p = new Person();
3、Class clazz3 = p.getClass(); 

反射API:
获取构造方法:

// 返回所有public构造方法
Constructor[] constructors = stuClass.getConstructors();
// 所有的构造方法(包括:私有、受保护、默认、公有)
Constructor[] constructors2 = stuClass.getDeclaredConstructors();

// 返回指定类型的public构造器(空参或随意参数)
Constructor constructor = stuClass.getConstructor(String.class, int.class);
// 返回指定类型的构造器(空参或随意参数)
Constructor constructor = stuClass.getDeclaredConstructor(int.class);

获取属性:

// 获取类中所有public类型的属性变量
Field[] getFields() 
// 获得类中所有属性变量 
Field[] getDeclaredFields()

// 根据变量名获取对应public类型的属性变量
Field getField(String name) 
// 根据变量名获得对应的变量
Field getDeclaredField(String name) 

获取方法:

// 获取“名称是name,参数是parameterTypes”的public的函数(包括从基类继承的、从接口实现的所有public函数)
public Method  getMethod(String name, Class[] parameterTypes)
// 获取“名称是name,参数是parameterTypes”,并且是类自身声明的函数,包含public、protected和private方法。
public Method  getDeclaredMethod(String name, Class[] parameterTypes)

// 获取全部的public的函数(包括从基类继承的、从接口实现的所有public函数)
public Method[]  getMethods()
// 获取全部的类自身声明的函数,包含public、protected和private方法。
public Method[]  getDeclaredMethods()

// 如果这个类是“其它类中某个方法的内部类”,调用getEnclosingMethod()就是这个类所在的方法;若不存在,返回null。
public Method  getEnclosingMethod()

// invoke()方法eg:  
List list = new ArrayList();
Method m = getMethod("add", String.calass)
m.invoke(list, "aaa");

1、Object转Map方法:

public static Map objectToMap(Object obj, boolean keepNullVal) {
	if (obj == null) {
		return null;
	}
	Map<String, Object> map = new HashMap<String, Object>();
	try {
		Field[] declaredFields = obj.getClass().getDeclaredFields();
		for (Field field : declaredFields) {
			field.setAccessible(true);
			if (keepNullVal == true) {
				map.put(field.getName(), field.get(obj));
			} else {
				if (field.get(obj) != null && !"".equals(field.get(obj).toString())) {
					map.put(field.getName(), field.get(obj));
				}
			}
		}
	} catch (Exception e) {
		e.printStackTrace();
	}
	return map;
}

2、通过反射越过泛型检查

public class Demo {  
   public static void main(String[] args) throws Exception{  
       ArrayList<String> strList = new ArrayList<>();  
       strList.add("aaa");  
       strList.add("bbb");  

	   // strList.add(100);  
       //获取ArrayList的Class对象,反向的调用add()方法,添加数据  
       Class listClass = strList.getClass(); //得到 strList 对象的字节码 对象  
       //获取add()方法  
       Method m = listClass.getMethod("add", Object.class);  
       //调用add()方法  
       m.invoke(strList, 100);  

       //遍历集合  
       for(Object obj : strList){  
           System.out.println(obj);
           System.out.println(obj.getClass());
       }  
   }  
}

3、动态代理的实现
作用:将你要使用的类,重新生成一个子类或本类,在该类的方法前后加一些代码。解耦合,不修改源码。
具体应用的话,比如可以添加调用日志,做事务控制等。动态代理是设计模式当中代理模式的一种。

主要实现原理:
InvocationHandler接口:
每一个动态代理类都必须要实现InvocationHandler这个接口,并且每个代理类的实例都关联到了一个handler,
当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的
invoke 方法来进行调用。我们来看看InvocationHandler这个接口的唯一一个方法 invoke 方法:

// proxy:  指代我们所代理的那个真实对象
// method:  指代的是我们所要调用真实对象的某个方法的Method对象
// args:  指代的是调用真实对象某个方法时接受的参数
Object invoke(Object proxy, Method method, Object[] args) throws Throwable

Proxy类:
Proxy这个类的作用就是用来动态创建一个代理对象的类,它提供了许多的方法,但是我们用的最多的就是
newProxyInstance 这个方法:

// loader:  一个ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载
// interfaces:  一个Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了
// h:一个InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException

demo:

/**
 * 真实对象的接口
 */
public interface Subject
{
    public void rent();
    
    public void hello(String str);
}

/**
 * 真实对象
 */
public class RealSubject implements Subject
{
    @Override
    public void rent()
    {
        System.out.println("I want to rent my house");
    }
    
    @Override
    public void hello(String str)
    {
        System.out.println("hello: " + str);
    }
}

/**
 * 动态代理类
 * 实现InvocationHandler接口
 */
public class DynamicProxy implements InvocationHandler
{
    // 这个就是我们要代理的真实对象
    private Object subject;
    
    //    构造方法,给我们要代理的真实对象赋初值
    public DynamicProxy(Object subject)
    {
        this.subject = subject;
    }
    
    @Override
    public Object invoke(Object object, Method method, Object[] args)
            throws Throwable
    {
        //  在代理真实对象前我们可以添加一些自己的操作
        System.out.println("before invoke");
        
        System.out.println("Method:" + method);
        
        //    当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用
        method.invoke(subject, args);
        
        //  在代理真实对象后我们也可以添加一些自己的操作
        System.out.println("after invoke");
        
        return null;
    }

}

/**
 * 客户端类
 */
public class Client
{
    public static void main(String[] args)
    {
        //    我们要代理的真实对象
        Subject realSubject = new RealSubject();

        //    我们要代理哪个真实对象,就将该对象传进去,最后是通过该真实对象来调用其方法的
        InvocationHandler handler = new DynamicProxy(realSubject);

        /*
         * 通过Proxy的newProxyInstance方法来创建我们的代理对象,我们来看看其三个参数
         * 第一个参数 handler.getClass().getClassLoader() ,我们这里使用handler这个类的ClassLoader对象来加载我们的代理对象
         * 第二个参数realSubject.getClass().getInterfaces(),我们这里为代理对象提供的接口是真实对象所实行的接口,表示我要代理的是该真实对象,这样我就能调用这组接口中的方法了
         * 第三个参数handler, 我们这里将这个代理对象关联到了上方的 InvocationHandler 这个对象上
         */
        Subject subject = (Subject)Proxy.newProxyInstance(handler.getClass().getClassLoader(), realSubject
                .getClass().getInterfaces(), handler);
        
        System.out.println(subject.getClass().getName());
        subject.rent();
        subject.hello("world");
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值