spring注解及扩展

1,spring配置注解

spring建议通过注解配置,替代原xml配置方式。

使用配置类替代xml配置的优势大体:

1,xml配置维护容易出错而且不易检查,java配置类基于java语法检查,对于java程序员更友好,易于维护;

2,注解配置,简洁、清晰,代码都内聚于java代码,理解和代码开发都更连贯;

主要缺点是:

1,结构没有xml文件结构清晰,分散不易于整体阅读;

2,部分注解不易于实现配置,建议仍然采用xml配置实现。

spring采用配置类的注解主要包括:

  • @Configuration 指示类声明一个或多个用@bean注解修饰的方法,并且可以由Spring容器处理,以便在运行时为这些bean生成bean定义和服务请求。指明给java配置类,替代beans.xml配置文件;
  • @ComponentScan 配置用于@Configuration 类的组件扫描路径等。 提供与xml配置上的的元素相同的功能;

  • @bean 指明方法生成由Spring容器管理的bean。替换xml配置中的bean标签;

2,容器组件注解

spring中和spring容器相关注解包括如下:

  • @component 指示带注释的类是“组件”。当使用基于注释的配置和类路径扫描时,此类被视为自动检测的候选类,自动装载进入容器。

  • @Service 指示带注释的类是“服务”,作为独立于模型的接口提供的操作,没有封装状态。行为类组件,应该是无状态bean。(具体概念参见领域驱动设计基本概念中的服务)

  • @Controller 指示带注释的类是“控制器”,在springMVC中的控制器。也是一个组件。

  • @Repository 指示带注释的类是“存储库”,封装存储、检索和模拟对象集合的搜索行为的机制。持久层操作。也是一个组件。(概念有别于dao层;具体参见领域驱动设计基本概念中的仓储)(实现传统2ee模式(如“数据访问对象”)的团队也可以将这种原型应用于DAO类,但是在这样做之前,应该注意理解数据访问对象和DDD风格仓储之间的区别。这个注释是一个通用的原型,单个团队可能会适当地缩小它们的语义和使用范围。)

  • @Conditional 指示组件只有在符合注册条件时才有资格注册。 用于修饰组件、bean等;

  • @Primary 当有多个组件或者bean有资格被依赖注入时,无法判断注入的bean。如果候选人中存在一个主bean,则该bean将是自动设置的值。 被@Primary修饰的component、bean被作为主bean被spring容器优先采用。

  • @Lazy 指示是否要延迟初始化bean,如延迟,则在第一次被使用时初始化加载。默认 true。

  • @scope 当作为类型级注释与@Component一起使用时,@Scope指示要用于带注释类型的实例的作用域范围的名称。 spring容器在不指定scope的情况下默认是单例的。可选:prototype、request、session、global session;

  • @Import 指示要导入的一个或多个@Configuration类。 允许导入@Configuration类、ImportSelector、ImportBeanDefinitionRegistrar 实现。向spring容器中注册bean。

  • @PostConstruct 自定义初始化函数,在bean创建后执行;对应有自定义的销毁函数@PreDestroy,在销毁前执行;

3,组件赋值注解

组件赋值注解包括如下:

  • @value 用于字段、方法/构造函数参数的注解,将表达式的值映射到受影响参数上。
  • @Autowired 将构造函数、字段、setter方法或config方法标记为自动使用Spring的依赖注入工具赋值。
  • @Qualifier 此注解可在字段或参数上用作候选bean的限定符。它也可用于注解其他自定义注解,这些注解随后可用作限定符。 比如和@Autowired共同使用,限定被依赖注入的具体组件;
  • @Resource默认按照ByName自动注入,由J2EE提供。@Resource有两个重要的属性:name和type,而Spring将@Resource注解的name属性解析为bean的名字,而type属性则解析为bean的类型。所以,如果使用name属性,则使用byName的自动注入策略,而使用type属性时则使用byType自动注入策略。如果既不制定name也不制定type属性,这时将通过反射机制使用byName自动注入策略。
  • @PropertySource 主要是让Spring的Environment接口读取属性配置文件用的,这个注解是标识在@Configuration配置类上的。
  • @profile 指示当一个或多个指定的profile环境被激活,组件才有资格注册。(启动参数设定环境方式、代码启动设定环境方式)

4,基于Aware 扩展

在使用spring的过程中,大多数功能相关组件直接通过配置类或者注解完成往spring容器中注册,整个过程对spring容器的框架对象是无感知的。但是基于某些特殊需求,我们需要使用spring容器的框架对象组件,那么spring提供了基于Aware的方式的扩展,可以获取spring容器的框架对象组件。

Aware 标记超级接口,表示bean有资格通过回调样式的方法由特定框架对象的Spring容器通知。实际可获取的框架对象组件,通过Aware的子接口来决定。 (相对常用如下:)

  • BeanFactoryAware 获得spring的bean Factory;该接口由希望获取beanFactory的bean实现。

  • ApplicationContextAware 当前的applicationContext从而调用容器的服务;该接口由希望获取applicationContext的bean实现。(ApplicationContext接口集成了MessageSource接口、ApplicationEventPublisher接口和ResourceLoader接口,因此当Bean实现ApplicationContextAware的时候就可以得到Spring容器的所有服务。)

  • MessageSourceAware 用于得到message source从而得到文本信息。

  • ApplicationEventPublisherAware 用于获取ApplicationEventPublisher,用于发布事件;

  • ResourceLoaderAware 获取ResourceLoader资源加载器,可以获得外部资源文件;

