zl程序教程

您现在的位置是:首页 >  后端

当前栏目

Spring 常用注解(IOC/AOP/MVC)

SpringAOPMVC 常用 注解 IOC
2023-09-14 09:14:08 时间

① 注解

和 XML 配置文件一样,注解本身并不能执行,注解本身仅仅只是做一个标记,具体的功能是框架检测到注解标记的位置,然后针对这个位置按照注解标记的功能来执行具体操作。

本质上:所有一切的操作都是Java代码来完成的,XML和注解只是告诉框架中的Java代码如何执行。

② 扫描

Spring 为了知道程序员在哪些地方标记了什么注解,就需要通过扫描的方式,来进行检测。然后根据注解进行后续操作。

一、SpringIOC 常用注解

四个典型注解没有本质区别:对于Spring使用IOC容器管理这些组件来说没有区别。所以@Controller、@Service、@Repository这三个注解只是给开发人员看的,让我们能够便于分辨组件的作用。

注意:虽然它们本质上一样,但是为了代码的可读性,为了程序结构严谨我们肯定不能随便胡乱标记。

1. 使用@Component注解标记的普通组件

通过查看源码我们得知,@Controller、@Service、@Repository这三个注解只是在@Component注解的基础上起了三个新的名字。

@Component
public class CommonComponent {
}

2. 使用@Controller注解标记的控制器组件

这个组件就是我们在三层架构中表述层里面,使用的控制器。以前是Servlet,以后我们将会使用Controller来代替Servlet。

@Controller
public class SoldierController {
}

3. 使用@Service注解标记的业务逻辑组件

这个组件就是我们在三层架构中使用的业务逻辑组件。

@Service
public class SoldierService {
​
}

4. 使用@Repository注解标记的持久化层组件

这个组件就是我们以前用的Dao类,但是以后我们整合了Mybatis,这里就变成了Mapper接口,而Mapper接口是由Mybatis和Spring的整合包负责扫描的。


由于Mybatis整合包想要把Mapper接口背后的代理类加入Spring的IOC容器需要结合Mybatis对Mapper配置文件的解析,所以这个事情是Mybatis和Spring的整合包来完成,将来由Mybatis负责扫描,也不使用@Repository注解。

@Repository
public class SoldierDao {
}

5. 组件的beanName

在我们使用XML方式管理bean的时候,每个bean都有一个唯一标识,便于在其他地方引用。现在使用注解后,每个组件仍然应该有一个唯一标识。

① 默认情况

类名首字母小写就是bean的id。例如:SoldierController类对应的bean的id就是soldierController。

@Controller
public class SoldierController {
}

② 使用value属性指定

@Controller(value = "tianDog")
public class SoldierController {
}

当注解中只设置一个属性时,value属性的属性名可以省略:

@Service("smallDog")
public class SoldierService {
​
}

二、SpringIOC 注入bean的注解

1. 自动装配的实现前提

参与自动装配的组件(需要装配别人、被别人装配)全部都必须在IOC容器中。

2. @Autowired注解

在成员变量上直接标记@Autowired注解即可,不需要提供setXxx()方法。

以后我们在项目中的常用的正式用法就是以下这样:

@Controller
public class SoldierController {
    
    @Autowired
    private SoldierService soldierService;
    
    public void getMessage() {
        soldierService.getMessage();
    }
    
}

3. @Autowired注解其他细节

① 标记在构造器

@Controller(value = "tianDog")
public class SoldierController {
    
    private SoldierService soldierService;
    
    @Autowired
    public SoldierController(SoldierService soldierService) {
        this.soldierService = soldierService;
    }
    ……

② 标记在setXxx()方法

@Controller("tianDog")
public class SoldierController {
​
    private SoldierService soldierService;
​
    @Autowired
    public void setSoldierService(SoldierService soldierService) {
        this.soldierService = soldierService;
    }
    ……

4. @Autowired工作流程 

  • 首先根据所需要的组件类型到IOC容器中查找

    • 能够找到唯一的bean:直接执行装配

    • 如果完全找不到匹配这个类型的bean:装配失败

