Spring学习-Spring核心技术(三)


读Spring框架官方文档记录。
主要讲如何在Java代码中使用注解来配置Spring容器。

1. 基本概念:@Bean和@Configuration

@Bean用于方法上,表示该方法实例化、配置并初始化一个被Spring容器管理新的对象,与XML配置中<bean/>标签的功能相同。@Bean可用在@Component注解的类方法中,但是最常用的还是@Configuration注解的类的方法中。
@Configuration用于类上,表示该类是可用于bean定义的类。而且,@Configuration类允许通过调用同一类中的其他@Bean方法来定义bean间的依赖关系。@Configuration使用举例如下:

@Configuration
public class AppConfig {

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

等价的xml配置如下:

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

@Bean用在@Configuration类中和用在@Component类/其它类中区别是什么
用在@Component/其他类中不能定义内部bean依赖,相反,它们对其包含组件的内部状态进行操作,还可以选择对它们可能声明的参数进行操作。这样的@Bean方法不能调用其它的@Bean方法。每个这样的方法字面上只是特定bean引用的一个工厂方法,没有任何特殊的运行时语义。这样的方法在运行时不需要应用CGLIB的子类,所以在类设计方面没有限制(也就是说,包含的类可以是final类等等)。
@Configuration类中使用@Bean是比较常用的方式,跨方法的引用也会因此被重定向到容器的生命周期管理中,CGLIB代理会减少难以追踪的bug。

2. 使用AnnotationConfigApplicationContext实例化Spring容器

AnnotationConfigApplicationContext是ApplicationContext的一个实现,能够处理@Configuration注解的类、@Component注解的类以及JSR-330元数据注解的类。

  • 当@Configuration类作为输入的时候,@Configuration类本身被注册为bean定义,同时,所有@Bean注解的方法也都被注册为bean定义。
  • 当@Component或JSR-330类作为输入时,类本身被注册为bean定义,并假设DI元数据如@Autowired/@Inject会在必要的时候在这些类中使用。

(1) 简单的构建

与实例化ClassPathXmlApplicationContext使用Sping XML文件的方式非常相似,当实例化AnnotationConfigApplicationContext时,可以使用@Configuration类作为输入,可以完全不使用XML。举例如下:

public static void main(String[] args) {
    ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
    MyService myService = ctx.getBean(MyService.class);
    myService.doStuff();
}
//@Component及JSR-330注解的类也可以,假设MyServiceImpl,Dependency1,Dependency2由@Autowired注入。
public static void main(String[] args) {
    ApplicationContext ctx = new AnnotationConfigApplicationContext(MyServiceImpl.class, Dependency1.class, Dependency2.class);
    MyService myService = ctx.getBean(MyService.class);
    myService.doStuff();
}

(2) 通过使用register(Class<?>…)以编程方式构建容器

可以通过一个无参构造实例化AnnotationConfigApplicationContext,并使用register()方法进行配置。

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();
}

(3) 使用scan(String…)开启组件扫描

可以使用@ComponentScan开启组件扫描,举例如下:

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

等价的xml配置

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

或者也可以使用AnnotationConfigApplicationContext的scan方法开启组件扫面功能

public static void main(String[] args) {
    AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
    ctx.scan("com.acme");
    ctx.refresh();//com.acme包及其子包中所有的@Bean方法都被处理并注册为容器内的bean定义。
    MyService myService = ctx.getBean(MyService.class);
}

(4) 使用AnnotationConfigWebApplicationContext以支持Web应用

当配置Spring ContextLoaderListener servlet 监听者、Spring MVC DispatcherServlet等可用AnnotationConfigWebApplicationContext。下面的web.xml片段配置了一个典型的Spring MVC web应用程序(注意使用了contextClass context-param和init-param)

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

3. 使用@Bean注解

@Bean是一个方法级注解,类似于XML中的<bean/>标签,它支持一些属性,如init-method,destroy-method,autowiring等。
@Bean可以被用在@Component注解的类或@Configuration注解的类中。

(1) 声明Bean

可以在一个方法上使用@Bean注解,来声明一个bean。可以使用此方法在容器中注册一个bean定义,该定义的类型指定为方法的返回值。默认情况下bean名和方法名相同。

@Configuration
public class AppConfig {

    @Bean
    public TransferServiceImpl transferService() {
        return new TransferServiceImpl();
    }
    //也可以使用接口或者基类作为方法的返回类型,但是使用接口类型会限制类型预测。且由于非延迟的单例bean依据声明的顺序被实例化,所以其它组件想匹配子类的时候可能匹配不到。
    @Bean
    public TransferService transferService() {
        return new TransferServiceImpl();
    }
}

