文档章节

深入理解Spring 进阶开发必知必会之扩展接口

f
 fashionbrot
发布于 2019/11/15 17:54
字数 2068
阅读 222
收藏 0

大家经常看到各种基于spring 开发的一下开源插件,使用简单方便 在启动类上直接 通过注解开启插件功能,那就由小编为你简单描述一下如何自己也能开发出一套简单好用的插件。

1、首先我们先通过 @EnableXXConfig 添加到启动类,或 配置类中

@Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(XXConfigBeanDefinitionRegistrar.class)  //通过spring 的 Import注解来实现 bean的 ioc 注入
public @interface EnableXXConfig {
    String appId() default "";
    String envCode() default "";
}

2、编写  XXConfigBeanDefinitionRegistrar 类实现 bean 的 ioc 注入 一个单列的bean

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.util.Assert;

import static org.springframework.core.annotation.AnnotationAttributes.fromMap;

public class xxConfigBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar, EnvironmentAware,BeanFactoryAware{

    private Environment environment;
    private ConfigurableListableBeanFactory beanFactory;

    @Override
    public void setEnvironment(Environment environment) {
        this.environment = environment;
    }
    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
    }
    @Override
    public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
        //通过以下方法获取 EnableXXConfig注解 上的参数 appid envcode 的值
        AnnotationAttributes attributes = fromMap(metadata.getAnnotationAttributes(EnableManagerConfig.class.getName()));
        /**
         * Register Global xx  Properties Bean  注入单例bean
          */
        registerGlobalXXProperties(attributes, registry, environment,beanFactory);
        /**
          *  注入需要的 bean(非单例)注册之前需要判断是否已注册 bean
           */
        if (!registry.containsBeanDefinition("XXBeanName")) {
            registerInfrastructureBeanIfAbsent(registry,"XXBeanName",XXBeanName.class);
        }
    }
}
public static void registerGlobalXXProperties(AnnotationAttributes attributes,
                                                 BeanDefinitionRegistry registry,
                                                 PropertyResolver propertyResolver,
                                                 ConfigurableListableBeanFactory beanFactory) {
    if (attributes == null) {
        return; // Compatible with null
    }
   
    GlobalXXProperties properties = new GlobalXXProperties();
    properties.setAppName(attributes.get("appId"));
    properties.setEnvCode(attributes.get("envCode"));
    registerSingleton(registry, "XXName", properties);
}
public static void registerSingleton(BeanDefinitionRegistry registry, String beanName, Object singletonObject) {
    SingletonBeanRegistry beanRegistry = null;
    if (registry instanceof SingletonBeanRegistry) {
        beanRegistry = (SingletonBeanRegistry) registry;
    } else if (registry instanceof AbstractApplicationContext) {
        beanRegistry = ((AbstractApplicationContext) registry).getBeanFactory();
    }
    if (beanRegistry != null) {
        beanRegistry.registerSingleton(beanName, singletonObject);
    }
}

3、注入需要的非单例bean 

public static void registerInfrastructureBean(BeanDefinitionRegistry registry, String beanName, Class<?> beanClass,
                                              Object... constructorArgs) {
    // Build a BeanDefinition for serviceFactory class
    BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.rootBeanDefinition(beanClass);
    for (Object constructorArg : constructorArgs) {
        beanDefinitionBuilder.addConstructorArgValue(constructorArg);
    }
    // ROLE_INFRASTRUCTURE
    beanDefinitionBuilder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
    // Register
    registry.registerBeanDefinition(beanName, beanDefinitionBuilder.getBeanDefinition());
}

 

 

以上就是通过 spring 类注册 bean 的简单流程

 

下面说一下经常注册bean 需要用的spring 的一些接口

1、Ordered 接口定义bean 加载顺序的

org.springframework.core.Ordered
public interface Ordered {
   int HIGHEST_PRECEDENCE = Integer.MIN_VALUE;
   int LOWEST_PRECEDENCE = Integer.MAX_VALUE;
   int getOrder();
}

2、InitializingBean接口为bean提供了初始化方法的方式,它只包括afterPropertiesSet方法,凡是继承该接口的类,在初始化bean的时候都会执行该方法。

