360SDN.COM

Spring Framework 5 权威指南(17)

来源:程序员阮威  2017-09-11 13:23:11    评论:0点击:

这是一个Spring 5专题系列,其诞生的动机是:

1.介绍最新版本的Spring Framework;

2.介绍最系统化的Spring Framework;

3.介绍最佳实践的Spring Framework;

10.基于Java的容器配置

基于Java的配置元数据的@Configuration@Bean注解,分别对应于基于XML的配置元数据的<beans/><bean/>元素。@Import注解对应<import/>元素。

最简单的基于Java的配置元数据如下:

@Configuration

public class AppConfig {

 

        @Bean

        publicMyService myService() {

                return newMyServiceImpl();

        }

 

}

等价的基于XML的配置元数据如下:

<beans>

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

</beans>

实例化容器的代码如下:

public static void main(String[] args) {

       ApplicationContext ctx = newAnnotationConfigApplicationContext(AppConfig.class);

        MyServicemyService = ctx.getBean(MyService.class);

       myService.doStuff();

}

也可以使用无参构造方法,然后注册相应的配置元数据:

public static void main(String[] args) {

       AnnotationConfigApplicationContext ctx = newAnnotationConfigApplicationContext();

       ctx.register(AppConfig.class, OtherConfig.class);

       ctx.register(AdditionalConfig.class);

       ctx.refresh();

        MyServicemyService = ctx.getBean(MyService.class);

       myService.doStuff();

}

注意,输入参数除了是标记为@Configuration的类以外,还可以是标记为@Component或者JSR-330的注解的类。

AnnotationConfigApplicationContext还支持组件扫描:

public static void main(String[] args) {

       AnnotationConfigApplicationContext ctx = newAnnotationConfigApplicationContext();

        ctx.scan("com.acme");

       ctx.refresh();

        MyServicemyService = ctx.getBean(MyService.class);

}

注意,@Component@Configuration的元注解,因此也会被扫描到。

Web环境下支持AnnotationConfigWebApplicationContext,配置如下(web.xml)