    • 和所需类型匹配的bean不止一个

      • 没有@Qualifier注解:根据@Autowired标记位置成员变量的变量名作为bean的id进行匹配

        • 能够找到:执行装配
        • 找不到:装配失败
      • 使用@Qualifier注解:根据@Qualifier注解中指定的名称作为bean的id进行匹配

        • 能够找到:执行装配
        • 找不到:装配失败

@Qualifier注解:根据@Qualifier注解中指定的名称作为bean的id进行匹配 

@Controller(value = "tianDog")
public class SoldierController {
    
    @Autowired
    @Qualifier(value = "maomiService222")
    // 根据面向接口编程思想,使用接口类型引入Service组件
    private ISoldierService soldierService;

5. @Autowired注解佛系装配

给@Autowired注解设置required = false属性表示:能装就装,装不上就不装。但是实际开发时,基本上所有需要装配组件的地方都是必须装配的,用不上这个属性。

@Controller(value = "tianDog")
public class SoldierController {
​
    // 给@Autowired注解设置required = false属性表示:能装就装,装不上就不装
    @Autowired(required = false)
    private ISoldierService soldierService;

6. @Configuration注解 创建配置类 

使用@Configuration注解将一个普通的类标记为Spring的配置类。

@Configuration
public class MyConfiguration {
}

7. @Bean注解 

@Bean注解相当于XML配置文件中的bean标签
@Bean注解标记的方法的返回值会被放入IOC容器 

@Configuration
public class MyConfiguration {
    // @Bean注解相当于XML配置文件中的bean标签
    // @Bean注解标记的方法的返回值会被放入IOC容器
    @Bean
    public CommonComponent getComponent() {
        CommonComponent commonComponent = new CommonComponent();
        commonComponent.setComponentName("created by annotation config");
        return commonComponent;
    }
}

8. @ComponentScan注解

@ComponentScan注解 在配置类中配置自动扫描的包

@Configuration
@ComponentScan("com.liush.ioc.component")
public class MyConfiguration {
    ……

9. Junit4和Junit5注解 

Junit4注解

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(value = {"classpath:applicationContext.xml"})
public class AOPTest {}

Junit5注解

@ExtendWith(SpringExtension.class) 表示使用 Spring 提供的扩展功能。
@ContextConfiguration(value = {"classpath:spring-context.xml"}) 还是用来指定 Spring 配置文件位置,和整合 junit4 一样。

@SpringJUnitConfig 注解综合了前面两个注解的功能,此时指定 Spring 配置文件位置即可。但是注意此时需要使用 locations 属性,不是 value 属性了。

@SpringJUnitConfig(locations = {"classpath:spring-context.xml"})
public class Junit5IntegrationTest {}
JUnit4JUnit5说明
@Test@Test表示该方法是一个测试方法。JUnit5与JUnit 4的@Test注解不同的是,它没有声明任何属性,因为JUnit Jupiter中的测试扩展是基于它们自己的专用注解来完成的。这样的方法会被继承,除非它们被覆盖
@BeforeClass@BeforeAll表示使用了该注解的方法应该在当前类中所有使用了@Test @RepeatedTest、@ParameterizedTest或者@TestFactory注解的方法之前 执行;
@AfterClass@AfterAll表示使用了该注解的方法应该在当前类中所有使用了@Test、@RepeatedTest、@ParameterizedTest或者@TestFactory注解的方法之后执行;
@Before@BeforeEach表示使用了该注解的方法应该在当前类中每一个使用了@Test、@RepeatedTest、@ParameterizedTest或者@TestFactory注解的方法之前 执行
@After@AfterEach表示使用了该注解的方法应该在当前类中每一个使用了@Test、@RepeatedTest、@ParameterizedTest或者@TestFactory注解的方法之后 执行
@Ignore@Disabled用于禁用一个测试类或测试方法
@Category@Tag用于声明过滤测试的tags,该注解可以用在方法或类上;类似于TesgNG的测试组或JUnit 4的分类。
@Parameters@ParameterizedTest表示该方法是一个参数化测试
@RunWith@ExtendWith@Runwith就是放在测试类名之前,用来确定这个类怎么运行的
@Rule@ExtendWithRule是一组实现了TestRule接口的共享类,提供了验证、监视TestCase和外部资源管理等能力
@ClassRule@ExtendWith@ClassRule用于测试类中的静态变量,必须是TestRule接口的实例,且访问修饰符必须为public。

三、SpringAOP 常用注解

1. @Aspect注解:表示这个类是一个切面类

@Order(2)注解:表示切面的优先级

