動態數據源切換和AOP編程
項目中經常會有數據源切換的需求,而aop編程實現數據源切換也很實用,由於本人是技術渣,一直沒有深究,只知道aop底層是代理模式。趁着現在有時間,惡補了一下aop底層原理。
本文演示:基於spring提供的接口實現對業務類的掃描並生成動態代理類,註冊到ioc容器中。這裏不多講直接上代碼。
首先定義需要的註解
DataSourceComponent:
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface DataSourceComponent {
/**
* 數據源
* @return
*/
DatasourceEnum DataSource() default DatasourceEnum.DB1;
/**
* 是否要將標識此註解的類註冊爲Spring的Bean
*
* @return
*/
boolean registerBean() default false;
}
該註解聲明在類上,表明該類使用哪個數據源。
DataSourceComponentScan:
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(BeanDefinitionRegistrar.class)
public @interface DataSourceComponentScan {
/**
* @return
*/
String[] value() default {};
/**
* 掃描包
*
* @return
*/
String[] basePackages() default {};
/**
* 掃描的基類
*
* @return
*/
Class<?>[] basePackageClasses() default {};
/**
* 包含過濾器
*
* @return
*/
Filter[] includeFilters() default {};
/**
* 排斥過濾器
*
* @return
*/
Filter[] excludeFilters() default {};
}
該註解在主類上聲明,basePackages屬性指定掃描哪個包:@DataSourceComponentScan(basePackages=“CglibRegisterToSpring.service”)
@Import(HsfBeanDefinitionRegistrar.class)引入配置類,如果該配置類實現了ImportBeanDefinitionRegistrar接口或者BeanDefinitionRegistryPostProcessor,則會調用該接口方法,而不是該實現類註冊爲bean.
ImportBeanDefinitionRegistrar用法大體和BeanDefinitionRegistryPostProcessor相同,但是值得注意的是ImportBeanDefinitionRegistrar只能通過由其它類import的方式來加載,通常是主啓動類或者註解。
2.配置類
2.1 掃描器
在容器啓動時調用,會加載import註解中聲明的類,配置類實現ImportBeanDefinitionRegistrar接口,自動執行registerBeanDefinitions方法
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
//拿到主類上的自定義註解的屬性
AnnotationAttributes annAttr = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(DataSourceComponentScan.class.getName()));
String[] basePackages = annAttr.getStringArray("value");
if (ObjectUtils.isEmpty(basePackages)) {
basePackages = annAttr.getStringArray("basePackages");
}
if (ObjectUtils.isEmpty(basePackages)) {
basePackages = getPackagesFromClasses(annAttr.getClassArray("basePackageClasses"));
}
if (ObjectUtils.isEmpty(basePackages)) {
basePackages = new String[] {ClassUtils.getPackageName(importingClassMetadata.getClassName())};
}
List<TypeFilter> includeFilters = extractTypeFilters(annAttr.getAnnotationArray("includeFilters"));
//增加一個包含的過濾器,掃描到的類只要不是抽象的,接口,枚舉,註解,及匿名類那麼就算是符合的
includeFilters.add(new HsfTypeFilter());
List<TypeFilter> excludeFilters = extractTypeFilters(annAttr.getAnnotationArray("excludeFilters"));
List<Class<?>> candidates = scanPackages(basePackages, includeFilters, excludeFilters);
if (candidates.isEmpty()) {
log.info("掃描指定包[{}]時未發現複合條件的類", basePackages.toString());
return;
}
//註冊處理器後,爲 對象注入環境配置信息
//通過該類對對象進行進一步操作
//registerHsfBeanPostProcessor(registry);
//註冊
registerBeanDefinitions(candidates, registry);
}
TypeFilter:
用於過濾抽象類,接口,註解,枚舉,內部類及匿名類以及應用了spring生成組件註解的類,該類 繼承AbstractClassTestingTypeFilter
protected boolean match(ClassMetadata metadata) {
Class<?> clazz = transformToClass(metadata.getClassName());
if (clazz == null || !clazz.isAnnotationPresent(DataSourceComponent.class)) {
return false;
}
DataSourceComponent hsfComponent = clazz.getAnnotation(DataSourceComponent.class);
if (hsfComponent.registerBean() && isAnnotatedBySpring(clazz)) {
throw new IllegalStateException("類{" + clazz.getName() + "}已經標識了Spring組件註解,不能再指定[registerBean = true]");
}
//過濾抽象類,接口,註解,枚舉,內部類及匿名類
return !metadata.isAbstract() && !clazz.isInterface() && !clazz.isAnnotation() && !clazz.isEnum()
&& !clazz.isMemberClass() && !clazz.getName().contains("$");
}
2.2 註冊器
通過spring的BeanDefinitionBuilder動態生成bean對象,definition.setBeanClass(InterfaceFactoryBean.class) 指定一個實現FactoryBean的類,其返回的對象不是指定類的一個實例,其返回的是該FactoryBean的getObject方法所返回的對象。
private void registerBeanDefinitions(List<Class<?>> internalClasses, BeanDefinitionRegistry registry) {
for (Class<?> clazz : internalClasses) {
if (HSF_UNDERLYING_MAPPING.values().contains(clazz)) {
log.debug("重複掃描{}類,忽略重複註冊", clazz.getName());
continue;
}
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(clazz);
GenericBeanDefinition definition = (GenericBeanDefinition) builder.getRawBeanDefinition();
definition.getPropertyValues().add("interfaceClass", clazz);
Enum value = clazz.getAnnotation(DataSourceComponent.class).DataSource();
definition.getPropertyValues().add("value",value);
definition.setBeanClass(InterfaceFactoryBean.class);
definition.setAutowireMode(GenericBeanDefinition.AUTOWIRE_BY_TYPE);
if (registerSpringBean(clazz)) {
log.debug("註冊[{}]Bean", clazz.getName());
registry.registerBeanDefinition(ClassUtils.getShortNameAsProperty(clazz), definition);
}
UNDERLYING_MAPPING.put(ClassUtils.getShortNameAsProperty(clazz), clazz);
}
}
FactoryBean:
//實現了FactoryBean 註冊器註冊對象時,會調用該方法,得到返回的代理對象
public T getObject() throws Exception {
// 檢查 h 不爲空,否則拋異常
Objects.requireNonNull(interfaceClass);
return (T) Enhancer.create(interfaceClass,new DymicInvocationHandler());
}
DymicInvocationHandler:
接下來就是實現cglib的動態代理了
@Override
public Object intercept(Object sub, Method method, Object[] objects,MethodProxy proxy) throws Throwable {
Class<?> declaringClass = method.getDeclaringClass();
DataSourceComponent declaredAnnotation = AnnotationUtils.findAnnotation(declaringClass, DataSourceComponent.class);
//dosomthing
//。。。
Object object = proxy.invokeSuper(sub, objects);
//dosomthing
//。。。
return object;
}