2021-03-06——玩转反射在框架中的应用

玩转反射在框架中的应用

前言:谈起反射,就不得不提字节码了,从字节码到反射类加载,到在框架中应用,到手写IOC和DI的实现

目录

1、字节码

2、反射

2.1 反射的优缺点

2.2 从JVM层面理解反射

2.3 总结反射性能慢的原因

3、类的装载过程

3.1 加载

3.2 连接

3.3 初始化

3.4 类完成初始化的时机

3.5 Class对象的获取方式

4、探究Class内部元素

5、单例模式

6、Spring IOC 和 DI

6.1 反射在框架中的应用

6.2 手写Spring IOC、DI

6.3 手写Testing Framework

7、Spring核心容器类结构

7.1 BeanFactory

7.2 BeanDefinition


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 总结反射性能慢的原因

  1. 反射调用会进行一系列的安全性校验
  2. 反射需要调用一系列的nativenative方法来实现
  3. 寻找ClassClass字节码的过程,比如通过CClassNamelassName找到对应的字节码找到对应的字节码CClasslass,然后进行加载、解析,这也会比较慢,而这也会比较慢,而new的方式则无需寻找,因为在连接的解析阶段已经将符号引用转为了直接引用
  4. 入参校验

3、类的装载过程

类装载的三部曲,加载、连接、初始化

3.1 加载

  1. •加载.class文件,主要有以下几个途径:
  2. •从本地的文件系统中加载
  3. •通过网络下载
  4. •从zip.jar包加载.class文件
  5. •从存储中间件中加载(数据库、缓存…)
  6. •在JVM运行期间通过动态字节码重组的方式(ASM)

3.2 连接

  1. 连接就是将已经读入到内存的类的二进制数据合并到JVM的运行时环境中,连接过程主要有三个阶段
  2. •类的验证,对类的文件进行检查(版本号、模数)以保证.class符合JVM规范
  3. •类的准备,分配常量池空间,解析常量池,静态变量的分配,但不会初始化,只会给默认值
  4. •类的解析,解析父类,解析接口,解析Filed,解析方法列表(包含栈、字节码表、异常表、局部变量表、运行指针),把类二进制的符号引用转变为直接引用

3.3 初始化

  1. JVM执行类的初始话语句,为类的静态变量赋值
  2. •如果这个类还没有被加载和连接,那就先进行加载和连接
  3. •如果这个类存在父类,并且父类还没有初始话,那就先初始化父类
  4. •如果类中存在初始化语句(Static块),依次执行初始化语句

3.4 类完成初始化的时机

  1. •创建类的实例 new xxxClass() || Class.newInstance() || constructor.newInstance()
  2. •访问某种的某个静态变量,或者对静态变量进行赋值
  3. •调用类的静态方法
  4. •Class.forName("包类名"),因为forName里有一个init参数是true
  5. •完成子类的初始化,也会完成对本类的初始化(接口除外)
  6. •该类是程序引导入口(main入口 或者test入口)

3.5 Class对象的获取方式

  1. •Class clazz = xxxClass.class;//没有完成初始化过程
  2. •Class class = instanceObj.getClass;//对象已生成,完成初始化过程
  3. •Class clazz = xxxClassLoader.loadClass("包类名");//未完成初始化过程
  4. •Class clazz = Class.forName("包类名");//完成初始化过程

4、探究Class内部元素

Class clazz = Class.forName(XXXX);

clazz.newInstance();

clazz.newInstance(); 本质是什么?

http://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/1c6e1f187fdc/src/share/vm/classfile/javaClasses.cpp

本质: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 配置文件的解析。这个解析过程主要通过下图中的类完成:

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值