org.springframework.beans.factory.InitializingBean
public interface InitializingBean {
   /**
    * Invoked by a BeanFactory after it has set all bean properties supplied
    * (and satisfied BeanFactoryAware and ApplicationContextAware).
    * <p>This method allows the bean instance to perform initialization only
    * possible when all bean properties have been set and to throw an
    * exception in the event of misconfiguration.
    * @throws Exception in the event of misconfiguration (such
    * as failure to set an essential property) or if initialization fails.
    */
   void afterPropertiesSet() throws Exception;

}

3、DisposableBean 就是在一个bean被销毁的时候,spring容器会帮你自动执行这个方法

org.springframework.beans.factory.DisposableBean
public interface DisposableBean {
   /**
    * Invoked by a BeanFactory on destruction of a singleton.
    * @throws Exception in case of shutdown errors.
    * Exceptions will get logged but not rethrown to allow
    * other beans to release their resources too.
    */
   void destroy() throws Exception;
}

4、ImportBeanDefinitionRegistrar  配合 @Inport 使用

5、EnvironmentAware  通过实现接口可获得 Environment 接口(Environment 是存储系统变量 和 用户配置的接口,通过接口可获取 系统配置和自定义配置)如下

 

Environment相关使用分为两部分,一个是Properties属性的使用一个是Profile使用。我们先来看看Properties属性的使用:

(1)xml 配置及获取方式

<context:property-placeholder location="classpath:db.properties" />
@Autowired
private Environment environment;
 
public void getProperty() {
  environment.getProperty("jdbc.driverClassName");
}

(2)注解配置及获取方式

@Configuration
@PropertySource("classpath:db.properties")
public class TestProperties {
	@Autowired
	Environment env;
 
	public void getProperty() {
		environment.getProperty("jdbc.driverClassName");
	}
}

 

6、BeanFactoryAware 实现此接口的 setBeanFactory 可获取 BeanFactory对象,用于判断bean 是否注册和注册bean

@Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(XXConfigBeanDefinitionRegistrar.class)  //通过spring 的 Import注解来实现 bean的 ioc 注入
public @interface EnableXXConfig {
    String appId() default "";
    String envCode() default "";
}
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.util.Assert;

import static org.springframework.core.annotation.AnnotationAttributes.fromMap;

public class xxConfigBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar, EnvironmentAware,BeanFactoryAware{

    private Environment environment;
    private ConfigurableListableBeanFactory beanFactory;

    @Override
    public void setEnvironment(Environment environment) {
        this.environment = environment;
    }
    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
    }
    @Override
    public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
        //通过以下方法获取 EnableXXConfig注解 上的参数 appid envcode 的值
        AnnotationAttributes attributes = fromMap(metadata.getAnnotationAttributes(EnableManagerConfig.class.getName()));
        /**
         * Register Global xx  Properties Bean  注入单例bean
          */
        registerGlobalXXProperties(attributes, registry, environment,beanFactory);
        /**
          *  注入需要的 bean(非单例)注册之前需要判断是否已注册 bean
           */
        if (!isBeanDefinitionPresent(registry, "XXBeanName", XXBeanName.class) && !registry.containsBeanDefinition("XXBeanName")) {
            registerInfrastructureBeanIfAbsent(registry,"XXBeanName",XXBeanName.class);
        }
    }
}

7、ApplicationEventPublisherAware  spring提供的一套发布订阅接口 使用方式spring ApplicationEventPublisherAware、ApplicationEventPublisher 的使用发布订阅模式

package org.springframework.context;

import org.springframework.beans.factory.Aware;

/**
 * Interface to be implemented by any object that wishes to be notified
 * of the ApplicationEventPublisher (typically the ApplicationContext)
 * that it runs in.
 *
 * @author Juergen Hoeller
 * @author Chris Beams
 * @since 1.1.1
 * @see ApplicationContextAware
 */
public interface ApplicationEventPublisherAware extends Aware {

	/**
	 * Set the ApplicationEventPublisher that this object runs in.
	 * <p>Invoked after population of normal bean properties but before an init
	 * callback like InitializingBean's afterPropertiesSet or a custom init-method.
	 * Invoked before ApplicationContextAware's setApplicationContext.
	 * @param applicationEventPublisher event publisher to be used by this object
	 */
	void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher);

}

 

8、实现spring  ApplicationContextAware 接口  可也获取到 ApplicationContext 接口 ;是 BeanFactory的子接口。它们都可以当做Spring的容器,Spring容器是生成Bean实例的工厂,并管理容器中的Bean。

