目录
@ControllerAdvice 和 @ExceptionHandler
标记容器类注解
通过使用这些注解,Spring能够自动扫描并管理这些组件,实现松耦合和更清晰的代码结构。
1. @Controller
@Controller
注解用于标注控制层组件,也就是MVC(Model-View-Controller)中的Controller层。被@Controller
注解的类通常负责处理用户的请求,并将相应的模型数据返回给视图进行展示。Spring的DispatcherServlet
会自动扫描并识别这些注解的类,然后将HTTP请求映射到标注了@RequestMapping
的方法上。
@Controller
public class MyController {
@RequestMapping("/home")
public String home() {
// 处理请求
return "home"; // 返回视图名
}
}
2. @Service
@Service
注解用于标注服务层组件,也就是业务逻辑层的类。它的主要目的是将业务逻辑代码与其他代码(例如控制层和数据访问层)分离。被@Service
注解的类通常包含业务逻辑方法,这些方法可以调用数据访问层的代码来获取和处理数据。
@Service
public class MyService {
public void performBusinessLogic() {
// 业务逻辑代码
}
}
3. @Repository
@Repository
注解用于标注数据访问层组件,也就是DAO(Data Access Object)层的类。它通常用于将数据从数据库中读取或写入数据库中。@Repository
注解还有一个额外的功能:Spring会将数据访问层的异常转换为Spring的数据访问异常(DataAccessException)。
@Repository
public class MyRepository {
public List<MyEntity> findAll() {
// 数据访问代码
}
}
4. @Component
@Component
注解用于标注一个通用的Spring管理组件。它是一个通用的注解,意味着可以将任何类标注为Spring的组件,使其被Spring容器管理。@Component
也是一个元注解,意味着其他注解(如@Controller
,@Service
,@Repository
)都是基于@Component
的特化形式。
@Component
public class MyComponent {
public void doSomething() {
// 一些通用操作
}
}
依赖注入注解
1. @Autowired
@Autowired
注解是Spring框架提供的,用于自动注入依赖对象。它可以用在构造器、Setter方法或字段上。默认情况下,@Autowired
按类型(byType)进行自动注入。如果Spring容器中存在多个相同类型的bean,可以结合@Qualifier
注解按名称(byName)进行注入。
- 按类型注入(byType):Spring容器会根据需要注入的bean类型,查找匹配的bean并注入。
- 按名称注入(byName):可以与
@Qualifier
注解结合使用,指定注入的bean的名称。
@Component
public class MyService {
@Autowired
private MyRepository myRepository; // 按类型注入
@Autowired
public MyService(MyRepository myRepository) {
this.myRepository = myRepository; // 按类型注入
}
@Autowired
public void setMyRepository(MyRepository myRepository) {
this.myRepository = myRepository; // 按类型注入
}
}
2. @Resource
@Resource
注解是JDK标准的注解(来自javax.annotation
包),它也可以用于依赖注入。@Resource
注解默认按名称(byName)进行注入,如果没有指定名称则按类型(byType)进行注入。它具有两个重要属性:name
和type
。
- name属性:指定要注入的bean的名称。
- type属性:指定要注入的bean的类型。
装配顺序:
- 如果同时指定了
name
和type
,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常。 - 如果指定了
name
,则从上下文中查找名称(id)匹配的bean进行装配,找不到则抛出异常。 - 如果指定了
type
,则从上下文中找到类型匹配的唯一bean进行装配,找不到或者找到多个都会抛出异常。 - 如果既没有指定
name
又没有指定type
,则默认按名称(byName)方式进行装配;如果没有匹配,则回退为按类型(byType)进行装配,如果匹配则自动装配。
@Component
public class MyService {
@Resource(name = "myRepository")
private MyRepository myRepositoryByName; // 按名称注入
@Resource(type = MyRepository.class)
private MyRepository myRepositoryByType; // 按类型注入
@Resource
private MyRepository myRepository; // 按名称注入(如果名称匹配失败,则按类型注入)
}
@Autowired 与 @Resource 的区别
-
默认注入方式:
@Autowired
:默认按类型(byType)注入。@Resource
:默认按名称(byName)注入,如果名称匹配失败,则按类型(byType)注入。
-
属性配置:
@Autowired
:可以与@Qualifier
结合使用来指定按名称注入。@Resource
:具有两个属性name
和type
,可以通过配置这两个属性来控制注入方式。
-
应用场景:
@Autowired
:推荐在Spring应用中使用,因为它是Spring提供的注解,功能更加强大和灵活。@Resource
:更适用于需要与其他Java EE技术集成的场景,因为它是JDK标准的注解。
web相关注解
@RequestMapping
@RequestMapping
注解是Spring MVC中用于映射Web请求到处理类和处理方法的核心注解。它可以应用在控制器类和方法上,用于定义URL路径、HTTP请求方法、请求参数、请求头等信息
属性介绍
- value:定义请求的路径。可以是单个路径或路径数组。
- method:指定处理请求的方法类型,如
GET
、POST
、PUT
、DELETE
等。默认处理所有类型的HTTP请求。 - produces:定义返回的媒体类型和字符集,通常用于指定响应的Content-Type,例如
"application/json"
、"text/html;charset=UTF-8"
等。 - consumes:定义可接受的媒体类型,通常用于指定请求的Content-Type。
- params:指定请求必须包含的一组参数。
- headers:指定请求必须包含的一组HTTP头。
用法示例
- 注解在类上:当
@RequestMapping
注解在类上时,所有该类中的处理方法的URL路径都会以类上定义的路径为前缀。 - 注解在方法上:方法上的
@RequestMapping
路径会继承类上的路径,同时可以定义自己的路径和其他属性。
@Controller
@RequestMapping("/api")
public class MyController {
@RequestMapping(value = "/home", method = RequestMethod.GET)
public String home() {
// 处理GET请求 "/api/home"
return "home"; // 返回视图名
}
@RequestMapping(value = "/submit", method = RequestMethod.POST, produces = "application/json;charset=UTF-8")
@ResponseBody
public String submit(@RequestParam("data") String data) {
// 处理POST请求 "/api/submit"
return "{\"status\":\"success\"}"; // 返回JSON数据
}
}
注意事项
- 路径继承:类上的路径和方法上的路径会进行拼接。例如,类上的路径是
/api
,方法上的路径是/home
,那么实际处理的路径是/api/home
。 - 请求方法:
method
属性指定的请求方法类型非常重要,可以避免由于处理不同请求类型的逻辑混淆而导致的问题。 - 媒体类型和字符集:使用
produces
属性可以确保返回的响应符合预期的格式和编码,避免乱码等问题。
@GetMapping 和 @PostMapping
@GetMapping
和@PostMapping
是@RequestMapping
的简化版本,分别用于处理GET和POST请求。
- @GetMapping:等同于
@RequestMapping(method = RequestMethod.GET)
- @PostMapping:等同于
@RequestMapping(method = RequestMethod.POST)
@Controller
@RequestMapping("/api")
public class MyController {
@GetMapping("/home")
public String home() {
// 处理GET请求 "/api/home"
return "home"; // 返回视图名
}
@PostMapping("/submit")
@ResponseBody
public String submit(@RequestParam("data") String data) {
// 处理POST请求 "/api/submit"
return "{\"status\":\"success\"}"; // 返回JSON数据
}
}
@RestController
@RestController
是一个组合注解,它结合了@Controller
和@ResponseBody
。使用@RestController
注解的类被Spring处理为一个控制器,且类中所有方法默认返回数据而不是视图页面。
特点
-
组合注解:
@RestController
相当于@Controller
和@ResponseBody
两个注解的组合。- 适用于开发只与页面进行数据交互的控制层。
-
自动添加@ResponseBody:
- 使用
@RestController
后,类中所有方法的返回值都会被自动转换为JSON或XML格式的响应数据。 - 无需在每个方法上单独添加
@ResponseBody
注解。
- 使用
@RestController
@RequestMapping("/api")
public class MyRestController {
@GetMapping("/data")
public MyData getData() {
return new MyData("example", 123);
}
@PostMapping("/submit")
public Response submitData(@RequestBody MyData data) {
// 处理提交的数据
return new Response("success");
}
}
@ControllerAdvice 和 @ExceptionHandler
通过这两个注解来定义全局注解,让spring的异常处理能跟其他数据的返回一致,前端才可以统一处理这些
@ControllerAdvice
@ControllerAdvice
注解用于定义全局的异常处理、数据绑定、数据格式化和数据预处理。它通常用于声明一个控制器建言,增强整个应用程序中的控制器。
- 作用:全局控制器增强,用于定义全局的异常处理、数据绑定等逻辑。
- 自动注册:
@ControllerAdvice
本质上是一个@Component
注解,会自动被Spring扫描并注册为一个Bean。
@ExceptionHandler
@ExceptionHandler
注解用于方法上,定义特定的异常处理逻辑。通过value
属性可以指定需要处理的异常类型。
- 作用:定义方法来处理特定的异常类型。
- 结合@ControllerAdvice:可以在全局控制器增强类中使用
@ExceptionHandler
来处理全局异常。
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(value = Exception.class)
public ResponseEntity<String> handleException(Exception e) {
return new ResponseEntity<>("An error occurred: " + e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
}
@ExceptionHandler(value = NullPointerException.class)
public ResponseEntity<String> handleNullPointerException(NullPointerException e) {
return new ResponseEntity<>("Null pointer exception: " + e.getMessage(), HttpStatus.BAD_REQUEST);
}
}
@RestController
@RequestMapping("/api")
public class MyRestController {
@GetMapping("/data")
public MyData getData() {
if (true) { // 模拟异常
throw new NullPointerException("Null value found");
}
return new MyData("example", 123);
}
@PostMapping("/submit")
public Response submitData(@RequestBody MyData data) {
// 处理提交的数据
return new Response("success");
}
}
后端参数接受有关注解
可以参考往期文章
Aop切面有关的注解
Spring AOP(Aspect-Oriented Programming)允许我们将横切关注点(如日志记录、事务管理、安全性等)从业务逻辑中分离出来。以下是常用的AOP注解及其作用:
1. @Aspect
@Aspect
注解用于声明一个类为切面类。切面类包含了通知(Advice)和切点(PointCut)。
@Aspect
@Component
public class MyAspect {
// 切面类
}
2 .@After
@After
注解用于声明后置通知,表示在目标方法执行后执行的逻辑。
@Aspect
@Component
public class MyAspect {
@After("execution(* com.example.service.*.*(..))")
public void afterAdvice(JoinPoint joinPoint) {
System.out.println("After method: " + joinPoint.getSignature());
}
}
3. @Before
@Before
注解用于声明前置通知,表示在目标方法执行前执行的逻辑。
@Aspect
@Component
public class MyAspect {
@Before("execution(* com.example.service.*.*(..))")
public void beforeAdvice(JoinPoint joinPoint) {
System.out.println("Before method: " + joinPoint.getSignature());
}
}
4. @Around
@Around
注解用于声明环绕通知,表示在目标方法执行前后都执行的逻辑。环绕通知可以完全控制目标方法的执行。
@Aspect
@Component
public class MyAspect {
@Around("execution(* com.example.service.*.*(..))")
public Object aroundAdvice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("Before method: " + proceedingJoinPoint.getSignature());
Object result = proceedingJoinPoint.proceed(); // 执行目标方法
System.out.println("After method: " + proceedingJoinPoint.getSignature());
return result;
}
}
5. @PointCut
@PointCut
注解用于声明切点,定义拦截规则,确定哪些方法会被切入。切点表达式可以与通知配合使用,以指定在哪些方法上应用通知。
@Aspect
@Component
public class MyAspect {
@PointCut("execution(* com.example.service.*.*(..))")
public void serviceMethods() {
// 定义一个切点,指示匹配com.example.service包下的所有方法
}
@Before("serviceMethods()")
public void beforeAdvice(JoinPoint joinPoint) {
System.out.println("Before method: " + joinPoint.getSignature());
}
}
总结
- @Aspect:声明一个类为切面类。
- @After:声明后置通知,在目标方法执行后执行。
- @Before:声明前置通知,在目标方法执行前执行。
- @Around:声明环绕通知,在目标方法执行前后执行。
- @PointCut:声明切点,定义拦截规则,确定哪些方法会被切入。
完整例子,判断登录逻辑
package com.imooc.pan.server.common.aspect;
import com.imooc.pan.cache.core.constants.CacheConstants;
import com.imooc.pan.core.response.R;
import com.imooc.pan.core.response.ResponseCode;
import com.imooc.pan.core.utils.JwtUtil;
import com.imooc.pan.server.common.annotation.LoginIgnore;
import com.imooc.pan.server.common.utils.UserIdUtil;
import com.imooc.pan.server.modules.user.constants.UserConstants;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Objects;
/**
* 统一的登录拦截校验切面逻辑实现类
*/
@Component
@Aspect
@Slf4j
public class CommonLoginAspect {
/**
* 登录认证参数名称
*/
private static final String LOGIN_AUTH_PARAM_NAME = "authorization";
/**
* 请求头登录认证key
*/
private static final String LOGIN_AUTH_REQUEST_HEADER_NAME = "Authorization";
/**
* 切点表达式
*/
private final static String POINT_CUT = "execution(* com.imooc.pan.server.modules.*.controller..*(..))";
@Autowired
private CacheManager cacheManager;
/**
* 切点模版方法
*/
@Pointcut(value = POINT_CUT)
public void loginAuth() {
}
/**
* 切点的环绕增强逻辑
* 1、需要判断需不需要校验登录信息
* 2、校验登录信息:
* a、获取token 从请求头或者参数
* b、从缓存中获取token,进行比对
* c、解析token
* d、解析的userId存入线程上下文,供下游使用
*
* @param proceedingJoinPoint
* @return
* @throws Throwable
*/
@Around("loginAuth()")
public Object loginAuthAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
if (checkNeedCheckLoginInfo(proceedingJoinPoint)) {
ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = servletRequestAttributes.getRequest();
String requestURI = request.getRequestURI();
log.info("成功拦截到请求,URI为:{}", requestURI);
if (!checkAndSaveUserId(request)) {
log.warn("成功拦截到请求,URI为:{}. 检测到用户未登录,将跳转至登录页面", requestURI);
return R.fail(ResponseCode.NEED_LOGIN);
}
log.info("成功拦截到请求,URI为:{},请求通过", requestURI);
}
return proceedingJoinPoint.proceed();
}
/**
* 校验token并提取userId
*
* @param request
* @return
*/
private boolean checkAndSaveUserId(HttpServletRequest request) {
String accessToken = request.getHeader(LOGIN_AUTH_REQUEST_HEADER_NAME);
if (StringUtils.isBlank(accessToken)) {
accessToken = request.getParameter(LOGIN_AUTH_PARAM_NAME);
}
if (StringUtils.isBlank(accessToken)) {
return false;
}
Object userId = JwtUtil.analyzeToken(accessToken, UserConstants.LOGIN_USER_ID);
if (Objects.isNull(userId)) {
return false;
}
Cache cache = cacheManager.getCache(CacheConstants.R_PAN_CACHE_NAME);
String redisAccessToken = cache.get(UserConstants.USER_LOGIN_PREFIX + userId, String.class);
if (StringUtils.isBlank(redisAccessToken)) {
return false;
}
if (Objects.equals(accessToken, redisAccessToken)) {
saveUserId(userId);
return true;
}
return false;
}
/**
* 保存用户ID到线程上下文中
*
* @param userId
*/
private void saveUserId(Object userId) {
UserIdUtil.set(Long.valueOf(String.valueOf(userId)));
}
/**
* 校验是否需要校验登录信息
*
* @param proceedingJoinPoint
* @return true 需要校验登录信息 false 不需要
*/
private boolean checkNeedCheckLoginInfo(ProceedingJoinPoint proceedingJoinPoint) {
Signature signature = proceedingJoinPoint.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
Method method = methodSignature.getMethod();
return !method.isAnnotationPresent(LoginIgnore.class);
}
}
测试常用的注解
Spring Boot 提供了一组强大的测试注解和工具,使得编写和运行测试变得更加简便和高效。以下是常用的测试注解及其作用。
1. @SpringBootTest
@SpringBootTest
注解用于在测试环境中启动整个Spring上下文。
- 作用:在测试中启动Spring应用程序上下文,并进行集成测试。
@SpringBootTest
public class MyApplicationTests {
@Test
public void contextLoads() {
// 测试上下文加载
}
}
2. @Test
@Test
注解用于将一个普通方法标记为测试方法,这是JUnit提供的基本注解。
- 作用:标记一个方法为测试方法。
@Test
public void testMethod() {
// 测试方法
}
3. @RunWith
@RunWith
注解用于指定测试运行器,Spring Boot 集成了JUnit。
- 作用:指定测试类运行的运行器。
- 常用运行器:
SpringJUnit4ClassRunner.class
@RunWith(SpringRunner.class)
@SpringBootTest
public class MyApplicationTests {
@Test
public void contextLoads() {
// 测试上下文加载
}
}
4. 其他JUnit测试注解
- @Ignore:忽略测试方法,测试时不会运行此方法。
@Test
@Ignore("Not ready yet")
public void ignoredTest() {
// 测试方法将被忽略
}
@Before:在每个测试方法之前运行
@After:在每个测试方法之后运行。
@Before
public void setUp() {
// 测试前初始化
}
- @BeforeClass:在所有测试方法之前运行一次,必须是静态方法。
- @AfterClass:在所有测试方法之后运行一次,必须是静态方法。
@BeforeClass
public static void setUpBeforeClass() {
// 测试前初始化
}
总结
- @SpringBootTest:用于启动Spring应用程序上下文,进行集成测试。
- @Test:标记一个方法为测试方法。
- @RunWith:指定测试运行器,常用于整合JUnit和Spring测试框架。
- @Ignore、@Before、@After、@BeforeClass、@AfterClass:JUnit提供的注解,用于管理测试方法的执行顺序和状态。
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest
public class MyApplicationTests {
@BeforeClass
public static void initAll() {
// 初始化所有测试前运行
System.out.println("BeforeClass: 初始化所有测试前运行");
}
@Before
public void init() {
// 初始化每个测试前运行
System.out.println("Before: 初始化每个测试前运行");
}
@Test
public void contextLoads() {
// 测试上下文加载
System.out.println("Test: 测试上下文加载");
}
@Test
public void anotherTest() {
// 另一个测试方法
System.out.println("Test: 另一个测试方法");
}
@Ignore("Test is ignored as a demonstration")
@Test
public void ignoredTest() {
// 被忽略的测试方法
System.out.println("Ignore: 这个测试方法将被忽略");
}
@After
public void tearDown() {
// 每个测试后运行
System.out.println("After: 每个测试后运行");
}
@AfterClass
public static void tearDownAll() {
// 所有测试后运行
System.out.println("AfterClass: 所有测试后运行");
}
}
一些常见的注解
1. @SpringBootApplication
@SpringBootApplication
是 Spring Boot 的核心注解,用于标注一个主程序类,启动 Spring Boot 应用。它是一个组合注解,包含了以下三个注解:
- @Configuration:标记一个类为配置类。
- @EnableAutoConfiguration:启用 Spring Boot 的自动配置机制。
- @ComponentScan:启用组件扫描。
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
2. @EnableAutoConfiguration
@EnableAutoConfiguration
自动加载符合条件的配置类,以便 Spring 应用程序根据类路径中的依赖和自定义配置自动配置 Spring 应用程序。
3. @Configuration
@Configuration
用于定义配置类,该类可以包含 Spring bean 定义
@Configuration
public class MyConfig {
@Bean
public MyBean myBean() {
return new MyBean();
}
}
4. @ComponentScan
@ComponentScan
用于自动扫描指定包及其子包中的 Spring 组件(如 @Component、@Service、@Controller、@Repository 等)。
@ComponentScan(basePackages = "com.example")
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
事务和缓存注解
5. @Transactional
@Transactional
用于声明事务,确保方法运行在事务上下文中。
@Service
public class MyService {
@Transactional
public void performTransaction() {
// 事务操作
}
}
6. @Cacheable
@Cacheable
用于声明缓存,方法的返回值会被缓存起来。
@Service
public class MyService {
@Cacheable("myCache")
public MyData getData(String id) {
return myRepository.findById(id);
}
}
配置和异步任务注解
7. @PropertySource
@PropertySource
用于引入外部属性文件到 Spring 环境中。
@Configuration
@PropertySource("classpath:application.properties")
public class MyConfig {
}
8. @Async
@Async
用于标识异步方法,方法会在独立的线程中执行。
@Service
public class MyService {
@Async
public void asyncMethod() {
// 异步操作
}
}
9. @EnableAsync
@EnableAsync
用于开启异步方法支持,通常用于配置类上。
10. @EnableScheduling
@EnableScheduling
用于开启计划任务的支持,通常用于配置类上。
11. @Scheduled
@Scheduled
用于声明计划任务方法,可以设置任务的执行时间和频率。
@Service
public class MyService {
@Scheduled(cron = "0 0 * * * ?")
public void scheduledTask() {
// 计划任务操作
}
}
标签:springboot,Spring,void,class,关键,import,注解,public
From: https://blog.csdn.net/weixin_64122713/article/details/139347226