射人先射马,擒贼先擒王,搞懂存和取,拿下Spring

在Spring中想要更简单的存储和读取对象的核心是使用注解

一、存储Bean对象

Spring相当于hash表,key就是String(Bean对象的name(id)),value就是Object(Bean对象)
key要保证唯一,key相同的键值对有一个会被取代

1、配置扫描路径

  1. 在Spring配置文件(resource目录下)中,配置扫描包路径;Spring容器只会扫描这个包路径下的所有的类,遇到添加注解的类,就会添加到Spring容器中
  2. Spring中不会扫描其它的路径,所以其他路径中即使有添加注解的类,也不会被存储到Spring容器中
  3. 对于指定包路径下的 未添加注释的 类不会存储到Spring容器中

一个项目中的有太多太多的文件和类,如果Spring一个一个的扫描,效率极低且开销大,出于对性能的考虑,就配置一个扫描路径,将需要存储的类都放到这个路径下

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:content="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
    <!-- 配置一下:bean注解扫描的根路径(方面后面更简单存储对象到spring容器) 目的 提高效率-->
    <content:component-scan base-package="com.bit"></content:component-scan>
</beans>

注意:第七行中的 base-package 代表的就是扫描路径

以下所有添加注解的类都是在 com.bit 这个包路径下的

2、添加注解存储Bean对象

(1)、类注解

  1. @Controller

@Controller = < bean id=“userController” class=“com.bit.UserController”>< /bean >

@Controller  
public class UserController {
    public void sayHello(){
        System.out.println("hello, Spring!!!");
    }
}
  1. @Service

@Service = < bean id=“userService” class=“com.bit.UserService”>< /bean >

@Service
public class UserService {
    public void sayHello(){
        System.out.println("hello, Spring!!!");
    }
}
  1. @Repository
@Repository
public class UserRepository {
    public void sayHello(){
        System.out.println("hello, Spring!!!");
    }
}
  1. @Configuration
@Configuration
public class UserConfiguration {
    public void sayHello(){
        System.out.println("hello, Spring!!!");
    }
}
  1. @Component
@Component
public class UserComponent {
    public void sayHello(){
        System.out.println("hello, Spring!!!");
    }
}