org.springframework.context.ApplicationContext

 

9、实现 spring  BeanPostProcessor 接口 是Spring IOC容器给我们提供的一个扩展接口 ;用于处理自定义bean 

import org.springframework.beans.BeansException;

/**
 * Factory hook that allows for custom modification of new bean instances,
 * e.g. checking for marker interfaces or wrapping them with proxies.
 *
 * <p>ApplicationContexts can autodetect BeanPostProcessor beans in their
 * bean definitions and apply them to any beans subsequently created.
 * Plain bean factories allow for programmatic registration of post-processors,
 * applying to all beans created through this factory.
 *
 * <p>Typically, post-processors that populate beans via marker interfaces
 * or the like will implement {@link #postProcessBeforeInitialization},
 * while post-processors that wrap beans with proxies will normally
 * implement {@link #postProcessAfterInitialization}.
 *
 * @author Juergen Hoeller
 * @since 10.10.2003
 * @see InstantiationAwareBeanPostProcessor
 * @see DestructionAwareBeanPostProcessor
 * @see ConfigurableBeanFactory#addBeanPostProcessor
 * @see BeanFactoryPostProcessor
 */
public interface BeanPostProcessor {

	/**
	 * Apply this BeanPostProcessor to the given new bean instance <i>before</i> any bean
	 * initialization callbacks (like InitializingBean's {@code afterPropertiesSet}
	 * or a custom init-method). The bean will already be populated with property values.
	 * The returned bean instance may be a wrapper around the original.
	 * @param bean the new bean instance
	 * @param beanName the name of the bean
	 * @return the bean instance to use, either the original or a wrapped one;
	 * if {@code null}, no subsequent BeanPostProcessors will be invoked
	 * @throws org.springframework.beans.BeansException in case of errors
	 * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet
	 */
	Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;

	/**
	 * Apply this BeanPostProcessor to the given new bean instance <i>after</i> any bean
	 * initialization callbacks (like InitializingBean's {@code afterPropertiesSet}
	 * or a custom init-method). The bean will already be populated with property values.
	 * The returned bean instance may be a wrapper around the original.
	 * <p>In case of a FactoryBean, this callback will be invoked for both the FactoryBean
	 * instance and the objects created by the FactoryBean (as of Spring 2.0). The
	 * post-processor can decide whether to apply to either the FactoryBean or created
	 * objects or both through corresponding {@code bean instanceof FactoryBean} checks.
	 * <p>This callback will also be invoked after a short-circuiting triggered by a
	 * {@link InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation} method,
	 * in contrast to all other BeanPostProcessor callbacks.
	 * @param bean the new bean instance
	 * @param beanName the name of the bean
	 * @return the bean instance to use, either the original or a wrapped one;
	 * if {@code null}, no subsequent BeanPostProcessors will be invoked
	 * @throws org.springframework.beans.BeansException in case of errors
	 * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet
	 * @see org.springframework.beans.factory.FactoryBean
	 */
	Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;

}
//bean初始化方法调用前被调用
Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
//bean初始化方法调用后被调用
Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;

 

10、spring 抽象类  InstantiationAwareBeanPostProcessorAdapter 多数使用场景是 bean 初始化 实例化后的自定义操作

用户实现 @Autowired  和 @Value 实现

 

package org.springframework.beans.factory.config;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Constructor;

import org.springframework.beans.BeansException;
import org.springframework.beans.PropertyValues;

/**
 * Adapter that implements all methods on {@link SmartInstantiationAwareBeanPostProcessor}
 * as no-ops, which will not change normal processing of each bean instantiated
 * by the container. Subclasses may override merely those methods that they are
 * actually interested in.
 *
 * <p>Note that this base class is only recommendable if you actually require
 * {@link InstantiationAwareBeanPostProcessor} functionality. If all you need
 * is plain {@link BeanPostProcessor} functionality, prefer a straight
 * implementation of that (simpler) interface.
 *
 * @author Rod Johnson
 * @author Juergen Hoeller
 * @since 2.0
 */
public abstract class InstantiationAwareBeanPostProcessorAdapter implements SmartInstantiationAwareBeanPostProcessor {

	@Override
	public Class<?> predictBeanType(Class<?> beanClass, String beanName) {
		return null;
	}