等价的xml配置

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

(2) Bean依赖

使用@Bean注解的方法可以由任意个参数来提供给bean的构造,解析机智与基于构造器的依赖注入十分相似。如下所示:

@Configuration
public class AppConfig {

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

(3) 生命周期调用

有@Bean注解的任何类都支持常规的生命周期调用,且可以使用@PostConstruct和@PreDestory注解。如果一个bean实现了InitializingBean、DisposableBean或LifeCyle,对应的方法都会被容器调用。标准的*ware接口(如: BeanFactoryAware, BeanNameAware, MessageSourceAware, ApplicationContextAware等)也都支持。
@Bean注解支持定制任意吃实话和销毁回调方法,如XML中的init-method以及destory-method属性,举例如下:

public class BeanOne {

    public void init() {
        // initialization logic
    }
}

public class BeanTwo {

    public void cleanup() {
        // destruction logic
    }
}

@Configuration
public class AppConfig {

    @Bean(initMethod = "init")
    public BeanOne beanOne() {
        return new BeanOne();
    }

    @Bean(destroyMethod = "cleanup")
    public BeanTwo beanTwo() {
        return new BeanTwo();
    }
}

//直接调用init方法也是可以的
@Configuration
public class AppConfig {

    @Bean
    public BeanOne beanOne() {
        BeanOne beanOne = new BeanOne();
        beanOne.init();
        return beanOne;
    }

    // ...
}

默认情况下,有公共的close/shutdown方法的Java配置的bean会自动使用销毁回调。如果希望禁止自动使用销毁回调,可以使用@Bean(destoryMethod=""),这只能针对DataSource来使用,因为Java引用服务器使用其它的会有点问题。

@Bean(destroyMethod="")
public DataSource dataSource() throws NamingException {
    return (DataSource) jndiTemplate.lookup("MyDS");
}

同样,对于@Bean方法,通常使用编程JNDI查找,通过使用Spring的JndiTemplate或JndiLocatorDelegate帮助程序或直接使用JNDI InitialContext,但不使用JndiObjectFactoryBean变体(这将迫使将返回类型声明为FactoryBean类型,而不是实际的目标类型,使它更难用于在其他@Bean方法中引用这里提供的资源的交叉引用调用) (作为一个小辣鸡,并不懂这里的JNDI是个啥,等我啥时候用到再学┑( ̄Д  ̄)┍)。

(4) 指定bean的作用域

可以使用@Scope注解,默认的作用域是singleton,使用举例如下:

@Configuration
public class MyConfiguration {

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

对于Web类型的作用域涉及的作用域依赖问题依赖Web容器作用域bean,使用Java方式也可也解决。@Scope中的proxyMode属性可达到和<aop:scoped-proxy/>一样的作用。默认是无代理(ScopedProxyMode.NO),也可以使用ScopedProxyMode.TARGET_CLASS 或ScopedProxyMode.INTERFACES。以下是使用举例:

// an HTTP Session-scoped bean exposed as a proxy
@Bean
@SessionScope //SessionScope中定义了ProxyMode属性
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;
}

(5) 自定义Bean的名称

默认情况下,配置类会使用@Bean方法的方法名作为生成的bean的名称。通过@Bean的name属性也可对其进行更改,使用举例如下:

@Configuration
public class AppConfig {

    @Bean(name = "myThing")
    public Thing thing() {
        return new Thing();
    }
}

(6) 设置Bean的别名

有时候可能要给一个bean多个名字,@Bean的name属性可以接收一个字符串数字为bean提供不同的名字。使用举例如下:

@Configuration
public class AppConfig {

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

(7) 设置Bean的描述

可以使用@Description注解,来描述bean,使用举例如下:

@Configuration
public class AppConfig {

    @Bean
    @Description("Provides a basic example of a bean")
    public Thing thing() {
        return new Thing();
    }
}

4. 使用@Configuration注解

@Configuration是个类级别的注解,表名对象是bean定义的一个源。@Configuration类通过公共的@Bean注解方法声明bean。

(1) 注入bean之间的依赖

当一个bean依赖其它bean,表示这种依赖就像一个bean的方法调用另一个bean的方法一样简单,如下所示:

@Configuration
public class AppConfig {

    @Bean
    public BeanOne beanOne() {
        return new BeanOne(beanTwo());
    }

