Spring - rlip/java GitHub Wiki
@Component - przed nazwą klasy - które klasy mają być zainicjowane przez Springa
@Repository - działa jak wyżej, ale żeby programista wiedział że to klasa do operacji crud
@Service - j.w. ale to klasa metody biznesowe dotyczące jakieś logiki
@Scope("prototype") - za każdym razem jest tworzony nowy komponent, domyślnie jest singleton
@Autowired - przed wartością, konstruktorem, bądź seterem, spring tam wsczyknie komponent
@Qualifier(value="lancelot") - podawany przed zmienną lub przed zmienną w konstruktorze,
dzieki temu jak mamy więcej beenów o takiej samej klasie to wstrzykujemy bean o poddanej nazwie
@Primary jeśli w konfigu to ustawimy dla jakiegoś beena których jest więcej, to wtedy ten będzie wybrany
@PropertySource("classpath:castle.properties") - przed klasą, ustawia źródło wartości
@Value("${my.castle.name:Domyslna nazwa zamku}") - przed wartością - ustawienie jakiejś wartości przez springa w tym przypadku z konfiguracji
@SpringBootApplication - główna klasa aplikacji
@ImportResource("classpath:config/spring-config.xml") - to jeśli używamy konfiguracji xml
@ComponentScan - można dać przed główną klasą, żeby wypisać miejsca lub klasy, które ma spring zainicjować
@ComponentScan(basePackageClasses = {Starter.class, Castle.class, Quest.class, Knight.class})
@ComponentScan({"com.clockworkjava.kursspring", "com.clockworkjava.component"})
@PostConstruct i @PreDestroy - wywołane po utworzeniu / przed usunięciem komponentu
@Overrride - info dla kompilatora, że nadpisujemy coś
@RunWith(SpringRunner.class)
@SpringBootTest - to i to wyżej nad klasą testującą dodaje kontekst springa, ale wtedy wczytuje się dużo dłużej niż jakby było bez tego
@EnableScheduling - to nad klasą główną
@Scheduled(fixedDelay = 1000) i to nad jakąś metodą pozwoli na wykonywanie tej metody co sekundę, fixRate - czas liczony od momentu uruchomienia, initialDelay - opóźnienie
@Bean(name="lonelyHeartsClubBand")
public CompactDisc sgtPeppers() {
return new SgtPeppers();
} // to w konfiguracji tworzy komponent do springowego kontekstu. Name może zmienić nazwę
//Tak też można od razu coś wstrzyknąć
@Bean
public CDPlayer cdPlayer() {
return new CDPlayer(sgtPeppers());
}
Walidacja
import javax.validation.constraints.Digits;
import javax.validation.constraints.Pattern;
import org.hibernate.validator.constraints.CreditCardNumber;
import javax.validation.constraints.NotBlank;
@NotBlank(message="Podanie imienia i nazwiska jest obowiązkowe.")
@CreditCardNumber(message="To nie jest prawidłowy numer karty kredytowej.")
@Pattern(regexp="^(0[1-9]|1[0-2])([\\/])([1-9][0-9])$", message="Wartość musi być w formacie MM/RR.")
@Digits(integer=3, fraction=0, message="Nieprawidłowy kod CVV.")
Aplikacje Springa mogą używać rest na kilka sposobów:-
- RestTemplate - prosty synchroniczny klient dostarczny przez Spring
- Traverson - synchroniczny, obsługujący hiperłącza klient, dostarczany przez Spring Hateoas
- WebClient - reaktywny, asynchroniczny, wprowadzony w Spring 5
obsługa błędów np.:
// w kontrollerze:
@ExceptionHandler(AbstractException.class)
ResponseEntity<ErrorDtoWithDescription> handleException(AbstractException exception) {
return ErrorDtoWithDescription.from(exception).asHttpResponse();
}
////
@Getter
public class AbstractException extends RuntimeException implements DigiPayException {
protected final Integer httpStatus;
protected final String description;
public AbstractException(String message, Integer status, String description) {
super(message);
this.httpStatus = status;
this.description = description;
}
}
////
public class BadRequestException extends AbstractException {
public BadRequestException(String message) {
super(HttpStatus.BAD_REQUEST.getReasonPhrase(), HttpStatus.BAD_REQUEST.value(), "The incoming request has a bad syntax: " + message);
}
}
////
public class ErrorDtoWithDescription {
@JsonIgnore
Integer httpStatus;
String message;
String description;
public static ErrorDtoWithDescription from(AbstractException exception) {
return new ErrorDtoWithDescription(exception.getHttpStatus(),
exception.getMessage(),
exception.getDescription());
}
public ResponseEntity<ErrorDtoWithDescription> asHttpResponse() {
return ResponseEntity
.status(httpStatus)
.body(this);
}
Ma wiele operacji, w zależności od tego jak i co chcemy wysłać i otrzymać i jakie restowe zapytanie chcemy wywołać.
RestTemplate rest = new RestTemplate(); //lub
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
//////////////////
public Ingredient createIngredient(Ingredient ingredient) {
return rest.postForObject("http://localhost:8080/ingredients", ingredient, Ingredient.class);
}
@RequestMapping("/api/user")
@SuppressWarnings("unused")
public class UserController {
@GetMapping("/currentUser")
public ApiResponse getCurrentUser(@CurrentUser UserPrincipal userPrincipal) {
@GetMapping("/zipCodeAutocomplete")
public List<ZipCode> zipCodeAutocomplete(@NotBlank @RequestParam("searchValue") String searchValue) {
return userService.getZipCodesLikeSearchValueLimited(searchValue);
}
/////////////////////////////
@PostMapping("/login")
public ApiResponse login(@Valid @RequestBody LoginRequest loginRequest) {
/////////////////////////////
@RequestMapping("/knight")
public String getKnight(@RequestParam("id") Integer id, Model model) { // jeśli robimy ?id=
@RequestMapping("/knight/delete/{id}")
public String deleteKnight(@PathVariable("id") Integer id, Model model) { // jeśli robimy /id
/////////////////////////////
@RestController @RequestMapping(path="/design", produces="application/json") // Obsługa żądań do /design
@CrossOrigin(origins="*") //Pozwala na żądania między domenami.
public class DesignTacoController {
/////////////////////////////
@PostMapping(consumes="application/json")
@ResponseStatus(HttpStatus.CREATED) // zwraca 201
public Taco postTaco(@RequestBody Taco taco) {
return tacoRepo.save(taco);
}
/////////////////////////////
@DeleteMapping(„/{orderId}")
@ResponseStatus(code=HttpStatus.NO_CONTENT)
public void deleteOrder(@PathVariable("orderId") Long orderId) {
try {
repo.deleteById(orderId);
} catch (EmptyResultDataAccessException e) {
}
/////////////////////////////
@PatchMapping(path="/{orderId}", consumes="application/json") // przykład aktualizacji częściowej
public Order patchOrder(@PathVariable("orderId") Long orderId, @RequestBody Order patch) {
Order order = repo.findById(orderId).get();
if (patch.getDeliveryName() != null) {
order.setDeliveryName(patch.getDeliveryName());
}
return repo.save(order);
}
Tak można robić obiekt, który będzie zapisany w sesji i będzie dostępny w różnych kontrolerach:
@Controller
@RequestMapping("/design")
@SessionAttributes("order") //wskazuje obiekty modelu, które powinny pozostać w sesji i być dostępne w wielu żądaniach.
public class DesignTacoController {
@ModelAttribute(name = "order")
public Order order() {
return new Order();
}
@ModelAttribute(name = "taco")
public Taco taco() {
return new Taco();
}
@PostMapping
public String processDesign(@Valid Taco design, Errors errors, @ModelAttribute Order order) {
// @ModelAttribute określa, że wartość pochodzi z modelu, a Spring MVC nie będzie dołączać do niego parametrów żądania.
if (errors.hasErrors()) {
return "design";
}
Taco saved = designRepo.save(design);
order.addDesign(saved);
return "redirect:/orders/current";
}
}
// jak już nie chcemy przechowywać obiektu w sesji to robimy setComplete
@PostMapping
public String processOrder(@Valid Order order, Errors errors, SessionStatus sessionStatus) {
if (errors.hasErrors()) {
return "orderForm";
}
orderRepo.save(order);
sessionStatus.setComplete();
return "redirect:/";
}
Dodanie hiperłączy w rest - HATEOAS. Spring w akcji V - 6.2.1
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-hateoas</artifactId>
</dependency>
Można korzystać z profili w konfiguracjach przed nazwą klasy lub metody, s90+. Komponenty bez profili przypisywane są zawsze. Ustawia się je jako spring.profiles.active lub gdy nie ustawiona to spring.profiles.default na wiele sposobów s90. Może być aktywnych wile profili na raz.
@Configuration @Profile("dev")
public class DevelopmentProfileConfig
W testach profil można tak ustawić:
@ActiveProfiles("dev")
public class PersistenceTest {
-
albo @Primary w konfigu
-
albo @Qualifier("iceCream") pod @Autowired. iceCream to domyślny kwalifikator klasy IceCream, ale można (i nawet dobrze) tworzyć własne kwalifikatory też za pomocą tej samej adnotacji @Qualifier("iceCream") pod jedną z adnotacji komponentu np. @Component albo też w konfigu pod @Bean.
-
można użyć adnotacji warunkowej @Conditional(xxx.class) z klasą implementującą Condition posiadającą klasę matches, zwracającą true jak komponent ma zostać utworzony. s93
-
jeśli to nie wystarcza można tworzyć własne adnotacje będące kwalityfikatorami s103
Ustawia się za pomocą @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) stasowanej z @Bean lub @Component s102
- Singleton — jedna instancja komponentu tworzona dla całej aplikacji;
- Prototype (prototyp) — jedna instancja komponentu tworzona za każdym razem, gdy komponent jest wstrzykiwany lub pobierany z kontekstu aplikacji Springa;
- Session (sesja) — w aplikacji internetowej jedna instancja obiektu utworzona dla każdej sesji;
- Request (żądanie) — w aplikacji internetowej jedna instancja obiektu utworzona dla każdego żądania.
- application
- websocket
jak używamy session lub request to trzeba użyć proxy by można te pole tym zainicjalizować przy starcie kontentu, zanim jest sesja czy request s103
@Component
@Scope(
value=WebApplicationContext.SCOPE_SESSION,
proxyMode=ScopedProxyMode.INTERFACES) // lub ScopedProxyMode.TARGET_CLASS jeśli ShoppingCart jest klasą
public ShoppingCart cart() {
-----------
@Component
public class StoreService {
@Autowired
public void setShoppingCart(ShoppingCart shoppingCart) {
this.shoppingCart = shoppingCart;
}
...
}
@Configuration
@PropertySource("classpath:/com/soundsystem/app.properties") //deklaracja źródła właściwości
public class ExpressiveConfig {
@Autowired
Environment env;
@Bean
public BlankDisc disc() {
return new BlankDisc(
env.getProperty("disc.title"), // Pobieranie wartości właściwości
env.getProperty("disc.artist"));
}
biblioteka o tokena - java JWT
@Configuration
@EnableGlobalMethodSecurity(
securedEnabled = true,
jsr250Enabled = true,
prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
securedEnabled – enables the spring @Secured annotation. jsr250Enabled – enables the JSR-250 standard java security annotations. prePostEnabled – enables the spring @PreAuthorize and @PostAuthorize annotations.
https://www.baeldung.com/spring-security-method-security
Tabelka do configa - Spring w akcji V str 127 i 128
Można w konfigu deklarować coś jako aspekt, i można to tam podłączyć pod jakąś funkcję i wywołać przed albo po niej s35 Można też aspekterm dodać implementację innego interfejsu jakieś klasie (ten naprawdę to obiektowi ją opakowującemu)
- Before — Funkcjonalność porady jest wykonywana przed wywołaniem metody z poradą.
- After — Funkcjonalność porady jest wykonywana po zakończeniu działania metody z poradą, niezależnie od wyniku jej działania.
- After-returning — Funkcjonalność porady jest wykonywana po prawidłowym zakończeniu metody z poradą.
- After-throwing — Funkcjonalność porady jest wykonywana po zgłoszeniu wyjątku przez metodę z poradą.
- Around — Porada realizuje tę samą funkcjonalność zarówno przed wywołaniem, jak i po zakończeniu metody z poradą. s121, s133
package concert;
public interface Performance {
public void perform();
}
execution(* concert.Performance.perform()) and !bean('woodstock')
//porada aspektu zostanie wpleciona do wszystkich komponentów, które mają nazwę różną od woodstock
@Configuration
@EnableAspectJAutoProxy // to potrzebne
@ComponentScan
public class ConcertConfig {
}
@Aspect
public class Audience {
// Żeby nie powtarzać punktu przecięcia przed każdą klasą to można zrobić tak
@Pointcut("execution(* concert.Performance.perform(..))")
public void performance() {
}
@Before("performance()")
public void silenceCellPhones() {
System.out.println("Widzowie wyciszają telefony komórkowe");
}
@Before("performance()")
public void takeSeats() {
System.out.println("Widzowie zajmują miejsca");
}
@AfterReturning("performance()")
public void applause() {
System.out.println("Brawooo! Oklaski!");
}
@AfterThrowing("performance()")
public void demandRefund() {
System.out.println("Buu! Oddajcie pieniądze za bilety!");
}
///Albo wszystko w jednej:
@Around("execution(* concert.Performance.perform(..))")
public void watchPerformance(ProceedingJoinPoint jp) {
try {
System.out.println("Widzowie wyciszają telefony komórkowe.");
System.out.println("Widzowie zajmują miejsca.");
jp.proceed();
System.out.println("Brawooo! Oklaski!");
} catch (Throwable t) {
System.out.println("Buu! Oddajcie pieniądze za bilety!");
}
}
}
public interface Encoreable {
void makeBis();
}
----
@Component
public class EncoreableImpl implements Encoreable {
@Override
public void makeBis() {
System.out.println("Robię BIS!!!");
}
}
----
@Aspect
public class EncoreableIntroducer {
//value - typ komponentów, które mają implementować interfejs, znak plusa na końcu oznacza,
// że chodzi o podtyp Performance, a nie o interfejs sam w sobie
//defaultImpl - identyfikuje klasy, które dostarczą implementacji dla wprowadzenia
// może tu też być delegate-ref - wtedy podajemy nazwę sprinowego beana
//Statyczna właściwość opatrzona adnotacją @DeclareParents określa wprowadzany interfejs
@DeclareParents(value="concert.Performance+", defaultImpl=EncoreableImpl.class)
public static Encoreable encoreable;
}
----
@Component
public class Starter implements CommandLineRunner {
@Autowired
Performance performance;
@Override
public void run(String... strings) {
performance.perform();
((Encoreable) performance).makeBis();
}
}