引言
在Spring开发中,请求参数处理、统一响应格式、分层架构设计以及依赖管理是构建可维护应用的核心要素。然而,许多开发者在实践中常面临以下问题:
-
如何规范接收不同格式的请求参数?
-
为何要引入分层架构?
-
什么是控制反转(IoC)和依赖注入(DI)?
-
Spring的注解如
@RestController
、@RequestBody
等有何区别?
本文将通过一个完整的案例演进,从基础请求处理出发,逐步引入分层架构与IoC容器,结合注解的深度解析,最终实现高内聚、低耦合的代码结构。过程中会详细讲解Bean对象管理、组件扫描机制,以及常用注解的核心用法。
一、请求参数处理与统一响应
1. 请求参数接收方式
这里用Postman作为测试工具
(1) 简单参数:@RequestParam
简单post请求
//保证参数名和请求参数名一致,或者用@RequestParam注解指定参数名@RequestMapping(value = "/simpleParam")public String simpleParam(String name, int age) {System.out.println(name + ": " + age);return "success";}
@RequestParam
:绑定请求参数到方法参数,支持:
name
:指定参数名(若省略则默认匹配方法参数名)
defaultValue
:设置默认值测试URL:
http://localhost:8080/user?name=Tom&age=20
(2)简单实体参数
如果传入参数太多,我们可以进行实体化再进行传入,需要注意的是,传递的参数名字和接口方法里的参数名字需要对应,否则就需要用上面提到的@RequestParam进行指定
User类
package com.ffyc.entity;public class User {private String name;private Integer age;private Address address;public String getName() {return name;}public void setName(String name) {this.name = name;}public Address getAddress() {return address;}public void setAddress(Address address) {this.address = address;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}@Overridepublic String toString() {return "User{" +"name='" + name + '\'' +", age=" + age +", address=" + address +'}';}}
//简单实体参数@RequestMapping(value = "/simpleEnt")public String simpleEnt(User user) {System.out.println(user);return "success";}
(3)复杂实体参数
假如我们需要传递用户的,姓名,年龄和地址,而地址作为一个新的对象,包含省份,城市,需要传递这些复杂的实体参数
Address类
package com.ffyc.entity;public class Address {private String province;private String city;public String getProvince() {return province;}public void setProvince(String province) {this.province = province;}public String getCity() {return city;}public void setCity(String city) {this.city = city;}@Overridepublic String toString() {return "Address{" +"province='" + province + '\'' +", city='" + city + '\'' +'}';}
}
//复杂实体参数@RequestMapping(value = "/complexEnt")public String complexEnt(User user) {System.out.println(user);return "success";}
(4)数组参数/集合参数
比如遇到复选框时我们可以选择数组,或者集合(list)进行传递
hobbies对应方法中的hobbies
//数组参数@RequestMapping(value = "/arrayParam")public String arrayParam(String[] hobbies) {System.out.println(Arrays.toString(hobbies));return "success";}
用list集合进行传递
hobby需要对应
//集合参数@RequestMapping(value = "/collectionParam")public String collectionParam(@RequestParam List<String>hobby) {System.out.println(hobby);return "success";}
(5)时间日期参数
//时间日期参数@RequestMapping(value = "/dateParam")public String dateParam(@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime updateTime ) {System.out.println(updateTime);return "success";}
需要用到@DateTimeFormat注解指定格式再进行传递
(6)JSON参数
//JSON参数@RequestMapping(value = "/jsonParam")public String jsonParam(@RequestBody User user) {System.out.println(user);return "success";}
@RequestBody: 将请求体中的JSON反序列化为Java对象
(7)路径参数
有时候传递的参数再路径中
//路径参数@RequestMapping(value = "/pathParam/{id}")public String pathParam(@PathVariable("id") Integer id) {System.out.println(id);return "success";}
@PathVariable:从URL路径中提取参数
2. 统一响应格式
统一规范,方便前后端数据交互
响应实体类定义
@Data
@AllArgsConstructor
public class Result<T> {private int code;private String message;private T data;public static <T> Result<T> success(T data) {return new Result<>(200, "success", data);}
}
使用@ResponseBody
返回JSON
@GetMapping("/user/{id}")
@ResponseBody
public Result<User> getUser(@PathVariable Long id) {User user = userService.findById(id);return Result.success(user);
}
3. @Controller
vs @RestController
代码对比
// 传统Controller返回视图
@Controller
public class PageController {@GetMapping("/home")public String home() {return "index.html"; // 返回视图名称}
}// RestController返回JSON
@RestController
public class UserController {@GetMapping("/api/user")public User getUser() {return new User("Tom", 20); // 自动转为JSON}
}
二、Bean管理与组件扫描
1. Bean对象的概念
定义:由Spring容器管理的对象,生命周期由容器控制
创建方式:
通过
@Component
及其派生注解(@Service
,@Repository
,@Controller
)标记类通过
@Bean
方法在配置类中显式定义
2. 组件扫描:@ComponentScan
作用:指定Spring扫描的包路径,自动注册标记了
@Component
的类为BeanSpring Boot默认行为:
@SpringBootApplication
已包含@ComponentScan
默认扫描主类所在包及其子包
手动配置示例:
@Configuration
@ComponentScan(basePackages = "com.example.service")
public class AppConfig { }
三、分层架构演进
1. 原始单层架构的问题
高度耦合的Controller:
@RestController
public class UserController {// 直接操作数据库(违反分层原则)@Autowiredprivate JdbcTemplate jdbcTemplate;@GetMapping("/user/{id}")public User getUser(@PathVariable Long id) {String sql = "SELECT * FROM user WHERE id = ?";return jdbcTemplate.queryForObject(sql, new UserRowMapper(), id);}
}
缺陷:
业务逻辑与数据访问混杂
难以复用和维护
2. 三层架构改造
(1) 分层结构
(2) 分层代码实现
DAO层:
@Repository
public class UserDaoImpl implements UserDao {@Autowiredprivate JdbcTemplate jdbcTemplate;@Overridepublic User findById(Long id) {String sql = "SELECT * FROM user WHERE id = ?";return jdbcTemplate.queryForObject(sql, new UserRowMapper(), id);}
}
Service层:
@Service
public class UserServiceImpl implements UserService {private final UserDao userDao;@Autowired // 构造器注入(推荐)public UserServiceImpl(UserDao userDao) {this.userDao = userDao;}@Overridepublic User findById(Long id) {return userDao.findById(id);}
}
Controller层:
@RestController
@RequestMapping("/api/users")
public class UserController {private final UserService userService;@Autowiredpublic UserController(UserService userService) {this.userService = userService;}@GetMapping("/{id}")public Result<User> getUser(@PathVariable Long id) {User user = userService.findById(id);return Result.success(user);}
}
3. 分层优势
四、IoC与依赖注入优化
1. 紧耦合问题演示
// 直接依赖具体实现类
public class UserServiceImpl implements UserService {private UserDao userDao = new UserDaoImpl(); // 紧耦合
}
问题:
更换DAO实现需修改代码
难以进行单元测试
2.控制反转 IOC
没有什么是加一个中间层不能解决的
容器里面的队象成为Bean,默认是该类的名字首字母小写,例如,类的名字叫 EmpService,那么对应的Bean对象的就是 empService ,可以通过 value属性进行指定名字,不过一般用不到
@Service
public class UserServiceImpl implements UserService {@Autowiredprivate UserDao userDao;
}
3.依赖注入 DI
用法区别
一个支付系统支持微信支付和支付宝支付
public interface PaymentGateway {void pay();
}@Component("wechatPay") // Bean名称=wechatPay
public class WechatPay implements PaymentGateway {@Overridepublic void pay() {System.out.println("微信支付");}
}@Component("alipay") // Bean名称=alipay
public class Alipay implements PaymentGateway {@Overridepublic void pay() {System.out.println("支付宝支付");}
}
使用@Autowired + @Qualifier
@Service
public class PaymentService {@Autowired@Qualifier("wechatPay")private PaymentGateway paymentGateway;
}
使用@Resource
@Service
public class PaymentService {@Resource(name = "wechatPay")private PaymentGateway paymentGateway;
}
五、总结
掌握从基础请求处理到分层架构设计的完整路径,理解Spring的IoC容器与依赖注入机制,是构建松耦合、高可维护应用的关键。