Spring 简介

发布于:2025-03-01 ⋅ 阅读:(108) ⋅ 点赞:(0)

1. Spring简介

1.1 Spring 核心设计思想

1.1.1 Spring 是什么?
  • Spring 是包含了众多具法的 IoC 容器。
  • Spring 指的是 Spring Framework(Spring 框架),它是个开源框架,Spring 持泛的应场景,它可以让 Java 企业级的应程序开发起来更简单。
1.1.1.1 什么是IoC容器?
  • 容器是来容纳某种物品的装置。
  • IoC = Inversion of Control 翻译成中是“控制反转”的意思,控制权发的反转,不再是上级对象创建并控制下级对象了,是下级对象把注将当前对象中,下级的控制权不再由上级类控制了,这样即使下级类发任何改变,当前类都是不受影响的,这就是 IoC 的实现思想。
  • Spring具备两个核心功能:将对象存到容器,从容器中取出对象。对象的创建和销毁的权利都交给 Spring 来管理了,它本身具备了存储对象和获取对象的能。
1.1.1.2 DI 概念说明

DI 是 Dependency Injection 的缩写,翻译成中是“依赖注”的意思。依赖注就是由 IoC 容器在运期间,动态地将某种依赖关系注到对象之中。所以,依赖注(DI)和控制反转(IoC)是从不同的度的描述的同件事情,就是指通过引 IoC 容器,利依赖关系注的式,实现对象之间的解耦,IoC 是“标”也是种思想,标和思想只是种指导原则,最终还是要有可的落地案, DI就属于具体的实现。

1.1.1.3 IoC容器和普通程序开发的区别
  • 将对象存储在 IoC 容器相当于将以后可能的所有具制作好都放到仓库中,需要的时候直接取就了,完再把它放回到仓库。
  • new 对象的式相当于,每次需要工具了才现做,用完扔掉了也不会保存,下次再用的时候还得重新做。

1.2 Spring 创建和使用

在这里插入图片描述

1.2.1 创建 Spring 项目

使 Maven 式来创建个 Spring 项,创建 Spring 项和 Servlet 类似。

  1. 创建个 Maven 项
    在这里插入图片描述
    然后跳转到了这个页面:
    在这里插入图片描述

  2. 添加 Spring 框架持
    在项的 pom.xml 中添加 Spring 框架的持spring-context(spring 上下)和spring-beans(管理对 象的模块),xml 配置如下:

    org.springframework spring-context 5.2.3.RELEASE
    <dependency> 
        <groupId>org.springframework</groupId> 
        <artifactId>spring-beans</artifactId> 
        <version>5.2.3.RELEASE</version> 
    </dependency>
    
  3. 添加启动类
    最后在创建好的项 java 件夹下创建个启动类,包含 main 法即可:

    public class App {
    public static void main(String[] args) {

    }
    

    }

到此为止,一个spring项目就搭建好了。

1.2.2 存储 Bean 对象

Bean 就是 Java 语中的个普通对象,例如:

public class User {
   public String sayHi(String name) { 
       return name + " hello!";
   }
}
  1. 创建 Bean

    public class User {
    public String sayHi(String name) {
    return name + " hello!";
    }
    }

  2. 将 Bean 注册到容器
    <<1>>在创建好的项中添加 Spring 配置件 spring-config.xml,将此件放到 resources 的根录下, 如下图所示:
    在这里插入图片描述
    <<2>> spring-config.xml 配置件的固定格式为以下内容,复制粘贴即可

    <?xml version="1.0" encoding="UTF-8"?>

<<3>> 将 User 对象注册到 Spring 中就可以,在 标签中添加配置 ,格式如下:

<beans>
  <bean id="user" class="com.bit.User"></bean>
</beans>
1.2.3 获取 Bean 对象
  1. 创建 Spring 上下对象
    <<1>>使 ApplicationContext

    ApplicationContext context = new ClassPathXmlApplicationContext(“spring-con fig.xml”);

<<2>>使 BeanFactory

BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("spring-config.xml"));

