`
raymond.chen
  • 浏览: 1418590 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

自定义注解扫描并动态注册bean

 
阅读更多

        缺省情况下,ClassPathBeanDefinitionScanner类只会扫描带有@Component(@RestController、@Controller、@Service、@Repository)、@ManagedBean、@Named 注解的类,如果要扫描自定义注解类,则需要通过创建ClassPathBeanDefinitionScanner类的子类实现该功能,该类对外提供scan方法,根据传入的包名,自动扫描加载BeanDefinition并将BeanDefinition注册到registry中。

 

ClassPathBeanDefinitionScanner子类在调用过程的位置有:

        @import注解类 >> ImportBeanDefinitionRegistrar注册 >> ClassPathBeanDefinitionScanner扫描 >> FactoryBean动态代理 >> Advice面向切面编程

 

        BeanFactoryPostProcessor >> ClassPathBeanDefinitionScanner扫描 >> FactoryBean动态代理 >> Advice面向切面编程

 

1、配置类

@Import({ScannerImportBeanDefinitionRegistrar.class})
//@Import({ScannerBeanFactoryPostProcessor.class})
class ScannerConfig {
	
}

 

2、BeanDefinition注册类

class ScannerImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar{
    @Override
    public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
    	ClassPathBeanDefinitionScanner scanner = new ScannerClassPathBeanDefinitionScanner(registry);
    	
    	//从指定包路径下扫描
    	scanner.scan(ScannerClassPathBeanDefinitionScanner.PACKAGE_PATH);
    }
}

 

     BeanFactoryPostProcessor类

/**
 * BeanFactoryPostProcessor
 * 		BeanFactoryPostProcessor在spring容器加载了bean的定义文件之后,在bean实例化之前执行。
 * 		使用BeanFactoryPostProcessor对BeanFactory中的BeanDefinition进行修改,或者创建BeanDefinition到BeanFactory中。
 */
class ScannerBeanFactoryPostProcessor implements BeanFactoryPostProcessor{
	@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
		ClassPathBeanDefinitionScanner scanner = new ScannerClassPathBeanDefinitionScanner((BeanDefinitionRegistry)beanFactory);
		
    	//从指定包路径下扫描
    	scanner.scan(ScannerClassPathBeanDefinitionScanner.PACKAGE_PATH);
	}
}

 

3、扫描器类

class ScannerClassPathBeanDefinitionScanner extends ClassPathBeanDefinitionScanner{
	public static String PACKAGE_PATH = "com.seasy.springboottest.examples.example2";
	
	//工厂Bean
	private ScannerFactoryBean scannerFactoryBean = new ScannerFactoryBean();
	
	public ScannerClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry) {
        super(registry);
        addIncludeFilters();
    }
	
	void addIncludeFilters(){
    	//基于 自定义注解类 进行扫描过滤
    	addIncludeFilter(new AnnotationTypeFilter(Mapper.class));
    	
    	//基于 接口类 进行扫描过滤
    	addIncludeFilter(new AssignableTypeFilter(UserMapper.class));
	}
	
	@Override
	protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
		Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
		
		ScannedGenericBeanDefinition definition;
		for(BeanDefinitionHolder holder : beanDefinitions){
			definition = (ScannedGenericBeanDefinition) holder.getBeanDefinition();
			
			if(definition.getMetadata().isInterface()){ //是接口类
				String beanName = holder.getBeanName();
				String beanClassName = definition.getBeanClassName();
				System.out.println(">>> beanName=" + beanName + ", beanClassName=" + beanClassName);

				//通过工厂Bean获取Bean对象
				definition.setBeanClass(scannerFactoryBean.getClass()); 
				
				//给FactoryBean类的interfaceClassName属性值设置值
				definition.getPropertyValues().add("interfaceClassName", beanClassName);
				
				//给FactoryBean类的构造函数添加入参值
				//definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName);
				
				//sqlSessionFactory属性值是一个bean对象,根据bean名形成依赖关系
				//definition.getPropertyValues().add("sqlSessionFactory", new RuntimeBeanReference(beanName));
				
			}else{
				System.out.println("###########  " + definition.getBeanClassName());
			}
		}
		
		return beanDefinitions;
	}
	
	/**
	 * 候选组件
	 */
	@Override
	protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
		AnnotationMetadata metadata = beanDefinition.getMetadata();
		
		//isIndependent独立的、isConcrete具体的、isInterface接口
		return metadata.isIndependent() 
				&& (metadata.isConcrete() || metadata.isInterface());
	}
}

 

4、FactoryBean类

class ScannerFactoryBean implements FactoryBean<Object>{
	private ProxyFactory factory;
	private String interfaceClassName;

	public void setInterfaceClassName(String interfaceClassName) {
		this.interfaceClassName = interfaceClassName;
		
		try {
			factory.setInterfaces(Class.forName(interfaceClassName));
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
	}

	public ScannerFactoryBean(){
		factory = new ProxyFactory(); //Spring代理工厂
	    factory.addAdvice(new CustomMethodInterceptor());
	}
	
	@Override
	public Object getObject() throws Exception {
		return factory.getProxy();
	}
	
	@Override
	public Class<?> getObjectType() {
		try {
			if(StringUtils.hasText(interfaceClassName)){
				return Class.forName(interfaceClassName);
			}
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
		return null;
	}
	
	@Override
	public boolean isSingleton() {
		return true;
	}
}

 

5、MethodInterceptor类

class CustomMethodInterceptor implements MethodInterceptor{
	@Override
	public Object invoke(MethodInvocation invocation) throws Throwable {
		Object object = null;
		
		//接口没有对应的实现类,不能直接调用 invocation.proceed() 方法
        //object = invocation.proceed();
		System.out.println(invocation.getMethod().getDeclaringClass().getName() + ": " + invocation.getMethod().getName());
		
        return object;
	}
}

 

分享到:
评论

相关推荐

    Spring启动后获取所有拥有特定注解的Bean实例代码

    主要介绍了Spring启动后获取所有拥有特定注解的Bean实例代码,分享了相关代码示例,小编觉得还是挺不错的,具有一定借鉴价值,需要的朋友可以参考下

    IoC容器的设计(利用反射、注解和工厂模式实现)

    @ComponentScan 注解扫描器 3.自定义两个业务类Group和User,创建一个测试类Test,对IoC容器进行测试; 实验思路: 我们需要将自定义四个注解,然后将Group和User类使用@Component注解,在User类中创建Group类的...

    remotetransfer

    自定义注解实现简单的RPC远程调用 博客地址: : 支持功能: 替换SpringCloud @Feign注解 支持Spring的@GetMapping,@ DeleteMapping,@ PutMapping,@ PostMapping注解(注意:注解需要完整,如案例中) 涉及知识点...

    关于mybatis spring与dubbo是怎么通过一个接口 你就能调用到一个bean方法的讲解

    Mybatis 或者 dubbo 或者 ...首先要弄清楚 spring的加载与生成bean的机制 spring在启动的时候 会 扫描 所有定义的 bean的类 并定义BeanDefinition 然后通过BeanDefinition 在调用creataBean方法的时候会 去实例 初始

    Spring 3 Reference中文

    4.10.2 自动检测类和bean 的注册. 97 4.10.3 使用过滤器来自定义扫描 98 4.10.4 使用组件定义bean 的元数据.. 99 4.10.5 命名自动检测组件 100 4.10.6 为自动检测组件提供范围. 101 4.10....

    Spring中文帮助文档

    3.12.3. 使用过滤器自定义扫描 3.12.4. 自动检测组件的命名 3.12.5. 为自动检测的组件提供一个作用域 3.12.6. 用注解提供限定符元数据 3.13. 注册一个LoadTimeWeaver 4. 资源 4.1. 简介 4.2. Resource接口 ...

    Spring API

    3.12.3. 使用过滤器自定义扫描 3.12.4. 自动检测组件的命名 3.12.5. 为自动检测的组件提供一个作用域 3.12.6. 用注解提供限定符元数据 3.13. 注册一个LoadTimeWeaver 4. 资源 4.1. 简介 4.2. Resource接口 ...

    spring boot 代码示例

    springboot例子, 包含druid数据源, ... 动态数据源, 读写分离, Redis Session, redis缓存, xml注入spring boot无法扫描到的bean, actuator监控, kafka消息队列, Swagger2配置, dubbo注解方式, elasticsearch搜索引擎

    深入浅出Struts 2 .pdf(原书扫描版) part 1

    3.8 动态方法调用 51 3.9 对动作类进行测试 51 3.10 小结 51 第4章 OGNL 52 4.1 Value Stack栈 52 4.2 读取Object Stack里的对象的属性 53 4.3 读取Context Map里的对象的属性 54 4.4 如何调用字段和方法 55 4.5 ...

    java二进制补码源码-coolmq:消息最终一致性方案,基于rabbitmq的分布式事务解决方案

    自定义注解插入发送切面 抽象消息存储 扩展元消息 项目结构说明 coolmq为实际包 microservice-demo们为spring-boot集成demo 使用说明 maven引入coolmq依赖 在项目启动中配置包扫描:@SpringBootApplication...

    从零开始学Spring Boot

    1.20 Spring Boot普通类调用bean 1.21 使用模板(thymeleaf-freemarker) 1.22 Spring Boot 添加JSP支持 1.23 Spring Boot Servlet 1.24 Spring Boot过滤器、监听器 1.25 Spring Boot 拦截器HandlerInterceptor 1.26...

    Spring攻略(第二版 中文高清版).part2

    2.7 自定义Bean初始化和析构 72 2.7.1 问题 72 2.7.2 解决方案 72 2.7.3 工作原理 72 2.8 用Java Config简化XML配置 77 2.8.1 问题 77 2.8.2 解决方案 77 2.8.3 工作原理 77 2.9 使Bean感知容器 ...

    Spring攻略(第二版 中文高清版).part1

    2.7 自定义Bean初始化和析构 72 2.7.1 问题 72 2.7.2 解决方案 72 2.7.3 工作原理 72 2.8 用Java Config简化XML配置 77 2.8.1 问题 77 2.8.2 解决方案 77 2.8.3 工作原理 77 2.9 使Bean感知容器 ...

    乐优商城.xmind

    自动注册到Spring容器,不需要再在applicationContext.xml文件定义bean了 @Autowired 它可以对类成员变量、方法及构造函数进行标注,完成自动装配的工作。 select select * from category c where c.pid = #{...

    spring3.1中文参考文档

    2.5.3.1 基于Java的bean元数据.................................................................................................. 25 2.5.3.2 使用组件定义bean的元数据.........................................

    JAVA上百实例源码以及开源项目

    6个目标文件,EJB来模拟银行ATM机的流程及操作:获取系统属性,初始化JNDI,取得Home对象的引用,创建EJB对象,并将当前的计数器初始化,调用每一个EJB对象的count()方法,保证Bean正常被激活和钝化,EJB对象是用...

    JAVA上百实例源码以及开源项目源代码

    6个目标文件,EJB来模拟银行ATM机的流程及操作:获取系统属性,初始化JNDI,取得Home对象的引用,创建EJB对象,并将当前的计数器初始化,调用每一个EJB对象的count()方法,保证Bean正常被激活和钝化,EJB对象是用...

Global site tag (gtag.js) - Google Analytics