类继承结构:
Aware继承结构

看下面的代码就知道为什么可以通过实现接口,获得对应spring框架对象组件:

    private void invokeAwareInterfaces(Object bean) {
        if (bean instanceof Aware) {
            if (bean instanceof EnvironmentAware) {
                ((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
            }
            if (bean instanceof EmbeddedValueResolverAware) {
                ((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver);
            }
            if (bean instanceof ResourceLoaderAware) {
                ((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
            }
            if (bean instanceof ApplicationEventPublisherAware) {
                ((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);
            }
            if (bean instanceof MessageSourceAware) {
                ((MessageSourceAware) bean).setMessageSource(this.applicationContext);
            }
            if (bean instanceof ApplicationContextAware) {
                ((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
            }
        }
    }

5, 基于BeanPostProcessor后置处理器扩展

BeanPostProcessor:spring创建bean时的工厂方法模式中,通过BeanPostProcessor暴露出来两个钩子函数,通过自定义实现该工厂钩子函数,实现需要的扩展,如:检查注解或者用代理包装bean等;

两个具体钩子:

    /**
     * 在参数中的bean已经实例化,依赖注入等完成后,但是在自定义初始化方法(类似@PostConstruct 修饰的自定义初始化函数)完成初始化之前调用。返回该bean或者被代理包装过的bean;
     */
    Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
    /**
     * 在参数中的bean已经实例化,依赖注入等完成后,但是在自定义初始化方法(类似@PostConstruct 修饰的自定义初始化函数)完成初始化之后调用。返回该bean或者被代理包装过的bean;
     */
    Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;

BeanPostProcessor有很多子接口,可以通过扩展实现,具体参见BeanPostProcessor的继承实现结构。

具体实现方式、代码,参照:java注解(java Annotation) 注解修饰类生效方式一。

6,基于BeanFactoryPostProcessor后置处理器扩展

BeanFactoryPostProcessor 允许自定义修改应用程序上下文的bean定义,调整上下文基础bean工厂的bean属性值。

比 BeanPostProcessor更强大,BeanPostProcessor用于bean在实例化后扩展;而BeanFactoryPostProcessor 用于在bean实例化之前读取bean的定义(bean的元数据),并可以修改它,使bean按照新的定义信息初始化bean;

可以按照需要定义很多个BeanFactoryPostProcessor 实例,注册进入spring容器,在spring bean factory初始化完成后,会通知所有processor执行。

BeanFactoryPostProcessor继承结构,有很多实现可以用于扩展,这里只就BeanFactoryPostProcessor和典型子接口BeanDefinitionRegistryPostProcessor进一步描述;

/**
 * 允许自定义修改应用程序上下文的bean定义,调整上下文基础bean工厂的bean属性值。
 */
public interface BeanFactoryPostProcessor {
    /**
     * 在bean factory的标准初始化都装载完成后,在bean实例化之前,允许修改应用程序上下文的内部bean factory。可以重载,也可以添加。
     */
    void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;

}
/**
 * 扩展标准的beanfactorypostProcessor SPI,允许在常规beanfactorypostProcessor检测开始之前注册进一步的bean定义。 (从BeanFactoryPostProcessor继承,可以同时实现两个接口)
 */
public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {

    /**
     * 修改应用程序上下文的内部bean定义注册表,在其标准的初始化之后。所有常规bean定义都将被加载,但是还没有bean被实例化。这允许在下一个后处理阶段开始之前进一步添加bean定义。在 beanfactorypostProcessor之前。
     */
    void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;

}

7,基于spring aop 扩展

AOP:Aspect Oriented Programming 面向切面编程;采用动态代理,在程序运行期间将切面(某段代码,实现通用性功能)织入到指定方法上运行(含前置、后置、返回、异常、环绕五种通知类型,确定了代码织入到目标方法的方式)。通过这种方式,业务逻辑行为和切面处理逻辑独立开来,业务逻辑行为将注意力集中在具体的业务逻辑上,对切面代码和业务逻辑行为代码进行了良好的解耦。

采用spring AOP机制实现功能扩展的实现例子参见:java注解 注解修饰类生效方式二。

在这里简要解析一下AOP是如何在spring框架中是如何生效,以便更好理解spring AOP(spring AOP的实现比较复杂,这里描述整体实现的骨架):

  • spring 注解方式加入AOP模块后,启动时,会往spring容器中注册一个AnnotationAwareAspectJAutoProxyCreator类;
  • AnnotationAwareAspectJAutoProxyCreator类继承自:AbstractAutoProxyCreator;AbstractAutoProxyCreator是一个BeanPostPorcessor同时也实现了BeanFactoryAware;
  • spring在容器准备好后,创建bean,创建bean的过程如下:

    ​1,创建bean实例;

    ​2,给bean属性赋值,注入等;

    ​3,初始化bean:

    ​ 3.1,处理Aware接口类型实现的方法回调;

    ​ 3.2,调用BeanPostProcessor的初始化前置钩子函数;

    ​ 3.3,调用自定义的初始化方法;(@PostConstruct注解等)

    ​ 3.4,调用BeanPostProcessor的初始化后置钩子函数;

  • AnnotationAwareAspectJAutoProxyCreator的钩子函数实现中,完成了如下处理,给目标对象的方法做了代理:

    1,获取所有拦截器,找到哪些切面是要通知到当前bean方法的,并排序;

    2,将bean保存在advisedBeans中;

    3,通过proxyFactory动态创建代理;

    4,给容器中返回当前增强了的代理对象;该代理对象会进入spring容器。

  • 目标方法执行时:

    ​1,方法调用时,代理对象执行方法;调用CglibAopProxy.intercept();

    ​2,职责链的方式进入每一个拦截器依次执行切面功能;

    ​3,实际执行就成了:

    ​ 3.1 前置通知切面依次执行;

    ​ 3.2 目标对象方法执行;

    ​ 3.3 后置通知切面依次执行;

    ​ 3.4 返回通知切面依次执行;

    ​ 3.5 异常通知切面依次执行。

8,基于SPI机制扩展

SPI就是:Service Provider Interface 服务提供者接口。基本含义就是,框架提供接口定义;而不同插件厂商提供具体的实现方案。实际使用时,根据配置,获取实现方案加载、实例化。这样就实现了模块插件的可插拔。SPI在各种框架中是重要的扩展机制,例如常见的dubbo、spring等都提供了自身框架支持的SPI机制。

jdk SPI机制,采用约定的方式,把所有扩展服务实现放在META-INF/services/目录下,读取定义的属性配置文件,获取实际实现类,然后装载、实例化。

在spring提供的SPI机制中,是通过处理器的形式加载,在spring容器准备好,加载了各种自身的processor和外部定义的processor后,refresh方法中,调用了AbstractApplicationContext的refresh方法;如下附代码,在准备好bean工厂,设置好所有beanPostProcessors后,通过invokeBeanFactoryPostProcessors 调用postProcessors;这样就把插件引入了spring。(例如:mybatis插件就是通过BeanDefinitionRegistryPostProcessor引入。)

    public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            // 为刷新、设置其启动日期和活动标志;以及初始化property sourse的初始化上下文准备此上下文。
            prepareRefresh();

            // Tell the subclass to refresh the internal bean factory.
            //刷新内部bean工厂
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

            // 配置beanFactory的上下文环境,Classloader、BeanPostProcessor等。
            prepareBeanFactory(beanFactory);

            try {
                // Allows post-processing of the bean factory in context subclasses.
                //设置了postProcessors;
                postProcessBeanFactory(beanFactory);

                // Invoke factory processors registered as beans in the context.
                //执行BeanFactoryPostProcessors,会调用各种BeanFactoryPostProcessor的实现方法;同时会调用 processConfigBeanDefinitions(registry) --> processDeferredImportSelectors;EnableAutoConfiguration注解中import的ImportSelector被调用执行;
                invokeBeanFactoryPostProcessors(beanFactory);

                // 注册BeanPostProcessor.
                registerBeanPostProcessors(beanFactory);

                // 初始化 message source.
                initMessageSource();

                // 初始化应用程序事件广播。如果上下文中没有定义任何消息,则使用simpleapplicationEv多发广播
                initApplicationEventMulticaster();

                // 初始化themesource、内嵌的servlet 容器.
                onRefresh();

                //将实现了ApplicationListener的bean放入listeners;
                registerListeners();

                // Instantiate all remaining (non-lazy-init) singletons.
                // 完成beanFactory的初始化,初始化所有剩余的单例bean。
                finishBeanFactoryInitialization(beanFactory);

                // Last step: publish corresponding event.
                // 完成刷新,调用LifecycleProcessor的onRefresh方法;并发布contextrefreshedevent事件;
                finishRefresh();
            }

            catch (BeansException ex) {
                if (logger.isWarnEnabled()) {
                    logger.warn("Exception encountered during context initialization - " +
                            "cancelling refresh attempt: " + ex);
                }

                // Destroy already created singletons to avoid dangling resources.
                destroyBeans();

                // Reset 'active' flag.
                cancelRefresh(ex);

                // Propagate exception to caller.
                throw ex;
            }

            finally {
                // Reset common introspection caches in Spring's core, since we
                // might not ever need metadata for singleton beans anymore...
                resetCommonCaches();
            }
        }
    }

9,小结

​ spring还有一些其它扩展方式,比如:基于ApplicationListener进行事件监听扩展等,基本方式类似BeanPostProcessor。基于理解spring启动和容器中各种处理过程后,就可以参照spring中的实现方式,进行自定义的扩展。良好的运用spring注解和扩展能让产品代码优雅且功能强大。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值