Spring/SpringBoot common annotation summary

1.Spring Bean related

1.1.@Autowired

Objects are automatically imported into classes, and the injected classes are also managed by the Spring container. For example: the Service class is injected into the Controller class.

@Service
public class UserService {
  ......
}

@RestController
@RequestMapping("/users")
public class UserController {
   @Autowired
   private UserService userService;
   ......
}

1.2.@Component,@Repository,@Service,@Controller

We generally use the @Autowired annotation to let the Spring container automatically assemble bean s for us. To identify a class as a class that can be used for @Autowired annotation auto-wired bean s, you can use the following annotations:

  • @Component: A general annotation that can mark any class as a Spring component. If a Bean does not know which layer it belongs to, it can be marked with the @Component annotation.
  • @Repository: Corresponds to the persistence layer, namely the Dao layer, which is mainly used for database-related operations.
  • @Service: Corresponds to the service layer, which mainly involves some complex logic and requires the use of the Dao layer.
  • @Controller: Corresponding to the Spring MVC control layer, the main user accepts user requests and calls the Service layer to return data to the front-end page.

1.3.@RestController

The @RestController annotation is a collection of @Controller and ResponseBody, indicating that this is a controller bean, and it is a REST-style controller that directly fills the return value of the function into the HTTP response body.
Using @Controller alone without adding @ResponseBody is generally used in the case of returning a view. This situation belongs to the more traditional Spring MVC application, corresponding to the situation where the front and back ends are not separated. @Controller+@ResponseBody returns JSON or XML form data

1.4.@Scope

Declare the scope of Spring Bean, use:

@Bean
@Scope("singleton")
public Person personSingleton() {
    return new Person();
}

Four common Spring Bean scopes:

  • singleton : the only bean instance, all beans in Spring are singletons by default.
  • prototype : A new bean instance will be created for each request.
  • request : Each HTTP request will generate a new bean, which is only valid within the current HTTP request.
  • session : Each HTTP request will generate a new bean, which is only valid within the current HTTP session.

1.5.@Configuration

Generally used to declare configuration classes, you can use @Component annotations instead, but using Configuration annotations to declare configuration classes is more semantic.

@Configuration
public class AppConfig {
    @Bean
    public TransferService transferService() {
        return new TransferServiceImpl();
    }
}

2. Handle common HTTP request types

5 common request types:

  • GET : Request to get a specific resource from the server. For example: GET /users (get all students)
  • POST : Creates a new resource on the server. For example: POST /users (create student)
  • PUT : Update the resource on the server (the client provides the updated entire resource). Example: PUT /users/12 (update student number 12)
  • DELETE : Delete a specific resource from the server. For example: DELETE /users/12 (delete student number 12)
  • PATCH : Update resources on the server (clients provide changed attributes, which can be regarded as partial updates), and are used less, so I won’t give examples here.

2.1. GET request

@GetMapping("users") is equivalent to @RequestMapping(value="/users",method=RequestMethod.GET)

@GetMapping("/users")
public ResponseEntity<List<User>> getAllUsers() {
 return userRepository.findAll();
}

2.2. POST request

@PostMapping("users") is equivalent to @RequestMapping(value="/users",method=RequestMethod.POST)

@PostMapping("/users")
public ResponseEntity<User> createUser(@Valid @RequestBody UserCreateRequest userCreateRequest) {
 return userRespository.save(user);
}

2.3. PUT request

@PutMapping("/users/{userId}") is equivalent to @RequestMapping(value="/users/{userId}",method=RequestMethod.PUT)

@PutMapping("/users/{userId}")
public ResponseEntity<User> updateUser(@PathVariable(value = "userId") Long userId,
  @Valid @RequestBody UserUpdateRequest userUpdateRequest) {
  ......
}

2.4. DELETE request

@DeleteMapping("/users/{userId}") is equivalent to @RequestMapping(value="/users/{userId}",method=RequestMethod.DELETE)

