Bean的实例化
1. 通过构造方法实例化
Spring通过setter方法往bean里注入它依赖的bean。所以记得提供对应的setter方法。
package com.example;
public class MyClass {
MyOtherClass myOtherClass;
public void setMyOtherClass(MyOtherClass myOtherClass) {
this.myOtherClass = myOtherClass;
}
public void run() {
myOtherClass.run();
}
}
定义一个Spring配置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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 定义另一个Bean,name属性是bean的别名,可多个,使用, ; 空格分隔 -->
<bean id="myOtherBean" name="name1 name2 name3" class="com.example.MyOtherClass">
<!-- 可以在这里设置myOtherBean的属性 -->
</bean>
<!-- 定义一个简单的Bean,依赖于myOtherBean -->
<bean id="myBean" class="com.example.MyClass">
<!-- property name代表给哪个属性赋值,ref标识给这个属性赋值哪个名字的bean -->
<property name="propertyName" ref="myOtherBean"/>
</bean>
</beans>
2. 通过静态工厂实例化
通过此方式可以提前进行一些配置。
1.创建工厂类,包含实例化方法
public class MyBeanFactory {
public static MyClass createMyClass() {
System.out.println("这里开始你的配置");
return new MyClass("初始化参数");
}
}
2.在XML文件中配置工厂中的bean
<bean id="myBean" class="com.example.MyBeanFactory" factory-method="createMyClass"/>
3. 通过实例化工厂实例化Bean
1.定义工厂类
首先,创建一个工厂类,这个类包含一个实例方法,该方法返回要创建的Bean对象。
public class MyBeanFactory {
public MyClass createMyClass() {
return new MyClass("初始化参数");
}
}
2.在Spring配置中定义Bean
在Spring的配置文件(如XML配置)中,定义工厂类的Bean和使用工厂方法创建的Bean。
<bean id="myBeanFactory" class="com.example.MyBeanFactory"/>
<bean id="myBean" factory-bean="myBeanFactory" factory-method="createMyClass"/>
4. 通过继承FactoryBean接口实例化Bean
1.实现FactoryBean接口
首先,你需要创建一个类实现FactoryBean接口。该接口要求实现三个方法:getObject()、getObjectType()和isSingleton()。
import org.springframework.beans.factory.FactoryBean;
public class MyBeanFactory implements FactoryBean<MyClass> {
@Override
public MyClass getObject() throws Exception {
// 创建并返回Bean实例
return new MyClass("初始化参数");
}
@Override
public Class<?> getObjectType() {
// 返回Bean的类型
return MyClass.class;
}
@Override
public boolean isSingleton() {
// 指示该Bean是否为单例
return true;
}
}
2.配置XML文件
<bean id="myBean" class="com.example.MyBeanFactory"/>
这样,你就可以在Spring中使用不同的方式来实例化Bean。
Spring Bean的生命周期
在Spring中,Bean的生命周期主要包括从创建、初始化到销毁的各个阶段。以下是有关如何在Spring中管理Bean生命周期的详细内容:
1.创建Bean类
首先,我们需要定义一个简单的Bean类。在这个类中,我们可以添加初始化和销毁方法,以便在Bean创建和销毁时执行某些操作。
package com.example;
public class MyBean {
// 初始化方法
public void initMethod() {
System.out.println("MyBean 初始化");
}
// 销毁方法
public void destroyMethod() {
System.out.println("MyBean 销毁");
}
}
2.编写Spring配置XML文件
接下来,在Spring的配置文件中,我们使用<bean>标签来配置Bean。在这个配置中,我们可以指定init-method和destroy-method,这两个属性分别指定初始化和销毁时要调用的方法。
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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 配置MyBean的初始化和销毁方法 -->
<bean id="myBean" class="com.example.MyBean"
init-method="initMethod"
destroy-method="destroyMethod"/>
</beans>
3.启动应用程序并测试Bean生命周期
在你的MainApp类中,你可以通过Spring的ApplicationContext加载配置文件,并获取Bean实例。此时,初始化方法会被调用,当容器关闭时,销毁方法也会被调用。
package com.example;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MainApp {
public static void main(String[] args) {
// 加载Spring配置文件
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
// 注册关闭钩子,确保容器关闭时销毁方法被调用
((ClassPathXmlApplicationContext) context).registerShutdownHook();
// 获取MyBean实例
MyBean myBean = (MyBean) context.getBean("myBean");
// 使用Bean
// ...
// 在应用程序结束时,容器关闭并调用销毁方法
// 上下文会在程序退出时自动关闭
}
}
关键点:
registerShutdownHook():确保虚拟机关闭前,Spring容器会被关闭,从而调用销毁方法。context.close():显式关闭上下文,也会触发销毁方法。
- 使用
InitializingBean和DisposableBean接口
除了使用init-method和destroy-method,你还可以通过实现InitializingBean和DisposableBean接口来管理Bean的生命周期。
4.1 实现InitializingBean接口
InitializingBean接口提供了一个方法afterPropertiesSet(),它在所有Bean属性被设置后自动调用。你可以在这个方法中编写初始化逻辑。
package com.example;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.DisposableBean;
public class MyBean implements InitializingBean, DisposableBean {
private String message;
// 设置属性
public void setMessage(String message) {
this.message = message;
}
// 重写初始化方法
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("MyBean 初始化: " + message);
}
// 重写销毁方法
@Override
public void destroy() throws Exception {
System.out.println("MyBean 销毁");
}
}
4.2 配置XML文件
对于使用InitializingBean和DisposableBean接口的Bean,你不需要在XML中显式指定init-method和destroy-method。Spring会自动调用这些接口中的方法。
<bean id="myBean" class="com.example.MyBean">
<property name="message" value="Hello, Spring!"/>
</bean>
bean的属性注入
1. Setter方法注入
通过提供Setter方法,可以在Spring配置中直接注入属性值。
示例代码:
public class MyBean {
private String dependency;
// 提供setter方法
public void setDependency(String dependency) {
this.dependency = dependency;
}
}
XML配置:
xml
<bean id="myBean" class="com.example.MyBean">
<property name="dependency" value="你好啊"/> <!-- 基本数据类型使用value注入 -->
</bean>
2. 构造方法注入
构造方法注入是通过构造函数来传递依赖项。如果Bean的构造方法有多个参数,可以通过constructor-arg元素指定每个参数的值或引用。
示例代码:
package com.example;
public class MyBean {
private Dependency dependency;
// 构造方法
public MyBean(Dependency dependency) {
this.dependency = dependency;
}
public void execute() {
dependency.doSomething();
}
}
XML配置:
<bean id="dependency" class="com.example.Dependency"/>
<bean id="myBean" class="com.example.MyBean">
<constructor-arg ref="dependency"/>
</bean>
如果构造方法有多个参数,你可以通过name属性指定参数名称来匹配:
<constructor-arg name="dependency" ref="dependency"/>
<constructor-arg name="anotherDependency" ref="anotherDependency"/>
自动装配
使用自动装配就不用手动的配置此bean的依赖bean。
按类型自动装配
<bean id="myBean" class="com.example.MyBean" autowire="byType"/>
在这种情况下,Spring会查找类型为 MyBean 的构造函数参数,并自动注入匹配的 Bean。
按名称自动装配
<bean id="myBean" class="com.example.MyBean" autowire="byName"/>
在此情况下,Spring会查找与 Bean ID 匹配的名称并进行注入。
集合注入
在Spring的XML配置中,注入集合属性(如列表、集合、映射等)是一个常见的需求。以下是如何通过XML配置注入集合属性的几种方式:
1. 注入列表(List)
<bean id="myBean" class="com.example.MyBean">
<property name="myList">
<list>
<value>Item1</value>
<value>Item2</value>
<value>Item3</value>
</list>
</property>
</bean>
2. 注入集合(Set)
<bean id="myBean" class="com.example.MyBean">
<property name="mySet">
<set>
<value>ItemA</value>
<value>ItemB</value>
<value>ItemC</value>
</set>
</property>
</bean>
3. 注入映射(Map)
<bean id="myBean" class="com.example.MyBean">
<property name="myMap">
<map>
<entry key="key1" value="value1"/>
<entry key="key2" value="value2"/>
<entry key="key3" value="value3"/>
</map>
</property>
</bean>
示例类
假设我们有一个类 MyBean,其中包含一个列表、一个集合和一个映射。
javaCopy Code
public class MyBean {
private List<String> myList;
private Set<String> mySet;
private Map<String, String> myMap;
// setters
public void setMyList(List<String> myList) {
this.myList = myList;
}
public void setMySet(Set<String> mySet) {
this.mySet = mySet;
}
public void setMyMap(Map<String, String> myMap) {
this.myMap = myMap;
}
// 方法来打印集合内容
public void printCollections() {
System.out.println("List: " + myList);
System.out.println("Set: " + mySet);
System.out.println("Map: " + myMap);
}
}
配置文件
当我们要使用外部类进行属性配置时,例如要连接数据库,这时候我们要将数据库的类交给 IOC 容器管理,并且给类中的属性,例如用户名,密码等数据项进行配置时,我们通过属性注入的方式进行配置,但是最好这些配置属性能抽离出来,于是我们把它们放到配置文件中,XML 配置中只要引用 properties 属性值即可注入。
1. 创建 properties 文件
service.url=http://example.com/api
2. 在 XML 文件中首先开启 context 命名空间,并加载配置文件
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
// 第一个配置点
xmlns:context="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
// 第三个
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 加载配置文件属性,location 多个属性可用逗号分隔 -->
<context:property-placeholder location="classpath:application.properties"/>
<!-- 扫描包 -->
<context:component-scan base-package="com.example"/>
</beans>
3. 配置 Bean 属性,通过表达式引用 properties
<bean id="thirdPartyService" class="com.example.ThirdPartyService">
<property name="url" value="${service.url}"/>
</bean>
这样就完成了将外部配置文件与 Spring 的 XML 配置结合的基本步骤,使用 ${} 语法引用 properties 文件中的属性。
注解开发
注解开发直接在类上加上 @Component 注解即可替代 XML 配置 bean。但需要 Spring 去扫描这个注解才能使用。
于是 XML 文件中需要配置扫描包 Component-scan 组件的扫描范围。
<context:component-scan base-package="com.example"/>
Spring 3.0 开启了纯注解开发,使用 Java 配置类代替配置文件。
1.定义一个配置类,上面加上 @Configuration 注解指明它是配置类
@Configuration:标记一个类为配置类,它可以包含 Bean 的定义,但仅仅使用 @Configuration 并不会触发组件扫描。
@ComponentScan:如果需要扫描特定包中的组件,需要在配置类中显式添加 @ComponentScan 注解。这个注解的作用是告诉 Spring 在指定的包中查找带有特定注解的类并将其注册为 Spring 的 Bean。
默认行为:如果没有配置 @ComponentScan,Spring 会扫描配置类本身所在的包(及其子包),但这仅限于配置类本身,并不会扫描其他类。
@ComponentScan 指定包中组件扫描范围
@PropertySource 加载 properties 文件
@Configuration
@ComponentScan(basePackages = "com.example") // 自动扫描指定包中的组件
@PropertySource("classpath:application.properties") // 加载properties文件
public class AppConfig {
// 配置类内容
}
在主程序中,可以使用 AnnotationConfigApplicationContext 来获取容器:
public class Main {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
ThirdPartyService service = context.getBean(ThirdPartyService.class);
// 使用service
}
}
Bean 的作用范围
Spring 中 Bean 的作用范围(Scope)决定了 Bean 的创建和生命周期。常见的作用范围包括:
1. singleton(单例)
默认作用域。Spring 容器只会创建一个 Bean 实例,并在整个应用上下文中共享这个实例。
@Bean
@Scope("singleton")
public MyBean myBean() {
return new MyBean();
}
2. prototype(原型)
每次请求都会创建一个新的 Bean 实例。对于每个 getBean() 调用,Spring 容器会返回一个新的实例。
@Bean
@Scope("prototype")
public MyBean myBean() {
return new MyBean();
}
Bean 的生命周期
在 Spring 框架中,Bean 的生命周期可以通过多种注解进行管理,主要包括以下几种注解:
1. @PostConstruct
用途:用于标注在一个方法上,表示该方法将在 Bean 的依赖注入完成后被调用。可以用于执行初始化逻辑。
使用示例:
public class MyBean {
@PostConstruct
public void init() {
// 初始化逻辑
System.out.println("Bean is initialized");
}
}
2.@PreDestroy
用途:用于标注在一个方法上,表示该方法将在 Bean 被销毁之前被调用。可以用于执行清理工作。
使用示例:
javaCopy Code
public class MyBean {
@PreDestroy
public void cleanup() {
// 清理逻辑
System.out.println("Bean is being destroyed");
}
}
3. @Bean(initMethod = "methodName") 和 @Bean(destroyMethod = "methodName")
用途:在 @Configuration 类中定义 Bean 时,可以通过这两个属性指定初始化和销毁的方法。
使用示例:
@Configuration
public class AppConfig {
@Bean(initMethod = "init", destroyMethod = "cleanup")
public MyBean myBean() {
return new MyBean();
}
}
public class MyBean {
public void init() {
// 初始化逻辑
}
public void cleanup() {
// 清理逻辑
}
}
依赖注入
## 依赖注入
### `@Autowired`
使用 `@Autowired` 注解,不需要提供对应的 setter 方法,它通过反射给属性注入值。可以用于构造器、字段和方法。
```java
@Component
public class UserService {
private final UserRepository userRepository;
@Autowired
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
// 业务逻辑方法
}
@Qualifier
用途:在存在多个相同类型的 Bean 时,结合 @Autowired 使用,以指定具体的 Bean。
javaCopy Code
@Component
public class UserService {
private final UserRepository userRepository;
@Autowired
public UserService(@Qualifier("specificUserRepository") UserRepository userRepository) {
this.userRepository = userRepository;
}
}
@Value
用途:用于注入外部化配置,如 properties 文件中的值。记得要在 Spring 配置文件上加注解 @PropertySource("classpath:application.properties") 来加载 properties 文件。
javaCopy Code
@Component
public class AppConfig {
@Value("${app.name}")
private String appName;
// Getter 和 Setter
}
管理第三方 Bean
当我们要管理第三方 Bean 时,不能在别人的代码上加注解,于是可以把它放到配置类中。
通过 @Bean 注解方式来定义 Bean。将当前方法的返回值对象交给 IOC 容器进行管理。通过注解的 name/value 属性指定 Bean 名称,默认为方法名。
javaCopy Code
@Configuration
public class CommonConfig {
// 声明 Bean 对象
@Bean
public SAXReader saxReader() {
return new SAXReader();
}
}
第三方 Bean 进行依赖注入
第三方 Bean 想要进行依赖注入,直接在方法中指定形参即可。
javaCopy Code
@Configuration
public class CommonConfig {
// 声明 Bean 对象
@Bean
public SAXReader saxReader(Service service) {
service.方法();
return new SAXReader();
}
}
方式一:定义另一个配置类
这时候可以自己定义另一个配置类,并加上 @Configuration 注解。
javaCopy Code
@Configuration
public class ThirdPartyConfig {
// 配置第三方 Bean
}
方式二:使用 @Import 注解导入其他配置类
在配置类上使用 @Import 注解,它能够导入其他配置类或者普通类,将类加入 IOC 容器中。
@Configuration
@Import(OtherConfig.class) // 导入其他配置类
public class MainConfig {
// Bean 定义
}
依赖注入
@Autowired
使用 @Autowired 注解,不需要提供对应的 setter 方法,它通过反射给属性注入值。可以用于构造器、字段和方法。
@Component
public class UserService {
private final UserRepository userRepository;
@Autowired
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
// 业务逻辑方法
}
@Qualifier
用途:在存在多个相同类型的 Bean 时,结合 @Autowired 使用,以指定具体的 Bean。
@Component
public class UserService {
private final UserRepository userRepository;
@Autowired
public UserService(@Qualifier("specificUserRepository") UserRepository userRepository) {
this.userRepository = userRepository;
}
}
@Value
用途:用于注入外部化配置,如 properties 文件中的值。记得要在 Spring 配置文件上加注解 @PropertySource("classpath:application.properties") 来加载 properties 文件。
@Component
public class AppConfig {
@Value("${app.name}")
private String appName;
// Getter 和 Setter
}
管理第三方 Bean
当我们要管理第三方 Bean 时,不能在别人的代码上加注解,于是可以把它放到配置类中。
通过 @Bean 注解方式来定义 Bean。将当前方法的返回值对象交给 IOC 容器进行管理。通过注解的 name/value 属性指定 Bean 名称,默认为方法名。
@Configuration
public class CommonConfig {
// 声明 Bean 对象
@Bean
public SAXReader saxReader() {
return new SAXReader();
}
}
第三方 Bean 进行依赖注入
第三方 Bean 想要进行依赖注入,直接在方法中指定形参即可。
@Configuration
public class CommonConfig {
// 声明 Bean 对象
@Bean
public SAXReader saxReader(Service service) {
service.方法();
return new SAXReader();
}
}
但是如果把他放到spring的配置类里,显得不是那么回事儿,这是第三方配置类,跟spring没关系。于是我们把他放到另一个文件中。
方式一:定义另一个配置类
这时候可以自己定义另一个配置类,并加上 @Configuration 注解。
@Configuration
public class ThirdPartyConfig {
// 配置第三方 Bean
}
方式二:使用 @Import 注解导入其他配置类
在配置类上使用 @Import 注解,它能够导入其他配置类或者普通类,将类加入 IOC 容器中。
@Configuration
@Import(OtherConfig.class) // 导入其他配置类
public class MainConfig {
// Bean 定义
}
答疑:
问:不使用componentScan,他会扫描到配置类吗?
答:使用 @Configuration 注解的类会被注册,但前提是这些类在主应用类所在包或通过其他方式(如 @Import)被显式提及。(例如主类通过注解获取IOC容器)。
问:@ComponentScan 注解会去注册配置类吗
答:是的,@ComponentScan 注解会注册指定包及其子包中的所有 Spring 组件,包括配置类(使用 @Configuration 注解的类)、服务类(使用 @Service 注解的类)、控制器类(使用 @Controller 注解的类)等。
问:也就是说,spring配置类假如被主类提及,那他包下的其他configuration配置类也会被注册吗
答:默认行为:这是因为 @ComponentScan 默认只扫描配置类所在的包及其子包。
问:@Import注解他能把普通类注册到IOC容器中,不需要CompontentScan扫描吗
答:是的,@Import 确实可以将普通类注册到 Spring 的 IOC 容器中,而不需要通过 @ComponentScan 进行扫描。