Author: Michael Schaffler-Glößl
Customers are regularly asking me if there is some overview over the most important Spring annotations. Until now I always refused to create such an overview, too simplified it would be, I thought.
Finally, I did it and I thought, maybe it be might be useful for beginners using Spring framework and related APIs.
Hope it helps anybody. If, please share using the buttons on the top.
Contents
Overview Spring Annotations. 1
Implicit bean definition
using stereotype annotations. 1
Spring configuration
classes. 2
Annotations for dependency
injection. 2
@Component
public class MyComponent {
// Spring Bean with arbitrary role in application
}
@Service
public class MyService {
// Spring bean that acts as a service
// i.e.
implements business logic
}
@RestController
@RequestMapping("/api/myrestcontroller")
public class MyRestController {
//spring bean that acts as a rest controller with base
url "/api/myrestcontroller"
}
@Repository
public class MyRepository {
// Spring bean which acts as a repository
// all database (jdbc,
jpa, ...) exceptions are mapped to subclasses of DataAccessException
}
@Configuration //
denotes the class as a configuration class
// Configuration classes are called only by the spring container
// never by the programmers code
public class MyConfig {
//configuration classes define 1 to n Spring
Beans
@Bean("albert") // Name of the bean
public String
nameOfGenius(){
return "Albert
Einstein";
}
@Bean //name
defaults to method name
public LocalDate birthDateOfGenius(){
return LocalDate.of(1879, 3 , 14);
}
@Bean //
Parameter injection example
public HumanBeing albertEinstein(@Value("#{albert}")String name,
@Value("#{birthDateOfGenius}")LocalDate birthDate){
return new HumanBeing(name,
birthDate);
}
}
public
class InjectionsUsingAutowired {
@Autowired // field dependency injection by type
private StudentRepo studentRepo;
private MyService myService;
private TrainingRepo trainingRepo;
@Autowired // constructor dependency injection by type
public InjectionsUsingAutowired(MyService myService) {
this.myService = myService;
}
@Autowired // setter dependency Injection by type
public void setTrainingRepo(TrainingRepo trainingRepo) {
this.trainingRepo = trainingRepo;
}
}
public
class InjectionsUsingValue {
@Value("#{studentRepo}") // field dependency injection by bean name
private StudentRepo studentRepo;
private MyService myService;
private TrainingRepo trainingRepo;
@Autowired // constructor dependency injection by bean name
public InjectionsUsingValue(@Value("#{myServiceImpl1}") MyService myService) {
this.myService = myService;
}
@Autowired // setter dependency Injection by bean name
public void setTrainingRepo(@Value("#{trainingRepo}") TrainingRepo trainingRepo) {
this.trainingRepo = trainingRepo;
}
}
@RestController
@RequestMapping("/api/customers")
public
class CustomerRestController {
@Autowired
private CustomerRepo customerRepo;
//GET http://localhost:8080/api/customers
@GetMapping
public List<Customer> findAllCustomers(){
return customerRepo.findAll();
}
// GET http://localhost:8080/api/customers/search?name=Michael
@GetMapping("search")
public List<Customer> findCustomerByName(@RequestParam("name")String name){
return customerRepo.findByName(name);
}
//GET http://localhost:8080/api/customers/12
@GetMapping("{id}")
public ResponseEntity<Customer> findById(@PathVariable("id") Long id){
return ResponseEntity.of(customerRepo.findById(id));
}
//DELETE
http://localhost:8080/api/customers/12
@DeleteMapping("{id}")
@PreAuthorize("hasAnyRole('ADMIN')")
public boolean deleteById(@PathVariable("id")Long id){
return customerRepo.delete(id);
}
}
/*
POST
localhost:8080/api/customers
Content-Type: application/json
{
"name": "Katarina",
"email":
"katarina@javatraining.at"
}
*/
@PostMapping
public Customer
save(@RequestBody Customer customer){
return customerRepo.save(customer);
}
}
@RestController
@RequestMapping("/api/customers")
public class CustomerController {
@Autowired
private CustomerRepo customerRepo;
@GetMapping("{id}")
public ResponseEntity> findById(@PathVariable("id") Long
id){
Optional<Customer> customerOpt = customerRepo.findById(id);
if (customerOpt.isPresent()){
return ResponseEntity.ok().body(customerOpt.get());
} else {
Error error = new Error("Could not find
customer", "id=" + id);
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(error);
}
}
}
@ControllerAdvice
public class MyExceptionHandler extends
ResponseEntityExceptionHandler {
@ExceptionHandler({DataAccessException.class})
public ResponseEntity<Error> handleJpaException(DataAccessException ex, WebRequest request){
//returning your own error object
Error error = new
Error("Database operation not sucessful.", ex.getMessage());
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(error);
}
@ExceptionHandler(NullPointerException.class)
public ResponseEntity<Object> handleNullpointerException(NullPointerException ex,
WebRequest request){
//convenience method inherited from ResponseEntityExceptionHandler
return handleExceptionInternal(ex,
"Sorry, something went wrong.",
new HttpHeaders(),HttpStatus.INTERNAL_SERVER_ERROR,
request);
}
}
@Component
@Scope("singleton")
/**
* Singleton Scope is the default scope
for Spring beans.
* There is only one instance of this
bean. It is created when application starts and lives until application
terminates
*/
public class ScopedBean {
//...
}
@Component
@Scope("prototype")
/**
* everytime
this bean is injected somewhere else
* a new instance will be created.
* It will live as long as the bean it is
injected into.
*/
public class ScopedBean {
//...
}
@Component
@RequestScope
// alternatively
@Scope("request")
/**
* for each user-session in the browser
* there will be one instance of this
bean
*/
public class ScopedBean {
//...
}
@Component
@SessionScope
// alternatively
@Scope("session")
/**
* for each user-session in the browser
* there will be one instance of this
bean
*/
public class ScopedBean {
//...
}
@Data
// tell lombok to generate getters, setters, equals, hashCode, toString
@NoArgsConstructor
//tell lombok to generate a noargs
constructor
@RequiredArgsConstructor
//tell lombok to generate a constructor for all NonNull fields
@Entity // required on every entity
@Table(name="students") //
tell JPA to which table this entity should be stored
public class Student {
@Id // tell JPA that
// this is the primary key of the entity
@GeneratedValue(strategy = GenerationType.IDENTITY) // tell JPA that DB generates this value
@Column(name="id") //
tell JPA in which column this field should be stored
private Long id;
@NonNull // tell lombok that this
field will not be null / may not be null
@Column(name="name")
private String name;
@NonNull
@Column(name="email")
private String email;
}
@Data
@NoArgsConstructor
@RequiredArgsConstructor
@Entity
@Table(name="trainings")
public class Training {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name="id")
private Long id;
…
@EqualsAndHashCode.Exclude // tell lombok that this
field should not be used for equals and hashCode
@ManyToOne(cascade = {CascadeType.MERGE, CascadeType.PERSIST}) // tell JPA about a foreign key (1:n)
relationship to entity trainer
@JoinColumn(name="id_trainer") //
name of the column in which the foreign key should be stored
private Trainer trainer;
@EqualsAndHashCode.Exclude
@ManyToMany(cascade = {CascadeType.MERGE, CascadeType.PERSIST}) // tell JPA about a foreign key (n:m) relationship to entity Student
@JoinTable(name = "trainings_to_students", // tell JPA in which mapping table the foreign key
relationship is mapped
joinColumns = @JoinColumn(name="id_training"), //
what is the column that relates to my own key
inverseJoinColumns = @JoinColumn(name="id_student")) //
what is the column that relates to the key of entity student
private List
}
//
definition of a Spring Data JPA repository
public interface TrainingRepo extends JpaRepository
public List
@Query("select t from Training t")
public List
@Query("select t from Training t where
t.trainer.name = :trainername") // using parameters in queries
public List
// directly query into a Data Transfer
Object
@Query("select new at.javatraining.jpa.entities.dtos.TrainingSummaryDto(t.id,
t.title, t.begin, t.students.size) from Training t")
public List<TrainingSummaryDto>
getTrainingSummaries();
}
public interface TrainingTitleSummaryView {
public Long getId();
public String
getTitle();
@Value("#{target.students.size()}")
public int getNumberOfStudents();
}
// definition
of a Spring Data JPA repository
public interface TrainingRepo extends JpaRepository
…
// query into a Spring projection
@Query("select t from Training t")
public List<TrainingTitleSummaryView>
getTrainingTitleSummaryView();
}
@Aspect
// define an aspect
@Component // make it a spring component
@Slf4j // generate variable with SLF4J Logger
public class LoggingAspect {
@Around("execution(* at.javatraining.trainings.service.*.*(..))") // define where the aspect should be called
public Object log(ProceedingJoinPoint
pjp) throws Throwable{
String methodName
= pjp.getSignature().getName();
String params = Arrays.toString(pjp.getArgs());
log.info("Method call: {} params: {}", methodName,
params);
Object result = pjp.proceed();
log.info("Returning call: {} params: {}", methodName,
params);
return result;
}
}