JAVA+SPRING BOOT SOFTWARE DEVELOPMENT :
Architettura layers Spring Boot:
1 .Model dove ci metto le classi entity:
- è una classe Employee annotata con @Entity
- @Table e @Column non obbligatori se non specificate, la tabella del DB prende nome dell’entity, mentre le colonne invece degli attributi.
@SuppressWarnings("serial") @Entity public class Employee implements Serializable { /* * serializable helps to transform this java class into different types of * stream. because this class is gonna be saved in a database, it's going to be * sent to the frontend as json so it's always best to make this classes * Serializable because helps in this process */ @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(nullable = false, updatable = false) private Long id; @Column(nullable = false) private String name; private String email; @Column(nullable = false) private String jobTitle; public Employee() { } public Employee(String name, String email, String jobTitle) { this.name = name; this.email = email; this.jobTitle = jobTitle; }
2. Repository classe che si interfaccia col DB.
- creare nuova interfaccia EmployeeRepo che extends JpaRepository (cosi ereditiamo le CRUD database operations di base per la classe Employee)
- annotiamo con @Repository
- extends jpaRepository che accetta parametri: type dell’entity e type dell’id
- da jpaRepository si ereditano in automatico i metodi findById,findAll etc
@Repository public interface EmployeeRepo extends JpaRepository<Employee, Long> { // void deleteEmployeeById(Long id); Optional findEmployeeById(Long id); }
3. Service classe dove si trova la business logic
@Service public class EmployeeService { @Autowired private EmployeeRepo employeerepo; public Employee addEmployee(Employee emp) { return employeerepo.save(emp); } public List findAllEmployees() { return employeerepo.findAll(); } public Employee updateEmployee(Employee emp) { return employeerepo.save(emp); } public Employee findEmployeeById(Long id) { //ritorna o un optional di employee oppure lancia exception return employeerepo.findEmployeeById(id) .orElseThrow(() -> new EmployeeNotFoundException("User by id: " + id + " was not found")); } public void deleteEmployee(Long id) { employeerepo.deleteById(id); } }
4. Controller – (oppure Resource) dove espongo le API
- creare nuova classe UserController annotata con @RestController
- @RequestMapping(“api/”) indica endpoint dove esponiamo la nostra API
- creiamo i vari metodi get, post etc annotati con es. @GetMapping(“users”)
- @Autowired per fare inject della classe repository
@RestController @RequestMapping("/employee") public class EmployeeResource { @Autowired private EmployeeService employeeService; /* * ResponseEntity è un generic quindi dobbiamo passare il type of data che ci * sarà nel body * * List è also a generic quindi dobbiamo specificare la list of what */ @GetMapping("/all") public ResponseEntity<List> getAllEmployees() { List employeesList = employeeService.findAllEmployees(); return new ResponseEntity<>(employeesList, HttpStatus.OK); } /* * con @PathVariable diciamo che l'id è quello del path e sarà di type Long */ @GetMapping("/find/{id}") public ResponseEntity getEmployeeById(@PathVariable("id") Long id) { Employee employee = employeeService.findEmployeeById(id); return new ResponseEntity<>(employee, HttpStatus.OK); } @PostMapping("/add") public ResponseEntity addEmployee(@RequestBody Employee employee) { Employee newEmployee = employeeService.addEmployee(employee); return new ResponseEntity<>(newEmployee, HttpStatus.CREATED); } @PutMapping("/update") public ResponseEntity updateEmployee(@RequestBody Employee employee) { Employee updateEmployee = employeeService.updateEmployee(employee); return new ResponseEntity<>(updateEmployee, HttpStatus.CREATED); } @DeleteMapping("/delete/{id}") public ResponseEntity<?> deleteEmployee(@PathVariable("id") Long id) { employeeService.deleteEmployee(id); return new ResponseEntity<>(HttpStatus.OK); } }
5. Test delle API (verifico le response della classe controller)
@RunWith(SpringRunner.class) @AutoConfigureMockMvc @SpringBootTest(classes = EmployeeManagerResourceAPITest.class) public class EmployeeManagerResourceAPITest { @Autowired MockMvc mvc; ObjectWriter objectWriter = new ObjectMapper().writer().withDefaultPrettyPrinter(); /* * test della GET getAllEmployees */ @Test void getAllEmployees() throws Exception { //GIVEN Employee employee = new Employee("asgas","asgas","asga"); employee.setId((long) 1); //lo trasformo in json da passare String stringJson = objectWriter.writeValueAsString(employee); //WHEN // request to endpoint mvc.perform(MockMvcRequestBuilders.post("/employee/add") // sent content type .accept(MediaType.APPLICATION_JSON).contentType(MediaType.APPLICATION_JSON).content(stringJson)) // expect status .andExpect(status().isCreated()) // return content type .andExpect(content().contentType(MediaType.APPLICATION_JSON)); //THEN //test della getAllEmployees mvc.perform(MockMvcRequestBuilders.get("/employee/all")).andExpect(status().isOk()) .andExpect(content().contentType(MediaType.APPLICATION_JSON)); } }
5.1 Test della business logic (classe service)
@SpringBootTest @TestInstance(Lifecycle.PER_CLASS) class EmployeeManagerServiceTest { @Autowired EmployeeService service; @Test void findAllEmployeesShouldBeEmpty() { Assertions.assertTrue(service.findAllEmployees().isEmpty()); } @Test @DisplayName(value="Dovrebbe restituire falso che la lista è vuota perchè ho aggiunto un employee, e dovrebbe dire che è true se lista size uguale a 1") void addEmployeeShouldBeCreated() { Employee employee = new Employee("Salvatore Barretta", "salvatore@gmail.com", "Software Engineer"); service.addEmployee(employee); Assertions.assertFalse(service.findAllEmployees().isEmpty()); //prima il parametro che ci aspettiamo come risultato, in questo caso 1, poi quello con cui confrontarlo Assertions.assertEquals(1, service.findAllEmployees().size()); } }
6. Exception personalizzata
@SuppressWarnings("serial") public class EmployeeNotFoundException extends RuntimeException { public EmployeeNotFoundException(String message) { super(message); } }
Sql
SELECT * FROM ordini JOIN clienti ON ordini.id_cliente = clienti.id_cliente
Nella entity cliente:
@Entity @Table(name="clienti") public class Cliente implements Serializable { // codice omesso per brevità @OneToMany(mappedBy= "cliente") private List OrdiniList; Nella entity ordini :
@Entity @Table(name="ordini") public class Ordine implements Serializable { // codice omesso per brevità @ManyToOne @JoinColumn(name= "id_cliente") private Cliente cliente;
Hibernate + Spring Boot
Questa dipendenza include l’API JPA, l’implementazione JPA, JDBC e le altre librerie necessarie. Poiché l’implementazione JPA predefinita è Hibernate, questa dipendenza è in realtà sufficiente.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
Hibernate è un framework open source di ORM (Object Relational Mapping).
Una raccolta di librerie Java per mappare (per rendere persistenti) classi Java (entity) nel database.
Hibernate implementa le specifiche JPA (Java Persistence API) per la persistenza dei dati.
Database relazionali vs non relazionali
Nei database relazionali, i dati vengono salvati in delle tabelle seguendo un preciso schema che ogni dato deve seguire per poter essere salvato; Nei database non relazionali, invece, i dati vengono salvati in documenti e non si deve seguire per forza uno schema ben preciso
Spring Boot
https://www.javainuse.com/spring/SpringBootInterviewQuestions
Framework backend per sviluppare Web Services (i Web services sono un tipo API, a cui è necessario accedere tramite una connessione di rete) e microservizi.
- Quindi Spring boot viene usato per creare ed esporre API
- Angular è un frontend framework che consuma API Rest
- è più semplice e veloce di Spring grazie allo starter
Spring Starter cos’è:
è un sito web dal quale posso creare il file di Maven per inizializzare il progetto.
ci si può accedere da Eclipse >new project > Spring Boot > Spring Starter project, oppure vai su Spring Initializr che ha la stessa cosa interfaccia e fai la stessa cosa poi scarichi il progetto e lo apri in eclipse.
MAVEN: strumento di gestione delle dipendenze per java
Un bean in Spring:
- è semplicemente un oggetto che viene creato (istanziato) e configurato da Spring all’interno di un container
- https://forum.html.it/forum/showthread/t-2952410.html
com’è questo container?
Differenza tra DTO ed Entity:
- Entity è la classe mappata alla tabella.
- Dto è mappato per la classe principalmente al livello “view”. Ciò che è necessario archiviare è l’entità e ciò che è necessario “mostrare” sulla pagina Web è DTO.
Differenza tra DAO vs Repository patterns :
- DAO è un concetto di basso livello, più vicino ai sistemi di storage.
- Repository è un concetto di alto livello, più vicino agli oggetti Domain
- https://www.baeldung.com/java-dao-vs-repository
Build automation con Maven
- Maven è uno strumento di compilazione e gestione dei progetti è uno strumento di building basato su pom.xml.
- Dentro al pom si aggiungono le dependency che sono delle librerie.
- quando fai maven install, dopo aver aggiunto le dependency, le scarica in una cartella utente.
- quindi maven è uno strumento di building che trasforma le dipendenze in file jar o war (cioè archivi di file java già compilati quindi .class)
comandi più comuni:
- mvn clean cancella la cartella target
- mvn package dice: esegui tutto ciò che c’è nel pom
- le dependency sono delle librerie. Quando aggiungi le dependency e fai maven install, le scarica in una cartella utente
- “maven: in realtà può fare un po’ di tutto, è uno strumento di building, questo vuol dire che può essere configurato per trasformare una cartella corrente e tutto ciò che contiene (ad esempio i vari file sorgenti) in “qualcosa”, o in altre parole, può costruire (build) questo “qualcosa” a partire dai file nella cartella corrente e nelle varie sottocartelle, questo “qualcosa” di solito si tratta di un file in .jar o in .war, entrambi archivi contenenti file java compilati, quindi in .class, e altri metadati, in altre parole, tutto ciò che può servire per usare quel “qualcosa” in un certo modo.
- In pratica, tutto ciò che maven fa lo potresti fare a mano, ma dato che a farlo a meno ogni volta che cambi una virgola nel codice c’è da impazzire, allora semplicemente si scrive un pom.xml (che sta per project object model, ovvero il modello secondo il quale creare un oggetto a partire dal progetto) che descrive i passaggi da fare per creare il .jar o il .warO qualsiasi altro oggetto, spesso chiamato anche artefatto, che possa servirti”
- quando “builda le dependency” in realtà fa i seguenti passaggi: verifica che le dipendenze (dei file jar) siano disponibili nel repository locale (ovvero sotto ~/.m2/repository, dove ~ sta per la home dell’utente corrente, ad esempio C:\Users\salvatore.beretta) e in caso li scarica da un repository remoto (di solito un url configurato in un file settings.xml che si trova da qualche parte nel sistema), poi o copia quei jar nell’artefatto finale (l’archivio .jar o .war prodotto da maven) oppure inserisce in quell’artefatto dei metadati che indicano che per usare quell’artefatto servono quelle dipendenze (se non ricordo male, di default le copia tutte dentro l’artefatto finale, ma potrei sbagliarmi)
Dependency Injection
La dependency injection è la tecnica per cui Spring inietta automaticamente le dipendenze richieste.
- Le dipendenze di una classe java, o meglio, di ogni oggetto istanziato a partire da quella classe (in gergo di spring boot e simili: un bean) sono altri bean di cui questo bean abbisogna per funzionare.
- Sostanzialmente, senza DI (dependency injection) servirebbe scrivere a mano un metodo da qualche parte che inizializzi tutti bean (ovvero una sfilza di new Questo(foo, bar) e new Quello(fizz, buzz) e farlo in ordine.
- Con DI tutto questo viene risolto automaticamente e lo sviluppatore si deve solo preoccupare della logica di business del progetto. L’idea alla base della Dependency Injection è quella di avere un componente esterno (assembler) che si occupi della creazione degli oggetti e delle loro relative dipendenze e di assemblarle mediante l’utilizzo dell’injection.
Application.properties:
è un file che contiene tutte le impostazioni dell’applicazione
API rest (Application Programming Interface)
- Le API sono come intemediari (protocolli, regole) che permettono di accedere a delle applicazioni (servizi di terze parti) tramite endpoint endpoint URI (Uniform Resource Indices)
- gli sviluppatori possono invocare le API per integrare tanti microservizi in un’unica applicazione.
- possono essere JSON (Rest) o XML (Soap)
- utilizzano i principali metodi tipici del protocollo HTTP:
- Get
- Post
- Put
- Delete
Microservizi
- I microservizi sono un approccio architetturale dei software.
- Vuol dire strutturare l’architettura dei software scomponendola in diversi piccoli servizi che comunicano tra loro tramite API.
- è un approccio opposto all’architettura delle applicazioni cosiddette monolitiche
- questi servizi possono essere deployati in modo indipendente
Git:
Git è un sistema di versionamento che consente il controllo delle versioni e di far collaborare più developer su uno stesso progetto contemporaneamente.
Docker
- è un container virtuale, un pacchetto con dentro applicazione e dipendenze che
- può essere eseguito su qualsiasi sistema operativo
A che serve?
Ciò semplifica la condivisione di un’applicazione o di un insieme di servizi, con tutte le loro dipendenze, nei vari ambienti.
perché permette di eseguire ambienti virtuali all’interno del sistema operativo.
Thread
“I thread puoi immaginarli come diversi processori che lavorano in parallelo per eseguire del codice, che sia lo stesso codice o codice diverso non ha importanza.”
JAVA BASICS:
Exception:
Le exception unchecked sono quelle che non possiamo conoscere prima perché avvengono a runtime.
Le exception checked sono quelle eccezioni che vengono controllate dal compilatore java stesso al momento della compilazione e non sono sotto la gerarchia delle classi di eccezioni di runtime.
Se un metodo genera un’eccezione verificata in un programma, il metodo deve gestire l’eccezione o passarla a un metodo sopra chiamante.
Dobbiamo gestire le eccezioni controllate usando try and catch block o usando una clausola throws nella dichiarazione del metodo. Se non viene gestito correttamente, verrà visualizzato un errore in fase di compilazione.
La maggior parte delle persone è confusa e afferma che le eccezioni verificate si verificano in fase di compilazione, è sbagliato. Tutte le eccezioni si verificano sempre solo in fase di esecuzione, ma alcune eccezioni vengono rilevate in fase di compilazione e altre in fase di esecuzione.
Le eccezioni controllate dal compilatore Java al momento della compilazione sono chiamate eccezioni verificate in Java. Tutte le eccezioni tranne RuntimeException, Error e le relative sottoclassi sono eccezioni controllate.
- situazioni anomale rispetto al flusso di esecuzione del codice.
- avvengono quando il programma crasha, quindi qualcosa è andato storto, il programma non ha gestito questo evento e quindi è crashato.
- eccezioni controllate e non controllate checked e unchecked
- Una exception va sempre handled o con il catch oppure dichiarandola con il throws se una exception non viene gestita es. lancio una exception checked nel try, la catcho nel catch ma poi dentro al catch lancio un’altra exception e se nel metodo non c’è nemmeno throws Exception mi darà compile error:
- Ricorda che un metodo che fa override di un altro può generare solo un sottoinsieme di eccezioni checked dichiarate nella clausola throws del metodo “padre”.
- System.exit(0); non fa andare nemmeno nel finally
- finally è sempre eseguito anche se c’è try poi viene lanciata exception e non c’è un catch, il finally viene comunque eseguito
- try deve essere seguito almeno da un catch o finally oppure posso esserci tutti e tre: try, catch, e finally
- dopo il return finisce. es se c’è nel catch fa il finally dopo e ritorna nel catch non stampa se c’è altro
- se try lancia exception che viene correttamente catchata subito dopo e poi lanciata un’altra exception dal catch che potrebbe essere a cascata presa dal catch dopo, in realtà nel catch entra solo una volta, e poi finally.
Checked Exceptions in Java:
- ClassNotFoundException
- InterruptedException
- InstantiationException
- IOException
- SQLException
- IllegalAccessException
- FileNotFoundException, etc
UnChecked Exceptions in Java:
- ArithmeticException
- ClassCastException
- NullPointerException
- ArrayIndexOutOfBoundsException
- NegativeArraySizeException
- ArrayStoreException
- IllegalThreadStateException
- SecurityException, etc.
Ereditarietà e Generics: sono due tecniche per riutilizzare il codice in Java
L’ereditarietà dunque consente di far ereditare attributi e metodi alle sottoclassi. Per farlo si usa Extends
Generics
- sono classi, interfacce e metodi GENERICI
https://www.html.it/pag/18028/il-tipo-generics-in-java/ - Sono una classe per domarli tutti i datatype.
- sono usati nelle struttura dati (= una lista) che infatti cambiano il tipo di datatype in base al contesto.
- I generics Java vengono utilizzati principalmente con il framework delle collection di Java. Le diverse raccolte come LinkedList, List, Map, HashMap, ecc. Utilizzano Generics per l’implementazione.
- ResponseEntity riceve un generics
Differenza tra interfacce e classi astratte:
- usiamo una classe astratta quando c’è una forte correlazione tra classe e sottoclasse
- usiamo un’interfaccia quando vogliamo associare/ereditare dei comportamenti (metodi)
- Una differenza significativa tra classi e interfacce è che le classi possono avere campi mentre le interfacce no. (solo costanti) perchè? *
Similitudini tra interfacce e classi astratte:
- Le interfacce e le classi astratte non hanno un costruttore!
- non possono essere istanziate
- possono avere metodi default e static
Interfacce:
- di base i metodi delle interfacce sono abstract e quindi senza body
- i metodi che nelle interfacce hanno il body devo essere definiti come default o static
- Le interfacce non implements nulla, possono extends più interfacce
- Qualsiasi variabile in un’interfaccia è implicitamente public, static e final, (una costante) indipendentemente dal fatto che queste parole chiave siano specificate o meno.
- i field possono solo essere costanti e non variabili a differenza della classe astratta.
- Un’interfaccia non è una classe! Indica dei comportamenti. Contiene quindi un’insieme di metodi senza body, ma che dovranno essere implementati dalle classi che le implementano.
Abstract class:
- Una classe abstract può anche non avere nessun metodo abstract. Ma se anche solo 1 metodo della classe viene dichiarato abstract, l’intera classe deve essere dichiarata abstract!
- a differenza di una interfaccia può avere field non statici, metodi non pubblici, un costruttore, insomma una classe a tutti gli effetti ma non istanziabile.
- una sottoclasse può extends solo una superclasse abstract *
Perchè? *
Uno dei motivi per cui il linguaggio di programmazione Java non consente di estendere più di una classe è evitare i problemi dell’ereditarietà multipla dello stato, che è la capacità di ereditare campi da più classi. Si supponga, ad esempio, di poter definire una nuova classe che estende più classi. Quando crei un oggetto istanziando quella classe, quell’oggetto erediterà i campi da tutte le superclassi della classe. Cosa succede se metodi o costruttori di diverse superclassi istanziano lo stesso campo? Quale metodo o costruttore avrà la precedenza? Poiché le interfacce non contengono campi, non devi preoccuparti dei problemi che derivano dall’ereditarietà multipla dello stato.
Polimorfismo: overload e override
è la capacità di un metodo di comportarsi in maniera diversa a seconda dell’oggetto dal quale viene invocato.
Immagina una classe animale che ha il metodo verso e la classe gatto o cane o rana che extende questa classe animale, loro avranno un implementazione diversa del metodo verso.
- Posso istanziare una sottoclasse in una variabile di type della superclasse.
Animale a = new Gatto();
- Nella programmazione ad oggetti per polimorfismo è strettamente legato al concetto di override e overload.
- Il nome del metodo della sottoclasse rimane lo stesso di quello della superclasse ma può essere ridefinito cosi:
- overload:
- Cambia la firma cioè quello che sta tra parentesi nel metodo.
- Può anche solo cambiare l’ordine dei parametri
- Il type di ritorno deve essere uguale solo se si tratta di primitivi
- override:
- Cambia l’implementazione del metodo quello che sta tra parentesi graffe. la logica di business.
Override ed Exception: (+ restrittivo o niente)
Il metodo che sta facendo override può generare un sottoinsieme dell’exception o una sottoclasse delle eccezioni generate dalla classe sottoposta a override. Anche l’assenza di clausola throws è valida poiché un set vuoto è un sottoinsieme valido.
The overriding method can throw a subset of the exception or subclass of the exceptions thrown by the overridden class. Having no throws clause is also valid since an empty set is a valid subset.
Incapsulamento: access modifiers
- L’incapsulamento permette di considerare una classe come una sorta di “scatola chiusa”, che permette di mostrare solo ciò che è necessario.
- l’incapsulamento generalmente significa rendere private gli attributi e fornire accessi public es. getter setters.
- L’incapsulamento permette di decidere quali metodi o variabili dare accesso all’esterno della classe.
- attraverso la definizione di access modifiers; public > protected > default > private
- public: i metodi/variabili sono accessibili anche da tutte le altre classi del progetto quindi anche fuori dal package della classe in cui si trovano;
- protected: i metodi/variabili sono accessibili dalla classe in cui si trovano e dalle sue sottoclassi (quindi anche se sono fuori dal package);
- default: se non viene specificato nulla di default i metodi/variabili sono accessibili solo nel package;
- private: i metodi/variabili sono accessibili solo dalla classe in cui si trovano
Ordine dell’inizializzazione della classe Java
1. Tutte le costanti, variabili e blocchi statici. Nell’ordine in cui appaiono nel codice.
2. Tutte le costanti, variabili e blocchi non statici. Nell’ordine in cui appaiono nel codice.
3. Costruttore.
Static – Cos’è un metodo statico in Java?
In Java un metodo static e una variabile static (detta anche variabile di classe) altro non è che un metodo/attributo che si applica alla classe e non ad una sua istanza. Concetto opposto agli access modifier: perchè sono accessibili a tutti. È possibile accedere a metodi/attributi statici senza istanziare un oggetto di una classe.
The static keyword is a non-access modifier used for methods and attributes. Static methods/attributes can be accessed without creating an object of a class.
Switch
boolean e long e le classi wrapper associate non sono supportate dalle istruzioni switch .
I tipi di dati supportati dalle istruzioni switch includono:
■ int e Integer
■ byte e Byte
■ short e Short
■ char e Character
■ String
■ valori enum
What will the following code print when run? public class TestClass { public void switchString(String input){ switch(input){ case "a" : System.out.println( "apple" ); case "b" : System.out.println( "bat" ); break; case "B" : System.out.println( "big bat" ); default : System.out.println( "none" ); } } public static void main(String[] args) throws Exception { TestClass tc = new TestClass(); tc.switchString("B"); } } risposta corretta: big bat none
Anche se “matchamo” il valore dentro lo switch col case, se non mettiamo nessun break dopo, il codice proseguirà a cascata fino alla fine.
Poiché non vi è alcuna interruzione break dopo questa istruzione case e la successiva istruzione case, il controllo passerà a quella successiva (che è l’impostazione default: ) e quindi verrà stampato anche “none”.
Differenza pass by value(primitives) e by reference(objects):
- Gli oggetti passati nei metodi vengono passati by reference e quindi gli effetti delle modifiche avvengono. Note that Arrays are Objects.
- I primitivi passati nei metodi invece si dice sono passed by value quindi a meno che non li assegni, le modifiche fatte sono fatte a una copia di quella reference.
// TODO loop
Continue:
il continue fa skippare quello che c’è dopo la corrente iterazione e riprende il loop da capo.
mentre il Break:
Interrompe il loop
Short Circuit operators:
|| e && sono anche noti come Short Circuit operators poiché non valutano il resto dell’espressione se il valore dell’espressione può essere determinato solo valutando parte dell’espressione, ad esempio ( true || (bool = false)) non assegneranno da false a bool perché il valore dell’espressione può essere detto semplicemente vedendo la prima parte, ovvero true. Ma ( true | (bool = false)) assegnerà false a bool.
// TODO list vs set
// TODO array
Discorso Progetti fatti:
-
avevamo un flusso di lavoro dove creavamo dei featured branch (brench) e merge su master solo quando avevamo passato i test
-
progetti con spring boot
-
testato la persistenza
-
ragionamenti sulle transazioni
-
non potevamo usare una transaction che avesse come perimetro una chiamata API (@transactional) ma abbiamo dovuto suddividere in blocchi con transazioni manuali per le considerazioni che abbiamo fatto.
-
persistenza nel ns progetto hibernate orm entity etc
jVM gestisce diversi thread.
webserver ha un pool di t. in ascolto sulla porta
quando arriva richiesta la prende in carico e ci fa cose-
fino a10 t. usano la nostra classe delegateImpl:
problema è un unica clsse con ujno stato +sono in 10 che usano la stessa cosa
ovvero int id
leggo variabile faccio cose e ci riscrivo
se siamo 2 t. che momentaneamnete la jvm ci da
diversi thread hanno accesso alla stessa variabile
atomic integer
è un contatore spiega ai thread che l’accesso è regolamentato.
synchronized List e Map
Pertanto, solo un thread può accedere alla sezione critica contrassegnata dalla parola chiave Synchronized.
quando c’è + di un thread che usa codice scritto e nostro codice non è stateless
quando dichiaro una variabile globale di classe quindi condivisa NON è thread-safe quindi ci preocc. di id
variabile interno al metodo sono sempre thread-safe
moveId.getAndIncrement()
questo metodo costringe a mettere in fila come in cassa al supermercato.
arrivi get id e poi incrementa,
quando salvi nelle liste qualcosa
Spiegazione Project SpringBoot Application con approccio API-first:
-
Partiamo definendo/descrivendo delle interfacce API usando OPENAPI con il tool Swagger editor. Design delle API con Swagger Editor ovvero creo un file YAML
-
ragionare sulla firma (signature) di queste API, quindi nome metodi, parametri in ingresso e cosa restituiscono
-
una volta convenuto su API sfruttiamo il plugin di OpenApi Maven Plugin per far si che lo stackholding (lo scheletro del codice) venga generato automaticamente.
- create automaticamente classi controller dell’API LAYER, le classi model quindi le Entity, (i components dello swagger)
- creiamo classe Service del business logic layer, classe Repository che si interfaccia col DB (data layer)
-
abbiamo implementato i nostri servizi ragionando sugli status code di response in caso soprattutto di errore: in caso di success 200 ok, in altri casi: Messaggi di risposta :
-
200 ok
-
201 created
-
400 bad request
-
401 unauthorized
-
403 forbidden
-
404 not found
-
405 method not allowed
-
1xx informational
-
2xx success
-
3xx redirect
-
4xx client error
-
5xx server error
-
-
in questo progetto abbiamo lavorato con un sistema di persistenza adottandone uno non persistito, quindi con una struttura dati (UNA LISTA) in memoria, a cui potevano accedere n° thread in contemporanea per questo motivo abbiamo deciso di adottare una struttura dati thread safe.
-
facendo una serie di considerazioni sul multithread e quindi sul fatto di usare atomic integer e strutture dati thread safe in modo che i thread non facessero a schiaffi per gestirle.
-
ci siamo dati un metodo di lavoro con GIT, un master branch dove abbiamo sempre garantito che il codice fosse compilabile e passasse tutti i test. Quando dovevamo fare modifiche creavamo featured branch in cui lavoravamo mergiando solo quando…
-
nel testare api abbiamo usato JUNIT test automatici ma abbiamo anche interagito con insomnia per testare API
-
// parla dell’ultimo atomic boolean usato. Avevo bisogno di un semaforo che all’avvio dell’endpoint start controllasse che non ci fosse già una sincronizzazione in corso e quindi stessi già scrivendo sul database. che fosse quindi usato in maniera atomica dai vari thread. come faccio a garantirlo che era un semaforo?perchè il container quarkus quando io annoto la classe con @ApplicationScoped mi garantisce che sia istanziata una sola volta e quindi ho un solo atomic boolean e questo atomic boolean viene scritto e letto in contemporanea. quando è rosso tutti gli altri si mettono in fila.