动态代理实现bean Convert工具类(二): 类型工具类ObjectUtil

动态代理实现bean Convert工具类(二): 类型工具类ObjectUtil

我们在开始着手写convert工具类之前应该着手写一些辅助用的工具类,具体有用于处理数组的ArrayUtil,用于处理json parse的FastJsonUtil,处理String类型的StringUtil和处理反射的RefletionUtil,以及spring相关操作的SpringUtil

1.FastJsonUtil

我们在这里使用的是alibaba提供的fastJson工具,所以我们只需要对这个工具进行封装即可,我们在这里就封装几个简单的功能给我们关键的工具类使用即可,完整的fastJson封装我会在上传到github上。


public static String beanToJson(Object object){
    return JSON.toJSONString(object, true);
}

这个是封装了将对象转换成json形式的键值字符串的功能,默认选择true

2.ReflectionUtil

关于封装一些常用的反射方法,这里依然只提供关键工具类需要用到的功能

public static Method getGetter(String name, Class<? extends Object> clazz) {
    try {
        return clazz.getMethod("get" + StringUtils.capitalize(name));
    } catch (NoSuchMethodException e) {
        try {
            return clazz.getMethod("is" + StringUtils.capitalize(name));
        } catch (NoSuchMethodException e1) {
            return null;
        }
    }
}

这个是封装获取getter函数的功能,这里的StringUtil是用的spring提供的工具包,可以进去看一下源码的实现

public static String capitalize(String str) {
    return changeFirstCharacterCase(str, true);
}

private static String changeFirstCharacterCase(String str, boolean capitalize) {
    if (!hasLength(str)) {
        return str;
    } else {
        char baseChar = str.charAt(0);
        char updatedChar;
        if (capitalize) {
            updatedChar = Character.toUpperCase(baseChar);
        } else {
            updatedChar = Character.toLowerCase(baseChar);
        }

        if (baseChar == updatedChar) {
            return str;
        } else {
            char[] chars = str.toCharArray();
            chars[0] = updatedChar;
            return new String(chars, 0, chars.length);
        }
    }
}
public static boolean hasLength(@Nullable String str) {
    return str != null && !str.isEmpty();
}

通过源码分析可以得到其功能是将String字符串的首字母变成大写,其他字面不变,很好的符合我们在获取getter函数时使用。

3.ArrayUtil

依然只是提供convert所需的方法,完整工具类可以到github上查看

public static <F, T> T[] transform(F[] fromArray, Class<T> componentType, Function<? super F, T> function) {
    if (fromArray == null) return null;

    T[] toArray = (T[]) Array.newInstance(componentType, fromArray.length);

    for (int i = 0; i < fromArray.length; i++) {
        toArray[i] = function.apply(fromArray[i]);
    }

    return toArray;
}

这里封装一个数组类型转换的方法,使用函数式编程接口,具体的转换有实现类完成,做到减低代码耦合性

4.SpringUtil

private static ApplicationContext context;

public static <T> Map<String, T> getBeansOfType(Class<T> clazz) {
    return context == null ? null : context.getBeansOfType(clazz);
}

public static <T> T getBean(Class<T> clazz) {
    return getBean(clazz, null);
}

public static <T> T getBean(Class<T> clazz, LazyLoadPolicy policy) {
    if (clazz == null) return null;
    if (policy != null) {
        return ProxyUtil.newProxyInstance(clazz, new BeanProxyInvocationHandler(null, clazz, policy), new ProxyUtil.CallbackFilter() {
            @Override
            public boolean accept(Method method) {
                return !method.getDeclaringClass().equals(Object.class);
            }
        });
    } else {
        return context == null ? null : context.getBean(clazz);
    }
}

// 上面需要的LazyPolicy类型是自定义内部接口,具体作用是规定延迟加载的策略,我们这里只需要传null就行了
public static interface LazyLoadPolicy {
    Object invoke(Object target, Method method, Object[] args);
}  
// 内部类用于实现增强策略
private static class BeanProxyInvocationHandler implements InvocationHandler, Callback {

    private String beanName;
    private Class<?> beanType;
    private LazyLoadPolicy policy;
    private volatile Object target;
    private volatile BlockingQueue<Callback> queue;

    public BeanProxyInvocationHandler(String name, Class<?> clazz, LazyLoadPolicy policy) {
        beanName = name;
        beanType = clazz;
        this.policy = policy;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object target = getInstance();
        if (target != null) {
            return method.invoke(target, args);
        } else {
            registerCallback(policy);
            return policy.invoke(proxy, method, args);
        }
    }

