Monday, February 10, 2014

Spring çatısında Olaya Dayalı Programlama

Tasarım yaparken, değişiklikleri daha iyi yönetebilmek amacıyla, sınıflar arasında gevşek bağlı bir yapı oluşturulmaya çalışılır. Bu amaçla öncelikle arayüz (=interface) tanımlanır. Bu arayüz bir tür sözleşme işlevi görür. Hizmet alan ve veren sınıflar biri birlerini doğrudan görmezler. Hizmet sağlayan sınıf bu arayüze ya da sözleşmeye  uygun olarak hizmet verir, istemci sınıf ise yine bu sözleşmeye uygun olarak hizmet alır. Böylelikle, ileride servis sağlayıcı sınıf değiştiğinde ya da başka bir sınıftan hizmet alındığında istemci sınıfta bir değişiklik yapmak gerekmez. Dört kafadarın (=GoF, Gang of Four) yayınladığı tasarım kalıpları incelendiğinde, bu şablon hemen hemen tüm kalıplarda karşımıza çıkar. Gevşek bağlı yapı oluşturmak için düşünülen mimari çözümlerden biri de Olaya Dayalı Programlamadır (Event Driven Programming).  Burada öncelikle olay tanımlanır. Sınıflardan biri belirli bir koşul gerçekleştiğinde olay nesnesini yayınlar. Bu olayın gerçekleşmesini gözlemlemek isteyen sınıflar ise olay için abone olurlar ve olay gerçekleştiğinde kayıtlı tüm nesnelere haber verilir. Bu yapıyı Spring çatısında gerçeklemek oldukça kolaydır. Öncelikle olay sınıfı, LargeAmountTransferEvent, oluşturulur:

package com.example.events;

import org.springframework.context.ApplicationEvent;

public class LargeAmountTransferEvent extends ApplicationEvent {
 private double amount;

 public LargeAmountTransferEvent(double amount, Object source) {
  super(source);
  this.amount = amount;
 }

 public double getAmount() {
  return amount;
 }

}

Gözlemci sınıfı oluşturmak için ilgili sınıfta Spring'in ApplicationListener arayüzünü gerçeklemek yeterli olur:

package com.example.events;

import java.sql.Date;
import java.sql.Timestamp;

import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

@Component
public class LargeAmountTransferListener implements ApplicationListener<LargeAmountTransferEvent>{

 @Override
 public void onApplicationEvent(LargeAmountTransferEvent event) {
  System.out.println("Large amount transfer has occured: "+event.getAmount()+" at "+new Timestamp(event.getTimestamp()));
 }

}

com.example.service.AccountService.java:
package com.example.service;

import com.example.domain.Account;

public interface AccountService {
 boolean transferMoney(Account from,Account to,double amount);
}

Olayın yayınlanacağı sınıfın ise Spring'in ApplicationEventPublisherAware arayüzünü gerçeklemesi gerekir.
Bu arayüzde setApplicationEventPublisher isimli ek bir metot yer alır. Olay ise Spring IoC kabı tarafından sağlanan ApplicationEventPublisher sınıfından bir nesne aracılığı ile yayınlanır:

com.example.service.AccountServiceImpl.java:
package com.example.service;

import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.stereotype.Service;

import com.example.domain.Account;
import com.example.domain.DeficitException;
import com.example.events.LargeAmountTransferEvent;

@Service("accountsrv")
public class AccountServiceImpl implements AccountService,
  ApplicationEventPublisherAware {
 private ApplicationEventPublisher publisher;

 public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
  this.publisher = publisher;
 }

 public boolean transferMoney(Account from, Account to, double amount) {
  if (amount >= 100_000_000)
   publisher.publishEvent(new LargeAmountTransferEvent(amount, this));
  boolean withdraw = false;
  boolean deposit = false;
  try {
   from.withdraw(amount);
   withdraw = true;
   to.deposit(amount);
   deposit = true;
  } catch (Exception e) {
   if (withdraw)
    from.deposit(amount);
   if (deposit)
    try {
     to.withdraw(amount);
    } catch (DeficitException e1) {
     return false;
    }
  }
  return true;
 }
}

