首先来讲Java中的反射,在Java中,当我们在使用一个类时,该类的字节码就会加载进内存,所以当我们也可以通过调用这个类的字节码去调用对象的一些属性以及设置并且不受关键字的修饰,
注意:这里要啰嗦的一点是,无论这个类去new了多少此次,该类的字节码是不会变化的,它就像是一个文件的骨架一样,无论它以后披什么皮,骨架只有一个,(画皮的灵感)
下面介绍调用字节码的几种方式:
方式一
Thread th = new Thread();
Class<? extends Thread> aClass = th.getClass();
System.out.println(aClass.getName());
通过创建一个该类的子类对象去调用getclass方法从而得到该类的字节码文件
方式二
Class<Thread> aClass = Thread.class;
aClass.getName();
通过调用该类的静态属性class可以调出该类的额字节码文件对象。
方式三
Class aClass = Class.forName("org.westos.demo3.Student");
同样是通过调用Class类中的静态方法来取该类的字节码文件对象。方法中的String类型是指的这个类的全路径。全路径:包名+类名。
通常第三种方法比较常用。
接下来,当我们拿到字节码文件对象后,就可以对这个类进行刨析了。
字节码获取构造方法
public Constructor<?>[] getDeclaredConstructors () 获取所有的构造方法 包括私有的,调用的方法不加s就是调用单个方法
public Constructor<T> getDeclaredConstructor (Class < ? >...parameterTypes)获取单个的构造方法包含私有的
因为调用私有时也可以调用公共的,所以一般就是调用私有这个方法就可以,
获取空参构造方法对象
Constructor constructor = aClass.getConstructor();
通过反射创建出一个类的对象
Object o = constructor.newInstance(); //通过方式创建对象的方式
总的来说一套操作就是如下“
Class aClass = Class.forName("org.westos.demo4.Student"); 通过该类的全路径获得字节码
Constructor constructor = aClass.getConstructor(String.class); 通过字节码掉该类的构造函数
Object o = constructor.newInstance("bbb"); 通过构造函数创建对象
如果该构造方法私有,则需要在调newInstance方法前再加上一句:
declaredConstructor.setAccessible(true);//取消语法检测
反射创建对象的方式
//使用反射时通过空参构造创建一个类的对象,有两种方式
Class aClass = Class.forName("org.westos.demo4.Student");
//aClass.getConstructor();
//方式1:通过构造方法对象里面的newInstance()方法来创建对象的
Constructor constructor = aClass.getDeclaredConstructor();
Object o = constructor.newInstance();
//方式2通过 Class里面的newInstance()来创建对象
Object o1 = aClass.newInstance();
System.out.println(o==o1);
字节码获取字段对象
//获取该类的字节码文件对象
Class aClass = Class.forName("org.westos.demo.Student");
//获取某个非私有的字段对象
Field name = aClass.getField("name"); 里面的字符串是指对象的属性名称
System.out.println(name.getName());
//获取私有的字段对象
Field sex = aClass.getDeclaredField("sex");
Object o = aClass.newInstance();
sex.setAccessible(true); //取消语法检测
name.set(o,"zhangsan"); 给属性设值时需要创建一个对象
Student student= (Student) o;
System.out.println(student.name);
给私有字段设值时需要在调用set方法前需要取消语法检测
通过反射运行配置文件内容
Properties properties = new Properties(); 创建一个集合
properties.load(new FileInputStream("src/peizhi.txt")); 创建一个输入流的配置文件,然后再从输入流中读取属性列表(键和元素对)
Class classname = Class.forName(properties.getProperty("classname")); 使用键找到值然后再通过值创建字节码文件
Object obj = classname.newInstance(); 创建一个新对象
Method methodname = classname.getDeclaredMethod(properties.getProperty("methodname")); 使用键找值然后通过值得名称得到构造方法的名称,
methodname.setAccessible(true); 取消语法检测
methodname.invoke(obj); 将对象传入方法中执行方法
配置文件的作用是在以后程序运行中如果需要修改则只需通过配置文件修改而不必更改原码。
简单的例子如下:
Class aClass = list.getClass();
Method add = aClass.getDeclaredMethod("add", Object.class);
add.setAccessible(true);
add.invoke(list, "张三");
System.out.println(list);
通过反射越过泛型检查,将任意类型添加到集合中。
反射(动态代理的概述和实现)
先贴代码:
接口如下:
public interface UserDao {
public abstract void add();
public abstract void delete();
public abstract void update();
public abstract void query();
}
实现类如下:
public class UserDaoImpl implements UserDao {
@Override
public void add() {
//System.out.println("校验用户权限的代码");
System.out.println("增加了一条数据");
// System.out.println("记录日志");
}
@Override
public void delete() {
//System.out.println("校验用户权限的代码");
System.out.println("删除了一条数据");
//System.out.println("记录日志");
}
@Override
public void update() {
//System.out.println("校验用户权限的代码");
System.out.println("修改了一条数据");
// System.out.println("记录日志");
}
@Override
public void query() {
//System.out.println("校验用户权限的代码");
System.out.println("查询一条数据");
// System.out.println("记录日志");
}
}
测试类如下:
public class MyTest {
public static void main(String[] args) {
UserDao userDao = new UserDaoImpl();
//userDao.add();
//userDao.delete();
//我们可以采用一种动态代理的模式,增强某个功能
//比如我们想在 add方法之前,增加校验功能,add 后面 增加记录日志功能
//这种动态代理的方式,不需要我们去改原有的代码,能实现对某个功能进行增强
//这种动态代理模式,也Spring框架采用一种对功能进行增强的模式
UserDao proxy = GetProxyUtils.getProxy(userDao);
proxy.add();
System.out.println("----------------");
proxy.delete();
System.out.println("----------------");
proxy.query();
System.out.println("----------------");
proxy.update();
//代理模式:动态代理是Java给我们提供的,动态代理,要求必须要有接口
//如果一个类没有接口,那么动态代理用不了,但是第三方的框架,可以针对没有接口的类进行代理,这种方式叫做cglib代理
}
}
代理类:
public static UserDao getProxy(UserDao dao) {
//java.lang.reflect 类 Proxy 代理公司,帮你在运行期间,生成一个代理人对象,帮你完成功能的增强
//我们要在运行期间,创建一个代理对象
//static Object newProxyInstance (ClassLoader loader, Class < ?>[]interfaces, InvocationHandler h)
//返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。
//loader:
//类加载器
//interfaces:
//接口对应的一个Class数组
//InvocationHandler:
//这个其实就是要代理对象所做的事情的一个类的封装
//InvocationHandler 是代理实例的调用处理程序 实现的接口。
UserDao obj = (UserDao) Proxy.newProxyInstance(dao.getClass().getClassLoader(), dao.getClass().getInterfaces(), new InvocationHandler() {0
/**
*
* @param proxy 代理对象
* @param method 被代理类中方法的对象
* @param args 方法中的参数
* @return
* @throws Throwable
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//在被代理人,核心功能执行之前,可以让代理对象,帮你做你做准备工作,
//权限校验
System.out.println("权限的校验");
Object invoke = method.invoke(dao);
System.out.println("记录日志");
return invoke;
}
});
return obj;//返回代理对象
}
}
通俗一点的说就是当你去实现一个接口发现它功能不够时,你就可以创建一个代理类,让它可以跟实现类组合实现更多功能。