查看启动类 App(并没有固定的路径

// 启动类
public class App {
    public static void main(String[] args) {
        // 获取 Spring 上下文对象
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");

        // 取出 bean
        UserController userController = context.getBean("userController", UserController.class);
        userController.sayHello();
    }

    // 打印 hello, Spring!!!
}


注意Bean对象的name(id)
分析源码可知

  1. 如果 类名 的第一个和第二个字符都是大写的,那么Bean对象的name就是 类名
  2. 否则的话,Bean的name 就是将类名的第一个字符小写得到
    在这里插入图片描述
关于类注解的关系
  1. 这五个类注解所起到的作用是相同的
  2. 使用这么多的类注解,就是让程序员看到类注解后,就能直接了解当前类的用途
  3. 查看源码,@Controller、@Service、@Repository、@Configuration这些注解里面都有一个注解 @Component,所以它们都是属于@Component的 “子类”

程序的分层调用流程

在这里插入图片描述

各个分层的功能

  1. Controller 业务逻辑层
    相当于“门卫”,对前端传来的参数进行校验,判断参数的合法性
  2. Service 服务层
    相当于“领导”,负责数据的组装,管理 调用数据库的接口
  3. Repository 数据持久层
    相当于“打工人”,直接操作数据库,进行数据的CRUD
  4. Configuration 配置层
    设置当前项目中所有的配置信息
  5. Component 实体类

(2)、方法注解

设置一个类,进行实验

package com.model;

public class UserInfo {
    private int id;
    private String name;
    private String password;

    public UserInfo(int id, String name, String password) {
        this.id = id;
        this.name = name;
        this.password = password;
    }

    @Override
    public String toString() {
        return "UserInfo{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}
  1. 方法注解是放到某个方法上的,将当前方法返回的对象存储到Spring容器中
  2. 在初始化Spring上下文对象的时候,Spring自动调用有方法注解的方法,将返回对象存储
  3. 方法注解一定要搭配类注解使用,这个类的Bean对象也被存储到了Spring容器中

为了提高性能,如果不搭配类注解,需要扫描所有的方法;搭配类注解,就只需要扫描指定类中的方法

  1. 如果没有重命名Bean对象,那么它默认的名字(id)就是方法名原汁原味的方法名, 千万不要和类注解时Bean的名字搞混)
@Component 
public class UserBeans {
    public void func(){
        System.out.println("wc");
    }
    // 将当前方法返回的对象,存储到spring容器中
    @Bean
    public UserInfo getUserInfo(){
        System.out.println("自动调用了get方法");
        UserInfo userInfo = new UserInfo(1,"qust","2020");
        return userInfo;
    }
}

启动类测试

// 启动类
public class App {
    public static void main(String[] args) {
        // 获取 Spring 上下文对象
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
        
        // 设置一个间隔
        System.out.println("yyds");
        
        // 取出 bean
        // 方法注解
        UserInfo userInfo = context.getBean("getUserInfo", UserInfo.class);
        System.out.println(userInfo);
        
        UserBeans userBeans = context.getBean("userBeans", UserBeans.class);
        userBeans.func();
    }
}

在这里插入图片描述

  1. 如果重命名了Bean对象,就不能使用方法名获取Bean对象了
  2. 一个Bean对象,可以指定多个名字;但是通过这多个名字获取是同一个对象
  1. 一个Bean对象,多个名字,相当于多个键值对(key不同,value指向同一个对象)
    name=可以省略
@Component
public class UserBeans {
    @Bean(name = {"userInfo", "user1"})
    public UserInfo getUserInfo(){
        UserInfo userInfo = new UserInfo(1,"qust","2020");
        return userInfo;
    }
}

启动类测试

// 启动类
public class App {
    public static void main(String[] args) {
        // 获取 Spring 上下文对象
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");

        // 指定 bean 的名称
        UserInfo userInfo = context.getBean("userInfo", UserInfo.class);
        System.out.println(userInfo);
        // 1,qust,2020
    }
   
}
  1. 多个Bean对象,使用相同的名称,后来的不会存储到Spring容器中
package com.bit;

@Component 
public class UserBeans {
    @Bean(name = "userInfo")
    public UserOutfo getUserInfo2(){
        UserOutfo userOutfo = new UserOutfo("郑州路","青岛科技大学");
        return userOutfo;
    }

    @Bean(name = "userInfo")
    public UserInfo getUserInfo(){
        UserInfo userInfo = new UserInfo(8848,"qust","123");
        return userInfo;
    }
}

启动类测试

// 启动类
public class App {
    public static void main(String[] args) {
        // 获取 Spring 上下文对象
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");

        // 指定 bean 的名称
        UserInfo userInfo = context.getBean("userInfo", UserInfo.class);
        System.out.println(userInfo);
        // 报错,Bean对象的类型不匹配
    }
   
}

二、获取Bean对象

获取Bean对象,是把对象取出来放到某个类中,也叫作对象注入,对象装配
程序员将创建对象(new一个对象)的控制权交给了Spring容器,需要使用时,从Spring容器中就取出这个对象(对象注入)
对象注入有三种方法:

  1. 属性注入
  2. 构造方法注入
  3. setter注入
  • 创建一个controller包,表示业务逻辑层
    在这个包中,创建一个类ArtController,执行相应操作
package com.bit.controller;

// 业务逻辑层
@Controller
public class ArtController {

    public void add(String title, String content){
        //校验前端传来的参数
        if(title != null && content != null && !title.equals("") && !content.equals("")){
            // 执行 后面的业务逻辑
            

        }else{
            System.out.println("传入的参数错误!!!");
        }
    }
}

  • 创建一个service包,表示服务层
    在这个包中,创建一个类ArtService,实际开发中,应该在这个类中注入Repository层的接口,此处省略
package com.bit.service;

// 服务层
// 组织数据和管理、调用数据库接口
@Service
public class ArtService {
    public void add(String title, String content){
        // 执行后面的流程,调用操作数据库的接口
        System.out.println("Service 执行流程:  " + title + " | " + content);
    }
}

根据程序分层的调用流程,在ArtController类中,注入Service层的ArtService类

1、属性注入

  1. 先根据类型(ArtService)查询,如果只有一个同类型的对象,返回;
  2. 没有找到同类型的对象,注入失败;
  3. 如果有两个同类型的对象,根据变量名(artService(自己随便命名))查询

2、构造方法注入

  1. 当类中只有一个构造方法的时候,@Autowired可以省略;有多个构造方法的时候,@Autowired不能省略;因为如果有多个构造方法时,Spring并不知道要调用哪一个
  2. Spring调用构造方法时,会自动注入这个参数的,从Spring容器中取ArtService的Bean对象
    创建Spring上下文对象(Spring容器中)时,初始化ArtController,调用其构造方法;如果Spring中有ArtService的Bean对象,将其取出,作为参数;如果没有,先初始化ArtService,将它的Bean对象存储到Spring中,再将ArtService的Bean对象取出,作为参数

3、setter注入

设置set方法时,任何时候都要加上@Autowired注解
与构造方法注入类似,Spring初始化ArtController时,自动调用setArtService方法,如果Spring中有ArtService的Bean对象,取出,作为参数;如果没有,初始化ArtService,将它的Bean对象存到Spring中,再取出,作为参数

代码描述

此处为了方便,多次设置同一属性 ArtService artService,这在实际开发中是不允许的

package com.bit.controller;

// 业务逻辑层
@Controller
public class ArtController {

    // 1. 属性注入
    @Autowired
    private ArtService artService;
    
    // 2. 构造方法注入
    private ArtService artService;
    @Autowired
    public ArtController(ArtService artService){
        this.artService = artService;
    }

    // 3. 使用 setter 注入
    private ArtService artService;
    @Autowired
    public void setArtService(ArtService artService){
        this.artService = artService;
    }


    public void add(String title, String content){
        if(title != null && content != null && !title.equals("") && !content.equals("")){
            // 执行 后面的业务逻辑
            artService.add(title, content);
        }else{
            System.out.println("传入的参数错误!!!");
        }
    }
}

启动类


// 启动类
public class App {
    public static void main(String[] args) {
        // 获取 Spring 上下文对象
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");

        ArtController artController = context.getBean("artController", ArtController.class);
        artController.add("总决赛", "雄鹿 VS 勇士");
    }
}

在这里插入图片描述

4、三种注入方式的优缺点

  1. 属性注入
    优点:简洁,使用方便;
    缺点:只能用于IoC容器,非IoC容器不可用
  2. 构造方法注入**(官方推荐使用的类注入方法)**
    优点:通用性
    缺点:如果有多个注入(参数多),会显得十分臃肿;
    官方声明,出现这种情况,你应该从自身找原因,考虑一下当前类是否符合程序单一职责的设计模式,这个锅,我不背!
  3. setter注入(官方早期推荐使用的类注入方法)
    优点:通用性,但是不如构造方法注入

5、@Autowired和@Resource

这两个关键字都用于 类注入 的时候
区别:

  1. 出身不同,@Autowired来自于Spring,@Resource来自于JDK的注解
  2. 设置的参数的不同,相比于@Autowired,@Resource支持更多的参数设置
  3. 出现的场景不同,@Autowired可以修饰属性注入、构造方法注入、setter注入;@Resource可以修饰属性注入和setter注入,不能用于构造方法注入

引入@Resource就是为了解决 Spring容器中有同一类型的多个Bean对象,注入时会报错的问题

设置一个UserOutfo对象

package com.model;

public class UserOutfo {
    private String address;
    private String university;

    public UserOutfo(String address, String university) {
        this.address = address;
        this.university = university;
    }

    @Override
    public String toString() {
        return "UserOutfo{" +
                "address='" + address + '\'' +
                ", university='" + university + '\'' +
                '}';
    }
}

Spring容器中存储同一类型的多个对象

package com.bit;

@Component 
public class UserBeans {
    @Bean
    public UserOutfo getUser1(){
        UserOutfo userOutfo = new UserOutfo("郑州路","青岛科技大学");
        return userOutfo;
    }

    @Bean
    public UserOutfo getUser2(){
        UserOutfo userOutfo = new UserOutfo("柳园路","聊城大学");
        return userOutfo;
    }
}

在ArtController中注入UserOutfo的Bean对象

package com.bit.controller;

// 业务逻辑层
@Controller
public class ArtController {

    // 1. 属性注入
    @Autowire
    private UserOutfo user;
    
    public void work(){
      System.out.println(user);
    }
}

启动类


// 启动类
public class App {
    public static void main(String[] args) {
        // 获取 Spring 上下文对象
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
    }
}

程序执行的结果:在这里插入图片描述

  1. 使用@Resource(name = " ")
  2. 使用@Autowired搭配@Qualifier(value = " ")

注意:@Resource(name = " ") 和 @Qualifier(value = " ")中,直接写@Bean对象的名字

package com.bit.controller;

// 业务逻辑层
@Controller
public class ArtController {

    // 1. 属性注入
    @Resource(name = "getUser1")
    private UserOutfo user;

    public void work(){
      System.out.println(user);
    }
}
package com.bit.controller;

// 业务逻辑层
@Controller
public class ArtController {

    // 1. 属性注入
    @Autowired
    @Qualifier(value = "getUser1")
    private UserOutfo user;

    public void work(){
      System.out.println(user);
    }
}

启动类

// 启动类
public class App {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");

        ArtController artController = context.getBean("artController", ArtController.class);
        artController.work();
    }
}

在这里插入图片描述

它们就是查找对应 Bean对象 的唯一标准(只要这个value/name(Bean的名字)对不上,直接报错)


package com.bit.controller;
import javax.annotation.Resource;

// 业务逻辑层
// 对前端传来的参数进行校验,如果验证成功就调用后面的流程
@Controller
public class ArtController {

    // 1. 属性注入
    @Resource(name = "user")
    private UserOutfo getUser2;

   // @Autowired
  //  @Qualifier(value = "user")
  //  private UserOutfo getUser2;

    public void add(String title, String content){
      System.out.println(getUser2);
    }
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

威少总冠军

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值