  • @Order(较小的数):优先级高
  • @Order(较大的数):优先级低
@Component
@Aspect //表明当前是一个切面类
public class WuDaAspect {
    //1.获取连接点的信息
    @Before("execution(* *Bing(..))")
    public void song(JoinPoint joinPoint){
        //joinPoint称之为连接点
        //1.获取方法签名
        Signature signature = joinPoint.getSignature();
        System.out.println(signature.getName());
        System.out.println(signature.getModifiers());
        System.out.println(signature.getDeclaringType().getName());

        //2.获取目标方法的实际参数
        Arrays.asList(joinPoint.getArgs()).forEach(System.out::println);
    }

    //2.获取目标方法的返回值信息
    @AfterReturning(value = "execution(* *Bing(..))",returning = "number")
    public void song2(Integer number){
        System.out.println("目标方法的返回值是:"+number);
    }

    //3.获取目标方法的异常信息
    @AfterThrowing(value = "execution(* *Bing(..))",throwing = "throwable")
    public void song3(Throwable throwable){
        System.out.println("得到的异常信息:"+throwable.getMessage());
        System.out.println("没事没事,高蛋白!");
    }

}

2. @Before注解:在方法执行之前执行(方法上)

value属性:切入点表达式,告诉Spring当前通知方法要套用到哪个目标方法上

 @Before("execution(* *Bing(..))")

3. @After注解: 在方法执行之后执行(方法上) 

从spring5.3.x开始,增强执行顺序是:
@Before
@AfterReturning 或者 @AfterThrowing
@After

@Component
@Aspect
public class WuDaAspect {

    @Before("execution(* *Bing(..))")
    public void song(){
        System.out.println("1.送大麦茶...");
    }

    @AfterReturning("execution(* *Bing(..))")
    public void song2(){
        System.out.println("2.送玫瑰茶");
    }

    @AfterThrowing("execution(* *Bing(..))")
    public void song3(){
        System.out.println("3.送茉莉花茶");
    }