com.example.domain.Account.java:
package com.example.domain;

public class Account {
 private String accountId;
 private double balance;

 public Account() {
 }

 public Account(String accountId, double balance) {
  this.accountId = accountId;
  this.balance = balance;
 }

 public String getAccountId() {
  return accountId;
 }

 public double getBalance() {
  return balance;
 }

 public void withdraw(double amount) throws DeficitException {
  if (amount <= 0.0)
   throw new IllegalArgumentException(
     "Amount cannot be negative or zero!");
  if (amount > balance)
   throw new DeficitException(
     "Your balance dows not cover your expenses!", amount
       - balance);
  balance -= amount;
 }

 public void deposit(double amount) {
  if (amount <= 0.0)
   throw new IllegalArgumentException(
     "Amount cannot be negative or zero!");
  balance += amount;
 }

 @Override
 public int hashCode() {
  final int prime = 31;
  int result = 1;
  result = prime * result
    + ((accountId == null) ? 0 : accountId.hashCode());
  return result;
 }

 @Override
 public boolean equals(Object obj) {
  if (this == obj)
   return true;
  if (obj == null)
   return false;
  if (getClass() != obj.getClass())
   return false;
  Account other = (Account) obj;
  if (accountId == null) {
   if (other.accountId != null)
    return false;
  } else if (!accountId.equals(other.accountId))
   return false;
  return true;
 }

 @Override
 public String toString() {
  return "Account [accountId=" + accountId + ", balance=" + balance + "]";
 }
}

com.example.domain.DeficitException.java:
package com.example.domain;

public class DeficitException extends Exception {
 private double deficit;

 public DeficitException(String message, double deficit) {
  super(message);
  this.deficit = deficit;
 }

 public double getDeficit() {
  return deficit;
 } 
}

com.example.config.AppConfig.java:
package com.example.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

import com.example.domain.Account;

@Configuration
@ComponentScan({"com.example.service","com.example.events"})
public class AppConfig {
 @Bean
 public Account acc1(){
  return new Account("TR123456789", 100_000_000);
 }
 @Bean
 public Account acc2(){
  return new Account("TR987654321", 250_000_000);
 }
}

com.example.TestAppConfig.java:
package com.example;

import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.example.config.AppConfig;
import com.example.domain.Account;
import com.example.service.AccountService;

public class TestAppConfig {
 public static void main(String[] args) {
  ConfigurableApplicationContext context=
   new AnnotationConfigApplicationContext(AppConfig.class);
  AccountService accountSrv=
    (AccountService) context.getBean("accountsrv");
  Account account1= context.getBean("acc1",Account.class);
  Account account2= context.getBean("acc2",Account.class);
  accountSrv.transferMoney(account1, account2, 102500000);
  context.close();    
 }
}

Uygulamayı çalıştığımızda aşağıdaki gibi ekran görüntüsü oluşmaktadır:

Feb 10, 2014 12:19:15 AM org.springframework.context.support.AbstractApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@33d4f6b4: startup date [Mon Feb 10 00:19:15 EET 2014]; root of context hierarchy
Feb 10, 2014 12:19:15 AM org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@4a2c1b1c: defining beans [org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,appConfig,org.springframework.context.annotation.ConfigurationClassPostProcessor.importAwareProcessor,accountsrv,largeAmountTransferListener,acc1,acc2]; root of factory hierarchy
Large amount transfer has occured: 1.025E8 at 2014-02-10 00:19:15.99
Feb 10, 2014 12:19:15 AM org.springframework.context.support.AbstractApplicationContext doClose
INFO: Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@33d4f6b4: startup date [Mon Feb 10 00:19:15 EET 2014]; root of context hierarchy
Feb 10, 2014 12:19:15 AM org.springframework.beans.factory.support.DefaultSingletonBeanRegistry destroySingletons
INFO: Destroying singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@4a2c1b1c: defining beans [org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,appConfig,org.springframework.context.annotation.ConfigurationClassPostProcessor.importAwareProcessor,accountsrv,largeAmountTransferListener,acc1,acc2]; root of factory hierarchy

