zoukankan      html  css  js  c++  java
  • Spring核心技术(十一)——基于Java的容器配置(一)

    基本概念: @Bean@Configuration

    Spring中新的基于Java的配置的核心就是支持@Configuration注解的类以及@Bean注解的方法。

    @Bean注解用来表示一个方法会实例化,配置,并初始化一个新的由Spring IoC容器所管理的对象。其作用等于XML配置中的<beans>标签下的<bean>子标签。开发者可以用@Bean注解来和任何的Spring@Component来联合使用,但是,最常见的情况下,@Bean注解还是应用到注解了@Configuration的类下面的。

    注解了@Configuration的类就表示这个类的首要目的是用来管理Bean的配置的。而且,@Configuration注解的类允许inter-bean之类的依赖在类中通过方法调用来引用。最简单的配置如下:

    @Configuration
    public class AppConfig {
    
        @Bean
        public MyService myService() {
            return new MyServiceImpl();
        }
    
    }

    上面的配置将等价于如下的XML配置:

    <beans>
        <bean id="myService" class="com.acme.services.MyServiceImpl"/>
    </beans>

    @Configuration对比轻@Bean模式
    @Bean注解的方法声明到了没有配置@Configuration的类中时,这些方法就会作为轻@Bean模式来处理。举例来说,Bean方法如果声明到了@Component注解的类,或者普通的类之中,就是轻@Bean模式。
    和使用@Configuration不同,轻@Bean方法不能够声明inter-bean的依赖,通常一个@Bean方法不应该调用另一个@Bean
    只有在注解了@Configuration的类中使用@Bean方法才是全模式。这回防止很多@Bean方法多次调用,并且消除一些细微的bug。这些bug在轻模式很难被跟踪。

    @Bean@Configuration注解将会在本文中详细讨论,首先我们需要了解各种通过基于Java的配置创建容器的方法。

    实例化Spring容器

    本节描述的是使用Spring的AnnotationConfigApplicationContext。在Spring 3.0后,各式各样的ApplicationContext实现都可用了,不只是@Configuration注解的类,还有一些注解了@Component的组件类以及注解了JSR-330元数据的类等。

    当注解了@Configuration的类作为输入的时候,注解了@Configuration的类本身也会被注册为一个Bean,其中注解了@Bean的方法都会被注册为Bean。

    当注解了@Component的类或者JSR-330的类作为输入的时候,他们同样会被注册为Bean,而且可以通过DI来注入到其他的类里面去,比如通过@Autowired或者@Inject注解。

    简单构造

    在Spring使用XML作为输入的时候,实例的ApplicationContextClassPathXmlApplicationContext,而通过@Configuration注解的类,实例化的ApplicationContextAnnotationConfigApplicationContext。下面的代码可以完全去除XML的配置就使用了Spring容器。

    public static void main(String[] args) {
        ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
        MyService myService = ctx.getBean(MyService.class);
        myService.doStuff();
    }

    如前面所述,AnnotationConfigApplicationContext不仅仅可以和注解了@Configuration的类配合,任何注解了@Component或者是JSR-330的类同样可以作为输入来构造Sprign容器,比如:

    public static void main(String[] args) {
        ApplicationContext ctx = new AnnotationConfigApplicationContext(MyServiceImpl.class, Dependency1.class, Dependency2.class);
        MyService myService = ctx.getBean(MyService.class);
        myService.doStuff();
    }

    上面这段代码让MyServiceImplDependency1以及Dependency2都可以使用Spring的依赖注入注解进行装载,比如@Autowired

    通过register(Class<?>..)来构建容器

    AnnotationConfigApplicationContext类除了通过类来初始化,也可以通过无惨构造函数来进行构造,之后通过register()方法来配置。这种方法在通过编程的方式来构建AnnotationConfigApplicationContext的过程很有用。

    public static void main(String[] args) {
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
        ctx.register(AppConfig.class, OtherConfig.class);
        ctx.register(AdditionalConfig.class);
        ctx.refresh();
        MyService myService = ctx.getBean(MyService.class);
        myService.doStuff();
    }

    使能组件扫描

    使能组件扫描在前文中也略有提及,只需要在@Configuration注解的类上配置即可:

    @Configuration
    @ComponentScan(basePackages = "com.acme")
    public class AppConfig  {
        ...
    }

    在XML等效的配置中,配置如下:

    <beans>
        <context:component-scan base-package="com.acme"/>
    </beans>

    在上面的例子中,com.acme包中的内容会被扫描,来查找其中注解了@Component的类,这些类都会被注册为Spring的Bean实例。AnnotationConfigApplicationContext也可以通过scan(String ...)方法来通过函数调用的方式来进行扫描配置:

    public static void main(String[] args) {
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
        ctx.scan("com.acme");
        ctx.refresh();
        MyService myService = ctx.getBean(MyService.class);
    }

    @Congirufation注解的类和@Component注解的类都是扫描的候选者。在上面的例子中,如果AppConfig类在com.acme包中(或者是其子包之中),AppConfig都会被scan方法扫描到,在refresh()方法调用后,其中的@Bean注解的方法都会作为Bean实例被注册到容器之中。

    对于Web应用的支持

    WebApplicationContext接口关于AnnotationConfigApplicationContext的一个变化的版本就是AnnotationConfigWebApplicationContext。这一实现可以在配置Spring的ContextLoaderListener这个Servlet的listener或者Spring MVC的DispatcherServlet的时候使用。下面就是web.xml中配置Spring MVC程序的一个配置:

    <web-app>
        <!-- Configure ContextLoaderListener to use AnnotationConfigWebApplicationContext
            instead of the default XmlWebApplicationContext -->
        <context-param>
            <param-name>contextClass</param-name>
            <param-value>
                org.springframework.web.context.support.AnnotationConfigWebApplicationContext
            </param-value>
        </context-param>
    
        <!-- Configuration locations must consist of one or more comma- or space-delimited
            fully-qualified @Configuration classes. Fully-qualified packages may also be
            specified for component-scanning -->
        <context-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>com.acme.AppConfig</param-value>
        </context-param>
    
        <!-- Bootstrap the root application context as usual using ContextLoaderListener -->
        <listener>
            <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
        </listener>
    
        <!-- Declare a Spring MVC DispatcherServlet as usual -->
        <servlet>
            <servlet-name>dispatcher</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
            <!-- Configure DispatcherServlet to use AnnotationConfigWebApplicationContext
                instead of the default XmlWebApplicationContext -->
            <init-param>
                <param-name>contextClass</param-name>
                <param-value>
                    org.springframework.web.context.support.AnnotationConfigWebApplicationContext
                </param-value>
            </init-param>
            <!-- Again, config locations must consist of one or more comma- or space-delimited
                and fully-qualified @Configuration classes -->
            <init-param>
                <param-name>contextConfigLocation</param-name>
                <param-value>com.acme.web.MvcConfig</param-value>
            </init-param>
        </servlet>
    
        <!-- map all requests for /app/* to the dispatcher servlet -->
        <servlet-mapping>
            <servlet-name>dispatcher</servlet-name>
            <url-pattern>/app/*</url-pattern>
        </servlet-mapping>
    </web-app>

    使用@Bean注解

    @Bean注解是一个方法级别的注解,和XML中的<bean />标签的功能一直。该注解支持<bean/>的一些配置属性,比如init-method,destroy-method,autowiring以及name等。

    @Bean注解可以在注解了@Configuration或者@Component的类中使用。

    声明Bean

    通过将方法注解@Bean即可声明实例为Bean。开发者通过这个方法将Bean注册到ApplicationContext,方法返回的类型就是Bean的类型。默认情况下,方法的名字就是Bean默认的名字,参考如下Bean的声明:

    @Configuration
    public class AppConfig {
    
        @Bean
        public TransferService transferService() {
            return new TransferServiceImpl();
        }
    }

    上面的代码完全等价于一下XML:

    <beans>
        <bean id="transferService" class="com.acme.TransferServiceImpl"/>
    </beans>

    两种声明都可以在ApplicationContext中声明一个名为transferService的Bean,实例的类型为TransferServiceImpl:

    transferService -> com.acme.TransferServiceImpl

    Bean依赖

    @Bean注解的方法可以有任意数量的参数来描述其依赖。距离来说,如果TransferService的其中一个依赖为AccountRepository的话,我们可以通过方法参数来构造:

    @Configuration
    public class AppConfig {
    
        @Bean
        public TransferService transferService(AccountRepository accountRepository) {
            return new TransferServiceImpl(accountRepository);
        }
    
    }

    解析的机制是和基于构造的依赖注入基本一致的,可以参考前文Spring的依赖及其注入了解相关内容。

    接收生命周期回调

    任何通过@Bean注解了的方法返回的类,都支持常规的生命周期回调,并可以通过使用JSR-250中的@PostContruct以及@PreDestroy注解。

    基本的Spring生命周期也是同样支持的,如果Bean实现了InitializingBeanDisposableBean或者是Lifecycle接口的话,这些方法都会被容器调用。

    标准的*Aware接口比如说BeanFactoryAware,BeanNameAware,MessageSourceAware,ApplicationContextAware等接口也是支持的。
    @Bean也支持指定任意的初始化以及销毁回调函数,跟Spring XML配置中的init-methoddestroy-method属性是一致的:

    public class Foo {
        public void init() {
            // initialization logic
        }
    }
    
    public class Bar {
        public void cleanup() {
            // destruction logic
        }
    }
    
    @Configuration
    public class AppConfig {
    
        @Bean(initMethod = "init")
        public Foo foo() {
            return new Foo();
        }
    
        @Bean(destroyMethod = "cleanup")
        public Bar bar() {
            return new Bar();
        }
    }

    默认情况下,通过Java配置的Bean都会有一个close或者shutdown方法来作为自动的销毁回调。如果开发者声明了close方法或者是shutdown方法,但是不希望由容器来调用的话,可以在注解中标记为@Bean(destroyMethod="")来代替默认的行为。

    当然,在上面的Foo例子当中,也可以直接调用init()方法:

    @Configuration
    public class AppConfig {
        @Bean
        public Foo foo() {
            Foo foo = new Foo();
            foo.init();
            return foo;
        }
    
        // ...
    
    }

    当直接使用Java的配置的时候,开发者可以任意操作对象,而不仅仅是依赖于容器的声明周期

    指定Bean的作用域

    使用@Scope注解

    开发者也可以通过Java配置的方式来指定@Bean的作用域,开发者可以使用所有的标准的作用域,关于作用域的信息,可以在Spring中Bean的作用域一文中了解。

    默认的作用域是singleton,但是开发者可以通过@Scope注解来覆盖掉默认值:

    @Configuration
    public class MyConfiguration {
        @Bean
        @Scope("prototype")
        public Encryptor encryptor() {
            // ...
        }
    }

    @Scope注解以及作用域代理

    Sprnig针对那些作用域的依赖是通过代理来工作的。在XML中,可以通过使用<aop:scoped-proxy/>标签来达到这个目的。通过Java配置的的@Scope注解也提供等价的支持,默认的没有代理配置为ScopedProxyMode.NO,也可以指定为ScopedProxyMode.TARGET_CLASS或者ScopedProxyMode.INTERFACES.

    Java配置如下:

    // an HTTP Session-scoped bean exposed as a proxy
    @Bean
    @SessionScope
    public UserPreferences userPreferences() {
        return new UserPreferences();
    }
    
    @Bean
    public Service userService() {
        UserService service = new SimpleUserService();
        // a reference to the proxied userPreferences bean
        service.setUserPreferences(userPreferences());
        return service;
    }

    自定义Bean的名字

    默认情况下,配置类会读取@Bean方法中的方法的名字值作为Bean的名字。当然可以通过name属性来覆盖这个功能。

    @Configuration
    public class AppConfig {
        @Bean(name = "myFoo")
        public Foo foo() {
            return new Foo();
        }
    }

    Bean的别名

    前文之中有针对Bean名字的描述,有时候会给一个Bean多个名字,作为Bean的别名,@Bean注解的name属性也支持Spring数组类型的值:

    @Configuration
    public class AppConfig {
    
        @Bean(name = { "dataSource", "subsystemA-dataSource", "subsystemB-dataSource" })
        public DataSource dataSource() {
            // instantiate, configure and return DataSource bean...
        }
    
    }

    Bean的描述

    有的时候为Bean提供额外的文本描述可以让别人更了解该Bean的作用。这一点尤其在监视容器中的Bean的时候很有效。

    可以通过使用@Description注解来做到:

    @Configuration
    public class AppConfig {
    
        @Bean
        @Description("Provides a basic example of a bean")
        public Foo foo() {
            return new Foo();
        }
    
    }
  • 相关阅读:
    微服务架构
    SQL语句优化方法30例(转)
    Repeater后台取前台textbox值
    JAVAJDK环境变量的配置(转)
    [转]javascript把数字格式化转换成货币型
    改变ascx的pageLoad和页面的pageLoad执行顺序(转)
    关于在Webservice里使用LinqToSQL遇到一对多关系的父子表中子表需要ToList输出泛型而产生循环引用错误的解决办法!(转)
    兼容IE,FILEFOX的JS 全选(转)
    Android xml 布局(转)
    LINQ to SQL Transaction(转)
  • 原文地址:https://www.cnblogs.com/qitian1/p/6461546.html
Copyright © 2011-2022 走看看