14. [TD]: [metier] katmanının web'de yayınlanması
Anahtar kelimeler: çok katmanlı mimari, Spring, bağımlılık enjeksiyonu, web hizmeti / jSON, istemci / sunucu.
Şimdi TD uygulamasının mevcut mimarisine geri dönelim:
![]() |
Bu mimariyi aşağıdaki gibi geliştireceğiz:
![]() |
Böylece iş katmanındaki [IMetier] arayüzünü web üzerinde sunabileceğiz. Bunun için 13.5. paragrafında açıklanan metodolojiyi uygulayacağız.
14.1. Support
![]() |
Bu bölümdeki projeler [support / chap-14] klasöründe bulunabilir.
14.2. [métier] katmanının Eclipse projesi
![]() |
![]() |
14.2.1. Maven Yapılandırması
[métier] katmanındaki proje, aşağıdaki [pom.xml] dosyası ile yapılandırılmış bir Maven projesidir:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>istia.st.elections</groupId>
<artifactId>elections-metier-dao-spring-data</artifactId>
<version>0.1.0</version>
<!-- bağımlılıklar -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.2.7.RELEASE</version>
</parent>
<dependencies>
<!-- katman [DAO] -->
<dependency>
<groupId>istia.st.elections</groupId>
<artifactId>elections-dao-spring-data-01</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<!-- Spring Boot -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot</artifactId>
<scope>test</scope>
</dependency>
<!-- Spring Boot Testi -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<properties>
<!-- her şey için UTF-8 kullan -->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.18.1</version>
</plugin>
</plugins>
</build>
</project>
- 18-22. satırlar: 12. paragrafta oluşturulan [DAO] katmanına olan bağımlılık;
- 23-34. satırlar: testler için gerekli bağımlılıklar;
14.2.2. Spring Yapılandırması
![]() |
[métier] katmanının projesi, aşağıdaki [MetierConfig] dosyası ile yapılandırılmış bir Spring projesidir:
package elections.metier.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Import;
import elections.dao.config.DaoConfig;
@Import({ DaoConfig.class })
@ComponentScan({ "elections.metier.service" })
public class MetierConfig {
}
- Burada, sınıfı bir Spring yapılandırma sınıfı haline getiren [@Configuration] notasyonunu kullanmıyoruz. [@Import, @ComponentScan] anotasyonlarının varlığı, sınıfı otomatik olarak bir yapılandırma sınıfı haline getirir;
- 8. satır: [DAO] katmanının yapılandırma dosyası içe aktarılır. Böylece, bu dosyada tanımlanan tüm bean’lere erişim sağlanır;
- 9. satır: Diğer Spring bean'leri [elections.metier.service] klasöründe aranmalıdır;
14.2.3. [métier] katmanının uygulanması
![]() |
[métier] katmanının uygulaması, 8.5. paragrafta tanımlanan uygulamadır.
14.2.4. [métier] katmanının testi
![]() |
Test sınıfı, 8.6. paragrafta açıklanan sınıftır.
Yapılması gerekenler: [métier] katman projesini uygulayın ve birim testini geçirin. Katman arşivini yerel Maven deposunda oluşturun (run as/ Maven / install).
14.3. [web] katmanının Eclipse projesi
![]() |
Web katmanı, Spring tabanlı MVC katmanıdır:
![]() |
Eclipse projesi aşağıdaki yapıya sahiptir:
![]() | ![]() |
- [Boot.java], web hizmetini başlatan sınıftır;
- [WebConfig.java], web hizmetinin yapılandırma sınıfıdır;
- [Response.java], web hizmetinin çeşitli URL'leri tarafından verilen yanıttır;
- [ElectionsController], web hizmetinin uygulama sınıfıdır;
14.4. Maven Yapılandırması
Proje, aşağıdaki [pom.xml] dosyası ile yapılandırılmış bir Maven projesidir:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>istia.st.elections</groupId>
<artifactId>elections-webjson-metier-dao-spring-data</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>elections-webjson-metier-dao-spring-data</name>
<description>couche métier exposée comme un service web / jSON</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.2.7.RELEASE</version>
</parent>
<dependencies>
<!-- iş mantığı katmanı -->
<dependency>
<groupId>istia.st.elections</groupId>
<artifactId>elections-metier-dao-spring-data</artifactId>
<version>0.1.0</version>
</dependency>
<!-- MVC katmanı -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.18.1</version>
</plugin>
</plugins>
</build>
</project>
- 19-23. satırlar: [métier] katman arşivine olan bağımlılık. Bu, 14. paragrafta oluşturduğumuz bağımlılıktır;
- 25-28. satırlar: Spring uygulaması MVC için bağımlılık;
14.5. Spring Yapılandırması
![]() |
[WebConfig] sınıfı, web hizmetini yapılandırır:
package elections.webjson.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.boot.context.embedded.EmbeddedServletContainerFactory;
import org.springframework.boot.context.embedded.ServletRegistrationBean;
import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.Scope;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import com.fasterxml.jackson.databind.ObjectMapper;
import elections.metier.config.MetierConfig;
@EnableWebMvc
@Import({ MetierConfig.class })
@ComponentScan({ "elections.webjson.service" })
public class WebConfig {
// -------------------------------- [web] katmanı yapılandırması
@Autowired
private ApplicationContext context;
@Bean
public DispatcherServlet dispatcherServlet() {
DispatcherServlet servlet = new DispatcherServlet((WebApplicationContext) context);
return servlet;
}
@Bean
public ServletRegistrationBean servletRegistrationBean(DispatcherServlet dispatcherServlet) {
return new ServletRegistrationBean(dispatcherServlet, "/*");
}
@Bean
public EmbeddedServletContainerFactory embeddedServletContainerFactory() {
return new TomcatEmbeddedServletContainerFactory("", 8080);
}
// eşleştirici jSON
@Bean
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public ObjectMapper jsonMapper() {
return new ObjectMapper();
}
}
- Bu yapılandırmanın anlamı 13.5.3.1. paragrafında açıklanmıştır. Burada sadece yeni özellikleri açıklayacağız:
- 22. satır: Tüm bean'lerinden yararlanmak için [métier] katmanının yapılandırma dosyası içe aktarılır;
- 23. satır: [elections.webjson.server.service] klasöründe başka bean'lerin de bulunacağı belirtilir;
14.6. Web hizmetinin başlatma sınıfı
![]() |
[Boot] sınıfı, web hizmetini şu şekilde başlatır:
package elections.webjson.boot;
import org.springframework.boot.SpringApplication;
import elections.webjson.config.WebConfig;
public class Boot {
public static void main(String[] args) {
SpringApplication.run(WebConfig.class, args);
}
}
- 10. satır: [SpringApplication.run] statik yöntemi, [WebConfig] yapılandırma dosyasını kullanacaktır. [@EnableAutoConfiguration] ek açıklaması nedeniyle, Spring Boot Tomcat sunucusunu başlatacak ve web hizmetini bu sunucuya dağıtacaktır;
14.7. Web hizmetinden gelen URL yanıtı
![]() |
Web hizmetinin tüm URL / jSON istekleri aynı türde yanıt gönderir:
package elections.webjson.service;
import java.util.List;
public class Response<T> {
// ----------------- özellikler
// işlem durumu
private int status;
// olası hata mesajları
private List<String> messages;
// yanıt gövdesi
private T body;
// yapıcılar
public Response() {
}
public Response(int status, List<String> messages, T body) {
this.status = status;
this.messages = messages;
this.body = body;
}
// getter ve setter'lar
...
}
Bu sınıf, 13.5.5.3. paragrafında tanıtılmış ve incelenmiştir.
14.8. / jSON web hizmetinin uygulaması
![]() |
/ jSON web hizmeti, aşağıdaki [ElectionsController] sınıfı tarafından uygulanmaktadır:
package elections.webjson.service;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import elections.dao.entities.ElectionsConfig;
import elections.dao.entities.ElectionsException;
import elections.metier.service.IElectionsMetier;
@Controller
public class ElectionsController {
// Spring bağımlılıkları
@Autowired
private ObjectMapper jsonMapper;
@Autowired
private IElectionsMetier metier;
@RequestMapping(value = "/getElectionsConfig", method = RequestMethod.GET, produces = "application/json; charset=UTF-8")
@ResponseBody
public String getElectionsConfig() throws JsonProcessingException {
// yanıt
Response<ElectionsConfig> response;
try {
response = new Response<>(0, null,
new ElectionsConfig(metier.getNbSiegesAPourvoir(), metier.getSeuilElectoral()));
} catch (ElectionsException e1) {
response = new Response<>(e1.getCode(), e1.getErreurs(), null);
} catch (RuntimeException e2) {
response = new Response<>(1000, getErreursForException(e2), null);
}
// yanıt
return jsonMapper.writeValueAsString(response);
}
@RequestMapping(value = "/getListesElectorales", method = RequestMethod.GET, produces = "application/json; charset=UTF-8")
@ResponseBody
public String getListesElectorales() throws JsonProcessingException {
throw new UnsupportedOperationException("Not supported yet");
}
@RequestMapping(value = "/setListesElectorales", method = RequestMethod.POST, consumes = "application/json; charset=UTF-8", produces = "application/json; charset=UTF-8")
@ResponseBody
public String setListesElectorales(HttpServletRequest request) throws JsonProcessingException {
throw new UnsupportedOperationException("Not supported yet");
}
@RequestMapping(value = "/calculerSieges", method = RequestMethod.POST, consumes = "application/json; charset=UTF-8", produces = "application/json; charset=UTF-8")
@ResponseBody
public String calculerSieges(HttpServletRequest request) throws JsonProcessingException {
throw new UnsupportedOperationException("Not supported yet");
}
// özel yöntemler -----------------------------
// bir RuntimeException'in hata mesajları listesi
private List<String> getErreursForException(Exception e) {
// istisnanın hata mesajları listesi alınır
Throwable cause = e;
List<String> erreurs = new ArrayList<>();
while (cause != null) {
// mesajı yalnızca !=null ve boş değilse alınır
String message = cause.getMessage();
if (message != null) {
message = message.trim();
if (message.length() != 0) {
erreurs.add(message);
}
}
// sonraki neden
cause = cause.getCause();
}
return erreurs;
}
}
Yapılması gereken iş: 13.5.5 numaralı paragrafta yapılanlara uygun olarak, [ElectionsController] sınıfının kodunu tamamlayın.
Notlar:
- Burada jSON filtreleri yoktur, çünkü [CONF] ve [LISTES] tabloları birbirlerine yabancı anahtar ilişkisi ile bağlı değildir; bu da web hizmetinin kodunu önemli ölçüde hafifletir;
- Gerekli olan çeşitli Spring anotasyonlarını unutmayın;
- URL'e ilişkili yöntemlerin adları verilecektir;
- [setListeElectorales] yöntemi, bir [POST] işlemiyle çağrılır. Gönderilen değer, veritabanına kaydedilmesi gereken [sieges, voix, elimine] özniteliklerine sahip yarışan listelerin dizisidir (ListeElectorale[] türü). Bu yöntem, hata olmaması durumunda [Response<Void>] türünde bir değer ve [status=0] alanını döndürür; aksi takdirde başka bir değer döndürür;
- [calculerSieges] yöntemi, bir [POST] işlemiyle çağrılır. Gönderilen değer, ListeElectorale[] türündeki yarışan listeler dizisi ve bunların [nom, voix] öznitelikleridir. Bu yöntem, gövdesi olarak [sieges, elimine] alanları başlatılmış seçim listelerini içeren [Response<ListeElectorale[]>] türünde bir değer döndürür;
14.9. Tests
Web hizmetini başlattıktan sonra, [Advanced Rest Client] yardımcı programı ile web hizmetinin düzgün çalıştığından emin olmak için aşağıdaki testleri gerçekleştireceksiniz:
![]() |
Önceki isteğe verilen jSON yanıtı şöyledir: [1]:
![]() |
1 ![]() | 2 ![]() |
[2]'te, yanıtı panoya kopyalayın, ardından bunu herhangi bir metin düzenleyicisine yapıştırın [3]:
![]() |
[body] alanındaki değeri ayırın ve örneğin listelerdeki oy sayısını değiştirin. Aşağıdaki [4] örneğinde, tüm listelerdeki oy sayısı 100 olarak değiştirilmiştir:
![]() |
jSON dizesinin [ et se termine par ] ile başladığını kontrol edin. Bu karakterler, jSON tablosunu sınırlamak için kullanılır. [5] alanına, yukarıdaki jSON dizesini yapıştırın. Bu, bir sonraki URL için gönderilecek değer olacaktır. Bunun için HTTP [POST] [7] yöntemini seçmeniz gerekir.
![]() |
- [6]'te, URL ve [setListesElectorales]'i talep edin. Bu URL, bir POST ile talep edilir. Gönderilen değer, sonuçlarının veritabanına kaydedilmesi gereken yarışma listelerinin jSON tablosudur;
Aşağıdaki sonuç elde edilir:
![]() |
[status=0] alanı, herhangi bir hata olmadığını gösterir. Bunu doğrulamak için yarışma listelerini yeniden isteyin ve listelerde yaptığınız değişikliklerin dikkate alındığını kontrol edin:
![]() |
Listelerin kazandığı koltuk sayısını hesaplamak için [POST] komutunu tekrar çalıştırıyoruz:
![]() |
- [1]'te: koltuk hesaplamasına ilişkin URL;
- [2]'e: [POST] komutunu çalıştırıyoruz;
- [3]'e: yarışan listeler. [voix] alanına TD'in değerleri verilir, tüm [sieges] değerleri 0 olarak ayarlanır, tüm [elimine] alanları false olarak ayarlanır;
Elde edilen sonuç şöyledir:
![]() |

