ApplicationContext VS BeanFactory

  • 继承关系和功能:Spring 容器有两个顶级的接:BeanFactory 和 ApplicationContext。其中 BeanFactory 提供了基础的访问容器的能, ApplicationContext 属于 BeanFactory 的类,它除了继承了 BeanFactory 的所有功能之外,它还拥有独特的特性, 还添加了对国际化持、资源访问持、以及事件传播等的持。
  • 性能:ApplicationContext 是次性加载并初始化所有的 Bean 对象, BeanFactory 是需要那个才去加载那个,因此更加轻量。
    <<3>>使用ClassPathXmlApplicationContext
    ClassPathXmlApplicationContext 属于 ApplicationContext 的类,拥有 ApplicationContext 的所有功能,通过 xml 的配置来获取所有的 Bean 容器。
  1. 获取指定的 Bean 对象

    // 1.得到 Spring 上下?对象
    ApplicationContext context = new ClassPathXmlApplicationContext(“spring-con fig.xml”);
    // 2.加载某个 bean
    User user = (User) context.getBean(“user”);

Bean 的 Id 要对应,如下图所示:
在这里插入图片描述

getBean 法的更多法

  • 根据类型获取 Bean:

    UserController user = context.getBean(UserController.class);

  • 名称 + 类型获取 Bean:

    UserController user = context.getBean(“user”, UserController.class);

当有个类型被重复注册到 spring-config.xml 中时,只能使根据名称获取了,如以 下场景就会导致程序报错:在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

1.2.4 使用 Bean
public class App {
    public static void main(String[] args) {
        // 1.得到 Spring 上下?对象
        ApplicationContext context =
                new ClassPathXmlApplicationContext("spring-config.xml"); 
        // 2.加载某个 bean
        User user = (User) context.getBean("user");
        // 3.调?相应的?法
        System.out.println(user.sayHi("Java"));
    }
}
1.2.4 使用注解读取和存储对象
  1. 配置扫描路径

配置下存储对象的扫描包路径,只有在被配置的包下且添加了注解的类才能被正确的识别并保存到 Spring 中。

在 spring-config.xml 添加如下配置:

<?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">
 
 <content:component-scan base-package="com.bit.service"></content:component-scan>
 
</beans>

在这里插入图片描述

  1. 添加注解存储 Bean 对象
    将对象存储在 Spring 中,有两种注解类型可以实现:
  • 类注解:@Controller(表示业务逻辑层)、@Service(服务层)、@Repository(持久层)、@Component、@Configuration(配置层)。
  • 法注解:@Bean
    类注解是添加到某个类上的,法注解是放到某个法上的。

Bean 命名规则:
通常 bean 使的都是标准的驼峰命名,读取的时候字写:
在这里插入图片描述
当字和第个字都是写时,不能正常读取到 bean :
在这里插入图片描述
Spring中 bean 存储时成的命名规则:
<1> 在 Idea 中使搜索关键字“beanName”可以看到以下内容:
在这里插入图片描述
在这里插入图片描述
它使的是 JDK Introspector 中的 decapitalize 法,源码如下:

public static String decapitalize(String name) {
   if (name == null || name.length() == 0) {
       return name;
   }
   // 如果第?个字?和第?个字?都为?写的情况,是把 bean 的?字?也?写存储了 
   if (name.length() > 1 && Character.isUpperCase(name.charAt(1)) && 
       Character.isUpperCase(name.charAt(0))){
       return name;
   }
	// 否则就将?字??写
   char chars[] = name.toCharArray();
   chars[0] = Character.toLowerCase(chars[0]);
   return new String(chars);
}

所以对于上报错的代码,只要改为以下代码就可以正常运了:
在这里插入图片描述

类注解使用示例:

  • @Controller(控制器存储)
    使 @Controller 存储 bean:

    @Controller // 将对象存储到 Spring 中
    public class UserController {
    public void sayHi(String name) {
    System.out.println(“Hi,” + name);
    }
    }

先使之前读取对象的式读取上的 UserController 对象:

public class Application {
	public static void main(String[] args) {
		// 1.得到 spring 上下?
		ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
		// 2.得到 bean
		UserController userController = (UserController) context.getBean("userController");
		// 3.调? bean ?法
		userController.sayHi("Bit");
	}
}
  • @Service(服务存储)
    使 @Service 存储 bean

    @Service
    public class UserService {
    public void sayHi(String name) {
    System.out.println(“Hi,” + name);
    }
    }

读取 bean

class App {
	public static void main(String[] args) {
		 // 1.得到 spring 上下?
		 ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
		 // 2.得到 bean
		 UserService userService = (UserService) context.getBean("userService");
		 // 3.调? bean ?法
		 userService.sayHi("Bit");
	}
}
  • @Repository(仓库存储)
    使 @Repository 存储 bean

    @Repository
    public class UserRepository {
    public void sayHi(String name) {
    System.out.println(“Hi,” + name);
    }
    }