@DeleteMapping("/users/{userId}")
public ResponseEntity deleteUser(@PathVariable(value = "userId") Long userId){
  ......
}

2.5. PATCH request

Generally, in actual projects, we use PATCH requests to update data after PUT is not enough.

@PatchMapping("/profile")
  public ResponseEntity updateStudent(@RequestBody StudentUpdateRequest studentUpdateRequest) {
        studentRepository.updateDetail(studentUpdateRequest);
        return ResponseEntity.ok().build();
    }

3. Passing values ​​from front to back end

3.1. @PathVariable and @RequestParam

@PathVariable is used to get path parameters and @RequestParam is used to get query parameters.

@GetMapping("/klasses/{klassId}/teachers")
public List<Teacher> getKlassRelatedTeachers(
         @PathVariable("klassId") Long klassId,
         @RequestParam(value = "type", required = false) String type ) {
...
}

If the url we request is: /klasses/{123456}/teachers?type=web
Then the data obtained by our service is: klassId=123456,type=web.

3.2. @RequestBody

It is used to read the body part of the Request request (possibly POST,PUT,DELETE,GET request) and the Content-Type is application/json format data. After receiving the data, it will automatically bind the data to the Java object. The system will use HttpMessageConverter or a custom HttpMessageConverter to convert the json string in the request body to a java object.

I use a simple example to demonstrate the basic use!

We have a registered interface:

@PostMapping("/sign-up")
public ResponseEntity signUp(@RequestBody @Valid UserRegisterRequest userRegisterRequest) {
  userService.save(userRegisterRequest);
  return ResponseEntity.ok().build();
}

UserRegisterRequest object:

@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserRegisterRequest {
    @NotBlank
    private String userName;
    @NotBlank
    private String password;
    @FullName
    @NotBlank
    private String fullName;
}

We send a post request to this interface, and the body carries JSON data:

{"userName":"coder","fullName":"shuangkou","password":"123456"}

In this way, our backend can directly map the data in json format to our UserRegisterRequest class.

👉 It should be noted that a request method can only have one @RequestBody, but can have multiple @RequestParam and @PathVariable. If your method must use two @RequestBody to accept data, there is a high probability that there is a problem with your database design or system design!

4. Read configuration information

Many times we need to put some commonly used configuration information such as Alibaba Cloud oss, sending SMS, WeChat authentication related configuration information, etc. into the configuration file.

Let's take a look at how Spring provides us with ways to help us read these configuration information from configuration files.

The content of our data source application.yml is as follows:

yongfan2022: la la la la

my-profile:
  name: code farmer user
  email: 16601532500@163.com

library:
  location: la la la la
  books:
    - name: Basic Law of Genius
      description: Twenty-two-year-old Lin Zhaoxi, on the day her father was diagnosed with Alzheimer's disease, learned that Pei Zhi, the campus male god she had a crush on for many years, was about to go abroad for further studies - the school that the other party was admitted to was the same school that her father gave up for her back then. Place.
    - name: order of time
      description: Why do we remember the past, but not the future? What does it mean for time to "pass"? Do we exist in time, or does time exist in us? Carlo·Rowe uses poetic words to invite us to think about this ancient problem-the nature of time.
    - name: amazing me
      description: How to form a new habit? How to make the mind become more mature? How to have a quality relationship?  How to get out of difficult moments in life?

4.1. @value (commonly used)

Use @Value("${property}") to read simpler configuration information:

@Value("${yongfan2022}")
String yongfan2022;

4.2. @ConfigurationProperties (commonly used)

Read configuration information through @ConfigurationProperties and bind it to bean s.

@Component
@ConfigurationProperties(prefix = "library")
class LibraryProperties {
    @NotEmpty
    private String location;
    private List<Book> books;

    @Setter
    @Getter
    @ToString
    static class Book {
        String name;
        String description;
    }
  to omit getter/setter
  ......
}

You can inject it into a class just like using a normal Spring bean.