    @After("execution(* *Bing(..))")
    public void song4(){
        System.out.println("4.送菊花茶...");
    }
}

4. @AfterReturning注解标记返回通知方法

在返回通知中,通过@AfterReturning注解的returning属性获取目标方法的返回值


在返回通知中获取目标方法返回值分两步:
第一步:在@AfterReturning注解中通过returning属性设置一个名称
第二步:使用returning属性设置的名称在通知方法中声明一个对应的形参

// @AfterReturning注解标记返回通知方法
// 在返回通知中获取目标方法返回值分两步:
// 第一步:在@AfterReturning注解中通过returning属性设置一个名称
// 第二步:使用returning属性设置的名称在通知方法中声明一个对应的形参
@AfterReturning(
        value = "execution(public int com.liush.aop.api.Calculator.add(int,int))",
        returning = "targetMethodReturnValue"
)
public void printLogAfterCoreSuccess(JoinPoint joinPoint, Object targetMethodReturnValue) {
    
    String methodName = joinPoint.getSignature().getName();
    
    System.out.println("[AOP返回通知] "+methodName+"方法成功结束了,返回值是:" + targetMethodReturnValue);
}

5. @AfterThrowing注解标记异常通知方法

在异常通知中,通过@AfterThrowing注解的throwing属性获取目标方法抛出的异常对象


在异常通知中获取目标方法抛出的异常分两步:
第一步:在@AfterThrowing注解中声明一个throwing属性设定形参名称
第二步:使用throwing属性指定的名称在通知方法声明形参,Spring会将目标方法抛出的异常对象从这里传给我们

// @AfterThrowing注解标记异常通知方法
// 在异常通知中获取目标方法抛出的异常分两步:
// 第一步:在@AfterThrowing注解中声明一个throwing属性设定形参名称
// 第二步:使用throwing属性指定的名称在通知方法声明形参,Spring会将目标方法抛出的异常对象从这里传给我们
@AfterThrowing(
        value = "execution(public int com.liush.aop.api.Calculator.add(int,int))",
        throwing = "targetMethodException"
)
public void printLogAfterCoreException(JoinPoint joinPoint, Throwable targetMethodException) {
    
    String methodName = joinPoint.getSignature().getName();
    
    System.out.println("[AOP异常通知] "+methodName+"方法抛异常了,异常类型是:" + targetMethodException.getClass().getName());
}

6. @Around注解:标明环绕通知方法

环绕通知对应整个try...catch...finally结构,包括前面四种通知的所有功能。

@Around(value = "com.liush.spring.aop.aspect.LogAspect.declarPointCut()")
public Object roundAdvice(ProceedingJoinPoint joinPoint) {

7. @PointCut注解:切入点表达式重用

在一处声明切入点表达式之后,其他有需要的地方引用这个切入点表达式。易于维护,一处修改,处处生效。声明方式如下:

// 切入点表达式重用
@Pointcut("execution(* *..*.add(..))")
public void declarPointCut() {}

切点表达式的语法:
① 用*号代替“权限修饰符”和“返回值”部分表示“权限修饰符”和“返回值”不限
   * *Bing(..)   第一个*表示访问修饰符和返回值类型不限
   public void *Bing(..) 访问修饰符必须是public,返回值类型必须是void
   * void *Bing(..)  错误写法 , 比较特殊的是,这种写法是允许的: public * *Bing(..)
② 在包名部分,*代表某一层包任意。
   例如: com.liush.dao.*.*.*Bing(..)  com.atguigu.dao包下的任意子包(有且只有一层),第二个*代表任意类名
③ 在包名部分,*..代表任意层的包
   例如: com.liush.*..*.*Bing(..) com.atguigu下任意子包(层数也是任意的)
④ 在类名部分,*代表任意类名称,也可以代表部分类名称
   例如:com.liush.dao.User*.*(..)
        com.liush.dao.*.add(..)
⑤ 在方法名部分:*代表方法名任意,也可以代表方法名的部分任意
   例如:com.liush.service.impl.UserServiceImpl.get*(..)   表示UserServiceImpl下的get方法
⑥ 在方法的参数部分:
   (..) 参数个数和类型任意
   (int,..) 第一个参数必须是int,后面的参数任意
   (int,..) 和(Integer,..)不是一回事
⑦ 在方法的访问修饰符和返回值部分:
   public int *..*.*(..)  所有的public修饰的方法,而且要求返回值类型是int
   * int *..*.*(..)    错误写法,因为第一个*已经表明访问修饰符和返回值类型不限制
   特殊的,这个是正确的:  public * *..*.*(..)
⑧ 切点表达式中也是支持逻辑运算符的: && || !
execution(* com.liush.book.service.impl.User*.*(..)) || execution(* com.liush.book.service.impl.Book*.*(..))

8. 事务的注解 

① 事务基本注解:只读、回滚、超时

@Transactional

@Transactional(readOnly = true)

  • readOnly = true:把当前事务设置为只读

@Transactional(readOnly = false, timeout = 3)

  • readOnly = false:把当前事务设置为非只读
  • timeout = 3:过三秒超时自动结束事务

@Transactional(rollbackFor = Exception.class)

  • rollbackFor属性:需要设置一个Class类型的对象
  • rollbackForClassName属性:需要设置一个字符串类型的全类名
  • rollbackFor = Exception.class,noRollbackFor = FileNotFoundException.class:除了 FileNotFoundException 之外,其他所有 Exception 范围的异常都回滚;但是碰到 FileNotFoundException 不回滚。

② 事务隔离级别

  • isolation属性:设置事务的隔离级别

@Transactional(isolation = Isolation.READ_UNCOMMITTED)

  • READ_UNCOMMITTED:读未提交

@Transactional(isolation = Isolation.READ_COMMITTED)

  • READ_COMMITTED:读已提交

@Transactional(isolation = Isolation.Repeatable_Read)

  • Repeatable_Read:可重复读

@Transactional(isolation = Isolation.SERIALIZABLE)

  • SERIALIZABLE:序列化

③ 事务传播行为

  • propagation属性:设置事务的传播行为

@Transactional(propagation = Propagation.REQUIRED)

@Transactional(propagation = Propagation.SUPPORTS)

@Transactional(propagation = Propagation.MANDATORY)

@Transactional(propagation = Propagation.REQUIRES_NEW)

@Transactional(propagation = Propagation.NOT_SUPPORTED)

@Transactional(propagation = Propagation.NEVER)

@Transactional(propagation = Propagation.NESTED)

四、SpringMVC 常用注解

1. @RequestMapping

@RequestMapping是一个用来处理请求地址映射的注解,可用于类或方法上。用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。

@RequestMapping注解的作用就是将请求 和 处理请求的 控制器方法 关联起来,建立映射关系。
SpringMVC 接收到指定的请求,就会来找到在映射关系中对应的控制器方法来处理这个请求。

2. @RequestMapping

@RequestMapping(value = "/emp",method = RequestMethod.GET)
@GetMapping("/emp")
@RequestMapping(value = "/emp",method = RequestMethod.POST)
@POSTMapping("/emp")

@RequestMapping注解的作用就是将请求 和 处理请求的 控制器方法 关联起来,建立映射关系。
SpringMVC 接收到指定的请求,就会来找到在映射关系中对应的控制器方法来处理这个请求。

属性:

  • value:指定为形参赋值的请求参数的参数名。
  • required:设置是否必须传输此请求参数,默认值为true,如果为true没传值则400。
  • defaultValue:不管required属性值为true或false,当value所指定的请求参数没有传输或传输的值为""时,则使用默认值为形参赋值。

3. @RequestHander

@RequestHeader是将请求头信息和控制器方法的形参创建映射关系。

通过这个注解获取请求消息头中的具体数据。

@RequestMapping("/request/header")
public String getRequestHeader(
        // 使用 @RequestHeader 注解获取请求消息头信息
        // name 或 value 属性:指定请求消息头名称
        // defaultValue 属性:设置默认值
        @RequestHeader(name = "Accept", defaultValue = "missing") String accept
) {
    
    logger.debug("accept = " +accept);
    return "target";
}

4. @CookieValue

获取当前请求中的 Cookie 数据。

@RequestMapping("/request/cookie")
public String getCookie(
        // 使用 @CookieValue 注解获取指定名称的 Cookie 数据
        // name 或 value 属性:指定Cookie 名称
        // defaultValue 属性:设置默认值
        @CookieValue(value = "JSESSIONID", defaultValue = "missing") String cookieValue,
    
        // 形参位置声明 HttpSession 类型的参数即可获取 HttpSession 对象
        HttpSession session
) {
    
    logger.debug("cookieValue = " + cookieValue);
    return "target";
}

5. @RequestBody

@RequestBody可以获取请求体,需要在控制器方法设置一个形参,使用@RequestBody进行标识,当前请求的请求体就会为当前注解所标识的形参赋值。

6. @ResponseBody

@ResponseBody用于标识一个控制器方法,可以将该方法的返回值直接作为响应报文的响应体响应到浏览器。

7. @RestController

@RestController注解是springMVC提供的一个复合注解,标识在控制器的类上,就相当于为类添加了@Controller注解,并且为其中的每个方法添加了@ResponseBody注解。

8. @ControllerAdvice

标记为异常处理类(Java代码异常,非Http请求状态异常),一个异常处理类有很多异常处理器。

9. @ExceptionHandler

异常处理器,在异常处理类中声明。

10. @Configuration

声明此类为注解类,可代替xml配置。

11. @PathVariable 

如果我们想要获取链接地址中的某个部分的值,就可以使用 @PathVariable 注解。

主要体现在restful风格上了。

12. @RequestParam

主要用于将请求参数区域的数据映射到控制层方法的参数上