    @Bean
    public BeanTwo beanTwo() {
        return new BeanTwo();
    }
}

beanOne通过构造器注入获得beanTwo的引用。注意这种声明bean之间的依赖关系只在@Configuration类中管用,在@Component类中不能这么使用。

(2) 查找(Lookup)方法注入

单例作用域bean依赖原型作用域bean,就可以使用查找方法注入。基于Java的配置如下所示:

public abstract class CommandManager {
    public Object process(Object commandState) {
        // grab a new instance of the appropriate Command interface
        Command command = createCommand();
        // set the state on the (hopefully brand new) Command instance
        command.setState(commandState);
        return command.execute();
    }

    // okay... but where is the implementation of this method?
    protected abstract Command createCommand();
}
// 使用Java配置,可以创建CommandManager的子类,createCommand()方法能够被重写,就可以找到一个新的command对象了,如下所示
@Bean
@Scope("prototype")
public AsyncCommand asyncCommand() {
    AsyncCommand command = new AsyncCommand();
    // inject dependencies here as required
    return command;
}

@Bean
public CommandManager commandManager() {
    // return new anonymous implementation of CommandManager with createCommand()
    // overridden to return a new prototype Command object
    return new CommandManager() {//这儿实现了匿名类,new了一个匿名类的对象,匿名类里面重写了createCommand方法。
        protected Command createCommand() {
            return asyncCommand();
        }
    }
}

(3) 关于基于java的配置如何在内部工作的进一步探讨

考虑如下例子,@Bean注解方法被调用两次

@Configuration
public class AppConfig {

    @Bean
    public ClientService clientService1() {
        ClientServiceImpl clientService = new ClientServiceImpl();
        clientService.setClientDao(clientDao());
        return clientService;
    }

    @Bean
    public ClientService clientService2() {
        ClientServiceImpl clientService = new ClientServiceImpl();
        clientService.setClientDao(clientDao());
        return clientService;
    }

    @Bean
    public ClientDao clientDao() {
        return new ClientDaoImpl();
    }
}

ClientDao在clientService1和clientService2中分别被调用一次。由于该方法返回ClientDaoImpl创建一个实例并返回,所以我们希望能有两个实例,但是在Spring默认是单例作用域。注意:所有@Configuration类都是在启动时用CGLIB子类化的。在子类中,子类的方法在调用父类方法前会首先检查是否有任何缓存的beans,然后创建新的实例。跟平常的单例bean的会不太一样。
注意:由于CGLIB在启动时动态增加的特征,配置类不能是final类型。如果不想使用CGLIB,就可以不在@Configuration类中定义@Bean,而是在其它类如@Component注解的类中使用@Bean。

5. 组合基于Java的配置

可以在基于Java的配置中组合注解,可以降低配置的复杂性。

(1) 使用@Import注解

类似于XML文件中的<import/>,基于Java配置中可以使用@Import注解从另一个配置类中加载Bean定义。举例如下:

@Configuration
public class ConfigA {

    @Bean
    public A a() {
        return new A();
    }
}

@Configuration
@Import(ConfigA.class)
public class ConfigB {

    @Bean
    public B b() {
        return new B();
    }
}

在实例化容器时,只需要ConfigB显式的声明即可

public static void main(String[] args) {
    ApplicationContext ctx = new AnnotationConfigApplicationContext(ConfigB.class);//这里只需要添加ConfigB.class

    // now both beans A and B will be available...
    A a = ctx.getBean(A.class);
    B b = ctx.getBean(B.class);
}

这种方法简化了容器实例化,因为只需要处理一个类,而不需要在构造期间记住大量的@Configuration类。
从Spring框架4.2开始,@Import也支持对常规组件类的引用,类似于AnnotationConfigApplicationContext.register方法。如果希望通过使用几个配置类作为入口点来显式定义所有组件,从而避免组件扫描,那么这一点特别有用。

1) 对import的@Bean定义注入依赖

@Import导入的bean定义可能会依赖其它bean,可以在@Bean注解的方法参数中完成依赖注入,举例如下:

@Configuration
public class ServiceConfig {

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

@Configuration
public class RepositoryConfig {

    @Bean
    public AccountRepository accountRepository(DataSource dataSource) {
        return new JdbcAccountRepository(dataSource);
    }
}

@Configuration
@Import({ServiceConfig.class, RepositoryConfig.class})
public class SystemTestConfig {