4.3. PropertySource (not commonly used)

@PropertySource reads the specified properties file

@Component
@PropertySource("classpath:website.properties")

class WebSite {
    @Value("${url}")
    private String url;

  to omit getter/setter
  ......
}

5. Globally handle Controller layer exceptions

Introduce the necessary global handling of Controller layer exceptions in our Spring project.

Related notes:

  1. @ControllerAdvice : Annotation defines a global exception handling class
  2. @ExceptionHandler : Annotation declares exception handling method

How to use it? Take our parameter validation in Section 5 as an example. If the method parameters are incorrect, a MethodArgumentNotValidException will be thrown, and we will handle this exception.

@ControllerAdvice
@ResponseBody
public class GlobalExceptionHandler {

    /**
     * Request parameter exception handling
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<?> handleMethodArgumentNotValidException(MethodArgumentNotValidException ex, HttpServletRequest request) {
       ......
    }
}

For more on Spring Boot exception handling, please see my two articles:

6.JPA related

6.1. Creating tables

@Entity declares that a class corresponds to a database entity.
The @Table setting indicates

@Entity
@Table(name = "role")
public class Role {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private String description;
    to omit getter/setter......
}

6.2. Create primary key

@Id : declares a field as the primary key.

After using the @Id declaration, we also need to define the primary key generation strategy. We can specify the primary key generation strategy using @GeneratedValue.

1. Directly use the four primary key generation strategies provided by JPA through @GeneratedValue to specify the primary key generation strategy.

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

JPA uses enumeration to define 4 common primary key generation strategies, as follows:

public enum GenerationType {

    /**
     * Use a specific database table to hold the primary key
     * The persistence engine generates the primary key through a specific table of the relational database,
     */
    TABLE,

    /**
     *In some databases, the self-growth of the primary key is not supported, such as Oracle and PostgreSQL, which provide a mechanism called "sequence (sequence)" to generate the primary key
     */
    SEQUENCE,

    /**
     * primary key self-growth
     */
    IDENTITY,

    /**
     *Give the primary key generation strategy to the persistence engine (persistence engine),
     *The persistence engine will choose one of the above three primary key generation strategies according to the database
     */
    AUTO
}

The default strategy for the @GeneratedValue annotation is GenerationType.AUTO

public @interface GeneratedValue {

    GenerationType strategy() default AUTO;
    String generator() default "";
}

Generally, if you use a MySQL database, it is more common to use the GenerationType.IDENTITY strategy (for distributed systems, you need to consider using distributed IDs).

2. Declare a primary key strategy through @GenericGenerator, and then @GeneratedValue uses this strategy

@Id
@GeneratedValue(generator = "IdentityIdGenerator")
@GenericGenerator(name = "IdentityIdGenerator", strategy = "identity")
private Long id;

Equivalent to:

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

The primary key generation strategies provided by jpa are as follows:

public class DefaultIdentifierGeneratorFactory
  implements MutableIdentifierGeneratorFactory, Serializable, ServiceRegistryAwareService {

 @SuppressWarnings("deprecation")
 public DefaultIdentifierGeneratorFactory() {
  register( "uuid2", UUIDGenerator.class );
  register( "guid", GUIDGenerator.class );   // can be done with UUIDGenerator + strategy
  register( "uuid", UUIDHexGenerator.class );   // "deprecated" for new use
  register( "uuid.hex", UUIDHexGenerator.class );  // uuid.hex is deprecated
  register( "assigned", Assigned.class );
  register( "identity", IdentityGenerator.class );
  register( "select", SelectGenerator.class );
  register( "sequence", SequenceStyleGenerator.class );
  register( "seqhilo", SequenceHiLoGenerator.class );
  register( "increment", IncrementGenerator.class );
  register( "foreign", ForeignGenerator.class );
  register( "sequence-identity", SequenceIdentityGenerator.class );
  register( "enhanced-sequence", SequenceStyleGenerator.class );
  register( "enhanced-table", TableGenerator.class );
 }

 public void register(String strategy, Class generatorClass) {
  LOG.debugf( "Registering IdentifierGenerator strategy [%s] -> [%s]", strategy, generatorClass.getName() );
  final Class previous = generatorStrategyToClassNameMap.put( strategy, generatorClass );
  if ( previous != null ) {
   LOG.debugf( "    - overriding [%s]", previous.getName() );
  }
 }
}

