JavaEE的知识记录

内容很多可以通过目录进行查找对应的内容。

目录

一、注解

元注解

[@RequestMapping 路由映射注解](#@RequestMapping 路由映射注解)

@RequestParam绑定请求参数到可控制器方法的参数

请求参数绑定可控制方法参数:

参数绑定重命名

@RequestBody请求正文注解

@ResponseBody响应体正文注解

[@PathVariable 请求url路径中url的参数与方法参数的绑定](#@PathVariable 请求url路径中url的参数与方法参数的绑定)

@RequestPart上传文件

[@RestController 和@Controller](#@RestController 和@Controller)

@Controller

@RestController

五大类注解和@Bean方法注解在下面有讲,可通过目录进行查找

[@Autowired 注解](#@Autowired 注解)

二、请求学习:

1.传递单个参数

2.传递多个参数

3.传递对象

4.传递数组

5.传递集合

6.传递JSON数据

7.从URL中获取参数

8.通过请求上传文件

9.获取Cookie/Session

获取Cookie

获取Session

10.获取Header

三、响应学习

1.返回静态页面

2.返回数据@ResponseBody

3.返回HTML代码片段

4.返回JSON

5.设置状态码

[四、Spring IOC&DI](#四、Spring IOC&DI)

1.IOC(控制反转)的核心思想

什么是IOC?

IOC容器的核心作用

2.DI(依赖注入):IOC的具体实现技术

什么是依赖?

什么是依赖注入(DI)?

常见的依赖注入方式

三种注入优缺点分析:

3.Spring框架中的核心概念:Bean注解

Bean是什么?

五大类注解和方法注解

类注解:标记Bean的角色

方法注解@Bean:定义Bean的创建逻辑

[1)通过@Bean方法手动创建外部类的实例,将其注册为Spring Bean](#1)通过@Bean方法手动创建外部类的实例,将其注册为Spring Bean)

2)定义多个对象的处理方法

通过重命名Bean或指定名称

Bean名和方法名一致

使用@Primary注解标识默认对象

使用@Qualifier

使用@Resource注解标注名称注入对应名称的@Bean

补充:Bean的命名规则

扫描路径


一、注解

元注解

元注解的作用就是负责注解其他注解,在其他注解的源码中可以看到元注解。常见的元注解有以下几种:

  • @Retention:指定其所修饰的注解的保留策略。比如SOURCE值表示注解只在源文件中保留,编译期间删除;CLASS表示注解只在编译期间存在于.class文件中,运行时JVM不可获取注解信息。
  • @Document:该注解是一个标记注解,用于指示一个注解将被文档化。
  • @Target:用来限制注解的使用范围。值为Type表示可以修饰类,接口,注解或枚举类型;FIELD表示修饰属性;METHOD表示可以修饰方法。除了这些还有很多值。
  • @Inherited:该注解使父类的注解能被其子类继承。
  • @Repeatable:该注解是java8新增的注解,用于开发重复注解。

@RequestMapping 路由映射注解

这是一个用来处理请求"地址映射"的注解,可用于映射一个请求或一个方法,可以用在类和方法是上。

  • 标注在方法上:

用于方法上,表示在父路径下追加方法上注解中的地址将会访问到该方法

  • 标注在类上:

用于类上,表示类中的所有相应请求的方法都是以该地址作为父路径。

此外@RequestMapping支持get和post方法,所以在用该注解修饰方法时可以自己设置method属性:

复制代码
@RequestMapping(value = "/sayhi/b",method = RequestMethod.GET)

@RequestParam绑定请求参数到可控制器方法的参数

请求参数绑定可控制方法参数:

java 复制代码
@RequestMapping("/m6")
public String method2(@RequestParam List<String> list) {
    return list.toString();
}

使用该注解,可以自动将HTTP请求中的同名多值参数绑定到该集合 ,若省略该注解,Spring不会自动绑定多值请求参数到该集合中,这会导致list为null或绑定失败,所以必须显式的添加该注解

++但是控制器方法的参数为数组类型时,即使不显式的使用该注解,Spring也会默认将HTTP请求中的同名多值参数自动绑定到该数组中,这是和其他集合类不一样的地方。++

java 复制代码
@RequestMapping("/m5")
    public String method( String[] arraryParam){
        return Arrays.toString(arraryParam);
    }

++当然了,数组也可以显式的去添加该注解。++

java 复制代码
@RequestMapping("/m5")
    public String method(@RequestParam String[] arraryParam){
        return Arrays.toString(arraryParam);
    }

参数绑定重命名

某种情况下,前端传递的参数key和后端传送的参数key不一致,比如前端传递了一个time,而后端使用createtime来接收,这样后端就会出现参数收不到的情况,若出现这种情况,我们可以使用注解@RequestParam来重命名后端参数的值。

复制代码
@RequestMapping("/m4")
public Object method_4(@RequestParam("time") String createtime) {
     return "接收到参数createtime:" + createtime;
}

上面的代码中,前端传递了time的值,然后传递给后端的createtime,这样就能够保证前端和后端参数定义可以不一致。

若前端利用createtime进行传递,浏览器就会报错;并且用了该注解,该参数必须进行传递。

当然,我们可以设置属性,让该参数变成不必传的参数,即使不传或者key参数错误,返回的value值都是null。

java 复制代码
@RequestMapping("/m4")
public Object method_4(@RequestParam("time",require =false) String createtime) {
     return "接收到参数createtime:" + createtime;
}
  • 传递错误的key:
  • 不传递值:

@RequestBody请求正文注解

具体实现可以看 6. 传递JSON数据

意思是这个注解作用在请求正文的数据绑定,请求参数必须写在请求正文中,++即HTTP请求正文直接映射到方法的参数。++

java 复制代码
 @RequestMapping("/m7")
    public Object method3(@RequestBody User user){
        return user.toString();
    }

@ResponseBody响应体正文注解

@ResponseBody既是类注解又是方法注解。这个注解用在类上表示该类的所有方法返回的都是数据;若作用在方法上,表示该方法返回的是数据。

这个注解的作用是方法的返回值作为响应体进行返回。

java 复制代码
   @ResponseBody
    @RequestMapping("/user")
    public String getUser() {
        return "index.html"; // 返回视图名称(如 user-page.jsp)
    }

@PathVariable 请求url路径中url的参数与方法参数的绑定

path variable:路径变量

这个注解主要作用在请求URL路径上的数据绑定

默认传递参数写在URL上,springMVC就可以通过数据绑定获取到该参数。

后端实现代码:

java 复制代码
@RequestMapping("/m8/{id}/{name}")
    public String method4(@PathVariable Integer id, @PathVariable String name){
        return "解析参数id:"+id+",name:"+name;
    }

同时我们可以对后端参数进行重命名,让前端参数和后端参数映射。

java 复制代码
@RequestMapping("/m8/{id}/{name}")
    public String method4(@PathVariable Integer id, @PathVariable("name") String username){
        return "解析参数id:"+id+",name:"+username;
    }

@RequestPart上传文件

是springmvc中用于处理多部分请求的注解,常用于文件上传、同时传递文件和其他结构化数据。

核心作用:

  • 处理文件上传:绑定请求中的文件部分到MultipartFile类型的参数
  • 结构化数据:解析请求中的非文件部分到java对象,通常结合@RequestBody使用。
  • 支持复杂请求:适用于需要同时上传文件和其他元数据的场景(上传用户头像并附带用户信息)。
java 复制代码
@RequestMapping("/m9")
    public String getFile(@RequestPart("file") MultipartFile file) throws IOException {
        //获取文件名称
        String filename = file.getOriginalFilename();
        //文件上传到指定路径
        file.transferTo(new File("D:/Postman/"+filename));
        return "接收到的文件名称为"+filename;
    }

使用postman发送请求:

@RestController 和@Controller

这两个注解有本质的区别,导致两者在返回结果时表现不同。

@Controller

本质是传统的Spring MVC控制器,Spring框架启动时加载,把这个对象交给Spring进行管理。

用法**:用于处理客户端发起的请求,并负责返回适当的视图(view)作为响应** ,即方法默认返回视图名称(需配合视图解析器解析为HTML/JSP等页面)。

java 复制代码
 @RequestMapping("/user")
    public String getUser() {
        return "/index.html"; // 返回视图名称(如 user-page.jsp)
    }

页面展示结果:

++其中,项目文件中要有对应的视图文件(index.html),若没有会返回404错误;并且该方法的类是引用@Controller这个注解的。++

@RestController

本质是@Controller和@ResponseBody的组合注解。

用法:在使用**@RestController注解标记的类中,每个方法的返回值都会以JSON或xml的形式直接写入HTTP响应体中,即** 所有方法默认返回数据(如JSON、xml),而非视图名称。

java 复制代码
@RequestMapping("/user")
    public String getUser() {
        return "/index.html"; // 返回视图名称(如 user-page.jsp)
    }

++其中该代码使用了@RestController注解,访问页面中会显示/index.html。++

五大类注解和@Bean方法注解在下面有讲,可通过目录进行查找

@Autowired 注解

需要搭配类级别的Spring管理注解使用,因为它的核心机制依赖于Spring容器来管理Bean的创建和依赖注入,所以被@Autowired注入的类必须被Spring容器管理。


二、请求学习:

请求的学习主要是学习如何传递参数

1.传递单个参数

2.传递多个参数

参数如果使用基本数据类型必须要传递值,不传递就会报错,所以开发时建议使用包装类型

3.传递对象

如果有很多形参时,就要传递很多参数,并且后续每增加一个参数,也需要修改方法声明,那么我们可以把这些参数封装成一个对象。

这样我们可以通过URL填写参数值,queryString的参数key与类型值要与后端的属性名要一致 ,如果属性类型是包装类型,queryString参数key和后端属性名不一致的话,返回的结果是null,若属性是基本类型,querystring参数key和后端参数的名不一致的话,返回就会报错。

4.传递数组

当请求参数名与形参数组名称相同且请求参数为多个,后端定义数组类型形参即可接收参数。

java 复制代码
   @RequestMapping("/m5")
    public String method(String[] arraryParam){
        return Arrays.toString(arraryParam);
    }

其中url还可以为:

http://127.0.0.1:8080/s/m5?arraryParam=zhangsan\&arraryParam=lisi

或者http://127.0.0.1:8080/s/m5?arraryParam=zhangsan%2Clisi%2Cwangwu

5.传递集合

集合参数:和数组类似,同一个请求参数名有多个,且需要使用@RequestParam绑定参数关系。

默认情况下,请求中参数名相同的多个值,是封装到数组。如果要封装到集合,要使用@RequestParam绑定参数关系。

其中url的请求方式和数组类似。

java 复制代码
@RequestMapping("/m6")
    public String method2(@RequestParam List<String> list){
        return list.toString();
    }

6.传递JSON数据

**JSON概念:**JavaScript Object Notation,即JavaScript对象表示法。

JSON是一种轻量级的数据交互格式,它基于ECMAScript的一个子集,采用完全独立于编程语言的文本格式来存储和表示数据。

简单来说:JSON就是一种数据格式,有自己的格式和语法,使用文本表示一个对象或数组的信息,因此JSON本质是字符串。主要负责在不同语言中数据传递和交换。

JSON和JavaScript的关系:

两者没有关系,只是语法相似,js开发者能更快的上手而已,但是他的语法本身比较简单,所以很好学。

JSON的语法:

  1. 数据在键值对(key/value)中
  2. 数据由逗号,分割
  3. 对象用{}表示
  4. 数组用[ ]表示
  5. 值可以为对象,也可以为数组,数组中可以包含多个对象。

下面可以看一段JSON数据:

javascript 复制代码
{
 "squadName": "Super hero squad",
 "homeTown": "Metro City",
 "formed": 2016,
 "secretBase": "Super tower",
 "active": true,
 "members": [{
 "name": "Molecule Man",
 "age": 29,
 "secretIdentity": "Dan Jukes",
 "powers": ["Radiation resistance", "Turning tiny", "Radiation 
blast"]
 }, {
 "name": "Madame Uppercut",
 "age": 39,
 "secretIdentity": "Jane Wilson",
 "powers": ["Million tonne punch", "Damage resistance", "Superhuman 
reflexes"]
 }, {
 "name": "Eternal Flame",
 "age": 1000000,
 "secretIdentity": "Unknown",
 "powers": ["Immortality", "Heat Immunity", "Inferno", 
"Teleportation", "Interdimensional travel"]
 }]
}

JSON的两种结构:

对象:用大括号{}保存的对象是一个无序的键值对集合,一个对象以左括号{开始,右括号}结束。每个键后跟一个冒号:,键值对使用逗号,分隔。

数组:中括号 [ ] 保存的数组是值(value)的有序集合。一个数组以左中括号[开始,右中括号]结束,值之间使用逗号,分隔。

JSON和java对象互转

JSON本质上是一个字符串,通过文本来存储和描述数据

SpringMVC框架也集成了JSON转换工具,我们可以直接使用来完成JSON字符串和java对象的互转。

springmvc已经把转换的依赖引进来了,所以不用再去官网上去找依赖并引进去。

JSON优点:

简单易用;跨语言支持;轻量级,占用宽带小;易于扩展,支持嵌套对象和数组;安全性好,JSON格式是一种纯文本格式不会执行恶意代码。

传递JSON对象:

接收JSON对象需要使用@RequestBody注解。

RequestBody:请求正文,意思是这个注解作用在请求正文的数据绑定,请求参数必须写在请求正文中(body)。

后端实现:

java 复制代码
 @RequestMapping("/m7")
    public Object method3(@RequestBody User user){
        return user.toString();
    }

利用postman来发送JSON请求参数:

响应结果:

7.从URL中获取参数

从URL获取参数是利用注解@PathVariable

java 复制代码
@RequestMapping("/m8/{id}/{name}")
    public String method4(@PathVariable Integer id, @PathVariable("name") String username){
        return "解析参数id:"+id+",name:"+username;
    }

8.通过请求上传文件

后端实现:

java 复制代码
@RequestMapping("/m9")
    public String getFile(@RequestPart("file") MultipartFile file) throws IOException {
        //获取文件名称
        String filename = file.getOriginalFilename();
        //文件上传到指定路径
        file.transferTo(new File("D:/Postman/"+filename));
        return "接收到的文件名称为"+filename;
    }

9.获取Cookie/Session

关于cookie和session的内容可以看这篇博客:session和cookie

获取Cookie

由于Spring MVC是基于Servlet API构建的原始Web框架,也是在Servlet的基础上实现的。

所以我们可以更简单的获取Cookie。

java 复制代码
  @RequestMapping("/getcookie")
    public String cookie(@CookieValue("bite") String bite){
        return "bite:"+bite;
    }

利用@CookieValue这个注解让参数"bite"与浏览器中的名称bite进行绑定然后获取对应的value值。

获取Session

通过Spring MVC内置对象HttpSession来获取。

java 复制代码
  @RequestMapping("/getSession")
    public String getSession(HttpSession session){
        session.setAttribute("name","java");
        String name = (String)session.getAttribute("name");
        return "name:"+name;
    }

Session不存在的情况下,创建session对象并存储信息。

此时创建session对象,服务器会返回携带有sessionID的cookie并存储到浏览器中。

10.获取Header

通过Spring MVC框架简洁获取Header。

java 复制代码
 @RequestMapping("/header")
    public String header(@RequestHeader("User-Agent") String userAgent){
        return "userAgent" + userAgent;
    }
    

@RequestHeader注解的参数值为HTTP请求报头中的"Key"


三、响应学习

http响应结果可以是数据,也可以是静态页面内,也可以针对响应设置状态码,Header信息等。

1.返回静态页面

创建前端页面index.html

html代码如下:

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
     <meta charset="UTF-8">
     <title>Index⻚⾯</title>
</head>
<body>
     Hello,Spring MVC,我是Index⻚⾯.
</body>
</html>

后端代码:

java 复制代码
//类路径为 /s
@RestController
public class IndexController {
 @RequestMapping("/index")
 public Object index(){
 //返回index.html
 return "/index.html";
 }
}

输入:http://127.0.0.1:8080/s/index

得到的结果为:

结果发现,页面未能正确返回,http响应把"/index.html"当做了http响应正文的数据。为了让springmvc能识别出index.html是一个静态页面并返回响应,我们把@RestController改为@Controller即可:

java 复制代码
@Controller
public class IndexController {
 @RequestMapping("/index")
 public Object index(){
 return "/index.html";
 }
}

再次输入刚刚的url即可访问该静态页面:

2.返回数据@ResponseBody

该注解是把返回的数据通过响应体进行返回。

由于@RestController是@Controller和@ResponseBody的结合,那么我们可以这样子:

java 复制代码
@Controller
public class IndexController {
     @RequestMapping("/index")
     public Object index(){
         return "/index.html";
     }
     @RequestMapping("/returnData")
     @ResponseBody
     public String returnData(){
         return "该⽅法返回数据";
     }
}

再次运行程序,浏览器响应结果为:

若去掉@ResponseBody,程序会报404错误。

java 复制代码
@Controller
public class IndexController {
     @RequestMapping("/index")
     public Object index(){
         return "/index.html";
     }
     @RequestMapping("/returnData")
     @ResponseBody
     public String returnData(){
         return "该⽅法返回数据";
     }
}

程序中就只剩下了@Controller,程序就会认为返回的是视图,根据内容去查找文件,但是查询不到,路径不在,报404.。

3.返回HTML代码片段

若后端返回数据时,如果数据中有HTML代码,也会被浏览器解析。

java 复制代码
@RequestMapping("/returnHtml")
@ResponseBody
public String returnHtml() {
   return "<h1>Hello,HTML~</h1>";
}

++响应体(body)的类型(Content-Type)为text/html。++

如果返回的代码片段是js,SpringMVC会自动设置++Content-Type 为++application/javascript。

如果请求的是css⽂件, Spring MVC会⾃动设置Content-Type为 text/css。

但是要配合上@Controller注解以及要有js文件和css文件。

java 复制代码
@RequestMapping("/index2")
    public Object index2(){
        return "/a.js";
    }

    @RequestMapping("/index3")
    public Object index3(){
        return "/b.css";
    }

抓包结果:

4.返回JSON

当方法返回的是对象时,content-type自动设置为application/json。

java 复制代码
 @RequestMapping("returnJson")
    public User returnJson(User user){
        return user;
    }

可以看到,返回的响应体为JSON格式的。

5.设置状态码

springmvc会根据我们方法的返回结果自动设置响应状态码,当然我们也可以手动指定状态码,通过springmvc的内置对象 HttpServletResponse 提供的方法来进行设置。


四、Spring IOC&DI

1.IOC(控制反转)的核心思想

什么是IOC?

IOC(Inversion of Control)是一种设计原则,其++核心思想是将对象的创建、依赖管理和生命周期的控制权交给一个容器(IOC容器)++,而不是由对象自身直接控制。

  • 传统方式:对象主动创建和管理依赖(如new UserService() ),导致类之间高度耦合。
  • IOC方式:由容器统一创建和管理对象,对象只需声明需要哪些依赖,容器负责注入到类中。

IOC容器的核心作用

创建对象:根据配置或注解,实例化类并管理其生命周期。

管理依赖关系:自动处理对象之间的依赖(如A类需要B类的实例)。

2.DI(依赖注入):IOC的具体实现技术

什么是依赖?

如果一个类需要另一个类的实例才能完成功能,则称其依赖这个类。

例如:++底盘依赖于车身,底盘是依赖方,车身是被依赖方,看下面的代码中,需要把底盘这个依赖注入到车身的类中。++

这是控制反转的一个简单的例子。

什么是依赖注入(DI)?

DI(Dependecy Injection)是实现++IOC的具体技术,++通过将依赖对象的实例从外部(IOC容器)注入到目标对象中,而非由目标对象自己创建。

  • 核心思想:对象的依赖关系由外部提供,而不是内部硬编码。
  • 关键角色:IOC容器负责创建依赖对象并注入到需要的位置。

常见的依赖注入方式

|----------|-------------------|
| 方式 | 描述 |
| 构造器注入 | 通过构造函数传递依赖对象 |
| Setter注入 | 通过Setter方法设置依赖对象 |
| 属性注入 | 直接通过字段(属性)的方式注入依赖 |

三种注入优缺点分析:
  • 属性注入:

属性注入的流程如下:

1.实例化对象,先调用类的默认无参构造器创建对象。

2.反射注入字段:通过反射机制直接对字段赋值。

优点

简洁,使用方便。

缺点

只用用于IOC容器,如果非IOC容器就不可以了,并且只有在使用的时候才会出现NPE(空指针异常)。

不能注入一个Final属性。

理由:Final字段必须在构造期间完成初始化,但是属性注入是在对象构造完后通过反射赋值,根据依赖注入的流程,无法满足final的要求。

java 复制代码
@Service
public class UserService {
 public void sayHi() {
 System.out.println("Hi,UserService");
 }
}


@Controller
public class UserController {
 //注⼊⽅法1: 属性注⼊
 @Autowired
 private UserService userService;
 public void sayHi(){
 System.out.println("hi,UserController...");
 userService.sayHi();
 }
}

若把 @Autowired注解去掉,就会报出空指针异常,spring不会自动创建该实例并进行赋值,因此,userService属性保持null值。

  • 构造函数注入:

构造器注入流程如下:

  1. 实例化对象:Spring直接调用有参构造器创建对象,不需要默认无参构造器。
  2. 注入依赖:依赖参数在构造器调用你时直接传入(无需后续反射赋值)。

优点:

可以注入final修饰的属性,注入的对象不会被修改,满足final的要求。

依赖对象在使用前一定会被完全初始化,因为依赖是在类的构造方法中执行的,而构造方法是在类加载阶段就会执行的。

通用性好,构造方法是JDK支持是、的,所以更换任何框架,他都是适用的。

缺点:

注入多个对象时,代码会比较繁琐。

java 复制代码
@Service
public class UserService {
 public void sayHi() {
 System.out.println("Hi,UserService");
 }
}

public class UserController {
    private final UserService userService;

     @Autowired
    // 构造器注入:依赖字段是 final 的
    public UserController(UserService userService) {
        this.userService = userService;
    }
}

注意事项:如果类只有一个构造方法,那么 @Autowired注解可以省略;如果类中有多个构造方法,那么需要添加上@Autowired来明确到底要使用哪个构造方法。

  • Setter注入:

Setter依赖注入流程:

  1. 实例化对象:调用默认无参构造器创建对象。
  2. 反射调用setter方法:通过反射调用Setter方法注入依赖。

优点:

方便在类实例之后,重新对该对象进行配置或者注入。

缺点:

不能注入一个Final修饰的属性。

注入对象可能会被改变,因为setter方法可能会被多次调用,就有被修改的风险。

java 复制代码
@Service
public class UserService {
 public void sayHi() {
 System.out.println("Hi,UserService");
 }
}

@Controller
public class UserController3 {
 //注⼊⽅法3: Setter⽅法注⼊
 private UserService userService;
 @Autowired
 public void setUserService(UserService userService) {
     this.userService = userService;
 }
}

若把@Autowired注解去掉,同属性注入一样,spring默认不会自动调用setter方法进行注入,因此 userService 字段会保持 null,后续调用的方法userService.sayHi() 时抛出空指针异常。

3.Spring框架中的核心概念:Bean注解

Bean是什么?

定义:由spring IOC容器管理的对象称为Bean。

生命周期:Bean的创建、初始化、依赖注入和销毁均为容器控制。

五大类注解和方法注解

类注解:标记Bean的角色

Spring通过注解标记类,告诉容器哪些类需要被管理为Bean,并明确角色:

|----------------|---------------------------------|
| 注解 | 用途 |
| @Component | 通过注解,标记任意类为Bean |
| @Controller | 标记为Web控制器(处理HTTP请求) |
| @Service | 标记业务逻辑层组件 |
| @Repository | 标记为数据访问层组件(DAO)(自动处理数据库异常转换) |
| @Configuration | 标记为配置类(定义Bean的创建方式,常与@Bean配合使用) |

++在Spring中,五大核心注解虽然最终都能将类注册为Spring Bean ,但它们设计的目的是为了区分代码的分层和职责,比如要在业务逻辑层使用@Component或@Service,很显然@Service是更好的选择。此外,这些注解还在功能上存在细微的差异,并非完全一样。++

++差异体现:++

|----------------|----------------------------------------------------------------|
| 注解 | 特殊行为 |
| @Component | 无特殊行为 |
| @Controller | @Controller注解的类通常与@RequestMapping,@GetMapping等注解配合使用,处理HTTP请求。 |
| @Service | 无特殊行为,但强调业务逻辑的封装 |
| @Repository | 自动转换数据访问异常为Spring异常 |
| @Configuration | 标记类为配置类,配合@Bean使用 |

我们可以查看注解源码:

可以看到,这些注解里面都有一个注解@Component,说明它们本身就是属于@Component的"子类"。这些注解被称为@Component的衍生注解。

方法注解@Bean:定义Bean的创建逻辑

@Bean 核心功能:

++在Spring配置类中,通过@Bean注解的方法手动显式的创建对象,并将对象交给Spring 容器管理(成为Spring Bean)。++

|-------|----------------------------------------------|
| 注解 | 用途 |
| @Bean | 在配置类中标注方法,表示该方法返回一个Bean(用于定义第三方库组件的创建方式) |

@Bean方法注解要搭配类注解进行使用。

java 复制代码
@Configuration
public class AppConfig {
    @Bean
    public UserRepository userRepository() {
        return new UserRepositoryImpl();
    }
}

补充:无论目标类是否有Spring注解,只要通过@Bean方法返回其实例,Spring就会管理该对象。比如上面的代码中,尽管UserRepository类没有被@Component这样的注解所标记,但是仍然能通过@Bean方法注册,Spring会直接使用该方法返回的实例。

但是目标类同时有@Component和@Bean时,Spring就会通过注解@Component扫描和@Bean方法注册,Spring就会报错,

我们知道类注解是添加到某个类上的,但是存在两个问题:

  • 1.当使用第三方库或外部依赖包中的类时,是无法直接修改其源码添加Spring注解(如@Component);
  • 2.一个类,需要多个对象,比如多个数据源。

++像这种场景,就需要可以使用方法注解@Bean来解决了++

1)通过@Bean方法手动创建外部类的实例,将其注册为Spring Bean
java 复制代码
@Configuration
public class ExternalConfig {
    // 将外部类 Gson 注册为 Bean
    @Bean
    public Gson gson() {
        return new Gson(); // 手动创建实例
    }
}

在配置类中,通过@Bean来获得外部依赖类的实例。

2)定义多个对象的处理方法

对于同一个类,如何定义和获取多个对象呢?

比如多个数据源的场景,类是同一个,但是配置不同,指向不同的数据源。

我们看下@Bean的使用

java 复制代码
@Component
public class BeanConfig {
 @Bean
 public User user1(){
 User user = new User();
 user.setName("zhangsan");
 user.setAge(18);
 return user;
 }
 @Bean
 public User user2(){
 User user = new User();
 user.setName("lisi");
 user.setAge(19);
 return user;
 }
}

定义了多个对象的话,根据类型获取对象,获取的是哪个对象呢?

java 复制代码
@SpringBootApplication
public class SpringIocDemoApplication {
    public static void main(String[] args) {
 //获取Spring上下⽂对象
 ApplicationContext context = 
    SpringApplication.run(SpringIocDemoApplication.class, args);
 //从Spring上下⽂中获取对象
 User user = context.getBean(User.class);
 //使⽤对象
 System.out.println(user);
 }
}

运行结果:

从上面的报错信息显示:期望只有一个匹配,结果发现了两个,user1,user2.

从报错信息中可以看出来,被@Bean注解的Bean的名称正是对应的方法名。

下面我们根据名称来获取bean对象

java 复制代码
@SpringBootApplication
public class SpringIocDemoApplication {
 public static void main(String[] args) {
 //获取Spring上下⽂对象
 ApplicationContext context = 
SpringApplication.run(SpringIocDemoApplication.class, args);
 //根据bean名称, 从Spring上下⽂中获取对象
 User user1 = (User) context.getBean("user1");
 User user2 = (User) context.getBean("user2");
 System.out.println(user1);
 System.out.println(user2);
 }
}

运行结果:

++可以看到,@Bean可以针对同一个类,定义多个对象。++

下面是解决同一类型中有多个对象时的解决方法:

通过重命名Bean或指定名称

可以通过设置name属性给Bean对象进行重命名操作:

java 复制代码
@Bean(name = {"u1","user1"})
public User user1(){
 User user = new User();
 user.setName("zhangsan");
 user.setAge(18);
 return user;
}

此时我们可以使用重命名后的u1这个名称来获得Bean对象了。

java 复制代码
User u1 = (User) context.getBean("u1");

当然,name={}可以省略,如下:

java 复制代码
@Bean({"u1","user1"})

只有一个名称时,{}也是可以省略的。

java 复制代码
@Bean("u1")
Bean名和方法名一致

以下是完整代码:

java 复制代码
@Configuration
public class A {

    @Bean
    public User User1(){
        User user = new User();
        user.setName("zhangsan");
        user.setAge(18);
        return user;
    }

    @Bean
    public User User2(){
        User user = new User();
        user.setName("zhangsi");
        user.setAge(10);
        return user;
    }

}

@Data
public class User {
    private String name;
    private Integer age;
}

@SpringBootApplication
public class Demo14Application {

    public static void main(String[] args) {

        ApplicationContext context = SpringApplication.run(Demo14Application.class, args);

        User user1 = (User) context.getBean("User1");
        User user2 = (User) context.getBean("User2");

        System.out.println(user1);
        System.out.println(user2);

    }

}

运行结果:

使用@Primary注解标识默认对象
java 复制代码
    @Primary
    @Bean
    public User User1(){
        User user = new User();
        user.setName("zhangsan");
        user.setAge(18);
        return user;
    }

使用@Primary注解,表示让Spring默认匹配该对象。

使用@Qualifier

使用@Qualifier注解:指定当前要注入的bean对象。在@Qualifier的value属性中,指定注入的bean的名称。

比如在属性注入中@Qualifier注解不能单独使用,必须配合@Autowired使用。

java 复制代码
   @Qualifier("User1")
    @Autowired
    private User user1;

属性注入中指定了对应的Bean对象(即有方法返回对应名称(User1)的Bean对象,没有会报错)。

java 复制代码
  @Bean
    public User User1(){
        User user = new User();
        user.setName("zhangsan");
        user.setAge(18);
        return user;
    }

**代码解读:**注入了指定名称的User1的Bean对象。

使用@Resource注解标注名称注入对应名称的@Bean

使用方法:

java 复制代码
@Resource(name = "User1")
    private User user1;

@Resource这个注解和@Autowire有点像,但是还是有区别的。

@Autowire和@Resource的区别:

  • @Autowire是Spring框架提供的注解,而@Resource是JDK提供的注解。
  • @Autowire默认是按照类型注入,而@Resource是按照名称注入,相比于@Autowire来说,@Resource支持更多的参数设置,例如根据Bean的name来获得想要的Bean。
补充:Bean的命名规则

1)五大注解存储的bean

  1. 前两位字母均为大写,bean名称为类名,比如SEvice类,bean的名称就为 SEvice。
  2. 其他的类为类名首字母小写,比如Service,bean的名称为service;service,bean的名称为service。
  3. 可以通过value属性设置@Controller(value="user")

2)@Bean注解存储的bean

  1. bean名称为方法名。
  2. 通过name属性设置@Bean(name={"u1","user1"})。

扫描路径

使用前面的学习的类注解去声明bean,一定会生效吗?

不一定!!

默认的的扫描范围是Springboot启动类所在的包里面。

如果不在启动类所在的包里面可以在配置类上添加@ComponentScan注解,该注解默认会扫描该类所在包下的所有的配置类。

相关推荐
wfsm几秒前
spring事件使用
java·后端·spring
微风粼粼18 分钟前
程序员在线接单
java·jvm·后端·python·eclipse·tomcat·dubbo
缘来是庄22 分钟前
设计模式之中介者模式
java·设计模式·中介者模式
rebel1 小时前
若依框架整合 CXF 实现 WebService 改造流程(后端)
java·后端
代码的余温2 小时前
5种高效解决Maven依赖冲突的方法
java·maven
慕y2742 小时前
Java学习第十六部分——JUnit框架
java·开发语言·学习
paishishaba2 小时前
Maven
java·maven
张人玉3 小时前
C# 常量与变量
java·算法·c#
Java技术小馆3 小时前
GitDiagram如何让你的GitHub项目可视化
java·后端·面试
Codebee3 小时前
“自举开发“范式:OneCode如何用低代码重构自身工具链
java·人工智能·架构