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 includeFilters = extractTypeFilters(annAttr.getAnnotationArray("includeFilters"));
       
        //增加一个包含的过滤器,扫描到的类只要不是抽象的,接口,枚举,注解,及匿名类那么就算是符合的
        includeFilters.add(new HsfTypeFilter());
       
        List excludeFilters = extractTypeFilters(annAttr.getAnnotationArray("excludeFilters"));
    
        List> 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> 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:
https://github.com/zhangjingchu/JavaStudy/tree/master/CglibRegisterToSpring

你可能感兴趣的:(码农进阶)