package org.test4j.module.spring.strategy.register;
import java.lang.reflect.Field;
import java.util.Queue;
import java.util.Set;
import org.springframework.beans.PropertyValue;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.test4j.module.core.utility.ModulesManager;
import org.test4j.module.jmockit.JMockitModule;
import org.test4j.module.jmockit.utility.JMockitModuleHelper;
import org.test4j.module.spring.annotations.Property;
import org.test4j.module.spring.exception.FindBeanImplClassException;
import org.test4j.tools.commons.ClazzHelper;
import org.test4j.tools.commons.StringHelper;
@SuppressWarnings("rawtypes")
public abstract class SpringBeanRegister {
public void register(final Class testedClazz, final Queue<Class> queue, final RegisterBeanDefinition definitions)
throws FindBeanImplClassException {
// o1 注册@SpringBeanByName注释的spring bean
Set<Field> fields = this.getRegisterField(testedClazz);
for (Field field : fields) {
if (ModulesManager.isModuleEnabled(JMockitModule.class)) {
JMockitModuleHelper.doesSpringBeanFieldIllegal(field);
}
BeanMeta beanMeta = new BeanMeta(queue, definitions);
this.initSpringBean(field, beanMeta);
if (beanMeta.isConcreteClazz()) {
beanMeta.registerBeanDefinition();
} else if (definitions.allowAutoInject(beanMeta.beanName)) {
Class impl = definitions.findImplementClass(testedClazz, beanMeta.beanName, field.getType());
if (impl != null) {
beanMeta.beanClaz = impl;
beanMeta.registerBeanDefinition();
}
}
}
}
/**
* 返回所有@SpringBeanByName @SpringBeanByType 定义的字段<br>
*
* @param testedClazz
* @return
*/
protected abstract Set<Field> getRegisterField(Class testedClazz);
/**
* 返回字段Meta定义
*
* @param field
* @return
*/
protected abstract void initSpringBean(final Field field, final BeanMeta beanMeta);
}
@SuppressWarnings("rawtypes")
class BeanMeta {
String initMethod;
String beanName;
Class beanClaz;
Property[] properties;
private final Queue<Class> queue;
private final RegisterBeanDefinition definitions;
public BeanMeta(final Queue<Class> queue, final RegisterBeanDefinition definitions) {
this.queue = queue;
this.definitions = definitions;
}
/**
* 是否是可实例化的类
*
* @return
*/
boolean isConcreteClazz() {
if (beanClaz.isAnnotation()) {
return false;
}
if (beanClaz.isEnum()) {
return false;
}
if (ClazzHelper.isInterfaceOrAbstract(beanClaz)) {
return false;
} else {
return true;
}
}
void registerBeanDefinition() {
RootBeanDefinition beanDefinition = RegisterDynamicBean.getRootBeanDefinition(beanName, beanClaz, initMethod,
false);
for (Property property : properties) {
this.checkPropertyDefine(property);
if (StringHelper.isBlankOrNull(property.value())) {
this.registerRefBean(property.ref(), property.clazz());
RuntimeBeanReference reference = new RuntimeBeanReference(property.ref());
PropertyValue pv = new PropertyValue(property.name(), reference);
beanDefinition.getPropertyValues().addPropertyValue(pv);
} else {
PropertyValue pv = new PropertyValue(property.name(), property.value());
beanDefinition.getPropertyValues().addPropertyValue(pv);
}
}
definitions.register(beanName, beanDefinition);
queue.offer(beanClaz);
}
/**
* 添加reference bean的spring定义
*
* @param refname
* @param clazz
*/
private void registerRefBean(String refname, Class clazz) {
if (clazz == Property.class) {
return;
}
if (ClazzHelper.isInterfaceOrAbstract(clazz)) {
throw new RuntimeException("the reference bean[" + refname
+ "] class should be an instancable class, but actual is " + clazz.getName() + ".");
}
RootBeanDefinition beanDefinition = RegisterDynamicBean.getRootBeanDefinition(refname, clazz, null, true);
definitions.register(refname, beanDefinition);
queue.add(clazz);
}
/**
* 检查BeanProperty定义的合法性
*
* @param property
*/
private void checkPropertyDefine(final Property property) {
if (StringHelper.isBlankOrNull(property.name())) {
throw new RuntimeException("the value of @BeanProperty name() can't be empty.");
}
if (StringHelper.isBlankOrNull(property.value()) && StringHelper.isBlankOrNull(property.ref())) {
throw new RuntimeException("the values of @BeanProperty value() and ref() can't be all empty.");
}
}
}