	@Override
	public Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, String beanName) throws BeansException {
		return null;
	}

	@Override
	public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
		return bean;
	}
      //实例化之前的后处理
	@Override
	public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
		return null;
	}
      //实例化后的后处理
	@Override
	public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
		return true;
	}

      //用于注入自定义的注解属性值
	@Override
	public PropertyValues postProcessPropertyValues(
			PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {

		return pvs;
	}
    
      //初始化之前的后处理
	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	} 

      //初始化后的后处理
	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}

}

 

11、MergedBeanDefinitionPostProcessor  配合  InstantiationAwareBeanPostProcessorAdapter 使用,用户 自定义注解实现

public interface MergedBeanDefinitionPostProcessor extends BeanPostProcessor {
    //在bean实例化完毕后调用 可以用来修改merged BeanDefinition的一些properties 或者用来给后续回调中缓存一些meta信息使用
    //这个算是将merged BeanDefinition暴露出来的一个回调
    void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName);
}

 

© 著作权归作者所有

f

fashionbrot

粉丝 3
博文 4
码字总数 3844
作品 3
海淀
私信 提问
加载中

评论(0)

java程序员从入门到精通

前言 java路漫漫,这是一份针对java服务端开发入门与进阶指南。 建议: 尽量用 google 查找技术资料。 有问题在 stackoverflow 找找,大部分都已经有人回答。 多看官方的技术文档。 ibm deve...

rock912
2016/07/04
153
4
服务端工程师入门与进阶 Java 版

前言 欢迎加入我们。这是一份针对实习生/毕业生的服务端开发入门与进阶指南。遇到问题及时问你的 mentor 或者直接问我。 建议: 尽量用google查找技术资料。 有问题在stackoverflow找找,大部...

天天顺利
2015/10/14
140
0
5本数据库经典之作,没看过的都白学了!

  【IT168 评论】1、 《数据库系统实现》   内容简介:书中对数据库系统实现原理进行了深入阐述,并具体讨论了数据库管理系统的三个主要成分――存储管理器、查询处理器和事务管理器的实...

it168网站
2017/11/13
0
0
@Transaction必知必会

1. Spring事务的基本原理 事务管理是应用系统开发中必不可少的一部分。Spring 为事务管理提供了丰富的功能支持。Spring 事务管理分为编码式和声明式的两种方式。编程式事务指的是通过编码方式...

maskwang520
2018/04/15
0
0
程序员开发必知必会之正则表达式学习资料

正则表达式30分钟入门教程(https://deerchao.net/tutorials/regex/regex.htm) 本教程目标:30分钟内让你明白正则表达式是什么,并对它有一些基本的了解,让你可以在自己的程序或网页里使用...

崔庆才
2019/07/26
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Java高级面试攻略:消息+缓存+高并发+高可用+架构+分布式+微服务

Java高级面试攻略:消息+缓存+高并发+高可用+架构+分布式+微服务,对每一个专题每一道列举出来的高频面试题,都会做一个面试官的心理分析以及题目的具体剖析,教你如何破解,攻克面试官! 攻...

眉目清俊
15分钟前
19
0
JavaScript 数组遍历方法的对比

前言 JavaScript 发展至今已经发展出多种数组的循环遍历的方法,不同的遍历方法运行起来那个比较快,不同循环方法使用在那些场景,下面将进行比较: 各种数组遍历的方法 for 语句 代码: var arr...

osc_fscujk71
24分钟前
5
0
Nginx和Tomcat配置https

Nginx配置htpps http{ #http节点中可以添加多个server节点 server{ #监听443端口 listen 443; #对应的域名,把baofeidyz.com改成你们自己的域名就可以了 ...

chinahufei
24分钟前
5
0
react调用方法

React里调用方法常见的方式有两种 (1)触发时通过回调函数触发 <Button type="primary" onClick={()=>this.updateBar()}>更新</Button> (2)直接通过方法名触发 <Button type="primary" o......

osc_vpogdtu8
25分钟前
26
0
Centos通过代理上网

阿里云ECS不能访问内外,购买NAT的除外,通过windows主机代理上网: 1.windows上安装代理工具,工具种类比较多,我这里使用的是privoxy 下载地址:http://www.privoxy.org/ 百度云盘(备用)...

osc_r94nrknb
26分钟前
7
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部