在Java开发的进阶之路上,注解(Annotation)绝对是提升效率的"神器"。它不像业务逻辑那样直观,却悄悄简化了配置、规范了代码、降低了耦合,让我们从繁琐的XML和重复代码中解放出来。
但注解种类繁多,不少初学者会陷入"学了不用就忘"的困境。今天,我们只聚焦实际开发中高频出现的注解,按"Spring核心、Web开发、数据持久层、代码工具、JDK基础"五大场景分类,结合真实业务案例讲透用法,看完直接复制到项目里用!
一、Spring核心注解:控制反转与依赖注入的基石
Spring框架的核心是IOC(控制反转)和AOP(面向切面编程),而注解正是实现这些特性的核心载体,日常开发中几乎每写一个类都可能用到。
1. @Component及其衍生注解:将类交给Spring管理
作用:标记类为"Spring受管Bean",让Spring自动创建实例并放入容器,无需手动new对象。这是依赖注入的前提。
衍生注解(语义更清晰,功能一致):
-
@Controller:用于Controller层(接收请求、返回响应),明确类的职责是处理前端请求。
-
@Service:用于Service层(业务逻辑处理),标识这是核心业务类,便于后续AOP切面增强(如事务、日志)。
-
@Repository:用于Dao层(数据访问),早期配合Spring Data使用,现在更多被MyBatis的@Mapper替代,但语义上仍推荐保留。
实战示例:
java
// Controller层:接收用户请求
@Controller
@RequestMapping("/user")
public class UserController {
// 依赖注入Service层对象
@Autowired
private UserService userService;
// 处理查询用户请求
@GetMapping("/{id}")
@ResponseBody
public User getUserById(@PathVariable Long id) {
return userService.getUserById(id);
}
}
// Service层:处理业务逻辑
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
// 事务注解:保证业务原子性
@Transactional
@Override
public User getUserById(Long id) {
// 业务逻辑处理
return userDao.selectById(id);
}
}
2. @Autowired与@Resource:依赖注入的"双雄"
作用:从Spring容器中获取Bean并注入到当前类中,避免手动调用getBean(),是解耦的关键。
核心区别(开发中必须分清):
-
@Autowired:Spring自带注解,按"类型(byType)"注入,若同类型有多个Bean,需配合@Qualifier指定Bean名称。
-
@Resource:JDK自带注解(javax.annotation.Resource),默认按"名称(byName)"注入,若名称匹配不上则按类型匹配,无需额外注解。
实战避坑:
java
@Service
public class OrderService {
// 场景1:同类型只有一个Bean,直接用@Autowired
@Autowired
private OrderDao orderDao;
// 场景2:同类型有多个Bean(如两个PaymentService实现类),需指定名称
@Autowired
@Qualifier("alipayService") // 对应Bean的名称(默认类名首字母小写)
private PaymentService paymentService;
// 场景3:用@Resource更简洁,直接指定名称
@Resource(name = "wechatPayService")
private PaymentService wechatPaymentService;
}
3. @Transactional:一行代码搞定事务管理
作用:为方法添加事务支持,保证"要么全成功,要么全失败",是解决数据一致性问题的核心注解(如订单创建+库存扣减必须同时成功)。
关键属性(开发中常用):
-
rollbackFor:指定哪些异常触发回滚(默认只回滚运行时异常),必须配置,否则非运行时异常(如IOException)不会回滚。
-
propagation:事务传播行为,如REQUIRED(默认,当前无事务则新建,有则加入)、REQUIRES_NEW(强制新建事务)。
实战示例:
java
@Service
public class OrderServiceImpl implements OrderService {
@Autowired
private OrderDao orderDao;
@Autowired
private StockDao stockDao;
// 核心业务:创建订单+扣减库存,必须原子性
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
@Override
public void createOrder(Order order) {
// 1. 创建订单
orderDao.insert(order);
// 2. 扣减库存(若此处抛异常,订单创建会回滚)
stockDao.decreaseStock(order.getProductId(), order.getNum());
}
}
二、Web开发注解:接口开发的"快捷键"
无论是Spring MVC还是Spring Boot,接口开发都离不开这些注解,它们定义了请求方式、参数映射、响应格式,是前后端交互的桥梁。
1. @RequestMapping及其衍生注解:定位接口地址
作用:映射HTTP请求到Controller的方法上,指定接口的访问路径和请求方式。
衍生注解(替代@RequestMapping,更简洁):
-
@GetMapping:对应GET请求,用于查询数据(如查用户、查订单)。
-
@PostMapping:对应POST请求,用于提交数据(如创建用户、下单)。
-
@PutMapping:对应PUT请求,用于更新数据(全量更新)。
-
@DeleteMapping:对应DELETE请求,用于删除数据。
实战示例:
java
@RestController // = @Controller + @ResponseBody,返回JSON格式
@RequestMapping("/product") // 类级别的路径前缀
public class ProductController {
// 查单个商品:GET /product/1
@GetMapping("/{id}")
public Product getProduct(@PathVariable Long id) {
// 业务逻辑
}
// 新增商品:POST /product
@PostMapping
public Result addProduct(@RequestBody Product product) { // @RequestBody接收JSON参数
// 业务逻辑
return Result.success();
}
// 更新商品:PUT /product/1
@PutMapping("/{id}")
public Result updateProduct(@PathVariable Long id, @RequestBody Product product) {
// 业务逻辑
return Result.success();
}
// 删除商品:DELETE /product/1
@DeleteMapping("/{id}")
public Result deleteProduct(@PathVariable Long id) {
// 业务逻辑
return Result.success();
}
}
2. 参数映射注解:@PathVariable、@RequestBody、@RequestParam
作用:将HTTP请求中的参数"取"出来,绑定到方法的形参上,是接口接收数据的核心。
-
@PathVariable:获取URL路径中的参数(如/order/{id}中的id),常用于RESTful风格接口。
-
@RequestBody:获取请求体中的JSON数据,用于接收复杂对象(如新增用户时的用户名、密码、手机号等组合参数)。
-
@RequestParam:获取URL查询参数(如/order?status=1&page=1),可指定是否必传、默认值。
实战示例:
java
@GetMapping("/order")
// 分页查询订单:/order?status=1&page=1&size=10
public PageInfo<Order> getOrderList(
@RequestParam(required = true) Integer status, // 必传参数
@RequestParam(defaultValue = "1") Integer page, // 非必传,默认值1
@RequestParam(defaultValue = "10") Integer size) { // 非必传,默认值10
return orderService.getOrderList(status, page, size);
}
三、数据持久层注解:与数据库交互的"简化器"
无论是MyBatis还是Spring Data JPA,这些注解都能替代繁琐的XML配置,让数据库操作更简洁。
1. MyBatis核心注解:@Mapper、@Select、@Insert等
作用:MyBatis的注解式开发,无需写Mapper.xml文件,直接在接口方法上定义SQL。
常用注解:
-
@Mapper:标记Dao接口,让MyBatis扫描并生成代理对象,无需在启动类加@MapperScan。
-
@Select/@Insert/@Update/@Delete:直接在注解中写SQL语句,适用于简单查询。
-
@Param:为方法参数指定别名,便于在SQL中引用(当参数超过1个时必须用)。
实战示例:
java
// Dao层接口
@Mapper
public interface UserDao {
// 简单查询:根据ID查用户
@Select("SELECT id, username, phone FROM user WHERE id = #{id}")
User selectById(Long id);
// 多参数查询:根据用户名和手机号查用户
@Select("SELECT id, username, phone FROM user WHERE username = #{name} AND phone = #{phone}")
User selectByNameAndPhone(@Param("name") String username, @Param("phone") String phone);
// 新增用户
@Insert("INSERT INTO user(username, phone, create_time) VALUES(#{username}, #{phone}, NOW())")
@Options(useGeneratedKeys = true, keyProperty = "id") // 返回自增主键
void insert(User user);
}
2. Spring Data JPA注解:@Entity、@Id、@Column
作用:JPA是ORM框架的规范,通过注解映射Java类与数据库表的关系,实现"零SQL"开发。
核心注解:
-
@Entity:标记类为"实体类",对应数据库中的一张表。
-
@Id:指定类的主键字段,对应表的主键。
-
@GeneratedValue:指定主键生成策略(如自增、UUID)。
-
@Column:指定字段与表列的映射关系(如列名、长度、是否可为空)。
实战示例:
java
// 实体类:对应数据库user表
@Entity
@Table(name = "user") // 若类名与表名一致,可省略
public class User {
// 主键:自增策略
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
// 用户名:列名username,非空,长度50
@Column(name = "username", nullable = false, length = 50)
private String username;
// 手机号:列名phone,唯一
@Column(unique = true)
private String phone;
// 创建时间:列名create_time,自动生成时间
@Column(name = "create_time")
private LocalDateTime createTime;
// getter/setter
}
四、代码工具注解:提升代码质量的"小助手"
这些注解不涉及业务逻辑,但能规范代码、减少重复代码,是团队开发的"默契"。
1. Lombok注解:消除"样板代码"
作用:Lombok是Java开发的"神器",通过注解自动生成getter/setter、构造方法、toString等重复代码,让类更简洁。
高频注解:
-
@Data:组合注解,包含@Getter、@Setter、@ToString、@EqualsAndHashCode、@RequiredArgsConstructor,开发中最常用。
-
@NoArgsConstructor:生成无参构造方法(JPA必须)。
-
@AllArgsConstructor:生成全参构造方法。
-
@Slf4j:自动生成日志对象log,无需手动声明private static final Logger log = LoggerFactory.getLogger(XXX.class)。
实战示例:
java
// 加了Lombok注解后,无需写getter/setter、toString等
@Data
@NoArgsConstructor
@AllArgsConstructor
@Slf4j
public class Order {
private Long id;
private String orderNo;
private Long userId;
private BigDecimal amount;
public void calculateAmount() {
// 直接使用log对象打印日志
log.info("订单{}开始计算金额", orderNo);
// 业务逻辑
}
}
2. 校验注解:@NotNull、@NotBlank、@Valid
作用:参数校验(如前端传的用户名不能为空、手机号格式正确),替代手动if-else判断,提升代码可读性。
常用注解(javax.validation.constraints包):
-
@NotBlank:字符串不为空且不能全是空格(适用于用户名、密码)。
-
@NotNull:对象不为null(适用于数字类型,如age、id)。
-
@Pattern:字符串匹配正则表达式(如手机号、邮箱)。
-
@Valid:触发参数校验,放在方法参数前。
实战示例:
java
// 接收前端参数的DTO类
@Data
public class UserDTO {
@NotBlank(message = "用户名不能为空")
private String username;
@NotBlank(message = "密码不能为空")
@Size(min = 6, max = 20, message = "密码长度必须在6-20之间")
private String password;
@Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式错误")
private String phone;
@NotNull(message = "年龄不能为空")
@Min(value = 1, message = "年龄不能小于1")
private Integer age;
}
// Controller层:触发校验
@PostMapping("/user")
public Result addUser(@Valid @RequestBody UserDTO userDTO, BindingResult bindingResult) {
// 校验失败,返回错误信息
if (bindingResult.hasErrors()) {
String errorMsg = bindingResult.getFieldError().getDefaultMessage();
return Result.fail(errorMsg);
}
// 校验通过,执行业务逻辑
userService.addUser(userDTO);
return Result.success();
}
五、JDK基础注解:Java原生的"小工具"
这些是Java自带的注解,虽然简单,但在开发中频繁出现,尤其是代码维护和序列化场景。
-
@Override:标记方法重写了父类的方法,若方法名写错(如把toString写成toStr),编译器会报错,避免低级错误。
-
@Deprecated:标记方法或类已过时,提醒开发者不要使用,同时可通过@deprecated文档注释说明替代方案。
-
@SuppressWarnings:抑制编译器警告(如"未使用的变量""unchecked"警告),谨慎使用,避免掩盖真正的问题。
实战示例:
java
public class StringUtils {
// 重写父类方法,加@Override确保正确性
@Override
public String toString() {
return "StringUtils工具类";
}
// 标记方法已过时,推荐用newFormat方法
@Deprecated
public static String format(String str) {
return str;
}
// 新的格式化方法
public static String newFormat(String str) {
// 优化后的逻辑
return "[" + str + "]";
}
// 抑制unchecked警告(如使用ArrayList未指定泛型时)
@SuppressWarnings("unchecked")
public static List getList() {
return new ArrayList();
}
}
六、总结:注解的核心价值
这些注解之所以在开发中"高频",本质是因为它们解决了实际问题:
-
简化配置:替代XML,让配置信息贴近代码(如@Service、@Entity)。
-
解耦代码:通过依赖注入(@Autowired)减少类之间的直接依赖。
-
规范行为:通过事务(@Transactional)、校验(@Valid)等注解统一业务规则。
-
提升效率:Lombok注解消除样板代码,让开发者聚焦核心业务。
最后提醒:注解不是越多越好,选择"合适的场景用合适的注解"才是关键。收藏这篇文章,开发时遇到注解困惑直接对照,效率翻倍!