Spring自定義註解加cglib動態代理,實現bean掃描注入和數據源切換

動態數據源切換和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;
	}

運行效果

在這裏插入圖片描述

github地址

發佈了17 篇原創文章 · 獲贊 41 · 訪問量 6276
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章