group.setProperties(contentBlockDO);
//group,com.alibaba.citrus.service.form.Group
//contentBlockDO 自定义的DO
上面简单的一行代码就将页面上填写的表单项赋值到对应的类实例里面。去看了下setProperties方法的实现,里面就是用的反射,动态获取类对象,循环遍历其set方法,将对应的值赋值给对象。
java反射就是获取动态对象,并获取其中的属性、方法。不仅可以动态的获取对象,也能动态的创建对象,同时能够访问对象的方法,修改对象的属性值。反射有这样一些常用的用处。
a)反射方式调用方法 动态代理 扩展API,例如sping ioc中的依赖注入。
b)反射可以操作某个类的私有变量和方法
c)反射操作对象更加灵活,例如如上述的例子中只要有了form对象和 property名字就可以利用反射给property赋值和取值 对这类操作 一个方法就可以搞定。
d)运行过程中对未知类进行初始化。
//首先获取Class对象,如下三种方式,可以看出可以由类名、路径、实例对象获取Clss对象
Class base = Base.class;
Base baseTest = new Base();
Class base1 = baseTest.getClass();
Class base2 = Class.forName("reflect.Base"); //需要捕获抛出异常
//获取类的属性,getDeclaredXxx和getXxx的区别是getDeclaredXxx可以获取私有的
Field[] fields = base.getFields();
Field[] fieldsDeclared = base.getDeclaredFields();
//获取类里面的方法,getMethods可以获取父类的方法
try {
Method[] methods = base.getMethods();
Method[] methods1 = base.getDeclaredMethods();
//实例化一个Base对象
Base test = (Base)base.newInstance();
//通过setB方法对属性赋值
Method m = base.getDeclaredMethod("setB", String.class);
//设置权限,如果是private类型的属性和方法必须设置此项才能访问
m.setAccessible(true);
m.invoke(test, "2");
m.invoke(baseTest, "2");
//通过属性名称对属性值赋值
Field f = base.getDeclaredField("b");
f.setAccessible(true);
f.set(test,"hello");
}catch (Exception e){
System.out.println("exception" + e);
}
对于java反射里面用到的setAccessible()方法刚接触到的时候感觉很强大又很奇怪。看了一下源码。
if (obj instanceof Constructor && flag == true) { Constructor<?> c = (Constructor<?>)obj; if (c.getDeclaringClass() == Class.class) { throw new SecurityException("Can not make a java.lang.Class" + " constructor accessible"); } } obj.override = flag;
里面涉及到了AccessibleObject这个类,这个类是Field、Method、Constructor三个的父类,这个函数主要是将里面的 override 设置为对应的值,值的含义就是调用相应的方法时候跳过java的权限检查。
感觉强大的是这个方法居然能去操作java的私有对象,奇怪的是这不是破坏了java的封装性嘛。看了看其他一些人的说法,整体上的感觉就是java封装也是防君子不防小人,而setAccessible()在很多场合下又是需要用的上的,例如不科学的封装、性能测试之类的。暂且这样理解吧,后面有更好更能说服自己的理解再来更新。
DefaultListableBeanFactory源码里面看到这个数据结构,以Map<String,BeanDefinition>的方式来存储.xml配置文件配置的bean,依赖注入的时候注入的对象就是从这个Map里面获取的。
/** Map of bean definition objects, keyed by bean name */
private final Map beanDefinitionMap = CollectionFactory.createConcurrentMapIfPossible(16);
分析下源码整个依赖注入的流程的入口可以参考
public Object createBean(Class beanClass, int autowireMode, boolean dependencyCheck) throws BeansException {
// Use non-singleton bean definition, to avoid registering bean as dependent bean. RootBeanDefinition bd = new RootBeanDefinition(beanClass, autowireMode, dependencyCheck);
bd.setScope(BeanDefinition.SCOPE_PROTOTYPE); return createBean(beanClass.getName(), bd, null);
}
追踪下去发现底层执行了两个关键的函数
.createBeanInstance:生成Bean所包含的java对象实例
.populateBean :对Bean属性的依赖注入进行处理
这里介绍下 populateBean 注入的过程,这里用到的就是java的反射。其中org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyPropertyValues 来进行bean属性的注入。
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#autowireByName
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#autowireByType
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyPropertyValues
使用反射来将bean里面依赖的bean、属性注入到bean里面。
总结下来,整个spring的DI注入可以分为两部分,第一部分是将bean存储在一个hashMap里面,第二部分是根据名字或类型去取出对应的bean使用反射注入到对应的实例里面去。
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
。。。
//调用之前
doBefore();
//调用原始对象的方法
result=method.invoke(obj, args);
//调用之后
doAfter();
。。。
}
最近看了java反射并写了一些小例子测试以后,最大的感受是理解了动态对象的方法、属性的访问。理解了对象的优雅赋值转换、理解spring的注入过程、知道了java的动态代理,后续听到团队其他人员讨论使用代理的方式来做xxx的时候也可以有自己的思考。