← Back

Annotations

In Java backend, most annotations belong to one of these groups:

1. Web Layer (API entry)

These annotations define how requests enter your system.

@RestController

@RestController = @Controller + @ResponseBody

Example

@RestController
@RequestMapping("/coupon")
public class CouponController {

    @GetMapping("/{id}")
    public Coupon getCoupon(@PathVariable Long id) {
        return couponService.getById(id);
    }
}

Runtime behavior

  1. HTTP request → Spring DispatcherServlet
  2. Finds this controller
  3. Executes method
  4. Automatically converts return value → JSON

Common bug


@RequestMapping

Defines base URL path

@RequestMapping("/coupon")

All methods inside become:

/coupon/xxx

Can also define method type:

@RequestMapping(value = "/test", method = RequestMethod.GET)

But usually replaced by @GetMapping, @PostMapping.


@GetMapping / @PostMapping

Shortcut annotations:

@GetMapping("/list")
@PostMapping("/create")

Meaning

Real flow

@PostMapping("/claim")
public void claim(@RequestBody ClaimRequest req) {
    couponService.claim(req);
}

@RequestBody

Convert JSON → Java object

Frontend sends:

{
  "userId": 1,
  "campaignId": 100
}

Backend:

public void claim(@RequestBody ClaimRequest req)

Spring automatically:

Common bug


@PathVariable

Used for URL path parameters:

@GetMapping("/coupon/{id}")
public Coupon get(@PathVariable Long id)

Request:

GET /coupon/10

→ id = 10

Common bug

@GetMapping("/coupon/{id}")
public Coupon get(@PathVariable("couponId") Long id) // wrong

2. Service / Bean Management (Spring IOC core)

Spring manages objects for you.

@Component

Basic annotation:

@Component
public class MyUtil {}

→ Spring creates this object automatically.


@Service

Same as @Component, but semantic meaning:

@Service
public class CouponService {}

Meaning:


@Autowired

Inject dependency automatically

@Autowired
private CouponService couponService;

Spring:


@Resource

Similar to @Autowired, but different rule:

@Resource
private CouponService couponService;

Difference

AnnotationMatch rule
@Autowiredby type
@Resourceby name first

Real bug

If multiple beans exist:

@Autowired // may fail
@Resource  // works if name matches

3. Transaction

@Transactional

This is very important for business correctness

@Transactional
public void claimCoupon() {
    deductStock();
    insertUserCoupon();
}

Meaning


Example problem

Without transaction:

deductStock(); // success
insertUserCoupon(); // fail

→ stock reduced but no coupon → data inconsistency

With @Transactional:

→ both rollback


Common bugs

  1. Transaction not working
this.claimCoupon(); // self-call → transaction NOT effective
  1. Wrong propagation
  2. Exception swallowed → no rollback

4. Validation

Used to validate input automatically.

@Valid

public void create(@Valid @RequestBody CouponDTO dto)

→ triggers validation


@NotNull

@NotNull
private Long userId;

→ must not be null


@NotBlank

@NotBlank
private String name;

→ must not be:


Real flow

If invalid:

{
  "name": ""
}

→ Spring throws exception BEFORE entering service


Common bug

5. Persistence (DB mapping)

@Table

@Table(name = "coupon")

→ maps class → DB table


@Column

@Column(name = "coupon_name")
private String name;

→ field → column


@Id

@Id
private Long id;

→ primary key


MyBatis mapper

Example:

<insert id="insert">
  INSERT INTO coupon(name) VALUES(#{name})
</insert>

or

@Insert("INSERT INTO coupon(name) VALUES(#{name})")

Real bug sources

6. Async / Scheduling / Messaging

These are non-request entry points

@Scheduled

Runs automatically:

@Scheduled(cron = "0 0 0 * * ?")
public void expireCoupons() {}

→ runs every day

Used for:


@Async

@Async
public void sendNotification() {}

→ runs in another thread

Used for:

Common bug


@KafkaListener

@KafkaListener(topics = "payment-success")
public void handlePayment(String msg) {}

→ listens to Kafka message


@RabbitListener

Same idea for RabbitMQ.


Real business example (from your coupon system)

Payment success:

  1. Payment system → send message
  2. Your system receives:
@KafkaListener(topics = "payment-success")
public void onPaymentSuccess(...) {
    couponService.useCoupon(...);
}

Common bugs here

7. How all of them connect (VERY IMPORTANT)

A full real flow looks like this:

HTTP Request

@RestController

@RequestMapping / @PostMapping

@RequestBody / @PathVariable

@Service

@Transactional

Mapper / DB (@Table / SQL)

Return JSON

OR async flow:

Message Queue

@KafkaListener

@Service

@Transactional

DB update

8. One mental model to remember everything

Think like this:

LayerResponsibility
Controllerreceive request
Servicebusiness logic
Transactionensure consistency
MapperDB operation
Validationprotect input
Async/MQbackground or external trigger