大家经常看到各种基于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);
}