读取 bean

class App {
	public static void main(String[] args) {
		// 1.得到 spring 上下?
		ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
		// 2.得到某个 bean
		UserRepository userRepository = (UserRepository) context.getBean("userRepository");
		// 3.调? bean ?法
		userRepository.sayHi("Bit");
	}
}
  • @Component(组件存储)
    使 @Component 存储 bean

    @Component
    public class UserComponent {
    public void sayHi(String name) {
    System.out.println(“Hi,” + name);
    }
    }

读取 bean

class App {
	public static void main(String[] args) {
		// 1.得到 spring 上下?
		ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
		// 2.得到某个 bean
		UserComponent userComponent = (UserComponent) context.getBean("userComponent");
	 	// 3.调? bean ?法
		userComponent.sayHi("Bit");
	 }
}
  • @Configuration(配置存储)

    @Configuration
    public class UserConfiguration {
    public void sayHi(String name) {
    System.out.println(“Hi,” + name);
    }
    }

读取 bean

class App {
	public static void main(String[] args) {
		// 1.得到 spring 上下?
		ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
		// 2.得到某个 bean
		UserConfiguration userConfiguration = (UserConfiguration) context.getBean("userConfiguration");
		// 3.调? bean ?法
		userConfiguration.sayHi("Bit");
	 }
}

为什么有这么多功能一样的注解:
它们的功能都是一样的,让程序员看到类注解之后,就能直接了解当前类的途。
程序的程分层,调流程:
在这里插入图片描述
查看 @Controller / @Service / @Repository / @Configuration 等注解的源码发现,这些注解都有个注解 @Component,说明它们本身就是属于 @Component 的“类”。
在这里插入图片描述

法注解@Bean 使用示例:

法注解要配合类注解使
在 Spring 框架的设计中,法注解 @Bean 要配合类注解才能将对象正常的存储到 Spring 容器中

  • 使用@Bean存储 bean

    @Component
    public class Users {

    @Bean
    public User user1() {
    User user = new User();
    user.setId(1);
    user.setName(“Java”);
    return user;
    }
    }

读取bean

public class Application {
   public static void main(String[] args) {
       ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml"); 
       User user = (User) context.getBean("user1"); 
       System.out.println(user.toString());
   }
}

在这里插入图片描述

  • 重命名 Bean
    可以通过设置 name 属性给 Bean 对象进重命名。重

    @Component
    public class Users {
    @Bean(name = {“u1”})
    public User user1() {
    User user = new User();
    user.setId(1);
    user.setName(“Java”);
    return user;
    }
    }

命名的 name 其实是个数组,个 bean 可以有多个名字。

@Bean(name = {"u1", "us1"}) public User user1() { 
 User user = new User(); 
 user.setId(1); 
 user.setName("Java"); 
 return user;
}

name={} 可以省略

@Bean({"u1", "us1"})
public User user1() { 
  User user = new User(); 
  user.setId(1); 
  user.setName("Java"); 
  return user;
}

此时使 u1/us1 就可以获取到 User 对象了

class App {
   public static void main(String[] args) {
       // 1.得到 spring 上下?
       ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml"); 
       // 2.得到某个 bean
       User user = (User) context.getBean("u1");
       // 3.调? bean ?法
       System.out.println(user);
   }
}
  1. 对象装配

获取 bean 对象也叫做对象装配,是把对象取出来放到某个类中,有时候也叫对象注。

<1> 属性注

属性注使 @Autowired 实现。
属性注的优点是简洁,使便;缺点是只能于 IoC 容器,如果是 IoC 容器不可,并且只 有在使的时候才会出现 NPE(空指针异常)。

示例:

Service 类的实现代码如下:

import org.springframework.stereotype.Service;

@Service
public class UserService {

   /**
    * 根据 ID 获取?户数据
    *
    * @param id
   * @return
    */
   public User getUser(Integer id) { 
       // 伪代码,不连接数据库 
       User user = new User(); 
       user.setId(id); 
       user.setName("Java-" + id); 
       return user;
   }
}
  • Controller 类的实现代码如下:

    import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller;

    @Controller
    public class UserController {

    // 注??法1:属性注?
    @Autowired
    private UserService userService;

    public User getUser(Integer id) {
    return userService.getUser(id);
    }
    }

核实现:
在这里插入图片描述
获取 Controller 中的 getUser 法:

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class UserControllerTest {
   public static void main(String[] args) {
       ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml"); 
       UserController userController = context.getBean(UserController.cla ss);
   		System.out.println(userController.getUser(1).toString());
   }
}

