Java面试准备之Spring框架系列三

hresh 475 0

Java面试准备之Spring框架系列三

@Component 和 @Bean 的区别是什么?

  1. 作用对象不同: @Component 注解作用于类,而@Bean注解作用于方法
  2. @Component 通常是通过类路径扫描来自动侦测以及自动装配到 Spring 容器中(我们可以使用 @ComponentScan 注解定义要扫描的路径从中找出标识了需要装配的类自动装配到 Spring 的 bean 容器中)。@Bean 注解通常是我们在标有该注解的方法中定义产生这个 bean,@Bean告诉了Spring这是某个类的示例,当我需要用它的时候还给我。
  3. @Bean 注解比 Component 注解的自定义性更强,而且很多地方我们只能通过 @Bean 注解来注册bean。比如当我们引用第三方库中的类需要装配到 Spring容器时,则只能通过 @Bean来实现。

@Compent 作用就相当于 XML配置

@Component
public class Student {

    private String name = "lkm";

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

@Bean 需要在配置类中使用,即类上需要加上@Configuration注解

@Configuration
public class WebSocketConfig {
    @Bean
    public Student student(){
        return new Student();
    }

}

下面这个例子是通过 @Component 无法实现的。

@Bean
public OneService getService(status) {
    case (status)  {
        when 1:
                return new serviceImpl1();
        when 2:
                return new serviceImpl2();
        when 3:
                return new serviceImpl3();
    }
}

将一个类声明为Spring的 bean 的注解有哪些?

我们一般使用 @Autowired 注解自动装配 bean,要想把类标识成可用于 @Autowired 注解自动装配的 bean 的类,采用以下注解可实现:

  • @Component :通用的注解,可标注任意类为 Spring 组件。如果一个Bean不知道属于哪个层,可以使用@Component 注解标注。
  • @Repository : 对应持久层即 Dao 层,主要用于数据库相关操作。
  • @Service : 对应服务层,主要涉及一些复杂的逻辑,需要用到 Dao层。
  • @Controller : 对应 Spring MVC 控制层,主要用于接受用户请求并调用 Service 层返回数据给前端页面。

@Autowired 的作用是什么?

@Autowired 是用在 JavaBean 中的注解,通过 byType 形式,用来给指定的字段或方法注入所需的外部资源。

先看一下 bean 实例化和@Autowired 装配过程:

  • 一切都是从 bean 工厂的 getBean 方法开始的,一旦该方法调用总会返回一个bean实例,无论当前是否存在,不存在就实例化一个并装配,否则直接返回。
  • 实例化和装配过程中会多次递归调用getBean方法来解决类之间的依赖。
  • Spring几乎考虑了所有可能性,所以方法特别复杂但完整有条理。
  • @Autowired最终是根据类型来查找和装配元素的,但是我们设置了后会影响最终的类型匹配查找。因为在前面有根据 BeanDefinition 的 autowire 类型设置 PropertyValue 值得一步,其中会有新实例的创建和注册。就是那个autowireByName方法。

下面通过@Autowired来说明一下用法

  • Setter 方法中的 @Autowired
    你可以在 JavaBean 中的 setter 方法中使用 @Autowired 注解。当 Spring 遇到一个在 setter 方法中使用的 @Autowired 注解,它会在方法中执行 byType 自动装配。

  • 属性中的 @Autowired
    你可以在属性中使用 @Autowired 注解来除去 setter 方法。当时使用为自动连接属性传递的时候,Spring 会将这些传递过来的值或者引用自动分配给那些属性。

  • 构造函数中的 @Autowired
    你也可以在构造函数中使用 @Autowired。一个构造函数 @Autowired 说明当创建 bean 时,即使在 XML 文件中没有使用 元素配置 bean ,构造函数也会被自动连接。

