栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

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

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

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

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

存储Bean对象

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

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

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



    
    

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

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

添加注解存储Bean对象 类注解
  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 实体类
方法注解

设置一个类,进行实验

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. 先根据类型(ArtService)查询,如果只有一个同类型的对象,返回;
  2. 没有找到同类型的对象,注入失败;
  3. 如果有两个同类型的对象,根据变量名(artService(自己随便命名))查询
构造方法注入
  1. 当类中只有一个构造方法的时候,@Autowired可以省略;有多个构造方法的时候,@Autowired不能省略;因为如果有多个构造方法时,Spring并不知道要调用哪一个
  2. Spring调用构造方法时,会自动注入这个参数的,从Spring容器中取ArtService的Bean对象
    创建Spring上下文对象(Spring容器中)时,初始化ArtController,调用其构造方法;如果Spring中有ArtService的Bean对象,将其取出,作为参数;如果没有,先初始化ArtService,将它的Bean对象存储到Spring中,再将ArtService的Bean对象取出,作为参数
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 勇士");
    }
}

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

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/855367.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号