6.3. Set field type

@Column declares a field.
Example:
Set the database field corresponding to the attribute userName as user_name, with a length of 32 and not empty

@Column(name = "user_name", nullable = false, length=32)
private String userName;

It is quite common to set the field type and add the default value.

Column(columnDefinition = "tinyint(1) default 1")
private Boolean enabled;

6.4. Specifying not to persist specific fields

@Transient : Declare fields that do not need to be mapped with the database, and do not need to be saved into the database when saving.
If we want the secrect field not to be persisted, we can use the @Transient keyword declaration.

Entity(name="USER")
public class User {

    ......
    @Transient
    private String secrect; // not persistent because of @Transient

}

In addition to the @Transient keyword declaration, the following methods can also be used:

static String secrect; // not persistent because of static
final String secrect = "Satish"; // not persistent because of final
transient String secrect; // not persistent because of transient

Generally, there are many ways to use annotations.

6.5. Declaring large fields

@Lob: Declare a field as a large field.

@Lob
private String content;

A more detailed statement:

@Lob
//Specify the acquisition strategy of Lob type data, FetchType.EAGER means non-lazy loading, and FetchType.LAZY means lazy loading;
@Basic(fetch = FetchType.EAGER)
//The columnDefinition attribute specifies the Lob field type corresponding to the data table
@Column(name = "content", columnDefinition = "LONGTEXT NOT NULL")
private String content;

6.6. Creating fields of enumerated type

Fields of enumerated type can be used, but enumerated fields should be decorated with @Enumerated annotation.

public enum Gender {
    MALE("male"),
    FEMALE("female");

    private String value;
    Gender(String str){
        value=str;
    }
}
@Entity
@Table(name = "role")
public class Role {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private String description;
    @Enumerated(EnumType.STRING)
    private Gender gender;
    to omit getter/setter......
}

The corresponding storage in the database is MAIL/FEMAIL.

6.7. Add audit function

As long as the class inherits AbstractAuditBase, the following four fields will be added by default.

@Data
@AllArgsConstructor
@NoArgsConstructor
@MappedSuperclass
@EntityListeners(value = AuditingEntityListener.class)
public abstract class AbstractAuditBase {

    @CreatedDate
    @Column(updatable = false)
    @JsonIgnore
    private Instant createdAt;

    @LastModifiedDate
    @JsonIgnore
    private Instant updatedAt;

    @CreatedBy
    @Column(updatable = false)
    @JsonIgnore
    private String createdBy;

    @LastModifiedBy
    @JsonIgnore
    private String updatedBy;
}

The corresponding configuration class of our corresponding audit function may be as follows (Spring Security project):

@Configuration
@EnableJpaAuditing
public class AuditSecurityConfiguration {
    @Bean
    AuditorAware<String> auditorAware() {
        return () -> Optional.ofNullable(SecurityContextHolder.getContext())
                .map(SecurityContext::getAuthentication)
                .filter(Authentication::isAuthenticated)
                .map(Authentication::getName);
    }
}

Briefly introduce some of the annotations designed above:

  1. @CreatedDate: Indicates that this field is a creation time field, and when this entity is insert ed, the value will be set
  2. @CreatedBy : Indicates that the field is the creator, and when this entity is insert ed, the value will be set
    Same for @LastModifiedDate and @LastModifiedBy.

@EnableJpaAuditing: Enable JPA auditing.

6.8. Delete/modify data

The @Modifying annotation reminds JPA that this operation is a modification operation, and it should be used in conjunction with the @Transactional annotation.