  • @Autowired 的(required=false)选项
    默认情况下,@Autowired 注解意味着依赖是必须的,它类似于 @Required 注解,然而,你可以使用 @Autowired 的 (required=false) 选项关闭默认行为。

@Qualifier

@Autowired 是用在 JavaBean 中的注解,通过 byType 形式,用来给指定的字段或方法注入所需的外部资源。 如果容器中有多个相同类型的 bean,则框架将抛出 NoUniqueBeanDefinitionException, 以提示有多个满足条件的 bean 进行自动装配。

在这种情况下,你可以使用 @Qualifier 注释和 @Autowired 注释通过指定哪一个真正的 bean 将会被装配来消除混乱。 即自动注入的策略就从 byType 转变成 byName 了。

@Autowired 可以对成员变量、方法以及构造函数进行注释,而@Qualifier 的标注对象是成员变量、方法入参、构造函数入参。正是由于注释对象的不同,所以 Spring 不将 @Autowired 和@Qualifier 统一成一个注释类。

@RestController vs @Controller

Controller 返回一个页面

单独使用 @Controller 不加 @ResponseBody 的话一般使用在要返回一个视图的情况,这种情况属于比较传统的Spring MVC 的应用,对应于前后端不分离的情况。

Java面试准备之Spring框架系列三

@RestController 返回 JSON 或 XML 形式数据

但@RestController只返回对象,对象数据直接以 JSON 或 XML 形式写入 HTTP 响应(Response)中,这种情况属于 RESTful Web服务,这也是目前日常开发所接触的最常用的情况(前后端分离)。

Java面试准备之Spring框架系列三

@Controller +@ResponseBody 返回JSON 或 XML 形式数据

如果你需要在 Spring4之前开发 RESTful Web 服务的话,你需要使用@Controller 并结合@ResponseBody注解,也就是说@Controller +@ResponseBody= @RestController(Spring 4 之后新加的注解)。

@ResponseBody 注解的作用是将 Controller 的方法返回的对象通过适当的转换器转换为指定的格式之后,写入到HTTP 响应(Response)对象的 body 中,通常用来返回 JSON 或者 XML 数据,返回 JSON 数据的情况比较多。

Java面试准备之Spring框架系列三

@Transactional 注解使用详解

@Transactional 的作用范围

  1. 方法 :推荐将注解使用于方法上,不过需要注意的是:该注解只能应用到 public 方法上,否则不生效。
  2. :如果这个注解使用在类上的话,表明该注解对该类中所有的 public 方法都生效。
  3. 接口 :不推荐在接口上使用。

@Transactional 的常用配置参数

Java面试准备之Spring框架系列三

@Transactional 事务注解原理

@Transactional 的工作机制是基于 AOP 实现的,AOP 又是使用动态代理实现的。如果目标对象实现了接口,默认情况下会采用 JDK 的动态代理,如果目标对象没有实现了接口,会使用 CGLIB 动态代理。

如果一个类或者一个类中的 public 方法上被标注@Transactional 注解的话,Spring 容器就会在启动的时候为其创建一个代理类,在调用被@Transactional 注解的 public 方法的时候,实际调用的是,TransactionInterceptor 类中的 invoke()方法。这个方法的作用就是在目标方法之前开启事务,方法执行过程中如果遇到异常的时候回滚事务,方法调用完成之后提交事务。

Spring AOP 自调用问题

若同一类中的其他没有 @Transactional 注解的方法内部调用有 @Transactional 注解的方法,有@Transactional 注解的方法的事务会失效。

这是由于Spring AOP代理的原因造成的,因为只有当 @Transactional 注解的方法在类以外被调用的时候,Spring 事务管理才生效。

@Transactional 的使用注意事项总结

  1. @Transactional 注解只有作用到 public 方法上事务才生效,不推荐在接口上使用;
  2. 避免同一个类中调用 @Transactional 注解的方法,这样会导致事务失效;
  3. 正确的设置 @Transactional 的 rollbackFor 和 propagation 属性,否则事务可能会回滚失败

Spring 的 @Transactional 注解控制事务有哪些不生效的场景?

  • 数据库引擎是否支持事务(MySQL的MyISAM引擎不支持事务);
  • 注解所在的类是否被加载成Bean类;
  • 注解所在的方法是否为 public 方法;
  • 是否发生了同类自调用问题;
  • 所用数据源是否加载了事务管理器;
  • @Transactional 的扩展配置 propagation(事务传播机制)是否正确。
  • 方法未抛出异常
  • 异常类型错误(最好配置rollback参数,指定接收运行时异常和非运行时异常)

推荐阅读:Spring事务Spring事务失效的 8 大原因,这次可以吊打面试官了!

Spring事务同步机制,推荐阅读:Spring是如何保证同一事务获取同一个Connection的?使用Spring的事务同步机制解决:数据库刚插入的记录却查询不到的问题【享学Spring】

@Scheduled

首先,在项目启动类上添加 @EnableScheduling 注解,开启对定时任务的支持

@SpringBootApplication
@EnableScheduling
public class ScheduledApplication {

    public static void main(String[] args) {
        SpringApplication.run(ScheduledApplication.class, args);
    }

}

其中 @EnableScheduling 注解的作用是发现注解@Scheduled 的任务并后台执行。

其次,编写定时任务类和方法,定时任务类通过 Spring IOC 加载,使用 @Component 注解,定时方法使用 @Scheduled 注解。

@Component
public class ScheduledTask {

    @Scheduled(fixedRate = 3000)
    public void scheduledTask() {
        System.out.println("任务执行时间:" + LocalDateTime.now());
    }

}

fixedRate 是 long 类型,表示任务执行的间隔毫秒数,以上代码中的定时任务每 3 秒执行一次。

@Scheduled详解

在上面的入门例子中,使用了@Scheduled(fixedRate = 3000) 注解来定义每过 3 秒执行的任务,对于 @Scheduled 的使用可以总结如下几种方式:

