Spring(二)容器
思维导图
Spring 核心概念
├── **核心注解**
│ ├── @Bean → 定义单例组件
│ ├── @Configuration → 配置类
│ ├── MVC 分层注解
│ │ ├── @Controller → 控制层
│ │ ├── @Service → 服务层
│ │ └── @Repository → 数据层
│ └── @Component → 通用组件
│
├── **组件扫描与导入**
│ ├── @ComponentScan → 批量扫描组件
│ └── @Import → 按需导入配置
│
├── **作用域与生命周期**
│ ├── @Scope → 控制组件作用域
│ └── @Lazy → 延迟初始化
│
└── **高级特性**
├── FactoryBean → 工厂模式创建对象
└── @Conditional → 条件注册组件
一 定义组件@Bean
在Spring框架中,@Bean
注解是一种用于定义和配置bean的方式。它通常与@Configuration
注解一起使用,用来告诉Spring容器哪些方法需要被当作是创建和管理的bean。当一个方法上标记了@Bean
注解时,这个方法会被Spring容器调用,并且其返回值将作为一个bean被注册到Spring的应用上下文中。
@Bean
的基本概念
- 作用:
@Bean
注解可以用于任何带有返回类型的Spring管理的方法上,表明该方法将会生成一个bean并将其注册到Spring容器中。 - 位置:它可以应用于类中的任何方法,但最常见的是在标注为
@Configuration
的配置类中使用。 - 默认ID:如果未指定ID,那么bean的名称(ID)将是方法名。当然,你也可以通过
@Bean("beanName")
或者@Bean(name = "beanName")
来明确指定bean的名称。
特点
- 组件的创建时机:容器启动过程中就会创建组件对象
- 对象是单实例特性:所有组件默认是单例的,每次直接从容器中拿,容器回提前创建组件。
(1)添加组件
代码实现:
package org.example.spring01.bean;
import lombok.Data;
@Data
public class Person {
private String name;
private int age;
private String gender;
}
实现类
package org.example.spring01;
import org.example.spring01.bean.Person;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
public class Spring01Application {
public static void main(String[] args) {
// 启动springboot应用
ConfigurableApplicationContext run = SpringApplication.run(Spring01Application.class, args);
// 获取容器中所有的默认组件
for (String beanDefinitionName : run.getBeanDefinitionNames()) {
System.out.println(" name " +beanDefinitionName);
}
System.out.println("run = " + run);
}
//给容器中注册一个自己的组件(方法名就是组件名)
@Bean
public Person person() {
Person person = new Person();
person.setAge(18);
person.setGender("男");
person.setName("张三");
return person;
}
}
(2)获取组件
代码实现
package org.example.spring01;
import org.example.spring01.bean.Dog;
import org.example.spring01.bean.Person;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import java.util.Map;
@SpringBootApplication
public class Spring01Application {
public static void main(String[] args) {
// 启动springboot应用
ConfigurableApplicationContext ioc = SpringApplication.run(Spring01Application.class, args);
System.out.println("run = " + ioc);
// 获取容器中所有的默认组件
// for (String beanDefinitionName : ioc.getBeanDefinitionNames()) {
// System.out.println(" name " +beanDefinitionName);
// }
//获取容器中person组件(组件的四大特性,名字,类型,对象,作用域)
// 1根据组件名获取组件
Person zj = (Person) ioc.getBean("LiSi");
System.out.println("person = " + zj);
// 2按照组件类型获取对象
Dog bean = ioc.getBean(Dog.class);
System.out.println("bean = " + bean);
// 3按照组件类型获取这种类型的所有对象
Map<String, Person> beansOfType = ioc.getBeansOfType(Person.class);
System.out.println("beansOfType = " + beansOfType);
// 4按照名字和类型获取指定对象
Person zhangSan = ioc.getBean("ZhangSan", Person.class);
System.out.println("zhangSan = " + zhangSan);
}
//给容器中注册一个自己的组件(方法名就是组件名)
@Bean("ZhangSan")
public Person person() {
Person person = new Person();
person.setAge(18);
person.setGender("男");
person.setName("张三");
return person;
}
@Bean("LiSi")
public Person lisi() {
Person person = new Person();
person.setAge(18);
person.setGender("男");
person.setName("李四");
return person;
}
@Bean("daHuang")
public Dog daHuang() {
Dog dog = new Dog();
dog.setAge(18);
dog.setGender("男");
dog.setName("大黄");
return dog;
}
}
二 配置类@Configuration
(1)配置类的作用
-
替代 XML 配置:传统 Spring 使用 XML 文件定义 Bean,而
@Configuration
允许用 Java 类实现同样的功能,更灵活且类型安全。 -
集中管理 Bean:在配置类中,可以通过
@Bean
注解显式定义 Bean 的创建逻辑,并管理它们的依赖关系。
配置类也是容器中的组件
代码实现
package org.example.spring01.config;
import org.example.spring01.bean.Person;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
// 告诉Spring容器,这是一个配置类
@Configuration
public class PersonConfig {
@Bean("ZhangSan")
public Person person() {
Person person = new Person();
person.setAge(18);
person.setGender("男");
person.setName("张三");
return person;
}
@Bean("LiSi")
public Person lisi() {
Person person = new Person();
person.setAge(18);
person.setGender("男");
person.setName("李四");
return person;
}
}
package org.example.spring01.config;
import org.example.spring01.bean.Dog;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class DogConfig {
@Bean("daHuang")
public Dog daHuang() {
Dog dog = new Dog();
dog.setAge(18);
dog.setGender("男");
dog.setName("大黄");
return dog;
}
}
三 MVC分层注解
- @Controller:
标记类作为控制器,处理HTTP请求。
- @Service:
标记服务层,处理业务逻辑。
- @Repository:
标记数据访问层,即DAO层,处理数据库操作。
默认分层注解能起作用的前提:这些组件必须在主程序所在的包及其子包结构下。
四 批量扫描@ComponentScan
(1) 默认扫描当前包及其子包
-
在配置类上添加
@ComponentScan
,不指定参数时,默认扫描 当前配置类所在包及其子包
@Configuration
@ComponentScan // 默认扫描当前包及子包
public class AppConfig {
}
(2) 指定扫描包路径
-
通过
basePackages
或value
参数指定要扫描的包路径(支持多个包)。
@Configuration
@ComponentScan(basePackages = {"com.example.service", "com.example.dao"})
public class AppConfig {
}
(3) 指定扫描类所在的包
-
通过
basePackageClasses
指定某个类所在的包作为扫描起点。
@Configuration
@ComponentScan(basePackageClasses = {UserService.class, UserDao.class})
public class AppConfig {
}
(4) 扫描指定包
五 使用第三方组件@Import
(1) 使用 @Bean
方法显式定义
@Bean
public CoreConstants coreConstants(){
return new CoreConstants();
}
(2)使用 @Import
引入其他配置类
//导入第三方组件
@Import(CoreConstants.class)
六 调整组件的作用域@Scope
(1)作用域的区别
代码实现:
//调整组件的作用域
@Scope("prototype")
// @Scope("prototype")->非单实例
// @Scope("singleton")->但实例(默认)
// @Scope("request")->每次请求单实例
// @Scope("session")->每次会话单实例
@Configuration
public class AppConfig {
@Bean
@Scope("prototype")
public PrototypeBean prototypeBean() {
return new PrototypeBean();
}
}
// 测试代码
PrototypeBean bean1 = context.getBean(PrototypeBean.class);
PrototypeBean bean2 = context.getBean(PrototypeBean.class);
System.out.println(bean1 == bean2); // 输出 false(不同对象)
@Configuration
public class AppConfig {
@Bean
@Scope("singleton") // 默认可省略
public SingletonBean singletonBean() {
return new SingletonBean();
}
}
// 测试代码
SingletonBean bean1 = context.getBean(SingletonBean.class);
SingletonBean bean2 = context.getBean(SingletonBean.class);
System.out.println(bean1 == bean2); // 输出 true(同一对象)
七 懒加载@Lazy
(1). @Lazy
注解的作用
-
懒加载:
@Lazy
的作用是 延迟初始化,即在 第一次使用时 才创建对象,而不是在容器启动时创建。 -
适用场景:
-
初始化耗时的对象(如数据库连接池)。
-
不常用的对象(如某些工具类)。
-
-
与作用域的关系:
-
@Lazy
不会改变作用域,单例 Bean 仍然是单例,只是延迟创建。 -
原型 Bean 仍然是原型,每次调用都会创建新实例。
-
代码实现:
//默认是prototype单实例,但是加上@Lazy注解,变成懒加载,是单实例,但是延迟创建
@Lazy
@Bean("ZhangSan")
public Person person() {
Person person = new Person();
person.setAge(18);
person.setGender("男");
person.setName("张三");
return person;
}
八 工厂模式FactoryBean
FactoryBean
是 Spring 框架中的一个接口,用于 封装复杂对象的创建过程。它的核心作用是将对象的初始化逻辑与配置解耦,适用于需要动态或条件化生成 Bean 的场景。
(1) FactoryBean
的作用
-
复杂对象创建:当对象的创建需要多步配置(如读取外部文件、动态参数设置)时,可以用
FactoryBean
封装这些逻辑。 -
延迟初始化:只有在真正需要时才创建对象。
-
动态代理:生成代理对象(如 AOP 中的动态代理)。
-
第三方库集成:适配需要复杂初始化的第三方组件(如 MyBatis 的
SqlSessionFactory
)。
(2) FactoryBean与普通Bean的区别
(3) FactoryBean
的核心方法
实现 FactoryBean
接口需要重写以下三个方法:
-
T getObject()
:返回工厂创建的目标对象。 -
Class<?> getObjectType()
:返回目标对象的类型。 -
boolean isSingleton()
:目标对象是否是单例。
代码实现:
package org.example.spring01.bean;
public class Car {
}
实现Factory:
package org.example.spring01.factory;
import org.example.spring01.bean.Car;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.stereotype.Component;
@Component
public class BYDFactory implements FactoryBean<Car> {
// 调用此方法判断是否单例
@Override
public boolean isSingleton() {
return FactoryBean.super.isSingleton();
}
// 调用此方法给容器中制造对象
@Override
public Car getObject() throws Exception {
return new Car();
}
// 调用此方法返回对象类型
@Override
public Class<?> getObjectType() {
return Car.class;
}
}
九 @Conditional