    @Bean
    public DataSource dataSource() {
        // return new DataSource
    }
}

public static void main(String[] args) {
    ApplicationContext ctx = new AnnotationConfigApplicationContext(SystemTestConfig.class);
    // everything wires up across configuration classes...
    TransferService transferService = ctx.getBean(TransferService.class);
    transferService.transfer(100.00, "A123", "C456");
}

由于@Configuration注解的类在容器中最终会被处理成一个bean,因此可以用@Autowired,@Value等注解完成依赖注入。但是要注意由于@Configuration在容器中会被很早的初始化,所以这种强制的依赖注入会导致有些bean过早的初始化,这是不希望看到的。所以应该尽量使用第一种方式。举例如下:

@Configuration
public class ServiceConfig {

    @Autowired
    private AccountRepository accountRepository;

    @Bean
    public TransferService transferService() {
        return new TransferServiceImpl(accountRepository);
    }
}

@Configuration
public class RepositoryConfig {

    private final DataSource dataSource;

    public RepositoryConfig(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    @Bean
    public AccountRepository accountRepository() {
        return new JdbcAccountRepository(dataSource);
    }
}

@Configuration
@Import({ServiceConfig.class, RepositoryConfig.class})
public class SystemTestConfig {

    @Bean
    public DataSource dataSource() {
        // return new DataSource
    }
}

public static void main(String[] args) {
    ApplicationContext ctx = new AnnotationConfigApplicationContext(SystemTestConfig.class);
    // everything wires up across configuration classes...
    TransferService transferService = ctx.getBean(TransferService.class);
    transferService.transfer(100.00, "A123", "C456");
}

注意:

  • 对于通过@Bean进行的BeanPostProcessor和BeanFactoryPostProcessor定义需要声明为static @Bean方法。否则,@Autowired和@Value可能对配置类本身不起作用,因为有可能在AutowiredAnnotationBeanPostProcessor之前将其创建为bean实例。
  • @Configuration类中的构造器注入仅仅在Spring框架4.3中支持,如果目标bean只定义了一个构造器,就没有必须要使用@Autowired,如上面例子中的RepositoryConfig类中。
2) 对import的bean进行完全限定以便更容易的查找

前面的例子中使用@Autowired能够很好的完成工作,但是bean定义还是有一定的模糊性,比如开发者在看ServiceConfig时,并不知道@Autowired AccountRepository bean在哪里声明。当然这可以通过IDE的一些功能来看到bean之间的相互依赖图。我们也可以@Autowired配置类本身,从而有从一个@Configuration类到另一个@Configuration类的直接导航。

@Configuration
public class ServiceConfig {

    @Autowired
    private RepositoryConfig repositoryConfig;//注意:这里配置的是RepositoryConfig本身

    @Bean
    public TransferService transferService() {
        // navigate 'through' the config class to the @Bean method!
        return new TransferServiceImpl(repositoryConfig.accountRepository());
    }
}

在上面的例子中,AccoutRepository在哪里配置是很明确的。但是ServiceConfig与RepositoryConfig紧密耦合在一块。通过使用基于接口或基于抽象类的@Configuration类,可以在一定程度上缓解这种紧密耦合。如下面例子所示:

@Configuration
public class ServiceConfig {

    @Autowired
    private RepositoryConfig repositoryConfig;

    @Bean
    public TransferService transferService() {
        return new TransferServiceImpl(repositoryConfig.accountRepository());
    }
}

@Configuration
public interface RepositoryConfig {

    @Bean
    AccountRepository accountRepository();
}

@Configuration
public class DefaultRepositoryConfig implements RepositoryConfig {

    @Bean
    public AccountRepository accountRepository() {
        return new JdbcAccountRepository(...);
    }
}

@Configuration
@Import({ServiceConfig.class, DefaultRepositoryConfig.class})  // import the concrete config!
public class SystemTestConfig {

