玩转反射在框架中的应用
前言:谈起反射,就不得不提字节码了,从字节码到反射类加载,到在框架中应用,到手写IOC和DI的实现
目录
1、字节码
字节码文件是一连串的8位字节的二进制流,它包含的数据项是按照固定次序组成的,且相邻的项之间没有间隔。——正因为每一项结构都严格规范,所以才能顺利被解析。
本次使用Java语言来讲述字节文件的结构。话不多说先放图,如下:
对于文件中的16进制代码,除了开头的cafe babe,剩下的内容大致可以翻译成: 啥玩意啊这是......
英雄莫慌,我们就从我们所能认识的"cafe babe"讲起吧。 文件开头的4个字节称之为 魔数,唯有以“cafe babe”开头的class文件方可被虚拟机所接受,这4个字节就是字节码文件的身份识别。 目光右移,0000是编译器jdk版本的次版本号0,0034转化为十进制是52,是主版本号,java的版本号从45开始,除1.0和1.1都是使用45.x外,以后每升一个大版本,版本号加一。也就是说,编译生成该class文件的jdk版本为1.8.0。 通过java -version命令稍加验证, 可得结果。
Java(TM) SE Runtime Environment (build 1.8.0_131-b11) Java HotSpot(TM) 64-Bit Server VM (build 25.131-b11, mixed mode)
结果验证成立。
继续往下是常量池。
常量池容量计数值(constant_pool_count),特殊的是,与java习惯不同,这个容量计数是从1而不是0开始,对于其他集合,包括接口索引集合、字段表集合、方法表集合等的容量计数都是与一般习惯相同,是从0开始的。
……
......字节码不是本文重点,感兴趣的同学可以自行详细了解......
2、反射
通俗点讲就是,利用JDK提供的反射API进行反射调用。
反射比普通方式创建实例方式更加灵活——扩展性好。
通过对比发现,反射耗时更久一些。反射机制性能问题分析:
@sun.reflect.CallerSensitive
public static native java.lang.Class<?> getCallerClass();
@CallerSensitive
public static Class<?> forName(String className)
throws ClassNotFoundException {
//调用native方法获取调用者的Class
Class<?> caller = Reflection.getCallerClass();
//调用native方法根据className查找字节码对象,并进行加载、解析
return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
}
/** Called after security check for system loader access checks have been made. */
private static native Class<?> forName0(String name, boolean initialize,
ClassLoader loader,
Class<?> caller)
throws ClassNotFoundException;
@CallerSensitive
public T newInstance()
throws InstantiationException, IllegalAccessException
{
//通过反射进行实例化的时候,如果有配置SecurityManager则要先进行安全校验,普通方式实例化则不需要
if (System.getSecurityManager() != null) {
checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), false);
}
// NOTE: the following code may not be strictly correct under
// the current Java memory model.
// Constructor lookup
if (cachedConstructor == null) {
if (this == Class.class) {
throw new IllegalAccessException(
"Can not call newInstance() on the Class for java.lang.Class"
);
}
try {
Class<?>[] empty = {};
final Constructor<T> c = getConstructor0(empty, Member.DECLARED);
// Disable accessibility checks on the constructor
// since we have to do the security check here anyway
// (the stack depth is wrong for the Constructor's
// security check to work)
//安全校验第一次
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction<Void>() {
public Void run() {
c.setAccessible(true);
return null;
}
});
//将构造器进行缓存,提高下次通过反射创建对象时的性能
cachedConstructor = c;
} catch (NoSuchMethodException e) {
throw (InstantiationException)
new InstantiationException(getName()).initCause(e);
}
}
Constructor<T> tmpConstructor = cachedConstructor;
// Security check (same as in java.lang.reflect.Constructor)
//安全校验第二次,这个modifier在JDK里是以一个int值来表示,
int modifiers = tmpConstructor.getModifiers();
if (!Reflection.quickCheckMemberAccess(this, modifiers)) {
Class<?> caller = Reflection.getCallerClass();
if (newInstanceCallerCache != caller) {
Reflection.ensureMemberAccess(caller, this, null, modifiers);
newInstanceCallerCache = caller;
}
}
// Run constructor
try {
//通过构造器来实例化对象
return tmpConstructor.newInstance((Object[])null);
} catch (InvocationTargetException e) {
Unsafe.getUnsafe().throwException(e.getTargetException());
// Not reached
return null;
}
}
2.1 反射的优缺点
优点
- 增加程序的灵活性,避免将固有逻辑写死
- 代码简洁,可读性强,可提高代码的复用率
缺点
- 相比较于直接调用,在访问量较大的情况下,反射会导致系统性能明显下降
- 打破了类的封装性,存在一定的安全隐患
2.2 从JVM层面理解反射
JVM通过Class的类名找到Class的字节码文件,然后再通过ClassLoader的类加载机制在堆内存中分配对象(暂不考虑对象在栈,TLAB上分配的情况)。
2.3 总结反射性能慢的原因
- 反射调用会进行一系列的安全性校验
- 反射需要调用一系列的nativenative方法来实现
- 寻找ClassClass字节码的过程,比如通过CClassNamelassName找到对应的字节码找到对应的字节码CClasslass,然后进行加载、解析,这也会比较慢,而这也会比较慢,而new的方式则无需寻找,因为在连接的解析阶段已经将符号引用转为了直接引用
- 入参校验
3、类的装载过程
类装载的三部曲,加载、连接、初始化
3.1 加载
- •加载.class文件,主要有以下几个途径:
- •从本地的文件系统中加载
- •通过网络下载
- •从zip.jar包加载.class文件
- •从存储中间件中加载(数据库、缓存…)
- •在JVM运行期间通过动态字节码重组的方式(ASM)
3.2 连接
- 连接就是将已经读入到内存的类的二进制数据合并到JVM的运行时环境中,连接过程主要有三个阶段
- •类的验证,对类的文件进行检查(版本号、模数)以保证.class符合JVM规范
- •类的准备,分配常量池空间,解析常量池,静态变量的分配,但不会初始化,只会给默认值
- •类的解析,解析父类,解析接口,解析Filed,解析方法列表(包含栈、字节码表、异常表、局部变量表、运行指针),把类二进制的符号引用转变为直接引用
3.3 初始化
- JVM执行类的初始话语句,为类的静态变量赋值
- •如果这个类还没有被加载和连接,那就先进行加载和连接
- •如果这个类存在父类,并且父类还没有初始话,那就先初始化父类
- •如果类中存在初始化语句(Static块),依次执行初始化语句
3.4 类完成初始化的时机
- •创建类的实例 new xxxClass() || Class.newInstance() || constructor.newInstance()
- •访问某种的某个静态变量,或者对静态变量进行赋值
- •调用类的静态方法
- •Class.forName("包类名"),因为forName里有一个init参数是true
- •完成子类的初始化,也会完成对本类的初始化(接口除外)
- •该类是程序引导入口(main入口 或者test入口)
3.5 Class对象的获取方式
- •Class clazz = xxxClass.class;//没有完成初始化过程
- •Class class = instanceObj.getClass;//对象已生成,完成初始化过程
- •Class clazz = xxxClassLoader.loadClass("包类名");//未完成初始化过程
- •Class clazz = Class.forName("包类名");//完成初始化过程
4、探究Class内部元素
Class clazz = Class.forName(XXXX);
clazz.newInstance();
clazz.newInstance(); 本质是什么?
本质:JDK→constructor
JVM→instanceKlass→instanceOop OopDesc→Object(Java)
5、单例模式
•私有化构造函数
•全局唯一的公有访问点
通过反射机制可以破坏单例模式。单例模式,请移步设计模式中另一篇文章:单例模式在JDK以及Spring源码中如何进行串联
6、Spring IOC 和 DI
IOC三种方式创建:
- 创建方式1:空参构造创建
- 创建方式2:静态工厂创建
- 创建方式3:实例工厂创建
DI三种方式创建:
- 创建方式1:有参构造创建
- 创建方式2:setter创建
- 创建方式3:field创建(very few usage)
6.1 反射在框架中的应用
在Sping中的IOC实例创建
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
// Make sure bean class is actually resolved at this point.
Class<?> beanClass = resolveBeanClass(mbd, beanName);
if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Bean class isn't public, and non-public access not allowed: " + beanClass.getName());
}
Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
if (instanceSupplier != null) {
return obtainFromSupplier(instanceSupplier, beanName);
}
if (mbd.getFactoryMethodName() != null) {
return instantiateUsingFactoryMethod(beanName, mbd, args);
}
// Shortcut when re-creating the same bean...
boolean resolved = false;
boolean autowireNecessary = false;
if (args == null) {
synchronized (mbd.constructorArgumentLock) {
if (mbd.resolvedConstructorOrFactoryMethod != null) {
resolved = true;
autowireNecessary = mbd.constructorArgumentsResolved;
}
}
}
if (resolved) {
if (autowireNecessary) {
return autowireConstructor(beanName, mbd, null, null);
}
else {
return instantiateBean(beanName, mbd);
}
}
// Candidate constructors for autowiring?
Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
return autowireConstructor(beanName, mbd, ctors, args);
}
// Preferred constructors for default construction?
ctors = mbd.getPreferredConstructors();
if (ctors != null) {
return autowireConstructor(beanName, mbd, ctors, null);
}
// No special handling: simply use no-arg constructor.
//实例化Bean的方法
return instantiateBean(beanName, mbd);
}
/**
* Instantiate the given bean using its default constructor.
* @param beanName the name of the bean
* @param mbd the bean definition for the bean
* @return a BeanWrapper for the new instance
*/
protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) {
try {
Object beanInstance;
final BeanFactory parent = this;
if (System.getSecurityManager() != null) {
beanInstance = AccessController.doPrivileged((PrivilegedAction<Object>) () ->
getInstantiationStrategy().instantiate(mbd, beanName, parent),
getAccessControlContext());
}
else {
beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent);
}
BeanWrapper bw = new BeanWrapperImpl(beanInstance);
initBeanWrapper(bw);
return bw;
}
catch (Throwable ex) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Instantiation of bean failed", ex);
}
}
public T newInstance(Object ... initargs)
throws InstantiationException, IllegalAccessException,
IllegalArgumentException, InvocationTargetException
{
if (!override) {
if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
Class<?> caller = Reflection.getCallerClass();
checkAccess(caller, clazz, null, modifiers);
}
}
if ((clazz.getModifiers() & Modifier.ENUM) != 0)
throw new IllegalArgumentException("Cannot reflectively create enum objects");
ConstructorAccessor ca = constructorAccessor; // read volatile
if (ca == null) {
ca = acquireConstructorAccessor();
}
@SuppressWarnings("unchecked")
T inst = (T) ca.newInstance(initargs);
return inst;
}
public static <T> T instantiateClass(Constructor<T> ctor, Object... args) throws BeanInstantiationException {
Assert.notNull(ctor, "Constructor must not be null");
try {
ReflectionUtils.makeAccessible(ctor);
return (KotlinDetector.isKotlinReflectPresent() && KotlinDetector.isKotlinType(ctor.getDeclaringClass()) ?
KotlinDelegate.instantiateClass(ctor, args) : ctor.newInstance(args));
}
catch (InstantiationException ex) {
throw new BeanInstantiationException(ctor, "Is it an abstract class?", ex);
}
catch (IllegalAccessException ex) {
throw new BeanInstantiationException(ctor, "Is the constructor accessible?", ex);
}
catch (IllegalArgumentException ex) {
throw new BeanInstantiationException(ctor, "Illegal arguments for constructor", ex);
}
catch (InvocationTargetException ex) {
throw new BeanInstantiationException(ctor, "Constructor threw exception", ex.getTargetException());
}
}
解析Bean 的 class spring 里使用的是通过 classLoader 的方式
@Nullable
private Class<?> doResolveBeanClass(RootBeanDefinition mbd, Class<?>... typesToMatch)
throws ClassNotFoundException {
ClassLoader beanClassLoader = getBeanClassLoader();
ClassLoader dynamicLoader = beanClassLoader;
boolean freshResolve = false;
if (!ObjectUtils.isEmpty(typesToMatch)) {
// When just doing type checks (i.e. not creating an actual instance yet),
// use the specified temporary class loader (e.g. in a weaving scenario).
ClassLoader tempClassLoader = getTempClassLoader();
if (tempClassLoader != null) {
dynamicLoader = tempClassLoader;
freshResolve = true;
if (tempClassLoader instanceof DecoratingClassLoader) {
DecoratingClassLoader dcl = (DecoratingClassLoader) tempClassLoader;
for (Class<?> typeToMatch : typesToMatch) {
dcl.excludeClass(typeToMatch.getName());
}
}
}
}
String className = mbd.getBeanClassName();
if (className != null) {
Object evaluated = evaluateBeanDefinitionString(className, mbd);
if (!className.equals(evaluated)) {
// A dynamically resolved expression, supported as of 4.2...
if (evaluated instanceof Class) {
return (Class<?>) evaluated;
}
else if (evaluated instanceof String) {
className = (String) evaluated;
freshResolve = true;
}
else {
throw new IllegalStateException("Invalid class name expression result: " + evaluated);
}
}
if (freshResolve) {
// When resolving against a temporary class loader, exit early in order
// to avoid storing the resolved Class in the bean definition.
if (dynamicLoader != null) {
try {
return dynamicLoader.loadClass(className);
}
catch (ClassNotFoundException ex) {
if (logger.isTraceEnabled()) {
logger.trace("Could not load class [" + className + "] from " + dynamicLoader + ": " + ex);
}
}
}
return ClassUtils.forName(className, dynamicLoader);
}
}
// Resolve regularly, caching the result in the BeanDefinition...
return mbd.resolveBeanClass(beanClassLoader);
}
设置实例属性
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
if (bw == null) {
if (mbd.hasPropertyValues()) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance");
}
else {
// Skip property population phase for null instance.
return;
}
}
// Give any InstantiationAwareBeanPostProcessors the opportunity to modify the
// state of the bean before properties are set. This can be used, for example,
// to support styles of field injection.
boolean continueWithPropertyPopulation = true;
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
continueWithPropertyPopulation = false;
break;
}
}
}
}
if (!continueWithPropertyPopulation) {
return;
}
PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);
if (mbd.getResolvedAutowireMode() == AUTOWIRE_BY_NAME || mbd.getResolvedAutowireMode() == AUTOWIRE_BY_TYPE) {
MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
// Add property values based on autowire by name if applicable.
if (mbd.getResolvedAutowireMode() == AUTOWIRE_BY_NAME) {
//根据名称注入
autowireByName(beanName, mbd, bw, newPvs);
}
// Add property values based on autowire by type if applicable.
if (mbd.getResolvedAutowireMode() == AUTOWIRE_BY_TYPE) {
//根据类型注入
autowireByType(beanName, mbd, bw, newPvs);
}
pvs = newPvs;
}
boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
boolean needsDepCheck = (mbd.getDependencyCheck() != AbstractBeanDefinition.DEPENDENCY_CHECK_NONE);
PropertyDescriptor[] filteredPds = null;
if (hasInstAwareBpps) {
if (pvs == null) {
pvs = mbd.getPropertyValues();
}
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
//通过@Resource @Autowre 的 BeanPostProcessor
PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
if (pvsToUse == null) {
if (filteredPds == null) {
filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
}
pvsToUse = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
if (pvsToUse == null) {
return;
}
}
pvs = pvsToUse;
}
}
}
if (needsDepCheck) {
if (filteredPds == null) {
filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
}
checkDependencies(beanName, mbd, filteredPds, pvs);
}
if (pvs != null) {
applyPropertyValues(beanName, mbd, bw, pvs);
}
}
/**
* Either this or {@link #getResourceToInject} needs to be overridden.
*/
//通过反射注入属性
protected void inject(Object target, @Nullable String requestingBeanName, @Nullable PropertyValues pvs)
throws Throwable {
if (this.isField) {
Field field = (Field) this.member;
ReflectionUtils.makeAccessible(field);
field.set(target, getResourceToInject(target, requestingBeanName));
}
else {
if (checkPropertySkipping(pvs)) {
return;
}
try {
Method method = (Method) this.member;
ReflectionUtils.makeAccessible(method);
method.invoke(target, getResourceToInject(target, requestingBeanName));
}
catch (InvocationTargetException ex) {
throw ex.getTargetException();
}
}
}
6.2 手写Spring IOC、DI
动态创建对象实例
动态操作实例对象的方法、属性
1.先创建IOC、DI类
public class A {
public A() {
System.out.println("A whitout parameters Constructor invoke!");
}
public static B createBObj() { //factory-method
System.out.println("A static function createBObj() invoke!");
return new B();
}
public C createCObj() { //instance-method
System.out.println("a's createCObj() invoke!");
return new C();
}
}
public class B {
}
public class C {
}
public class D {
private A a;
private B b;
public D() {
System.out.println("D without parameters Constructor invoke!");
}
public D(A a, B b) {
System.out.println("D constructor with parameters invoke!");
this.a = a;
this.b = b;
}
}
public class E {
private A a;
private B b;
public void setA(A a) {
System.out.println("E setA() invoke!");
this.a = a;
}
public void setB(B b) {
System.out.println("E setB() invoke!");
this.b = b;
}
}
2.BeanConfig
public class BeanConfig { //spring-ioc.xml spring-di.xml
private String id;
private String clazz;
private String factoryMethod;
private String factoryBean;
private HashMap dependencyHashMap = new HashMap<String, List<String>>();
private List<String> constructBeans = new ArrayList<>();
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getClazz() {
return clazz;
}
public void setClazz(String clazz) {
this.clazz = clazz;
}
public String getFactoryMethod() {
return factoryMethod;
}
public void setFactoryMethod(String factoryMethod) {
this.factoryMethod = factoryMethod;
}
public String getFactoryBean() {
return factoryBean;
}
public void setFactoryBean(String factoryBean) {
this.factoryBean = factoryBean;
}
public void addConstructBeanId(String beanId){
constructBeans.add(beanId);
}
public List<String> getConstructBeans() {
return constructBeans;
}
public void putDependcyBeanId(String propName,String beanId){
dependencyHashMap.put(propName,beanId);
}
public HashMap<String, String> getDependencyHashMap(){
return dependencyHashMap;
}
}
3.BootStrap
public class BootStrap {
public static void main(String[] args) throws Exception {
// 模拟解析xml 拿到 beanConfigs
List<BeanConfig> beanConfigs = parseXmltoBeanConfig();
//循环
for (BeanConfig tmpconfig : beanConfigs) {
if (null != tmpconfig.getClazz()) {
//拿到clazz
Class clazz = Class.forName(tmpconfig.getClazz());
if (null != tmpconfig.getFactoryMethod())
{
//基于Class对象获取method对象
Method method = clazz.getDeclaredMethod(tmpconfig.getFactoryMethod());
IOCContainer.putBean(tmpconfig.getId(), method.invoke(null));
} else {
// hold the bean reference
// 参数构造
if(tmpconfig.getConstructBeans().size()>0) {
List<String> contructBeanIds = tmpconfig.getConstructBeans();
Object[] constructorParams = new Object[contructBeanIds.size()];
Class[] contructorClasses = new Class[contructBeanIds.size()];
for (int i = 0; i < contructBeanIds.size(); i++) {
Object parami = IOCContainer.getBean(contructBeanIds.get(i));
if (parami == null) {
System.out.println("init error,");
throw new Exception("init-err");
}
constructorParams[i] = parami;
contructorClasses[i] = parami.getClass();
}
Constructor c = clazz.getConstructor(contructorClasses);//获取有参构造
IOCContainer.putBean(tmpconfig.getId(), c.newInstance(constructorParams));
}else {
IOCContainer.putBean(tmpconfig.getId(), clazz.newInstance());
}
//DI
if(tmpconfig.getDependencyHashMap().size()>0){
for(Map.Entry<String,String> entry : tmpconfig.getDependencyHashMap().entrySet()){
Object bean = IOCContainer.getBean(tmpconfig.getId());
String methodKey = entry.getKey();
String beanId = entry.getValue();
Object[] params = new Object[1];
params[0] = IOCContainer.getBean(beanId);
Class[] paramTypes = new Class[1];
paramTypes[0] = params[0].getClass();
String methodName
= "set"
+ methodKey.substring(0,1).toUpperCase()
+ (methodKey.length()>1?methodKey.substring(1,methodKey.length()-1):"");
Method setx = bean.getClass().getMethod(methodName,paramTypes);
setx.invoke(bean,params);
}
}
}
} else if(null != tmpconfig.getFactoryBean()) {
//从容器中拿到实体bean
Object obj = IOCContainer.getBean( tmpconfig.getFactoryBean());
Method method = obj.getClass().getDeclaredMethod(tmpconfig.getFactoryMethod());
IOCContainer.putBean(tmpconfig.getId(), method.invoke(obj));
}else{
System.out.println("Configuration is required!");
}
}
//IOC 测试
A a = IOCContainer.getBean("a",A.class);
B b = IOCContainer.getBean("b",B.class);
C c = IOCContainer.getBean("c",C.class);
D d = IOCContainer.getBean("d",D.class);
E e = IOCContainer.getBean("e", E.class);
}
public void doDependencyInjection(){
// IOCContainer get Object
// 反射 get Method
// 解析依赖注入的关系 DependencyHashMap
// 反射调用 3种 Object Constructor, setter 方法设置对象之间依赖关系 field(very few usage)
}
/**
* 模拟一个解析XML过程
*
* @return
*/
private static List<BeanConfig> parseXmltoBeanConfig() {
//TODO
List<BeanConfig> beanConfigs = new ArrayList<BeanConfig>();
BeanConfig beanConfig1 = new BeanConfig();
beanConfig1.setClazz("com.iocdi.A");
beanConfig1.setId("a");
beanConfigs.add(beanConfig1);
BeanConfig beanConfig2 = new BeanConfig();
beanConfig2.setClazz("com.iocdi.A");
beanConfig2.setId("b");
beanConfig2.setFactoryMethod("createBObj");
beanConfigs.add(beanConfig2);
BeanConfig beanConfig3 = new BeanConfig();
beanConfig3.setId("c");
beanConfig3.setFactoryBean("a");
beanConfig3.setFactoryMethod("createCObj");
beanConfigs.add(beanConfig3);
BeanConfig beanConfig4 = new BeanConfig();
beanConfig4.setId("d");
beanConfig4.setClazz("com.iocdi.D");
beanConfig4.addConstructBeanId("a");
beanConfig4.addConstructBeanId("b");
beanConfigs.add(beanConfig4);
BeanConfig beanConfig5 = new BeanConfig();
beanConfig5.setId("e");
beanConfig5.setClazz("com.iocdi.E");
beanConfig5.putDependcyBeanId("a","a");
beanConfig5.putDependcyBeanId("b","b");
beanConfigs.add(beanConfig5);
return beanConfigs;
}
}
4.IOCContainer
public class IOCContainer {
private static HashMap IoContainer = new HashMap();
public static void putBean(String id,Object object){
IoContainer.put(id,object);
}
public static Object getBean(String id){
return IoContainer.get(id);
}
public static <T> T getBean(String id,Class<T> clazz){
return (T)IoContainer.get(id);
}
}
6.3 手写Testing Framework
1.注解Test
/**
* Indicates that the annotated method is a test method.
* This annotation should be used only on parameterless static methods.
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE, ElementType.FIELD})
public @interface Test {
// method
// normal
// single element
}
2.OrderTestCase
//@Test
public class OrderTestCase {
@Test
public static void m1() {System.out.println("m1 is ok."); }
public static void m2() { }
@Test
public static void m3() {
throw new RuntimeException("Boom");
}
public static void m4() { }
@Test
public static void m5() { System.out.println("m5 is ok."); }
public static void m6() { }
@Test
public static void m7() {
throw new RuntimeException("Crash");
}
public static void m8() { }
}
3.MainTest
public class MainTest {
public static void main(String[] args) {
int passed=0, failed=0;
try{
// hard code to get TestCase Classes
//TODO scan package to get all of TestCase Classes
Method[] methods = Class.forName(args[0]).getMethods(); // how many methods?
for(Method m : methods){
if (m.isAnnotationPresent(Test.class)){
try{
m.invoke(null);
passed++;
}catch (Throwable ex){
System.out.printf("Test %s failed: %s %n", m, ex.getCause());
failed++;
}
}
}
}catch (ClassNotFoundException ex){
ex.printStackTrace();
}
System.out.printf("Passed: %d, Failed %d%n", passed, failed);
}
}
7、Spring核心容器类结构
7.1 BeanFactory
Spring Bean 的创建是典型的工厂模式,这一系列的 Bean 工厂,也即 IOC 容器为开发者管理对象间的依赖关系提供了很多便利和基础服务,在 Spring 中有许多的 IOC 容器的实现供用户选择和使用,其相互关系如下:
其中
BeanFactory 作为最顶层的一个接口类,它定义了 IOC 容器的基本功能规范, BeanFactory 有三个子类: ListableBeanFactory 、 Hierarc hicalBeanFactory 和 AutowireCapableBeanFactory 。
但是从上图中我们可以发现最终的默认实现类是 DefaultListableBeanFactory ,他实现了所有的接口。那为何要定义这么多层次的接口呢?查阅这些接口的源码和说明发现,每个接口都有他使用的场合,它主要是为了区分在 Spring 内部在操作过程中对象的传递和转化过程中,对对象的数据访问所做的限制。例如 ListableBeanFactory 接口表示这些 Bean 是可列表的,而 HierarchicalBeanFactory表示的是这些 Bean 是有继承关系的,也就是每个 Bean 有可能有父 Bean 。AutowireCapableBeanFactory 接口定义 Bean 的自动装配规则。这四个接口共同定义了 Bean 的集合、 Bean 之间的关系、以及 Bean 行为最基本的IOC 容器接口 BeanFactory
public interface BeanFactory {
//对FactoryBean的转义定义,因为如果使用bean的名字检索FactoryBean得到的对象是工厂生成的对象,
//如果需要得到工厂本身,需要转义
String FACTORY_BEAN_PREFIX = "&";
//根据bean的名字,获取在IOC容器中得到bean实例
Object getBean(String name) throws BeansException;
//根据bean的名字和Class类型来得到bean实例,增加了类型安全验证机制。
<T> T getBean(String name, @Nullable Class<T> requiredType) throws BeansException;
Object getBean(String name, Object... args) throws BeansException;
<T> T getBean(Class<T> requiredType) throws BeansException;
<T> T getBean(Class<T> requiredType, Object... args) throws BeansException;
//提供对bean的检索,看看是否在IOC容器有这个名字的bean
boolean containsBean(String name);
//根据bean名字得到bean实例,并同时判断这个bean是不是单例
boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;
boolean isTypeMatch(String name, @Nullable Class<?> typeToMatch) throws NoSuchBeanDefinitionException;
//得到bean实例的Class类型
@Nullable
Class<?> getType(String name) throws NoSuchBeanDefinitionException;
//得到bean的别名,如果根据别名检索,那么其原名也会被检索出来
String[] getAliases(String name); }
在BeanFactory 里只对 IOC 容器的基本行为作了定义,根本不关心你的 Bean 是如何定义怎样加载的。正如我们只关心工厂里得到什么的产品对象,至于工厂是怎么生产这些对象的,这个基本的接口不关心。
而要知道工厂是如何产生对象的,我们需要看具体的 IOC 容器实现, Spring 提供了许 多 IOC 容器的实现。比如 XmlBeanFactoryClasspathXmlApplicationContext 等。其中 XmlBeanFactory就是针对最基本的 IOC 容器的实现,这个 IOC 容器可以读取 XML 文件定义的 BeanDefinition XML文件中对 bean 的描述) 如果说 XmlBeanFactory 是容器中的屌丝, ApplicationContext 应该算容器中的高帅富。
ApplicationContext 是 Spring 提供的一个高级的 IOC 容器,它除了能够提供 IOC 容器的基本功能外, 还为用户提供了以下的附加服务。
从ApplicationContext 接口的实现,我们看出其特点:
1. 支持信息源,可以实现国际化。(实现 MessageSource 接口)
2. 访问资源。 实现 ResourcePatternResolver 接口,后面章节会讲到
3. 支持应用事件。 实现 ApplicationEventPublisher 接口
7.2 BeanDefinition
SpringIOC 容器管理了我们定义的各种 Bean 对象及其相互的关系, Bean 对象在 Spring 实现中是以 BeanDef inition 来描述的,其继承体系下:
Bean的解析过程非常复杂,功能被分的很细,因为这里需要被扩展的地方很多,必须保证有足够的灵活性,以应对可能的变化。 Bean 的解析主要就是对 Spring 配置文件的解析。这个解析过程主要通过下图中的类完成: