Yazılım geliştirmek zor iş. Bu zorluğun temel nedeni, yazılımın soyut bir varlık olmasıdır. İnsanlar genel olarak soyut varlıklar ile çalışma konusunda pek başarılı değildir. Bugün ilkokulda anlatılmaya başlanan küme teorisi 18. yüzyılda geliştirilmiştir (Georg Cantor (1845-1918)). Saymayı insanlık çok geç keşfetmiştir. Saymak son derece soyut bir kavramdır. Ama biz öğrenmeyi beş duyu organımızı kullanarak gerçekleştiriyoruz. Bebeklikten itibaren duyarak, görerek, dokunarak öğreniyoruz.
Yazılıma dokunabilir miyiz? Yazılımın bir kokusu ya da bir rengi var mıdır? Yazılımı yere sert bir zemine düşürdüğümüzde nasıl ses çıkarır?
- Yaratımsal (=Creational) Kalıplar
- Yapısal (=Structural) Kalıplar
- Davranışsal (=Behavioral) Kalıplar
Bu yazının amacı aslında tasarım kalıplarını çalışmak değil. Bu 23 kalıbın neredeyse tamamında kullanımını gördüğümüz bir prensibi inceleyerek devam etmek istiyorum: Yüksek Uyum Düşük Bağlantı (High Cohesion Low Coupling).
Değişimi yönetmek için sınıfın metodları arasında yüksek uyum olmasını istiyoruz:
Şekil-1 Düşük uyumlu bir sınıf |
|
Yukarıdaki nedenlerden dolayı Şekil-3'de verilen tasarım düşük kaliteli bir çözüm sunar: Tek sorumluluk ilkesi ihlal edildiği için sınıf içi metodlar arası uyum düşük, sınıflar arasında ise kalıtımdan dolayı sıkı bağ kurulmuş.
Şekil-3 Düşük kaliteli bir çözüm: Değişimi yönetmek ve test etmek zor, çözüm tekrar kullanılabilir değil. |
Şekil-4 Daha kaliteli bir çözüm: Test etmek kolay, tekrar kullanılabilir. |
Şekil-5 Değişimi yönetebileceğimiz kaliteli bir çözüm |
Employee.java:
package com.example.hr.domain; public class Employee { private String identityNo; private String firstName; private String lastName; private int birthYear; private double salary; public Employee() { } public Employee(String identityNo, String firstName, String lastName, int birthYear, double salary) { this.identityNo = identityNo; this.firstName = firstName; this.lastName = lastName; this.birthYear = birthYear; this.salary = salary; } public String getIdentityNo() { return identityNo; } public void setIdentityNo(String identityNo) { this.identityNo = identityNo; } 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 int getBirthYear() { return birthYear; } public void setBirthYear(int birthYear) { this.birthYear = birthYear; } public double getSalary() { return salary; } public void setSalary(double salary) { this.salary = salary; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Employee employee = (Employee) o; return identityNo.equals(employee.identityNo); } @Override public int hashCode() { return identityNo.hashCode(); } @Override public String toString() { return "Employee{" + "identityNo='" + identityNo + '\'' + ", firstName='" + firstName + '\'' + ", lastName='" + lastName + '\'' + ", birthYear=" + birthYear + ", salary=" + salary + '}'; } }
EmployeeRepository.java:
package com.example.hr.repository; import com.example.hr.domain.Employee; import java.util.Collection; import java.util.Optional; public interface EmployeeRepository { Optional<Employee> find(String identityNo); Optional<Employee> delete(String identityNo); Optional<Employee> add(Employee employee); Optional<Employee> update(Employee employee); Collection<Employee> findAll(); Collection<Employee> findTop10OrderBySalary(); Collection<Employee> findTop10OrderByAge(); }
package com.example.hr.repository.impl; import com.example.hr.domain.Employee; import com.example.hr.repository.EmployeeRepository; import com.example.hr.service.IdentityService; import java.util.Collection; import java.util.Map; import java.util.Optional; import java.util.stream.Collectors; public class InMemoryEmployeeRepository implements EmployeeRepository { private Map<String,Employee> employees; private IdentityService identityService; public void setEmployees(Map<String, Employee> employees) { this.employees = employees; } public void setIdentityService(IdentityService identityService) { this.identityService = identityService; } @Override public Optional<Employee> find(String identityNo) { Employee employee; return (((employee = employees.get(identityNo)) != null) || employees.containsKey(identityNo)) ? Optional.of(employee) : Optional.empty(); } @Override public Optional<Employee> delete(String identityNo) { Employee employee; employee= employees.remove(identityNo); return employee!=null ? Optional.of(employee) : Optional.empty(); } @Override public Optional<Employee> add(Employee employee) { if (employee.getIdentityNo()==null) employee.setIdentityNo(identityService.createIdentity()); String identityNo = employee.getIdentityNo(); Employee existing; return ((existing= employees.putIfAbsent(identityNo,employee)) == null ) ? Optional.of(employee) : Optional.empty(); } @Override public Optional<Employee> update(Employee employee) { String identityNo = employee.getIdentityNo(); Employee existing; return ((existing= employees.replace(identityNo,employee)) != null ) ? Optional.of(employee) : Optional.empty(); } @Override public Collection<Employee> findAll() { return employees.values(); } @Override public Collection<Employee> findTop10OrderBySalary() { return employees.values().stream() .sorted((e1,e2) -> Double.compare(e2.getSalary(),e1.getSalary())) .limit(10) .collect(Collectors.toList()); } @Override public Collection<Employee> findTop10OrderByAge() { return employees.values().stream() .sorted((e1,e2) -> Integer.compare(e1.getBirthYear(),e2.getBirthYear())) .limit(10) .collect(Collectors.toList()); } }
package com.example.hr.service.impl; import com.example.hr.service.IdentityService; import java.util.Collection; import java.util.HashSet; import java.util.Random; import java.util.Set; public class SimpleIdentityService implements IdentityService { private Set<String> identities= new HashSet<>(); private Random random= new Random(); @Override public String createIdentity() { String candidateIdentity; do{ candidateIdentity= getIdentity(); }while(identities.contains(candidateIdentity)); return candidateIdentity; } @Override public Collection<String> createIdentity(int size) { Collection<String> uniqueIdentities= new HashSet<>(); while (uniqueIdentities.size()<size) uniqueIdentities.add(createIdentity()); return uniqueIdentities; } @Override public String registerIdentity(String identity) { if (identities.contains(identity)) return createIdentity(); identities.add(identity); return identity; } private String getIdentity() { int digits[]=new int[11]; digits[0] = getRandomDigit(1,9); for(int i = 1; i < 9; i++) { digits[i] = getRandomDigit(0,9); } digits[9] = ((digits[0] + digits[2] + digits[4] + digits[6] + digits[8]) * 7 - (digits[1] + digits[3] + digits[5] + digits[7])) % 10; digits[10] = (digits[0] + digits[1] + digits[2] + digits[3] + digits[4] + digits[5] + digits[6] + digits[7] + digits[8] + digits[9]) % 10; StringBuilder identity= new StringBuilder(); for (int digit: digits) identity.append(digit); return identity.toString(); } private int getRandomDigit(int min, int max) { return random.nextInt(max-min+1)+min; } }
package com.example.hr.service; import java.util.Collection; public interface IdentityService { String createIdentity(); Collection<String> createIdentity(int size); String registerIdentity(String identity); }
1. XML dokümanında bileşenleri yapılandırmak
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="empRepo" class="com.example.hr.repository.impl.InMemoryEmployeeRepository"> <property name="identityService" ref="identitySrvc"/> <property name="employees"> <map> <entry key="55724320514" value-ref="jack"/> <entry key="16522830254" value-ref="kate"/> </map> </property> </bean> <bean id="identitySrvc" class="com.example.hr.service.impl.SimpleIdentityService"> </bean> <bean id="55724320514" name="jack" class="com.example.hr.domain.Employee"> <property name="identityNo" value="55724320514" /> <property name="firstName" value="Jack" /> <property name="lastName" value="Shephard" /> <property name="birthYear" value="1971" /> <property name="salary" value="125000" /> </bean> <bean id="16522830254" name="kate" class="com.example.hr.domain.Employee"> <property name="identityNo" value="16522830254" /> <property name="firstName" value="Kate" /> <property name="lastName" value="Austen" /> <property name="birthYear" value="1980" /> <property name="salary" value="75000" /> </bean> </beans>
package com.example.hr.app; import com.example.hr.domain.Employee; import com.example.hr.repository.EmployeeRepository; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Application { static final Logger logger = LoggerFactory.getLogger(Application.class); public static void main(String[] args) { ConfigurableApplicationContext context= new ClassPathXmlApplicationContext("spring-config.xml"); EmployeeRepository employeeRepository= context.getBean(EmployeeRepository.class); employeeRepository.add(new Employee(null,"James","Sawyer",1972,50000)); employeeRepository.findAll() .forEach( employee -> logger.info(employee.toString()) ); context.close(); } }
C:\opt64\java\jdk1.8.0_77\bin\java -Didea.launcher.port=7532 -Didea.launcher.bin.path=C:\opt\idea-2016.1\bin -Dfile.encoding=UTF-8 -classpath C:\opt64\java\jdk1.8.0_77\jre\lib\charsets.jar;C:\opt64\java\jdk1.8.0_77\jre\lib\deploy.jar;C:\opt64\java\jdk1.8.0_77\jre\lib\ext\access-bridge-64.jar;C:\opt64\java\jdk1.8.0_77\jre\lib\ext\cldrdata.jar;C:\opt64\java\jdk1.8.0_77\jre\lib\ext\dnsns.jar;C:\opt64\java\jdk1.8.0_77\jre\lib\ext\jaccess.jar;C:\opt64\java\jdk1.8.0_77\jre\lib\ext\jfxrt.jar;C:\opt64\java\jdk1.8.0_77\jre\lib\ext\localedata.jar;C:\opt64\java\jdk1.8.0_77\jre\lib\ext\nashorn.jar;C:\opt64\java\jdk1.8.0_77\jre\lib\ext\sunec.jar;C:\opt64\java\jdk1.8.0_77\jre\lib\ext\sunjce_provider.jar;C:\opt64\java\jdk1.8.0_77\jre\lib\ext\sunmscapi.jar;C:\opt64\java\jdk1.8.0_77\jre\lib\ext\sunpkcs11.jar;C:\opt64\java\jdk1.8.0_77\jre\lib\ext\zipfs.jar;C:\opt64\java\jdk1.8.0_77\jre\lib\javaws.jar;C:\opt64\java\jdk1.8.0_77\jre\lib\jce.jar;C:\opt64\java\jdk1.8.0_77\jre\lib\jfr.jar;C:\opt64\java\jdk1.8.0_77\jre\lib\jfxswt.jar;C:\opt64\java\jdk1.8.0_77\jre\lib\jsse.jar;C:\opt64\java\jdk1.8.0_77\jre\lib\management-agent.jar;C:\opt64\java\jdk1.8.0_77\jre\lib\plugin.jar;C:\opt64\java\jdk1.8.0_77\jre\lib\resources.jar;C:\opt64\java\jdk1.8.0_77\jre\lib\rt.jar;C:\var\workspace\sources-binkurt-blogspot\stufy-spring-component\target\classes;C:\Users\bkurt\.m2\repository\org\springframework\spring-context\4.2.5.RELEASE\spring-context-4.2.5.RELEASE.jar;C:\Users\bkurt\.m2\repository\org\springframework\spring-aop\4.2.5.RELEASE\spring-aop-4.2.5.RELEASE.jar;C:\Users\bkurt\.m2\repository\aopalliance\aopalliance\1.0\aopalliance-1.0.jar;C:\Users\bkurt\.m2\repository\org\springframework\spring-beans\4.2.5.RELEASE\spring-beans-4.2.5.RELEASE.jar;C:\Users\bkurt\.m2\repository\org\springframework\spring-core\4.2.5.RELEASE\spring-core-4.2.5.RELEASE.jar;C:\Users\bkurt\.m2\repository\commons-logging\commons-logging\1.2\commons-logging-1.2.jar;C:\Users\bkurt\.m2\repository\org\springframework\spring-expression\4.2.5.RELEASE\spring-expression-4.2.5.RELEASE.jar;C:\Users\bkurt\.m2\repository\ch\qos\logback\logback-classic\1.1.7\logback-classic-1.1.7.jar;C:\Users\bkurt\.m2\repository\ch\qos\logback\logback-core\1.1.7\logback-core-1.1.7.jar;C:\Users\bkurt\.m2\repository\org\slf4j\slf4j-api\1.7.20\slf4j-api-1.7.20.jar;C:\opt\idea-2016.1\lib\idea_rt.jar com.intellij.rt.execution.application.AppMain com.example.hr.app.Application Jul 19, 2016 1:39:12 PM org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@515f550a: startup date [Tue Jul 19 13:39:12 EEST 2016]; root of context hierarchy Jul 19, 2016 1:39:12 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions INFO: Loading XML bean definitions from class path resource [spring-config.xml] 13:39:12.824 [main] INFO com.example.hr.app.Application - Employee{identityNo='55724320514', firstName='Jack', lastName='Shephard', birthYear=1971, salary=125000.0} Jul 19, 2016 1:39:12 PM org.springframework.context.support.ClassPathXmlApplicationContext doClose 13:39:12.828 [main] INFO com.example.hr.app.Application - Employee{identityNo='16522830254', firstName='Kate', lastName='Austen', birthYear=1980, salary=75000.0} INFO: Closing org.springframework.context.support.ClassPathXmlApplicationContext@515f550a: startup date [Tue Jul 19 13:39:12 EEST 2016]; root of context hierarchy 13:39:12.828 [main] INFO com.example.hr.app.Application - Employee{identityNo='84371896950', firstName='James', lastName='Sawyer', birthYear=1972, salary=50000.0} Process finished with exit code 0
<bean id="empRepo" class="com.example.hr.repository.impl.InMemoryEmployeeRepository"> <property name="identityService" ref="identitySrvc"/> . . . </bean>
<bean id="empRepo" autowire="byType" class="com.example.hr.repository.impl.InMemoryEmployeeRepository"> </bean>
- Varsayılan kurucu ile önce yaratılır daha sonra set metodları ile ilklendirilir:
<bean id="55724320514" name="jack" class="com.example.hr.domain.Employee"> <property name="identityNo" value="55724320514" /> <property name="firstName" value="Jack" /> <property name="lastName" value="Shephard" /> <property name="birthYear" value="1971" /> <property name="salary" value="125000" /> </bean>
- Parametre alan kurucu fonksiyon ile ilklendirilererek yaratılır.
<bean id="84371896950" name="james" class="com.example.hr.domain.Employee"> <constructor-arg name="identityNo" value="84371896950" /> <constructor-arg name="firstName" value="James" /> <constructor-arg name="lastName" value="Sawyer" /> <constructor-arg name="birthYear" value="1972" /> <constructor-arg name="salary" value="50000" /> </bean>
Her iki yöntemin de kendine göre kazanımı ve yitimi bulunmaktadır. setter metodlarını kullanarak ilklendirmenin kazanımı sınıfın kalabalık özniteliğinin olması durumunda okumayı kolaylaştırmasıdır. Sınıf geliştikçe eklenen özniteliklerin kurucu fonksiyon fonksiyon kalabalığı yaratır. Bir öznitelik için setter çağrısı unutulursa, yürütme zamanında muhtemel bir NullPointerException alınacaktır. Kurucu fonksiyonları uygulama programcısını tüm öznitelikler için değer vermeye ve ilklendirmeye zorlar. Ancak sınıflar arasında çevresel bir bağımlılık varsa kurucu fonsiyonlar üzerinden ilklendiremezsiniz. Yukarıda verilen XML konfigürasyonu ile yapılan tanımın aynısını p-schema ve c-schema ile de gerçekleştirebiliriz:
<?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:p="http://www.springframework.org/schema/p" xmlns:c="http://www.springframework.org/schema/c" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd" > <bean id="empRepo" autowire="byType" class="com.example.hr.repository.impl.InMemoryEmployeeRepository"> </bean> <bean id="identitySrvc" class="com.example.hr.service.impl.SimpleIdentityService"> </bean> <bean id="55724320514" name="jack" class="com.example.hr.domain.Employee" p:identityNo="55724320514" p:firstName="Jack" p:lastName="Shephard" p:birthYear="1971" p:salary="125000" /> <bean id="16522830254" name="kate" class="com.example.hr.domain.Employee" c:identityNo="16522830254" c:firstName="Kate" c:lastName="Austen" c:birthYear="1980" c:salary="75000" /> </beans>
Spring bileşenlerinin ne zaman yaratılacağı ve ne kadar süre yaşayacaklarını erim (=scope) tanımları belirler. Spring çatısında var sayılan erim "singleton"'dır. Fabrikayı açar açmaz
ConfigurableApplicationContext context= new ClassPathXmlApplicationContext("spring-config.xml");
EmployeeRepository employeeRepository= context.getBean(EmployeeRepository.class);
<bean id="empRepo" autowire="byType" lazy-init="true" class="com.example.hr.repository.impl.InMemoryEmployeeRepository"> </bean>
- singleton
- prototype
- request
- session
prototype eriminde, bileşenden her istediğimizde yeni bir kopyası üretilir. request ve session erimleri web uygulaması geliştirirken kullanabileceğimiz erimlerdir. erimi değiştirmek için bean tanımında scope özniteliğini kullanıyoruz:
<bean id="identitySrvc" scope="prototype" class="com.example.hr.service.impl.SimpleIdentityService"> </bean>
2. Minimum bir XML dokümanı ve not düşerek bileşenleri yapılandırmak
Spring bileşenlerinin yapılandırılması için kullanılabilecek ikinci yöntem ise minimum bir XML konfigürasyon dokümanı ve daha çok Java notlarını kullanmaktır. XML ile yaptığımız tüm tanımlamaları Java notlarını kullanarak gerçekleştirebiliriz. Önce InMemoryEmployeeRepository ve SimpleIdentityService sınıflarının birer bileşen olduklarını göstermek üzere sınıflara not düşüyoruz:
SimpleIdentityService.java:
package com.example.hr.service.impl; import com.example.hr.service.IdentityService; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Service; import java.util.Collection; import java.util.HashSet; import java.util.Random; import java.util.Set; @Service @Scope("singleton") public class SimpleIdentityService implements IdentityService { private Set<String> identities= new HashSet<>(); private Random random= new Random(); @Override public String createIdentity() { String candidateIdentity; do{ candidateIdentity= getIdentity(); }while(identities.contains(candidateIdentity)); return candidateIdentity; } @Override public Collection<String> createIdentity(int size) { Collection<String> uniqueIdentities= new HashSet<>(); while (uniqueIdentities.size()<size) uniqueIdentities.add(createIdentity()); return uniqueIdentities; } @Override public String registerIdentity(String identity) { if (identity==null || identities.contains(identity)) return createIdentity(); identities.add(identity); return identity; } private String getIdentity() { int digits[]=new int[11]; digits[0] = getRandomDigit(1,9); for(int i = 1; i < 9; i++) { digits[i] = getRandomDigit(0,9); } digits[9] = ((digits[0] + digits[2] + digits[4] + digits[6] + digits[8]) * 7 - (digits[1] + digits[3] + digits[5] + digits[7])) % 10; digits[10] = (digits[0] + digits[1] + digits[2] + digits[3] + digits[4] + digits[5] + digits[6] + digits[7] + digits[8] + digits[9]) % 10; StringBuilder identity= new StringBuilder(); for (int digit: digits) identity.append(digit); return identity.toString(); } private int getRandomDigit(int min, int max) { return random.nextInt(max-min+1)+min; } }
package com.example.hr.repository.impl; import com.example.hr.domain.Employee; import com.example.hr.repository.EmployeeRepository; import com.example.hr.service.IdentityService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Repository; import javax.annotation.Resource; import java.util.Collection; import java.util.Map; import java.util.Optional; import java.util.stream.Collectors; @Repository @Scope("singleton") public class InMemoryEmployeeRepository implements EmployeeRepository { @Resource private Map<String,Employee> employees; @Autowired private IdentityService identityService; public void setEmployees(Map<String, Employee> employees) { this.employees = employees; } public void setIdentityService(IdentityService identityService) { this.identityService = identityService; } @Override public Optional<Employee> find(String identityNo) { Employee employee; return (((employee = employees.get(identityNo)) != null) || employees.containsKey(identityNo)) ? Optional.of(employee) : Optional.empty(); } @Override public Optional<Employee> delete(String identityNo) { Employee employee; employee= employees.remove(identityNo); return employee!=null ? Optional.of(employee) : Optional.empty(); } @Override public Optional<Employee> add(Employee employee) { String identityNo = employee.getIdentityNo(); identityNo= identityService.registerIdentity(identityNo); employee.setIdentityNo(identityNo); Employee existing; return ((existing= employees.putIfAbsent(identityNo,employee)) == null ) ? Optional.of(employee) : Optional.empty(); } @Override public Optional<Employee> update(Employee employee) { String identityNo = employee.getIdentityNo(); Employee existing; return ((existing= employees.replace(identityNo,employee)) != null ) ? Optional.of(employee) : Optional.empty(); } @Override public Collection<Employee> findAll() { return employees.values(); } @Override public Collection<Employee> findTop10OrderBySalary() { return employees.values().stream() .sorted((e1,e2) -> Double.compare(e2.getSalary(),e1.getSalary())) .limit(10) .collect(Collectors.toList()); } @Override public Collection<Employee> findTop10OrderByAge() { return employees.values().stream() .sorted((e1,e2) -> Integer.compare(e1.getBirthYear(),e2.getBirthYear())) .limit(10) .collect(Collectors.toList()); } }
- @Component
- @Service
- @Repository
Java EE 6 ile gelen Context and Dependency Injection (CD) şartnamesinde ise bir sınıfı bileşen olarak tanımlamak için tek bir not bulunur: @Named. Buna karşılık olarak Spring çatısında yer alan üç notun aslında biri birinden farkı yoktur. Her bir not bileşenin hangi katmanın bir bileşeni olduğunu göstermek dışında özel bir anlamı bulunmaz. @Component ile damgalanan bileşen sunum katmanının bir bileşenidir. @Service ile damgalanan bileşen iş mantığı katmanının ve son olarak @Repository ile damgalanan bileşen tümleştirme katmanının bir bileşeni olduğunu betimler. Hepsini sadece @Component notunu düşerek de bileşen olarak tanımlayabilirdik. Spring MVC ile buna iki tane daha not eklenmiştir:
- @Controller
- @RestController
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:p="http://www.springframework.org/schema/p" xmlns:c="http://www.springframework.org/schema/c" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <context:annotation-config/> <context:component-scan base-package="com.example.hr.repository.impl"/> <context:component-scan base-package="com.example.hr.service.impl"/> <bean id="55724320514" name="jack" class="com.example.hr.domain.Employee" p:identityNo="55724320514" p:firstName="Jack" p:lastName="Shephard" p:birthYear="1971" p:salary="125000"/> <bean id="16522830254" name="kate" class="com.example.hr.domain.Employee" c:identityNo="16522830254" c:firstName="Kate" c:lastName="Austen" c:birthYear="1980" c:salary="75000" /> </beans>
Erimi tanımlamak için ise @Scope notunu kullanıyoruz. CDI'da her farklı erim için ayrı bir not kullanılırken Spring çatısında tüm erimler için aynı notu kullanıyoruz:
- @Scope("singleton")
- @Scope("prototype")
- @Scope("request")
- @Scope("session")
Bağımlılıkları otomatik olarak çözmek için @Autowired notunu kullanıyoruz. @Autowired notunu doğrudan özniteliği damgalayarak kullanabileceğiniz gibi setter metodunu damgalayarak da kullanabilirsiniz:
@Repository @Scope("singleton") public class InMemoryEmployeeRepository implements EmployeeRepository { @Resource private Map<String,Employee> employees; @Autowired private IdentityService identityService;
@Repository @Scope("singleton") public class InMemoryEmployeeRepository implements EmployeeRepository { private Map<String,Employee> employees; private IdentityService identityService; @Resource public void setEmployees(Map<String, Employee> employees) { this.employees = employees; } @Autowired public void setIdentityService(IdentityService identityService) { this.identityService = identityService; }
Elle XML yapılandırma dosyasında kodladığımız çalışanları başka bir dosyaya almayı ve daha sonra bu dosyayı kök yapılandırma dosyasında bağlamayı tercih ederiz:
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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <context:annotation-config/> <context:component-scan base-package="com.example.hr.repository.impl"/> <context:component-scan base-package="com.example.hr.service.impl"/> <import resource="employees.xml" /> </beans>
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:p="http://www.springframework.org/schema/p" xmlns:c="http://www.springframework.org/schema/c" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="jack" class="com.example.hr.domain.Employee" p:identityNo="55724320514" p:firstName="Jack" p:lastName="Shephard" p:birthYear="1971" p:salary="125000"/> <bean id="kate" class="com.example.hr.domain.Employee" c:identityNo="16522830254" c:firstName="Kate" c:lastName="Austen" c:birthYear="1980" c:salary="75000" /> </beans>
@Repository @Scope("singleton") @Lazy public class InMemoryEmployeeRepository implements EmployeeRepository { . . . . }
3. Salt Java kullanarak bileşenleri yapılandırmak
Spring 3 ile birlikte bileşenleri sadece Java kullanarak, tek satır XML yazmadan yapılandırabiliyoruz. Bunun için @Configuration notunu düştüğümüz Java sınıfında XML dokümanında tanımladıklarımızın bire bir aynısını tanımlayabiliyoruz:
package com.example.hr.config; import com.example.hr.domain.Employee; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; @Configuration @ComponentScan(basePackages = { "com.example.hr.service.impl", "com.example.hr.repository.impl" }) public class AppConfig { @Bean(name= {"55724320514","jack"}) Employee jack(){ return new Employee("55724320514", "Jack","Shephard",1971,125000); } @Bean(name= {"16522830254","kate"}) Employee kate(){ return new Employee("16522830254", "Kate","Austen",1980,75000); } }
package com.example.hr.app; import com.example.hr.config.AppConfig; import com.example.hr.domain.Employee; import com.example.hr.repository.EmployeeRepository; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; public class Application { static final Logger logger = LoggerFactory.getLogger(Application.class); public static void main(String[] args) { ConfigurableApplicationContext context= new AnnotationConfigApplicationContext(AppConfig.class); EmployeeRepository employeeRepository= context.getBean(EmployeeRepository.class); employeeRepository.add(new Employee(null,"James","Sawyer",1972,50000)); employeeRepository.findAll() .forEach( employee -> logger.info(employee.toString()) ); context.close(); } }
package com.example.hr.config; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; @Configuration @ComponentScan(basePackages = { "com.example.hr.service.impl", "com.example.hr.repository.impl" }) @Import(EmployeesConfig.class) public class AppConfig { }
package com.example.hr.config; import com.example.hr.domain.Employee; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class EmployeesConfig { @Bean(name= {"55724320514","jack"}) Employee jack(){ return new Employee("55724320514", "Jack","Shephard",1971,125000); } @Bean(name= {"16522830254","kate"}) Employee kate(){ return new Employee("16522830254", "Kate","Austen",1980,75000); } }
package com.example.hr.config; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.ImportResource; @Configuration @ComponentScan(basePackages = { "com.example.hr.service.impl", "com.example.hr.repository.impl" }) @ImportResource("classpath:employees.xml") public class AppConfig { }
4. Yapılandırmada SPEL (SPring Expression Language) İfadelerinin ve properties Dosyasının Kullanımı
SPEL kullanılarak yürütme zamanında bileşenleri sorgulamak ve erişmek mümkündür. SPEL ifadelerini #{ } içine yazıyoruz. Buraya Java'da geçerli herhangi bir ifadeyi yerleştirebilirsiniz.
<?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:p="http://www.springframework.org/schema/p" xmlns:c="http://www.springframework.org/schema/c" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd" > <context:property-placeholder location="classpath:kate.properties"/> <bean id="empRepo" autowire="byType" class="com.example.hr.repository.impl.InMemoryEmployeeRepository"> </bean> <bean id="identitySrvc" class="com.example.hr.service.impl.SimpleIdentityService"> </bean> <bean id="55724320514" name="jack" class="com.example.hr.domain.Employee" p:identityNo="55724320514" p:firstName="Jack" p:lastName="Shephard" p:birthYear="1971" p:salary="#{ kate.getSalary() + 10000.0 }" /> <bean id="16522830254" name="kate" class="com.example.hr.domain.Employee" c:identityNo="${katea.identityNo}" c:firstName="${katea.firstName}" c:lastName="${katea.lastName}" c:birthYear="${katea.birthYear}" c:salary="${katea.salary}" /> <bean id="70210491790" name="james" class="com.example.hr.domain.Employee" c:identityNo="70210491790" c:firstName="James" c:lastName="Sawyer" c:birthYear="1972" c:salary="#{systemProperties['james.salary']}" /> </beans>
kate.properties:
katea.identityNo=16522830254 katea.firstName=Kate katea.lastName=Austen katea.birthYear=1980 katea.salary=75001
<?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:p="http://www.springframework.org/schema/p" xmlns:c="http://www.springframework.org/schema/c" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd" > <context:property-placeholder location="classpath:kate.properties"/> . . . . . . </beans>
<bean id="16522830254" name="kate" class="com.example.hr.domain.Employee" c:identityNo="${katea.identityNo}" c:firstName="${katea.firstName}" c:lastName="${katea.lastName}" c:birthYear="${katea.birthYear}" c:salary="${katea.salary}" />
package com.example.hr.config; import com.example.hr.domain.Employee; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; @Configuration @PropertySource("classpath:kate.properties") public class EmployeesConfig { @Value("${katea.salary}") private Double katesSalary; @Value("#{ kate.getSalary() + 10000}") private Double jackSalary; @Bean(name= {"55724320514","jack"}) Employee jack(){ return new Employee("55724320514", "Jack","Shephard",1971,jackSalary); } @Bean(name= {"16522830254","kate"}) Employee kate(){ return new Employee("16522830254", "Kate","Austen",1980,katesSalary); } @Bean(name= {"70210491790","james"}) Employee james(){ return new Employee("70210491790", "James","Sawyer",1972,50000); } @Bean public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() { return new PropertySourcesPlaceholderConfigurer(); } }
5. Bileşen Yapılandırmada @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. Bu konunun detaylarını bu yazıdan okuyabilirsiniz.
6. Spring Boot Uygulamalarının Yapılandırılması
Spring Boot uygulamalarının yapılandırılmasından application.properties dosyasından yararlanıyoruz. Bu dosyayı config paketine ya da classpath'e koyarak tanıtabilirsiniz. Aşağıda tipik bir application.properties dosyası yer alıyor:
server.port=8001 server.context-path=/hr server.servlet-path=/resources spring.datasource.url=jdbc:mysql://localhost:3306/hr spring.datasource.username=root spring.datasource.password=root spring.datasource.driver-class-name=com.mysql.jdbc.Driver
java -jar hr-app.jar -Dspring.profiles.active=dev
No comments:
Post a Comment