    @Bean
    public DataSource dataSource() {
        // return DataSource
    }

}

public static void main(String[] args) {
    ApplicationContext ctx = new AnnotationConfigApplicationContext(SystemTestConfig.class);
    TransferService transferService = ctx.getBean(TransferService.class);
    transferService.transfer(100.00, "A123", "C456");
}

(2) 选择性的包含@Configuration类或@Bean方法

根据系统状态,选择性的地启用或禁用一个完整的@Configuration类,甚至单个的@Bean方法是非常有用的。一个很常见的例子就是当在Spring Environment中启用了特定的profile时去使用@Profile注解来激活Bean。@Conditional是@Profile的一个灵活的实现类。@Conditional表示特定的org.springframework.context.annotation.Condition部署应该在@Bean注册之前实现。Condition接口的实现提供了一个matches()方法,返回true/false。下面就是mathces方法的一种实现方式:

@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
    // Read the @Profile annotation attributes
    MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(Profile.class.getName());
    if (attrs != null) {
        for (Object value : attrs.get("value")) {
            if (context.getEnvironment().acceptsProfiles(((String[]) value))) {
                return true;
            }
        }
        return false;
    }
    return true;
}

(3) 结合Java和XML配置

Spring中对@Configuration类的支持并不是希望完全代替XML。在XML方便或者必要的情况下,我们可以选择以XML为中心来通过ClassPathXmlApplicationContext等实例化容器或者通过Java配置为核心来通过AnnotationConfigApplicationContext实例化容器并通过@ImportResource注解导入必要的XML。

1) 以XML为中心使用@Configuration类

从XML引导Spring容器启动并以一种特别的方式包含@Configuration类可能更可取。例如,在使用Spring XML的大型现有代码库中,更容易根据需要创建@Configuration类,并从现有XML文件中包含它们。
a. 将@Configuration类声明为普通的Spring <bean/>元素
@Configuration最终会是容器中的bean定义。在下面的例子中,我们创建名为AppConfig类的@Configuration类并将配置类包含在system-test-config.xml作为bean定义。因为<context:annotation-config/>被打开了,容器识别@Configuration注解并将AppConfig中的@Bean方法进行合适的处理。举例如下:

//Java中普通的配置类
@Configuration
public class AppConfig {

    @Autowired
    private DataSource dataSource;

    @Bean
    public AccountRepository accountRepository() {
        return new JdbcAccountRepository(dataSource);
    }

    @Bean
    public TransferService transferService() {
        return new TransferService(accountRepository());
    }
}
<!--system-test-config.xml举例-->
<beans>
    <!-- enable processing of annotations such as @Autowired and @Configuration -->
    <context:annotation-config/>
    <context:property-placeholder location="classpath:/com/acme/jdbc.properties"/>

    <bean class="com.acme.AppConfig"/>

    <bean class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>
</beans>
jdbc.properties文件中的内容如下:
jdbc.url=jdbc:hsqldb:hsql://localhost/xdb
jdbc.username=sa
jdbc.password=
public static void main(String[] args) {
    ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:/com/acme/system-test-config.xml");
    TransferService transferService = ctx.getBean(TransferService.class);
    // ...
}

使用 <context:component-scan/>获取@Configuration类
因为@Configuration是用@Component进行元注解的,所以@Configuration类自动成为组件扫描的候选者。使用与前面示例中描述的场景相同的场景,我们可以重新定义system-test-config.xml来利用组件扫描的优势。注意,在本例中,我们不需要显式声明<context:annotation-config/>,因为<context:component-scan/>启用相同的功能。

<!--system-test-config.xml-->
<beans>
    <!-- picks up and registers AppConfig as a bean definition -->
    <context:component-scan base-package="com.acme"/>
    <context:property-placeholder location="classpath:/com/acme/jdbc.properties"/>

    <bean class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>
</beans>
2) 以@Configuration类为中心并通过@ImportResource使用XML

在@Configuration类是配置容器的主要机制的应用程序中,可能仍然需要使用至少一些XML。在这些场景中,您可以使用@ImportResource并只定义所需的XML。这样做可以实现以Java为中心的方法来配置容器,并尽可能少的使用XML。下面的示例(包括一个配置类、一个定义bean的XML文件、一个属性文件和主类)展示了如何使用@ImportResource注释实现以Java为中心的配置,并根据需要使用XML。

@Configuration
@ImportResource("classpath:/com/acme/properties-config.xml")
public class AppConfig {

    @Value("${jdbc.url}")
    private String url;

    @Value("${jdbc.username}")
    private String username;

    @Value("${jdbc.password}")
    private String password;

    @Bean
    public DataSource dataSource() {
        return new DriverManagerDataSource(url, username, password);
    }
}
<!--properties-config.xml-->
<beans>
    <context:property-placeholder location="classpath:/com/acme/jdbc.properties"/>
</beans>
jdbc.properties
jdbc.url=jdbc:hsqldb:hsql://localhost/xdb
jdbc.username=sa
jdbc.password=
public static void main(String[] args) {
    ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
    TransferService transferService = ctx.getBean(TransferService.class);
    // ...
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值