Uygulamanın kaynak koduna bu adresten maven projesi olarak erişebilirsiniz.

Sunday, February 9, 2014

Spring çatısında Profile kullanımı

Spring 3.1 ile gelen yeniliklerden biri, değişik ortamlar için farklı ayarlar kullanmamıza olanak sağlayan profil tanımlamasıdır. Yazılım bir dizi etkinlik sonucu ortaya çıkar. Yazılım yaşam döngüsü olarak adlandırılan bu süreçte, yazılımın farklı ortamlarda çalıştırılır: geliştirme ortamı, test ortamı, üretim ortamı. Bu farklı ortamların ayarlarını her bir ortam için geliştirdiğimiz profillerde tanımlıyoruz. Profilleri Spring çatısının XML tabanlı yapılandırma dosyasında tanımlayabileceğimiz gibi @Profile damgasını kullanarak Java tabanlı olarak da tanımlayabiliriz.

1. XML tabanlı yapılandırma dosyasında profil tanımlama

spring-config.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
 xmlns:util="http://www.springframework.org/schema/util"
 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
  http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.2.xsd
  http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd">
 <beans profile="prod">
  <context:component-scan base-package="com.example.service" />
  <context:component-scan base-package="com.example.dao">
    <context:exclude-filter type="regex" expression=".*InMemory.*"/>
  </context:component-scan>
  <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
   <property name="dataSource" ref="dataSource" />
  </bean>
  <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
   <property name="driverClassName" value="com.mysql.jdbc.Driver" />
   <property name="url" value="jdbc:mysql://localhost:3306/customerdb" />
   <property name="username" value="root" />
   <property name="password" value="root" />
  </bean>
 </beans>
 <beans profile="dev">
  <context:annotation-config />
  <context:component-scan base-package="com.example.service" />
  <context:component-scan base-package="com.example.dao">
    <context:exclude-filter type="regex" expression=".*Jdbc.*"/>  
  </context:component-scan>
  <bean id="123456789101" class="com.example.domain.Customer">
   <property name="firstName" value="Jack" />
   <property name="lastName" value="Shephard" />
   <property name="identityNo" value="123456789101" />
  </bean>
  <bean id="109876543210" class="com.example.domain.Customer">
   <property name="firstName" value="Kate" />
   <property name="lastName" value="Austen" />
   <property name="identityNo" value="109876543210" />
  </bean>
 </beans>
</beans>

com.example.domain.Customer.java:
package com.example.domain;

public class Customer {
 private String firstName;
 private String lastName;
 private String identityNo;

 public Customer() {
 }

 public String getFirstName() {
  return firstName;
 }

 public void setFirstName(String firstName) {
  this.firstName = firstName;
 }

 public String getLastName() {
  return lastName;
 }

 public void setLastName(String lastName) {
  this.lastName = lastName;
 }

 public String getIdentityNo() {
  return identityNo;
 }

 public void setIdentityNo(String identityNo) {
  this.identityNo = identityNo;
 }

 @Override
 public String toString() {
  return "Customer [firstName=" + firstName + ", lastName=" + lastName
    + ", identityNo=" + identityNo + "]";
 }

 @Override
 public int hashCode() {
  final int prime = 31;
  int result = 1;
  result = prime * result
    + ((identityNo == null) ? 0 : identityNo.hashCode());
  return result;
 }

 @Override
 public boolean equals(Object obj) {
  if (this == obj)
   return true;
  if (obj == null)
   return false;
  if (getClass() != obj.getClass())
   return false;
  Customer other = (Customer) obj;
  if (identityNo == null) {
   if (other.identityNo != null)
    return false;
  } else if (!identityNo.equals(other.identityNo))
   return false;
  return true;
 }
}

com.example.dao.CustomerDao.java:
package com.example.dao;

import com.example.domain.Customer;

public interface CustomerDao {
 void addCustomer(Customer customer);
 void deleteCustomer(String id);
 void updateCustomer(Customer customer);
 Customer findCustomerById(String id);
}

com.example.dao.CustomerInMemoryDao.java:
package com.example.dao;

import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Repository;
import com.example.domain.Customer;

@Repository("custdao")
@Profile("dev")
public class CustomerInMemoryDao implements CustomerDao {
 @Autowired
 private Map<String, Customer> customers;

 public CustomerInMemoryDao() {
 }

 public void setCustomers(Map<String, Customer> customers) {
  this.customers = customers;
 }

 public void addCustomer(Customer customer) {
  customers.put(customer.getIdentityNo(), customer);
 }

 public void deleteCustomer(String id) {
  customers.remove(id);
 }

 public void updateCustomer(Customer customer) {
  if (customers.containsKey(customer.getIdentityNo())) {
   customers.put(customer.getIdentityNo(), customer);
  }
 }

 public Customer findCustomerById(String id) {
  return customers.get(id);
 }
}

com.example.dao.CustomerJdbcDao.java:
package com.example.dao;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Profile;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import com.example.domain.Customer;

@Repository("custdao")
@Profile("prod")
public class CustomerJdbcDao implements CustomerDao {
 private static final String SQL_INSERT_CUSTOMER = "INSERT INTO CUSTOMERS(IDENTITYNO,FIRSTNAME,LASTNAME) VALUES ( ? , ? , ?)";
 private static final String SQL_SELECT_CUSTOMER_BY_ID = "SELECT * FROM CUSTOMERS WHERE IDENTITYNO=?";
 private static final String SQL_DELETE_CUSTOMER_BY_ID = "DELETE FROM CUSTOMERS WHERE IDENTITYNO=?";
 private static final String SQL_UPDATE_CUSTOMER_BY_ID = "UPDATE CUSTOMERS SET FIRSTNAME = ? , LASTNAME = ? WHERE IDENTITYNO = ?";

 @Autowired
 private JdbcTemplate jdbcTemplate;

 public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
  this.jdbcTemplate = jdbcTemplate;
 }

 public CustomerJdbcDao() {
 }

 @Override
 public void addCustomer(Customer customer) {
  jdbcTemplate.update(
    SQL_INSERT_CUSTOMER,
    new Object[] { customer.getIdentityNo(),
      customer.getFirstName(), customer.getLastName() });
 }

 @Override
 public void deleteCustomer(String id) {
  jdbcTemplate.update(SQL_DELETE_CUSTOMER_BY_ID, new Object[] { id });
 }

 @Override
 public void updateCustomer(Customer customer) {
  jdbcTemplate.update(SQL_UPDATE_CUSTOMER_BY_ID,
    new Object[] { customer.getFirstName(), customer.getLastName(),
      customer.getIdentityNo() });
 }

 @Override
 public Customer findCustomerById(String id) {
  return jdbcTemplate.queryForObject(SQL_SELECT_CUSTOMER_BY_ID,
    new Object[] { id }, new BeanPropertyRowMapper<Customer>(
      Customer.class));
 }
}

com.example.TestXmlConfigProfileApp.java:
package com.example;

import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.example.dao.CustomerDao;
import com.example.domain.Customer;

public class TestXmlConfigProfileApp {
 public static void main(String[] args) {
  System.setProperty("spring.profiles.active", "prod");
  ConfigurableApplicationContext context = new ClassPathXmlApplicationContext(
    "spring-config.xml");
  CustomerDao custDao = (CustomerDao) context.getBean("custdao");
  Customer jack = custDao.findCustomerById("123456789101");
  System.out.println(jack);
  Customer kate = custDao.findCustomerById("109876543210");
  System.out.println(kate);
  context.close();
 }
}