    /**
         * 注册回调函数,在context加载完成后执行
         *
         * @param policy
         */
    private void registerCallback(LazyLoadPolicy policy) {
        if (!(policy instanceof Callback)) {
            return;
        }
        BlockingQueue<Callback> queue = getQueue();
        queue.offer((Callback) policy);
        SpringUtil.registerCallback(this);
    }

    /**
         * 从Spring容器延迟加载实例
         *
         * @return
         */
    private Object getInstance() {
        if (target == null) {
            synchronized (beanType) {
                if (target == null) {
                    //上下文未初始化直接返回NULL
                    if (context == null) {
                        return null;
                    }

                    //从上下文加载bean
                    if (beanName != null) {
                        target = context.getBean(beanName, beanType);
                    } else {
                        target = context.getBean(beanType);
                    }
                }
            }
        }

        return target;
    }

    private BlockingQueue<Callback> getQueue() {
        if (queue == null) {
            synchronized (BeanProxyInvocationHandler.class) {
                if (queue == null) {
                    queue = new LinkedBlockingQueue<Callback>();
                }
            }
        }
        return queue;
    }

    @Override
    public void execute(Object context) {
        BlockingQueue<Callback> queue = getQueue();
        if (queue.isEmpty()) {
            return;
        }

        Object target = getInstance();
        try {
            Callback[] callbacks = queue.toArray(new Callback[]{});
            for (Callback callback : callbacks) {
                callback.execute(target);
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

​ 需要注意,这里的getBean方法是由spring中BeanFactory接口定义的,我们这里的context是ApplicationContext类型的对象,这个接口继承了ListableBeanFactory,而ListableBeanFactory继承了BeanFactory对象,而具体实现是由AbstractApplicationContext完成的,这个类实现了ConfigurableApplicationContext接口,而ConfigurableApplicationContext接口继承自ApplicationContext,具体继承关系如下图所示,而getBeansOfType方法是定义在ListableBeanFactory接口内的,实现关系也是如此

继承
继承
继承
实现
BeanFactory
ListableBeanFactory
ApplicationContext
ConfigurableApplicationContext
AbstractApplicationContext

接下来进入AbstractApplicationContext源码中看一下具体的实现逻辑

private final AtomicBoolean active = new AtomicBoolean();

private final AtomicBoolean closed = new AtomicBoolean();

@Override
public <T> Map<String, T> getBeansOfType(@Nullable Class<T> type) throws BeansException {
    assertBeanFactoryActive();
    return getBeanFactory().getBeansOfType(type);
}

protected void assertBeanFactoryActive() {
    if (!this.active.get()) {
        if (this.closed.get()) {
            throw new IllegalStateException(getDisplayName() + " has been closed already");
        }
        else {
            throw new IllegalStateException(getDisplayName() + " has not been refreshed yet");
        }
    }
}

@Override
public abstract ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException;

首先是getBeanOfType方法,开始会调用assertBeanFactoryActive方法,这个方法是判断BeanFactory是否处于激活状态,如果不是则会抛出异常,之后钓调用的是getBeanFactory返回的bean工程的getBeansOfType方法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2ElyJVtW-1612076041141)(mdPics/image-20210131123946313.png)]

可以看到这是个抽象方法,实现这个方法的有两个子类,这两个子类返回都是同一个类型的对象DefaultListableBeanFactory,学习过spring源码的同学应该知道,这是一个spring中的核心关键类,是实现ApplicationContext接口的子类之一。进入DefaultListableBeanFactory查看具体实现逻辑

// 单例和非单例bean名称的映射
private final Map<Class<?>, String[]> allBeanNamesByType = new ConcurrentHashMap<>(64);

// 判断是否可以为所有bean缓存定义数据
private volatile boolean configurationFrozen;

@Override
public <T> Map<String, T> getBeansOfType(@Nullable Class<T> type) throws BeansException {
    return getBeansOfType(type, true, true);
}

@Override
@SuppressWarnings("unchecked")
public <T> Map<String, T> getBeansOfType(
    @Nullable Class<T> type, boolean includeNonSingletons, boolean allowEagerInit) throws BeansException {

    String[] beanNames = getBeanNamesForType(type, includeNonSingletons, allowEagerInit);
    Map<String, T> result = CollectionUtils.newLinkedHashMap(beanNames.length);
    for (String beanName : beanNames) {
        try {
            Object beanInstance = getBean(beanName);
            if (!(beanInstance instanceof NullBean)) {
                result.put(beanName, (T) beanInstance);
            }
        }
        catch (BeanCreationException ex) {
            Throwable rootCause = ex.getMostSpecificCause();
            if (rootCause instanceof BeanCurrentlyInCreationException) {
                BeanCreationException bce = (BeanCreationException) rootCause;
                String exBeanName = bce.getBeanName();
                if (exBeanName != null && isCurrentlyInCreation(exBeanName)) {
                    if (logger.isTraceEnabled()) {
                        logger.trace("Ignoring match to currently created bean '" + exBeanName + "': " +
                                     ex.getMessage());
                    }
                    onSuppressedException(ex);
                    // Ignore: indicates a circular reference when autowiring constructors.
                    // We want to find matches other than the currently created bean itself.
                    continue;
                }
            }
            throw ex;
        }
    }
    return result;
}

@Override
public String[] getBeanNamesForType(@Nullable Class<?> type, boolean includeNonSingletons, boolean allowEagerInit) {
    if (!isConfigurationFrozen() || type == null || !allowEagerInit) {
        return doGetBeanNamesForType(ResolvableType.forRawClass(type), includeNonSingletons, allowEagerInit);
    }
    Map<Class<?>, String[]> cache =
        (includeNonSingletons ? this.allBeanNamesByType : this.singletonBeanNamesByType);
    String[] resolvedBeanNames = cache.get(type);
    if (resolvedBeanNames != null) {
        return resolvedBeanNames;
    }
    resolvedBeanNames = doGetBeanNamesForType(ResolvableType.forRawClass(type), includeNonSingletons, true);
    if (ClassUtils.isCacheSafe(type, getBeanClassLoader())) {
        cache.put(type, resolvedBeanNames);
    }
    return resolvedBeanNames;
}

@Override
public boolean isConfigurationFrozen() {
    return this.configurationFrozen;
}
private String[] doGetBeanNamesForType(ResolvableType type, boolean includeNonSingletons, boolean allowEagerInit) {
		List<String> result = new ArrayList<>();

		// Check all bean definitions.
		for (String beanName : this.beanDefinitionNames) {
			// Only consider bean as eligible if the bean name is not defined as alias for some other bean.
			if (!isAlias(beanName)) {
				try {
					RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
					// Only check bean definition if it is complete.
					if (!mbd.isAbstract() && (allowEagerInit ||
							(mbd.hasBeanClass() || !mbd.isLazyInit() || isAllowEagerClassLoading()) &&
									!requiresEagerInitForType(mbd.getFactoryBeanName()))) {
						boolean isFactoryBean = isFactoryBean(beanName, mbd);
						BeanDefinitionHolder dbd = mbd.getDecoratedDefinition();
						boolean matchFound = false;
						boolean allowFactoryBeanInit = (allowEagerInit || containsSingleton(beanName));
						boolean isNonLazyDecorated = (dbd != null && !mbd.isLazyInit());
						if (!isFactoryBean) {
							if (includeNonSingletons || isSingleton(beanName, mbd, dbd)) {
								matchFound = isTypeMatch(beanName, type, allowFactoryBeanInit);
							}
						}
						else {
							if (includeNonSingletons || isNonLazyDecorated ||
									(allowFactoryBeanInit && isSingleton(beanName, mbd, dbd))) {
								matchFound = isTypeMatch(beanName, type, allowFactoryBeanInit);
							}
							if (!matchFound) {
								// In case of FactoryBean, try to match FactoryBean instance itself next.
								beanName = FACTORY_BEAN_PREFIX + beanName;
								matchFound = isTypeMatch(beanName, type, allowFactoryBeanInit);
							}
						}
						if (matchFound) {
							result.add(beanName);
						}
					}
				}
				catch (CannotLoadBeanClassException | BeanDefinitionStoreException ex) {
					if (allowEagerInit) {
						throw ex;
					}
					// Probably a placeholder: let's ignore it for type matching purposes.
					LogMessage message = (ex instanceof CannotLoadBeanClassException ?
							LogMessage.format("Ignoring bean class loading failure for bean '%s'", beanName) :
							LogMessage.format("Ignoring unresolvable metadata in bean definition '%s'", beanName));
					logger.trace(message, ex);
					// Register exception, in case the bean was accidentally unresolvable.
					onSuppressedException(ex);
				}
				catch (NoSuchBeanDefinitionException ex) {
					// Bean definition got removed while we were iterating -> ignore.
				}
			}
		}

		// Check manually registered singletons too.
		for (String beanName : this.manualSingletonNames) {
			try {
				// In case of FactoryBean, match object created by FactoryBean.
				if (isFactoryBean(beanName)) {
					if ((includeNonSingletons || isSingleton(beanName)) && isTypeMatch(beanName, type)) {
						result.add(beanName);
						// Match found for this bean: do not match FactoryBean itself anymore.
						continue;
					}
					// In case of FactoryBean, try to match FactoryBean itself next.
					beanName = FACTORY_BEAN_PREFIX + beanName;
				}
				// Match raw bean instance (might be raw FactoryBean).
				if (isTypeMatch(beanName, type)) {
					result.add(beanName);
				}
			}
			catch (NoSuchBeanDefinitionException ex) {
				// Shouldn't happen - probably a result of circular reference resolution...
				logger.trace(LogMessage.format(
						"Failed to check manually registered singleton with name '%s'", beanName), ex);
			}
		}

		return StringUtils.toStringArray(result);
	}

注意这里的isCurrentlyInCreation方法其实是在ConfigurableBeanFactory里定义的,而这个方法的具体实现是在DefaultSingletonBeanRegistry里实现的,这里的DefaultListableBeanFactory是继承自DefaultSingletonBeanRegistry的,所以可以进入DefaultSingletonBeanRegistry查看isCurrentlyInCreation方法的具体逻辑

// 当前从创建检查中排除的bean的名称集合
private final Set<String> inCreationCheckExclusions=Collections.newSetFromMap(new ConcurrentHashMap<>(16));

//当前正在创建的bean的名称
private final Set<String> singletonsCurrentlyInCreation=Collections.newSetFromMap(new ConcurrentHashMap<>(16));

public boolean isCurrentlyInCreation(String beanName) {
    Assert.notNull(beanName, "Bean name must not be null");
    return (!this.inCreationCheckExclusions.contains(beanName) && isActuallyInCreation(beanName));
}

protected boolean isActuallyInCreation(String beanName) {
    return isSingletonCurrentlyInCreation(beanName);
}
// 作用是判断这个bean是否在创建中
public boolean isSingletonCurrentlyInCreation(String beanName) {
    return this.singletonsCurrentlyInCreation.contains(beanName);
}

结合上面三个类的源码得出这个getBeansOfType方法是返回给定对象类型匹配的bean实例,包括子类,会返回一个Map类型的对象,bean的名称作为键,相应的bean的实例作为值

这就知道了刚刚的getBeansOfType方法只是对spring底层源码做一个封装,进行一个判断,如果上下文对象为空的情况直接返回null,不为空就调用spring底层getBeansOfType方法获取我们传入的类型匹配的bean实例

在来看getBean方法,我们先自定义一个内部类分别实现之前的InvocationHandler接口,这个接口的作用是实现方法的增强,可以看到我们具体实现逻辑的目的是实现懒加载模式。当我们调用getBean方法时,会去判断是否开启懒加载策略,如果开启,我们调用之前所写的ProxyUtil类去生成一个代理对象并返回出去,这里CallbackFilter的实现使用匿名内部类,这个接口的功能是断言判断,这里调用method原生的getDeclaringClass方法进行判断。

如果没有开启延迟加载策略,则直接返回spring底层的getBean方法,进入源码发现具体实现还是和上面一样是DefaultListableBeanFactory,作用是返回唯一匹配的给定对象类型的bean实例,如果找不到给定的类型Bean或者找到多个给定的类型bean都会报错。

空的情况直接返回null,不为空就调用spring底层getBeansOfType方法获取我们传入的类型匹配的bean实例

在来看getBean方法,我们先自定义一个内部类分别实现之前的InvocationHandler接口,这个接口的作用是实现方法的增强,可以看到我们具体实现逻辑的目的是实现懒加载模式。当我们调用getBean方法时,会去判断是否开启懒加载策略,如果开启,我们调用之前所写的ProxyUtil类去生成一个代理对象并返回出去,这里CallbackFilter的实现使用匿名内部类,这个接口的功能是断言判断,这里调用method原生的getDeclaringClass方法进行判断。

如果没有开启延迟加载策略,则直接返回spring底层的getBean方法,进入源码发现具体实现还是和上面一样是DefaultListableBeanFactory,作用是返回唯一匹配的给定对象类型的bean实例,如果找不到给定的类型Bean或者找到多个给定的类型bean都会报错。

到此,convert相关的工具类所需的功能就全部介绍完了,我们进入到了spring的底层查看了spring获取bean实例的具体实现。现在为止,我们学习了cglib动态代理,学习了json处理,和spring ioc容器获取bean的实现方式,convert工具类的全部准备内容都以及学习完毕,下一章我们将实际上手开发convert工具类

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值