@Repository
public interface UserRepository extends JpaRepository<User, Integer> {

    @Modifying
    @Transactional(rollbackFor = Exception.class)
    void deleteByUserName(String userName);
}

6.9. Association relationship

  • @OneToOne declares a one-to-one relationship
  • @OneToMany declares a one-to-many relationship
  • @ManyToOne declares a many-to-one relationship
  • MangToMang declares a many-to-many relationship

7. Transaction @Transactional

Just use the @Transactional annotation on the method to open the transaction!

@Transactional(rollbackFor = Exception.class)
public void save() {
  ......
}

We know that Exception is divided into runtime exception RuntimeException and non-runtime exception. If you do not configure the rollbackFor attribute in the @Transactional annotation, then things will only roll back when they encounter RuntimeException. Adding rollbackFor=Exception.class allows things to roll back when they encounter non-runtime exceptions.

The @Transactional annotation is generally used on classes or methods.

  • Acting on the class: When the @Transactional annotation is placed on the class, it means that all public methods of the class are configured with the same transaction attribute information.
  • Acting on the method: When the class is configured with @Transactional and the method is also configured with @Transactional, the transaction of the method will override the transaction configuration information of the class.

8. json data processing

8.1. Filtering json data

@JsonIgnoreProperties acts on the class to filter out specific fields that are not returned or parsed.

//Filter the userRoles attribute when generating json
@JsonIgnoreProperties({"userRoles"})
public class User {

    private String userName;
    private String fullName;
    private String password;
    @JsonIgnore
    private List<UserRole> userRoles = new ArrayList<>();
}

@JsonIgnore is generally used on the properties of the class, and has the same effect as @JsonIgnoreProperties above.

public class User {

    private String userName;
    private String fullName;
    private String password;
   //Filter the userRoles attribute when generating json
    @JsonIgnore
    private List<UserRole> userRoles = new ArrayList<>();
}

8.2. Formatting json data

@JsonFormat is generally used to format json data. :
for example:

@JsonFormat(shape=JsonFormat.Shape.STRING, pattern="yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", timezone="GMT")
private Date date;

8.3. Flattening objects

@Getter
@Setter
@ToString
public class Account {
    @JsonUnwrapped
    private Location location;
    @JsonUnwrapped
    private PersonInfo personInfo;

  @Getter
  @Setter
  @ToString
  public static class Location {
     private String provinceName;
     private String countyName;
  }
  @Getter
  @Setter
  @ToString
  public static class PersonInfo {
    private String userName;
    private String fullName;
  }
}

Before flattening:

{
    "location": {
        "provinceName":"Hubei",
        "countyName":"Wuhan"
    },
    "personInfo": {
        "userName": "coder1234",
        "fullName": "shaungkou"
    }
}

After flattening objects with @JsonUnwrapped:

@Getter
@Setter
@ToString
public class Account {
    @JsonUnwrapped
    private Location location;
    @JsonUnwrapped
    private PersonInfo personInfo;
    ......
}
{
  "provinceName":"Hubei",
  "countyName":"Wuhan",
  "userName": "coder1234",
  "fullName": "shaungkou"
}

9. Test related

@ActiveProfiles generally act on test classes to declare valid Spring configuration files.

@SpringBootTest(webEnvironment = RANDOM_PORT)
@ActiveProfiles("test")
@Slf4j
public abstract class TestBase {
  ......
}

@Test declares a method as a test method
The data of the test method declared by @Transactional will be rolled back to avoid polluting the test data.
@WithMockUser Provided by Spring Security, it is used to simulate a real user and can grant permissions

 @Test
    @Transactional
    @WithMockUser(username = "user-id-18163138155", authorities = "ROLE_TEACHER")
    void should_import_student_success() throws Exception {
        ......
    }

Tags: Java Spring Spring Boot

Posted by rjs34 on Fri, 16 Dec 2022 02:42:50 +0300