<web-app>

        <!-- Configure ContextLoaderListener to useAnnotationConfigWebApplicationContext

               instead of the defaultXmlWebApplicationContext -->

        <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 ormore comma- or space-delimited

                fully-qualified @Configurationclasses. 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 usualusing 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 defaultXmlWebApplicationContext -->

                <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 ormore 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 dispatcherservlet -->

        <servlet-mapping>

                <servlet-name>dispatcher</servlet-name>

                <url-pattern>/app/*</url-pattern>

        </servlet-mapping>

</web-app>

默认情况下,@Bean标记的方法的名字即为bean的名字。可以指定为其他的名字:

@Configuration

public class AppConfig {

 

        @Bean(name = "myFoo")

        publicFoo foo() {

                return newFoo();

        }

 

}

还可以为bean指定别名:

@Configuration

public class AppConfig {

 

        @Bean(name = { "dataSource","subsystemA-dataSource", "subsystemB-dataSource" })

        publicDataSource dataSource() {

                // instantiate, configure and return DataSource bean...

        }

 

}

还可以为bean指定描述,用于bean暴露于监控时(例如通过JMX)

@Configuration

public class AppConfig {

 

        @Bean

        @Description("Providesa basic example of a bean")

        publicFoo foo() {

                return newFoo();

        }

 

}

依赖注入形式一:

@Configuration

public class ServiceConfig {

 

        @Bean

        publicTransferService transferService(AccountRepository accountRepository) {

                return newTransferServiceImpl(accountRepository);

        }

 

}

 

@Configuration

public class RepositoryConfig {

 

        @Bean

        publicAccountRepository accountRepository(DataSource dataSource) {

                return newJdbcAccountRepository(dataSource);

        }

 

}

 

@Configuration

@Import({ServiceConfig.class, RepositoryConfig.class})

public class SystemTestConfig {

 

        @Bean

        publicDataSource dataSource() {

                // return new DataSource

        }

 

}

 

public static void main(String[] args) {

        ApplicationContext ctx = newAnnotationConfigApplicationContext(SystemTestConfig.class);

        // everything wires up across configuration classes...

       TransferService transferService = ctx.getBean(TransferService.class);

       transferService.transfer(100.00, "A123", "C456");

}

依赖注入形式二:

@Configuration

public class ServiceConfig {

 

        @Autowired

        privateAccountRepository accountRepository;

 

        @Bean

        publicTransferService transferService() {

                return newTransferServiceImpl(accountRepository);

        }

 

}

 

@Configuration

public class RepositoryConfig {

 

        privatefinal DataSource dataSource;

 

        @Autowired

        publicRepositoryConfig(DataSource dataSource) {

                this.dataSource = dataSource;

        }

 

        @Bean

        publicAccountRepository accountRepository() {

                return newJdbcAccountRepository(dataSource);

        }

 

}

 

@Configuration

@Import({ServiceConfig.class, RepositoryConfig.class})

public class SystemTestConfig {

 

        @Bean

        publicDataSource dataSource() {

                // return new DataSource

        }

 

}

 

public static void main(String[] args) {

       ApplicationContext ctx = newAnnotationConfigApplicationContext(SystemTestConfig.class);

        // everything wires up across configuration classes...

       TransferService transferService = ctx.getBean(TransferService.class);

       transferService.transfer(100.00, "A123", "C456");

}

方法注入:

@Bean

@Scope("prototype")

public AsyncCommand asyncCommand() {

        AsyncCommandcommand = new AsyncCommand();

        // inject dependencies here as required

        return command;

}

 

@Bean

public CommandManager commandManager() {

        // return new anonymous implementation of CommandManagerwith command() overridden

        // to return anew prototype Command object

        return newCommandManager() {

                protectedCommand createCommand() {

                       return asyncCommand();

                }

        }

}

注意,所有标记为@Configuration的类在启动时都会被CGLIB子类化。从Spring3.2开始,CGLIB已经被打包进spring-core.jar里面了。

基于XML的配置元数据和基于Java的配置元数据可以混合使用,即可以在XML文件中定义@Configuration注解标注的类,也可以在@Configuration注解标注的类中导入XML文件:

@Configuration

@ImportResource("classpath:/com/acme/properties-config.xml")

public class AppConfig {

 

        @Value("${jdbc.url}")

        privateString url;

 

        @Value("${jdbc.username}")

        privateString username;

 

        @Value("${jdbc.password}")

        privateString password;

 

        @Bean

        publicDataSource dataSource() {

                return newDriverManagerDataSource(url, username, password);

        }

 

}

除了可以在@Configuration注解标记的类中使用@Bean注解,也可以在@Component注解标注的类中使用@Bean注解进行bean定义。区别是,后者的类不会被CGLIB增强来拦截方法调用。下面定义了一个工厂方法组件:

@Component

public class FactoryMethodComponent{

 

        privatestatic int i;

 

        @Bean

        @Qualifier("public")

        publicTestBean publicInstance() {

                return newTestBean("publicInstance");

        }

 

        // use of a custom qualifier and autowiring of method parameters

        @Bean

        protectedTestBean protectedInstance(

                       @Qualifier("public")TestBean spouse,

                       @Value("#{privateInstance.age}")String country) {

               TestBean tb = new TestBean("protectedInstance", 1);

               tb.setSpouse(spouse);

               tb.setCountry(country);

                return tb;

        }

 

        @Bean

        privateTestBean privateInstance() {

                return newTestBean("privateInstance", i++);

        }

 

        @Bean

        @RequestScope

        publicTestBean requestScopedInstance() {

                return newTestBean("requestScopedInstance", 3);

        }

 

        @Bean @Scope("prototype")

        publicTestBean prototypeInstance(InjectionPoint injectionPoint) {

                return newTestBean("prototypeInstance for "+ injectionPoint.getMember());

        }

 

}

方法级别的注解,如@Qualifier@Lazy@Scope等都可以应用于该定义方法之上。

阅读原文

为您推荐

友情链接 |九搜汽车网 |手机ok生活信息网|ok生活信息网|ok微生活
 Powered by www.360SDN.COM   京ICP备11022651号-4 © 2012-2016 版权