运行结果:
在这里插入图片描述

<2>构造法注

构造法注是在类的构造法中实现注。
构造法注是 Spring 推荐的注式,它的缺点是如果有多个注会显得较臃肿,但出现这 种情况应该考虑下当前类是否符合程序的单职责的设计模式,优点是通性,在使之前定能把保证注的类不为空。

示例:

@Controller
public class UserController2 {

   // 注??法2:构造?法注? 
   private UserService userService;     // 创建userService引用

   @Autowired 
   public UserController2(UserService userService) {   // 注入并通过构造方法让上面的引用指向注入进来的这个对象
       this.userService = userService;
  }

   public User getUser(Integer id) { 
       return userService.getUser(id); 
   }
}

<<1>> 如果只有个构造法,那么 @Autowired 注解可以省略
在这里插入图片描述
<<2>>如果类中有多个构造法,那么需要添加上 @Autowired 来明确指定到底使哪个构造法,否则 程序会报错
在这里插入图片描述

<3> Setter 注

Setter 注和 构造方法注入 实现类似,只不过在设置 set 法的时候需要加上 @Autowired 注解。
Setter 式是 Spring 前期版本推荐的注式,但通性不如构造法,所有 Spring 现版本已 经推荐使构造法注的式来进类注了。

示例:

@Controller
public class UserController3 {

   // 注??法3:Setter注? 
   private UserService userService;

   @Autowired
   public void setUserService(UserService userService) { 
       this.userService = userService;
   }

   public User getUser(Integer id) { 
       return userService.getUser(id); 
   }
}

<4> @Resource注入

@Autowired 和 @Resource 的区别:

  • @Autowired 来于 Spring, @Resource 来于 JDK 的注解
  • 相于 @Autowired ,@Resource 持更多的参数设置,例如 name 设置,根据名称获取 Bean。
  • @Autowired 可于 Setter 注、构造方法注和 属性注。 @Resource 只能于 Setter 注和 属性注 不能于构造函数注。

<5>使用同类型多个 Bean 报错处理,当出现以下多个 Bean,返回同对象类型时程序会报错。

示例:

@Component
public class Users {
    
   // 第一个bean
   @Bean
   public User user1() { 
       User user = new User(); 
       user.setId(1); 
       user.setName("Java"); 
       return user;
   }

   // 第二个bean
   @Bean
   public User user2() { 
       User user = new User(); 
       user.setId(2); 
       user.setName("MySQL"); 
       return user;
   }
}

在另个类中获取 User 对象:

@Controller
public class UserController4 {

   // 注? 
   @Resource 
   private User user;

   public User getUser() { 
       return user;
   }
}

执结果:
在这里插入图片描述
解决方法:
<<1>>使 @Resource(name=“XXX”)来指定bean

@Controller
class UserController4 { 
   // 注?
   @Resource(name = "user1") 
   private User user;

   public User getUser() { 
       return user;
   }
}

<2> 使 @Qualifier来指定bean

@Controller
public class UserController5 { 
    // 注?
    @Autowired 
    @Qualifier(value = "user2") 
    private User user;

    public User getUser() { 
        return user;
    }
}

2. Bean 详解

2.1 Bean的作用域

限定程序中变量的可范围叫做作域,或者说在源代码中定义变量的某个区域就叫做作域。
Bean 的作域是指 Bean 在 Spring 整个框架中的某种为模式。
Bean 有 6 种作域:singleton(单例作域),prototype(原型作域/多例作域), request(请求作域),session(回话作域),application(全局作域),websocket(HTTP WebSocket 作域)。后 4 种状态是 Spring MVC 中的值,在普通的 Spring 项中只有前两种。

2.1.1 singleton

Spring默认选择该作域,该作域下的Bean在IoC容器中只存在个实例:获取Bean(即通过applicationContext.getBean等法获取)及装配Bean(即通过@Autowired等注)都是同个对象。通常状态的Bean(Bean对象的属性状态不需要更新)使该作域,单例可以很程度上提性能。
示例:
公共bean

@Component
public class Users {
 	@Bean
 	public User user1() {
 		User user = new User();
 		user.setId(1);
 		user.setName("Java"); // 【重点:名称是 Java】
 		return user;
 	}
}

A 户使时,进了修改操作:

@Controller
public class BeanScopesController {
	 @Autowired
 	 private User user1;
	 public User getUser1() {
		 User user = user1;
		 System.out.println("Bean 原 Name:" + user.getName());
		 user.setName("悟空"); // 【重点:进?了修改操作】
		 return user;
	 }
}

B 户再去使公共 Bean :

@Controller
public class BeanScopesController2 {
	 @Autowired
	 private User user1;
	 public User getUser1() {
		 User user = user1;
		 return user;
	 }
}

查看 A 户和 B 户公共 Bean 的值:

public class BeanScopesTest {
 public static void main(String[] args) {
	 ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
 
	 BeanScopesController beanScopesController = context.getBean(BeanScopesController.class);
	 System.out.println("A 对象修改之后 Name:" + beanScopesController.getUser1().toString());
 
	 BeanScopesController2 beanScopesController2 = context.getBean(BeanScopesController2.class);
	 System.out.println("B 对象读取到的 Name:" + beanScopesController2.getUser1().toString());
 }
}

执结果:
在这里插入图片描述

2.1.2 prototype

每次对该作域下的Bean的请求都会创建新的实例:获取Bean(即通过applicationContext.getBean等法获取)及装配Bean(即通过@Autowired注)都是新的对象实例。通常有状态(Bean对象的属性状态需要更新)的Bean使该作域。

2.1.3 request

每次http请求会创建新的Bean实例,类似于prototype。用于次http的请求和响应的共享Bean,限定SpringMVC中使。

2.1.4 session

在个http session中,定义个Bean实例,用于用户回话的共享Bean,如:记录个户的登陆信息。限定SpringMVC中使。

2.1.5 application

在个http servlet Context中,定义个Bean实例,用于Web应的上下信息,如:记录个应的共享信息。限定SpringMVC中使。

application 是 Spring Web 中的作域,作于 Servlet 容器。singleton 是 Spring Core 的作域,singleton 作于 IoC 的容器。

2.1.6 websocket

在个HTTP WebSocket的命周期中,定义个Bean实例,用于WebSocket的每次会话中,保存了个Map结构的头信息,将来包裹客户端消息头。第次初始化后,直到WebSocket结束都是同个Bean。限定Spring WebSocket中使。

2.2 设置Bean的作用域

使 @Scope 标签就可以来声明 Bean 的作域。
<1> 直接设置值:@Scope(“prototype”)
<2> 使枚举设置:@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)

示例;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Component
public class Users {
	 @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
	 @Bean(name = "u1")
	 public User user1() {
		 User user = new User();
		 user.setId(1);
		 user.setName("Java"); // 【重点:名称是 Java】
		 return user;
	 }
}

2.2 Bean的生命周期

2.2.1 Spring 执行流程

在这里插入图片描述

2.2.2 Bean 的执行流程

启动 Spring 容器 --> 实例化 Bean(分配内存空间,从到有) --> Bean注册到Spring中(存操作) --> 将Bean装配到需要的类中(取操作)。

2.2.3 Bean的生命周期

命周期指的是个对象从诞到销毁的整个命过程,我们把这个过程就叫做个对象的命周期。

在这里插入图片描述

  1. 实例化 Bean(为 Bean 分配内存空间)
  2. 设置属性(Bean 注和装配)
  3. Bean 初始化
    3.1 实现了各种 Aware 通知的法,如 BeanNameAware、BeanFactoryAware ApplicationContextAware >的接法;
    3.2 执 BeanPostProcessor 初始化前置法;
    3.3 执 @PostConstruct 初始化法,依赖注操作之后被执;
    3.4 执指定的 init-method 法(如果有指定的话);
    3.5 执 BeanPostProcessor 初始化后置法。
  4. 使 Bean
  5. 销毁 Bean。执行销毁容器的各种法,如 @PreDestroy、DisposableBean 接法、destroy-method。
  • 实例化和初始化的区别:
    实例化和属性设置是 Java 级别的系统“事件”,其操作过程不可预和修改;初始化是给开发者 提供的,可以在实例化之后,类加载完成之前进定义“事件”处理。
  • 如何理解Bean生命周期:
    Bean 的命流程看似繁琐,但咱们可以以活中的场景来理解它,如我们现在需要买栋房,那么我们的流程是这样的:
  1. 先买房(实例化,从到有);
  2. 装修(设置属性);
  3. 买家电,如洗机、冰箱、电视、空调等([各种]初始化);
  4. 住(使 Bean);
  5. 卖出去(Bean 销毁)。

网站公告

今日签到

点亮在社区的每一天
去签到