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注解,该注解默认会扫描该类所在包下的所有的配置类。

相关推荐
zyxzyx66610 分钟前
Canal 解析与 Spring Boot 整合实战
java·spring boot·后端
Studying_swz1 小时前
Spring WebFlux之流式输出
java·后端·spring
计算机学长felix2 小时前
基于SpringBoot的“酒店管理系统”的设计与实现(源码+数据库+文档+PPT)
spring boot·毕业设计
糖心何包蛋爱编程2 小时前
(二)Reactor核心-前置知识1
java·响应式编程·lambda表达式·干货分享
江沉晚呤时2 小时前
C#原型模式:通过克隆对象来优化创建过程
java·开发语言·microsoft·c#·asp.net·.netcore
飞翔中文网4 小时前
Java设计模式之装饰器模式
java·设计模式
Suwg2095 小时前
【Java导出word】使用poi-tl轻松实现Java导出数据到Word文档
java·开发语言·word·poi-tl
坚持拒绝熬夜5 小时前
JVM的一些知识
java·jvm·笔记·java-ee
修炼成精6 小时前
C#实现的一个简单的软件保护方案
java·开发语言·c#
网安-轩逸6 小时前
网络安全——SpringBoot配置文件明文加密
spring boot·安全·web安全