万字详文带你使用java的Spring框架

353 阅读11分钟

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-methoddestroy-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():显式关闭上下文,也会触发销毁方法。

  1. 使用InitializingBeanDisposableBean接口

除了使用init-methoddestroy-method,你还可以通过实现InitializingBeanDisposableBean接口来管理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文件

对于使用InitializingBeanDisposableBean接口的Bean,你不需要在XML中显式指定init-methoddestroy-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 匹配的名称并进行注入。

image.png

集合注入

在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 文件中的属性。

image.png

注解开发

注解开发直接在类上加上 @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 进行扫描。