  • @Scheduled(fixedRate = 3000) :上一次开始执行时间点之后 3 秒再执行(fixedRate 属性:定时任务开始后再次执行定时任务的延时(需等待上次定时任务完成),单位毫秒)
  • @Scheduled(fixedDelay = 3000) :上一次执行完毕时间点之后 3 秒再执行(fixedDelay 属性:定时任务执行完成后再次执行定时任务的延时(需等待上次定时任务完成),单位毫秒)
  • @Scheduled(initialDelay = 1000, fixedRate = 3000) :第一次延迟1秒后执行,之后按fixedRate的规则每 3 秒执行一次(initialDelay 属性:第一次执行定时任务的延迟时间,需配合fixedDelay或者fixedRate来使用)
  • @Scheduled(cron="0 0 2 1 * ? *") :通过cron表达式定义规则

其中,常用的cron表达式有:

  • 0 0 2 1 * ? * :表示在每月 1 日的凌晨 2 点执行
  • 0 15 10 ? * MON-FRI :表示周一到周五每天上午 10:15 执行
  • 0 15 10 ? 6L 2019-2020 :表示 2019-2020 年的每个月的最后一个星期五上午 10:15 执行
  • 0 0 10,14,16 * * ? :每天上午 10 点,下午 2 点,4 点执行
  • 0 0/30 9-17 * * ? :朝九晚五工作时间内每半小时执行
  • 0 0 12 ? * WED :表示每个星期三中午 12 点执行
  • 0 0 12 * * ? :每天中午 12点执行
  • 0 15 10 ? * * :每天上午 10:15 执行
  • 0 15 10 * * ? :每天上午 10:15 执行
  • 0 15 10 * * ? * :每天上午 10:15 执行
  • 0 15 10 * * ? 2019 :2019 年的每天上午 10:15 执行

处理常见的 HTTP 请求类型

5 种常见的请求类型:

  • GET :请求从服务器获取特定资源。举个例子:GET /users(获取所有学生)
  • POST :在服务器上创建一个新的资源。举个例子:POST /users(创建学生)
  • PUT :更新服务器上的资源(客户端提供更新后的整个资源)。举个例子:PUT /users/12(更新编号为 12 的学生)
  • DELETE :从服务器删除特定的资源。举个例子:DELETE /users/12(删除编号为 12 的学生)
  • PATCH :更新服务器上的资源(客户端提供更改的属性,可以看做作是部分更新),使用的比较少,这里就不举例子了。

GET 请求

@GetMapping("users") 等价于@RequestMapping(value="/users",method=RequestMethod.GET)

@GetMapping("/users")
public ResponseEntity<List<User>> getAllUsers() {
 return userRepository.findAll();
}

POST 请求

@PostMapping("users") 等价于@RequestMapping(value="/users",method=RequestMethod.POST)

@PostMapping("/users")
public ResponseEntity<User> createUser(@Valid @RequestBody UserCreateRequest userCreateRequest) {
 return userRespository.save(user);
}

PUT 请求

@PutMapping("/users/{userId}") 等价于@RequestMapping(value="/users/{userId}",method=RequestMethod.PUT)

@PutMapping("/users/{userId}")
public ResponseEntity<User> updateUser(@PathVariable(value = "userId") Long userId,
  @Valid @RequestBody UserUpdateRequest userUpdateRequest) {
  ......
}

DELETE 请求

@DeleteMapping("/users/{userId}")等价于@RequestMapping(value="/users/{userId}",method=RequestMethod.DELETE)

@DeleteMapping("/users/{userId}")
public ResponseEntity deleteUser(@PathVariable(value = "userId") Long userId){
  ......
}

PATCH 请求

一般实际项目中,我们都是 PUT 不够用了之后才用 PATCH 请求去更新数据。

  @PatchMapping("/profile")
  public ResponseEntity updateStudent(@RequestBody StudentUpdateRequest studentUpdateRequest) {
        studentRepository.updateDetail(studentUpdateRequest);
        return ResponseEntity.ok().build();
    }

GET,POST,UPDATE,delete哪一个是幂等的?

幂等概念:

在编程中一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同。

通俗点说:重复发送同一个http请求多次,结果同发送一次。即不会产生任何副作用(数据资源的改变)。

Java面试准备之Spring框架系列三

get、put、delete 是幂等的,post、patch 不是。

put 和 patch 都是用于更新数据资源的。 区别 在于

  • put 做更新操作时候是提交一整个更新后的实体(即全部),而不是需要修改的实体中的部分属性。当 URI 指向一个存在的资源,服务器要做的事就是查找并替换。

  • patch 做更新操作的时 资源的指令,该指令指导服务器来对资源做出修改。

参考文献

关于 RESTful 幂等性,尤其是PUT,PATCH和DELETE

发表评论 取消回复
表情 图片 链接 代码

分享