Uygulamayı "prod" profilinde çalıştırdığımızda oluşan ekran görüntüsü aşağıda verilmiştir:
Feb 09, 2014 3:10:12 AM org.springframework.context.support.AbstractApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@2ef7d41f: startup date [Sun Feb 09 03:10:12 EET 2014]; root of context hierarchy
Feb 09, 2014 3:10:12 AM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [spring-config.xml]
Feb 09, 2014 3:10:13 AM org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@49ea903c: defining beans [customerSrv,org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,custdao,jdbcTemplate,dataSource,org.springframework.context.annotation.ConfigurationClassPostProcessor.importAwareProcessor]; root of factory hierarchy
Customer [firstName=Jin, lastName=Kwon, identityNo=123456789101]
Customer [firstName=Sun, lastName=Kwon, identityNo=109876543210]
Feb 09, 2014 3:10:13 AM org.springframework.context.support.AbstractApplicationContext doClose
INFO: Closing org.springframework.context.support.ClassPathXmlApplicationContext@2ef7d41f: startup date [Sun Feb 09 03:10:12 EET 2014]; root of context hierarchy
Feb 09, 2014 3:10:13 AM org.springframework.beans.factory.support.DefaultSingletonBeanRegistry destroySingletons
INFO: Destroying singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@49ea903c: defining beans [customerSrv,org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,custdao,jdbcTemplate,dataSource,org.springframework.context.annotation.ConfigurationClassPostProcessor.importAwareProcessor]; root of factory hierarchy

Uygulamayı "dev" profilinde çalıştırdığımızda oluşan ekran görüntüsü aşağıda verilmiştir:
Feb 09, 2014 3:14:41 AM org.springframework.context.support.AbstractApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@2ef7d41f: startup date [Sun Feb 09 03:14:40 EET 2014]; root of context hierarchy
Feb 09, 2014 3:14:41 AM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [spring-config.xml]
Feb 09, 2014 3:14:41 AM org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@6cf49909: defining beans [org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,customerSrv,custdao,123456789101,109876543210,org.springframework.context.annotation.ConfigurationClassPostProcessor.importAwareProcessor]; root of factory hierarchy
Customer [firstName=Jack, lastName=Shephard, identityNo=123456789101]
Customer [firstName=Kate, lastName=Austen, identityNo=109876543210]
Feb 09, 2014 3:14:42 AM org.springframework.context.support.AbstractApplicationContext doClose
INFO: Closing org.springframework.context.support.ClassPathXmlApplicationContext@2ef7d41f: startup date [Sun Feb 09 03:14:40 EET 2014]; root of context hierarchy
Feb 09, 2014 3:14:42 AM org.springframework.beans.factory.support.DefaultSingletonBeanRegistry destroySingletons
INFO: Destroying singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@6cf49909: defining beans [org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,customerSrv,custdao,123456789101,109876543210,org.springframework.context.annotation.ConfigurationClassPostProcessor.importAwareProcessor]; root of factory hierarchy

2. @Profile damgasını kullanarak Java tabanlı profil tanımlama

com.example.AppConfig.java:
package com.example.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.Profile;

@Configuration
@Profile({"prod","dev"})
@Import(value = { AppConfigDev.class, AppConfigProd.class })
public class AppConfig {
}

com.example.config.AppConfigDev.java:
package com.example.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import com.example.domain.Customer;

@Configuration
@Profile("dev")
@ComponentScan({ "com.example.service", "com.example.dao" })
public class AppConfigDev {
 @Bean(name = "123456789101")
 public Customer customer1() {
  Customer cust = new Customer();
  cust.setFirstName("Jack");
  cust.setLastName("Shephard");
  cust.setIdentityNo("123456789101");
  return cust;
 }

 @Bean(name = "109876543210")
 public Customer customer2() {
  Customer cust = new Customer();
  cust.setFirstName("Kate");
  cust.setLastName("Austen");
  cust.setIdentityNo("109876543210");
  return cust;
 }
}

com.example.config.AppConfigProd.java:
package com.example.config;

import org.apache.commons.dbcp.BasicDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.jdbc.core.JdbcTemplate;

