一、反射
1、概念
java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为Java语言的反射机制。通俗的讲就是调用某个方法间接获取类的属性以及方法。这是比较官方的解释,我个人理解就是通过某种方法拿到类的字节码对象(即Class),字节码存储了这个类的所有信息,比如类的属性、成员变量、方法。换句话说就是,我们可以通过字节码对象Class对象,获取我们想要的属性、变量、方法去做一些事情。
2、获取Class对象可通过Class.forName(String className)方法,classname如果不在同一包下,需要写全包名。在jvm里,每个类在对应的一个类加载器(ClassLoader)中只存在一个Class对象,通常情况下,一个jvm只存在一个ClassLoader,当然也可以根据需求实现自己想要的ClassLoader。
3、有了Class对象之后,就可以通过Class做很多事情,具体可做什么可查看jdk源码中java.lang.reflect下的Class.java类,java.lang.reflect提供了诸多供反射使用的类以及方法,这里只列举其中的一小部分。
a、通过反射可获取构造方法Constructor,有了Constructor我们就可以创建实例,Constructor也在java.lang.reflect包下。
b、通过反射获取方法Method,可通过Method去执行对应的方法。
c、通过反射获取对应的成员变量
// 通过反射获取字节码对象
Class<?> clazz = Class.forName("com.hczhifu.demo.demo.TestReflect");
// 通过反射获取所有公有构造函数
Constructor<?>[] constructors = clazz.getConstructors();
// 通过反射获取指定参数类型公有构造函数
Constructor<?> constructor = clazz.getConstructor(String.class);
// 通过反射获取所有私有构造函数
Constructor<?>[] declaredConstructors = clazz.getDeclaredConstructors();
// 通过反射获取指定参数类型的构造函数可匹配私有公有
Constructor<?> declaredConstructor = clazz.getDeclaredConstructor(int.class);
// 解除私有鉴定,不然在其他包中执行会抛出异常
declaredConstructor.setAccessible(true);
// 通过构造函数创建实例,以下会多次用到这个实例对象
TestReflect instance = (TestReflect) declaredConstructor.newInstance(888);
// 通过反射获取所有方法,包括私有方法
Method[] declaredMethods = clazz.getDeclaredMethods();
// 通过反射获取指定方法名以及方法参数类型的方法可匹配私有、公有
Method declaredMethod = clazz.getDeclaredMethod("testP", String.class);
// 解除方法私有鉴定,如不解除,在其他包中执行会抛出异常
declaredMethod.setAccessible(true);
// 执行方法
declaredMethod.invoke(instance, "lalalallalallala");
// 通过反射获取公有方法
Method[] methods = clazz.getMethods();
// 通过反射获取指定方法名以及参数类型的公有方法
Method method = clazz.getMethod("setType", String.class);
// 根据反射获取指定方法名的公有方法
Method method2 = clazz.getMethod("getType");
// 执行方法,传入对象实例以及参数
method.invoke(instance, "成功");
// 执行方法返回结果
Object object = method2.invoke(instance);
System.out.println(object);
// 通过反射获取所有的属性包括私有的
Field[] declaredFields = clazz.getDeclaredFields();
System.out.println("属性信息测试======开始");
for (Field f : declaredFields) {
// 解除私有限定
f.setAccessible(true);
// 通过反射可获取对象中的属性值,只举例其中一种类型
if (int.class == f.getType()) {
System.out.println("属性【" + f.getName() + "】值为" + f.getInt(instance));
}
}
System.out.println("属性信息测试======结束");
// 通过反射获取公有属性
Field[] fields = clazz.getFields();
for (Field f : fields) {
System.out.println(f.getName());
}
System.out.println("构造函数测试=======开始");
for (Constructor c : declaredConstructors) {
// 可通过反射获取构造函数的参数类型
Class[] parameterTypes = c.getParameterTypes();
Object[] param = null;
if (parameterTypes.length > 0) {
param = new Object[parameterTypes.length];
for (int i = 0; i < parameterTypes.length; i++) {
// 只举例考虑一种情况
if (parameterTypes[i] == String.class) {
param[i] = "构造函数测试type值";
}
if (parameterTypes[i] == int.class) {
param[i] = 999;
}
}
}
// 解除私有限定
c.setAccessible(true);
// 调用构造函数newInstance方法构造对象
TestReflect tr = (TestReflect) c.newInstance(param);
System.out.println("初始化值:type值【" + tr.getType() + "】size值【" + tr.getSize() + "】");
}
System.out.println("构造函数测试=======结束");
public class TestReflect {
private String type;
private int size;
public String publicValue;
/**
* 公有无参构造函数
*/
public TestReflect() {
System.out.println("执行构造方法");
}
/**
* 公有有参构造函数
*
* @param type
*/
public TestReflect(String type) {
this.type = type;
System.out.println("执行带参数构造构造方法");
}
/**
* 私有有参构造函数
*
* @param size
*/
private TestReflect(int size) {
this.size = size;
}
public int getSize() {
return size;
}
public void setSize(int size) {
this.size = size;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
private void testP(String value) {
System.out.println("执行私有方法========" + value);
}
}
4、反射总结
由以上代码可知,通过反射我们可以获取字节码对象中的所有信息,即使是私有方法、私有构造函数、私有成员变我们可以通过解除私有限定然后执行相关的语句获得我们想要的结果,换句话说如果想要执行某个类的私有方法,我们可以通过反射去执行。可想而知,反射的功能非常强大,主要涉及的类有Class.java、Constructor.java、Method.java、Field.java。
二、动态代理
1、首先要知道的是什么是代理,简单的说代理就是某个事物代替另外一个事物去执行某个动作,这个过程称之为代理。java程序中的代理可分为静态代理和动态代理,那么什么是静态代理呢,静态代理就是程序运行的过程代码由程序员创建或者程序开发工具生成,在编译期间就已经把目标接口、被代理类(实现目标接口)、代理类(持有被代理对象)的class文件生成;代理类在程序运行时创建的叫做动态代理。以下是静态代理与动态代理的demo。
a、 静态代理
/**
* 目标接口
* @author zhengyuyuan
*
*/
public interface AbstraceService {
void service();
}
/**
* 实现目标接口具体实现类
* @author zhengyuyuan
*
*/
public class SocialService implements AbstraceService {
@Override
public void service() {
System.out.println("缴纳社保");
}
}
/**
* 代理类,代替缴纳社保
* @author zhengyuyuan
*
*/
public class SocialServiceProxy {
//持有被代理对象
private SocialService socialService;
public SocialServiceProxy(){
}
public SocialServiceProxy(AbstraceService service){
this.socialService = (SocialService) service;
}
public void doSocial(){
//可添加其他业务代码
socialService.service();
}
}
main方法,测试执行
//创建实例对象
AbstraceService service = new SocialService();
//创建代理对象,程序员创建
SocialServiceProxy proxy = new SocialServiceProxy(service);
//“代理类”代理“被代理对象”执行
proxy.doSocial();
b、动态代理
/**
* 目标接口
* @author zhengyuyuan
*
*/
public interface AbstraceService {
void service();
}
/**
* 实现目标接口具体实现类
* @author zhengyuyuan
*
*/
public class SocialService implements AbstraceService {
@Override
public void service() {
System.out.println("缴纳社保");
}
}
/**
* 实现InvocationHandler 持有目标接口
* @author zhengyuyuan
*
*/
public class InvocationHandlerImpl implements InvocationHandler {
//持有目标接口
private AbstraceService service;
public InvocationHandlerImpl(){
}
public InvocationHandlerImpl(AbstraceService service){
this.service=service;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("代理执行invoke方法");
Object invoke = method.invoke(service,args);
return invoke;
}
}
main方法测试执行
//创建一个实例对象
AbstraceService ss = new SocialService();
//创建一个与代理对象相关联的InvocationHandler
InvocationHandler handler = new InvocationHandlerImpl(ss);
//创建一个代理对象ssProxy来代理ss,这个代理是又程序生成,代理对象执行方法时都会替换执行Invocation中的invoke方法
//必须是接口接受这个参数,不能是具体实现
AbstraceService ssProxy = (AbstraceService)Proxy.newProxyInstance(ss.getClass().getClassLoader(),
ss.getClass().getInterfaces(), handler);
//代理对象执行
ssProxy.service();
由上可知,动态代理与静态代理的最大区别在于有无代理类,动态代理的代理类在程序运行的过程中动态生成,要是有多个代理对象,能很好的减少代码的冗余,这就是动态代理的优势,用动态代理,我们可以在InvocationHandler的invoke实现众多的业务逻辑,动态代理对象必须是目标接口接收。很多框架都采用了动态代理模式,如常用的spring的AOP。