@Configuration
@Profile("prod")
@ComponentScan({ "com.example.service", "com.example.dao" })
public class AppConfigProd {
 @Bean
 public JdbcTemplate jdbcTemplate() {
  BasicDataSource dataSource = new BasicDataSource();
  dataSource.setDriverClassName("com.mysql.jdbc.Driver");
  dataSource.setUrl("jdbc:mysql://localhost:3306/customerdb");
  dataSource.setUsername("root");
  dataSource.setPassword("root");
  return new JdbcTemplate(dataSource);
 }
}

com.example.TestAppConfig.java:
package com.example;

import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import com.example.config.AppConfig;
import com.example.dao.CustomerDao;
import com.example.domain.Customer;

public class TestAppConfig {

 public static void main(String[] args) {
  ConfigurableApplicationContext context = new AnnotationConfigApplicationContext();
  context.getEnvironment().setActiveProfiles("prod");
  ((AnnotationConfigApplicationContext) context)
    .register(AppConfig.class);
  context.refresh();
  CustomerDao custDao = (CustomerDao) context.getBean("custdao");
  Customer jack = custDao.findCustomerById("123456789101");
  System.out.println(jack);
  Customer kate = custDao.findCustomerById("109876543210");
  System.out.println(kate);
  context.close();
 }
}

Uygulamayı "dev" profilinde çalıştırdığımızda oluşan ekran görüntüsü aşağıda verilmiştir:
Feb 09, 2014 3:47:57 AM org.springframework.context.support.AbstractApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@8bfaf9f: startup date [Sun Feb 09 03:47:57 EET 2014]; root of context hierarchy
Feb 09, 2014 3:47:58 AM org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@3843d287: defining beans [org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,appConfig,org.springframework.context.annotation.ConfigurationClassPostProcessor.importAwareProcessor,customerSrv,custdao,com.example.config.AppConfigDev,109876543210,123456789101]; root of factory hierarchy
Customer [firstName=Jack, lastName=Shephard, identityNo=123456789101]
Customer [firstName=Kate, lastName=Austen, identityNo=109876543210]
Feb 09, 2014 3:47:58 AM org.springframework.context.support.AbstractApplicationContext doClose
INFO: Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@8bfaf9f: startup date [Sun Feb 09 03:47:57 EET 2014]; root of context hierarchy
Feb 09, 2014 3:47:58 AM org.springframework.beans.factory.support.DefaultSingletonBeanRegistry destroySingletons
INFO: Destroying singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@3843d287: defining beans [org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,appConfig,org.springframework.context.annotation.ConfigurationClassPostProcessor.importAwareProcessor,customerSrv,custdao,com.example.config.AppConfigDev,109876543210,123456789101]; root of factory hierarchy

Uygulamayı "prod" profilinde çalıştırdığımızda oluşan ekran görüntüsü aşağıda verilmiştir:
Feb 09, 2014 3:50:05 AM org.springframework.context.support.AbstractApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@60a2f435: startup date [Sun Feb 09 03:50:05 EET 2014]; root of context hierarchy
Feb 09, 2014 3:50:06 AM org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@6cb2b9ec: defining beans [org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,appConfig,org.springframework.context.annotation.ConfigurationClassPostProcessor.importAwareProcessor,customerSrv,custdao,com.example.config.AppConfigProd,jdbcTemplate]; root of factory hierarchy
Customer [firstName=Jin, lastName=Kwon, identityNo=123456789101]
Customer [firstName=Sun, lastName=Kwon, identityNo=109876543210]
Feb 09, 2014 3:50:07 AM org.springframework.context.support.AbstractApplicationContext doClose
INFO: Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@60a2f435: startup date [Sun Feb 09 03:50:05 EET 2014]; root of context hierarchy
Feb 09, 2014 3:50:07 AM org.springframework.beans.factory.support.DefaultSingletonBeanRegistry destroySingletons
INFO: Destroying singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@6cb2b9ec: defining beans [org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,appConfig,org.springframework.context.annotation.ConfigurationClassPostProcessor.importAwareProcessor,customerSrv,custdao,com.example.config.AppConfigProd,jdbcTemplate]; root of factory hierarchy

Projeyi maven projesi olarak bu adresten indirebilirsiniz.