SSM02-Spring框架(IOC/DI/Bean的管理)

Spring框架

一、Spring概述

Spring是Java SE/EE轻量级开源框架。它最为核心的理念是IoC(控制反转)和AOP(面向切面编程),其中,IoC是Spring的基础,它支撑着Spring对JavaBean的管理功能;AOP是Spring 的重要特性,AOP是通过预编译方式和运行期间动态代理实现程序功能,也就是说可以在不修改源代码的情况下,给程序统一添加功能。

1.1. Spring框架的作用

  • 非侵入式设计:Spring框架的API不会在业务逻辑上出现,业务逻辑代码也可以从Spring框架快速地移植到其他框架。

  • 降低耦合性,方便开发:所有对象的创建和依赖关系的维护工作都交给Spring容器管理,降低了组件之间耦合性。

  • 支持AOP编程

  • 支持声明式事务:通过Spring配置文件管理数据库事务

  • 方便程序的测试:Spring提供了对Junit的支持

  • 方便集成各种优秀框架

  • 降低Java EE API的使用难度:Spring对Java EE开发中的一些API都进行了封装,大大降低了这些API的使用难度。

1.2. Spring的体系结构

image-20241217111747712

1.核心容器模块(Core Container)

  • Beans模块。它提供了BeanFactory类,是工厂模式的经典实现,Beans模块的主要作用是创建和管理Bean对象。

  • Core模块。它提供了Spring框架的基本组成部分,包括IoC和DI功能。

  • Context模块。它构建于Beans模块和Core模块的基础之上,它可以通过ApplicationContext接口提供上下文信息。

  • SpEL模块。提供了对SpEL表达式语言的支持,SpEL表达式语言是一个在程序运行时支持操作对象图的表达式语言。

2.数据访问及集成模块(Data Access/Integration)

  • JDBC模块。它提供了一个JDBC的抽象层,消除了冗长的JDBC编码并能够解析数据库供应商特有的错误代码。

  • ORM模块。它为主流的对象关系映射API提供了集成层,用于集成主流的对象关系映射框架。

  • OXM模块。它提供了对XML映射的抽象层的支持,如JAXB、Castor等。

  • JMS模块。它主要用于传递消息,包含消息的生产和消费。

  • Transactions模块。它的主要功能是事务管理。

3.Web模块

  • Web模块。它提供了针对Web开发的集成特性,如大部分文件上传功能等。此外,Web模块还包含一个HTTP客户端和Spring远程处理支持的Web相关部分。

  • Servlet模块。它提供了Spring的模型、视图、控制器以及Web应用程序的REST Web服务实现。

  • WebSocket模块。它是Spring 4.0以后新增的模块,它提供了WebSocket 和SockJS的实现,以及对STOMP的支持。

  • Portlet模块。它类似Servlet模块的功能,提供了Portlet环境下的MVC实现。

4.其他模块

  • AOP模块。它提供了对面向切面编程的支持,程序可以定义方法拦截器和切入点,将代码按照功能进行分离,以降低程序的耦合性。

  • Aspects模块。它提供了与AspectJ集成的支持。

  • Instrumentation模块。它提供了对类工具的支持,并且实现了类加载器,该模块可以在特定的应用服务器中使用。

  • Messaging模块。它是Spring 4.0以后新增的模块,它提供了对消息传递体系结构和协议的支持。

  • Test模块。它提供了对程序单元测试和集成测试的支持。

1.3 Spring5的新特征

  • 更新JDK基线:JDK 8以上

  • 修订核心框架:反射增强,新注解(@Nullable和@NotNull)

  • 更新核心容器:支持候选组件索引作为类路径扫描的替代方案。从索引读取实体类,会使加载组件索引开销更低

  • 支持响应式编程

  • 支持函数式Web框架:HandlerFunction和RouterFunction

  • 支持Kotlin

  • 提升测试功能:完全支持Junit 5 Jupiter

二、Spring-控制反转与依赖注入

**2.1.控制反转定义(IOC):**IoC控制反转机制指的是对象由Ioc容器统一管理,当程序需要使用对象时,可以直接从IoC容器中获取。这样对象的控制权就从应用程序转移到了IoC容器。它是借助于IoC容器实现具有依赖关系对象之间的解耦,各个对象类封装之后,通过IoC容器来关联这些对象类。

**2.2.依赖注入定义(DI):**由IoC容器在运行期间动态地将某种依赖资源注入对象之中。例如,将对象B注入(赋值)给对象A的成员变量。依赖注入的基本思想是:明确地定义组件接口,独立开发各个组件,然后根据组件的依赖关系组装运行。

2.3.区别:

  • 依赖注入是从应用程序的角度描述,即应用程序依赖IoC容器创建并注入它所需要的外部资源;
  • 控制反转是从IoC容器的角度描述,即IoC容器控制应用程序,由IoC容器反向地向应用程序注入应用程序所需要的外部资源。

2.4.依赖注入的实现方法

  • 构造方法注入

构造方法注入是指Spring容器调用构造方法注入被依赖的实例,构造方法可以是有参的或者是无参的。Spring在读取配置信息后,会通过反射方式调用实例的构造方法,如果是有参构造方法,可以在构造方法中传入所需的参数值,最后创建类对象。

<bean id="user1" class="com.itheima.User1">
        <constructor-arg name="id" value="1"></constructor-arg>
        <constructor-arg name="name" value="张三"></constructor-arg>
        <constructor-arg name="password"value="123"></constructor-arg>
</bean>
  • 设置方法注入

它是在被注入的类中声明一个setter方法,通过setter方法的参数注入对应的值。

<bean id="user2" class="com.itheima.User2">
    <property name="id" value="2"></property>
    <property name="name" value="李四"></property>
    <property name="password" value="456"></property>
</bean>

三、Spring中的Bean的管理

控制反转和依赖注入都是通过Bean实现的,Bean是注册到Spring容器中的Java类,任何一个Java类都可以是一个Bean。Bean由Spring进行管理。

3.1 Spring IoC容器

1.BeanFactory接口

BeanFactory接口是Spring容器最基本的接口,它的实现机制采用的是Java经典的工厂模式。BeanFactory接口提供了创建和管理Bean的方法。

方法名称描述
getBean(String name)根据bean的id或name获取Bean
getBean(String name, Class type)根据bean的name或id、参数类型获取Bean

Spring提供了几个BeanFactory接口的实现类,其中最常用的是XmlBeanFactory,它可以读取XML文件并根据XML文件中的配置信息生成BeanFactory接口的实例

BeanFactory beanFactory = new XmlBeanFactory(new FileSystemResource(“D:/applicationContext.xml”));

2.ApplicationContext接口

ApplicationContext接口建立在BeanFactory接口的基础之上,它丰富了BeanFactory接口的特性,例如,添加了对国际化、资源访问、事件传播等方面的支持。

  • ApplicationContext接口的常用实现类
类名称描述
ClassPathXmlApplicationContext类路径加载配置文件实例化接口
FileSystemXmlApplicationContext文件系统加载配置文件,实例化接口
AnnotationConfigApplicationContext注解中加载配置文件,实例化接口
WebApplicationContext在Web应用中使用,从相对于Web根目录的路径中加载配置文件,实例化ApplicationContext接口
ConfigurableWebApplicationContext扩展了WebApplicationContext类,它可以通过读取XML配置文件的方式实例化WebApplicationContext类

3.2 Bean的配置

Spring容器支持XML和Properties两种格式的配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       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
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd">
</beans>
  • <bean>元素的常用属性
属性描述
idid属性是元素的唯一标识符,Spring容器对Bean的配置和管理通过id属性完成,装配Bean时也需要根据id值获取对象。
namename属性可以为Bean指定多个名称,每个名称之间用逗号或分号隔开。
classclass属性可以指定Bean的具体实现类,其属性值为对象所属类的全路径
scopescope属性用于设定Bean实例的作用范围,其属性值有:singleton(单例)、prototype(原型)、request、session和global session。
  • <bean>元素的常用子元素
元素描述
<constructor-arg>使用<constructor-arg>元素可以为Bean的属性指定值(构造注入)。
index:用于设置构造参数的序号
type:用于指定构造参数的类型
**注意:**在使用有参的注入时,无需关注参数顺序
<property> <property>元素的作用是调用Bean实例中的setter方法完成属性赋值,从而完成依赖注入(setter注入)。
refref是<property><constructor-arg>等元素的属性,可用于指定Bean工厂中某个Bean实例的引用。
valuevalue是<property><constructor-arg>等元素的属性,用于直接指定一个常量值。
<list>元素是等元素的子元素,用于指定Bean的属性类型为List或数组。
<set>元素是等元素的子元素,用于指定Bean的属性类型为set。
<map> 元素是等元素的子元素,用于指定Bean的属性类型为Map。
<entry> 元素是元素的子元素,用于设定一个键值对。元素的key属性指定字符串类型的键

3.3 Bean的实例化

  • 构造方法实例化

Spring容器通过Bean对应类中默认的无参构造方法来实例化Bean

<bean id="user1" class="com.tyut.example02.pojo.User"/>
public class User {
    private String username;
    private String password;

    public User() {
        System.out.println("一个用户已经被构造");
    }
}

@Test
public void test03() {
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
    User user1 = (User) applicationContext.getBean("user1");

}
  • 静态工厂实例化

使用静态方法创建Bean的实例时,Bean的class指向的是静态工厂类,用Bean中的factory-method指定定义的静态方法

<bean id="userFactory" class="com.tyut.example02.utils.UserFactory" factory-method="getUser"/>
public class UserFactory {
    public static User getUser() {
        return new User();
    }
}

@Test
public void test04() {
    ApplicationContext applicationContext =
            new ClassPathXmlApplicationContext("applicationContext.xml");
    User user1 = (User) applicationContext.getBean("userFactory");
    System.out.println(user1);
}
  • 实例工厂实例化

直接创建Bean的实例,在XML配置文件中,不使用class属性直接指向Bean实例所属的类,通过factory-bean指向Bean的实例工厂,factory-method指定调用工厂的实例方法

 <!--  指定要创建的实例工厂  -->
<bean id="userFactory" class="com.tyut.example02.utils.UserFactory"/>
<!-- 通过实例工厂创建对象:factory-bean指定实例工厂,factory-method指定实例方法   -->
<bean id="user" factory-bean="userFactory" factory-method="getUser"/>
public class UserFactory {
    public User getUser() {
        return new User();
    }
}

@Test
public void test05() {
    ApplicationContext applicationContext =
            new ClassPathXmlApplicationContext("applicationContext.xml");
    User user1 = (User) applicationContext.getBean("user");
    System.out.println(user1);
}

3.4 Bean的作用域

作用域描述
singleton单例模式。在单例模式下,Spring 容器中只会存在一个共享的Bean实例,所有对Bean的请求,只要请求的id(或name)与Bean的定义相匹配,会返回Bean的同一个实例。
prototype原型模式,每次从容器中请求Bean时,都会产生一个新的实例。
request每一个HTTP请求都会有自己的Bean实例,该作用域只能在基于Web的Spring ApplicationContext中使用。
session每一个HTTPsession请求都会有自己的Bean实例,该作用域只能在基于Web的Spring ApplicationContext中使用。
global session限定一个Bean的作用域为Web应用(HTTPsession)的生命周期,只有在Web应用中使用Spring时,该作用域才有效。
  • singleton 作用域

singleton是Spring容器默认的作用域,当Bean的作用域为singleton时,Spring容器就只会存在一个共享的Bean实例,该实例可重复使用。

Spring容器管理着Bean的生命周期,包括Bean的创建、初始化、销毁。因为创建和销毁Bean实例会带来一定的系统开销,所以singleton作用域可避免反复创建和销毁实例造成的资源消耗。

<bean id="scope" class="com.tyuy.scope.Scope" scope="singleton"/>
  • prototype作用域

当Bean的作用域为prototype时,每次对Bean请求时都会创建一个新的Bean实例。

Spring容器只负责创建Bean实例而不再管理其生命周期。

<bean id="scope" class="com.itheima.tyut.Scope" scope=" prototype "/>

3.5 Bean的装配

Bean的装配就是Bean的依赖注入,Bean的装配方式即Bean依赖注入的方式。

  • 基于XML的装配

在Spring实例化Bean的过程中,spring首先会调用Bean的默认构造方法来实例化Bean对象,然后通过反射的方式调用setter方法来注入属性值。

setter注入(property)

​ (1)Bean类必须提供一个默认的无参构造方法。

​ (2)Bean类必须为需要注入的属性提供对应的setter方法。

构造方法注入(constructor-arg)

​ (1)Bean类中有一个有参的构造函数

基于XML开发案例:

目标:做一个登录模拟,username=zhangsan,password = 123456

  • pom.xml配置

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>ssm-demo01</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <!--  spring的基础包      -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>5.3.28</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>5.3.28</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.3.28</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-expression</artifactId>
            <version>5.3.28</version>
        </dependency>

        <!--   spring其他依赖包     -->
        <dependency>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
            <version>1.2</version>
        </dependency>

        <!--   junit测试包     -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
            <scope>test</scope>
        </dependency>

    </dependencies>

</project>
  • pojo包
public class User {
    private String username;
    private String password;

    public User(String username, String password) {
        this.username = username;
        this.password = password;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}
  • dao包
public interface UserDao {
    public User selectUserByUsername(String username);
}

public class UserDaoImpl implements UserDao{
    @Override
    public User selectUserByUsername(String username) {
        // 模拟数据库查询操作
        if ("zhangsan".equals(username)) {
            return new User("zhangsan", "123456");
        }
        return null;
    }
}
  • service包
public interface UserService {
    public void login(String username, String password);
}

public class UserServiceImpl implements UserService{
    private UserDao userDao;
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }
    @Override
    public void login(String username, String password) {
        User user = userDao.selectUserByUsername(username);
        if ("zhangsan".equals(user.getUsername()) && password.equals(user.getPassword())) {
            System.out.println("登录成功");
        } else {
            System.out.println("登录失败");
        }
    }
}
  • spring配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       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
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd">

    <bean id="userDao" class="com.tyut.example02.dao.UserDaoImpl"/>
    <bean id="userService" class="com.tyut.example02.service.UserServiceImpl">
        <property name="userDao" ref="userDao"></property>
    </bean>
</beans>
  • 测试类
public class Test01 {
    @Test
    public void test02() {
        //初始化Spring容器,加载配置项
        ApplicationContext applicationContext =
                new ClassPathXmlApplicationContext("applicationContext.xml");

        //通过容器加载实例
        UserService userService = (UserService) applicationContext.getBean("userService");
        userService.login("zhangsan", "123456");
        userService.login("zhangsan", "1234");

    }
}
  • 基于注解(Annotation)的装配
注解描述
@Component指定一个普通的Bean,可以作用在任何层次。
@Controller指定一个控制器组件Bean,用于将控制层的类标识为Spring中的Bean,功能上等同于@Component。
@Service指定一个业务逻辑组件Bean,用于将业务逻辑层的类标识为Spring中的Bean,功能上等同于@Component。
@Repository指定一个数据访问组件Bean,用于将数据访问层的类标识为Spring 中的Bean,功能上等同于@Component。
@Scope指定Bean实例的作用域。
@Value指定Bean实例的注入值。
@Autowired指定要自动装配的对象。
@Resource指定要注入的对象。
@Qualifier指定要自动装配的对象名称,通常与@Autowired联合使用。
@PostConstruct指定Bean实例完成初始化后调用的方法。
@PreDestroy指定Bean实例销毁前调用的方法。

案例:将上述XML开发转换成注解开发

  • pom.xml
<!--  aop依赖  -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aop</artifactId>
    <version>5.3.28</version>
</dependency>
  • pojo
public class User {
    private String username;
    private String password;

    public User(String username, String password) {
        this.username = username;
        this.password = password;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}
  • dao
public interface UserDao {
    public User selectUserByUsername(String username);
}

@Repository("userDao")
public class UserDaoImpl implements UserDao {
    @Override
    public User selectUserByUsername(String username) {
        // 模拟数据库查询操作
        if ("zhangsan".equals(username)) {
            return new User("zhangsan", "123456");
        }
        return null;
    }
}
  • Service
public interface UserService {
    public void login(String username, String password);
}

@Service("userService")
public class UserServiceImpl implements UserService {

    @Resource(name = "userDao")
    private UserDao userDao;
    @Override
    public void login(String username, String password) {
        User user = userDao.selectUserByUsername(username);
        if ("zhangsan".equals(user.getUsername()) && password.equals(user.getPassword())) {
            System.out.println("登录成功");
        } else {
            System.out.println("登录失败");
        }
    }
}
  • controller
@Controller("userController")
public class UserController {
    @Resource(name = "userService")
    private UserService userService;

    public void login(String username, String password) {
        userService.login(username, password);
    }
}
  • 自动装配

Spring的<bean>元素中包含一个autowire属性,可以通过设置autowire属性的值实现Bean的自动装配。

属性值描述
default(默认值)<bean>的上级元素<beans>的default-autowire属性值确定。
byName根据<bean>元素id属性的值自动装配。
byType根据<bean>元素的数据类型(Type)自动装配,如果一个Bean的数据类型,兼容另一个Bean中的数据类型,则自动装配。
constructor根据构造函数参数的数据类型进行byType模式的自动装配。
no默认值,不使用自动装配,Bean依赖必须通过元素或ref属性定义。
<bean id="userDao" class="com.tyut.example02.dao.UserDaoImpl"/>
        <bean id="userService" class="com.tyut.example02.service.UserServiceImpl" autowire="byName">
        </bean>

image-20241217181053501

3.6 Bean的生命周期

Bean的生命周期是指Bean实例被创建、初始化和销毁的过程。在Bean的两种作用域singleton和prototype中,Spring容器对Bean的生命周期的管理是不同的。在singleton作用域中,Spring容器可以管理Bean的生命周期,控制着Bean的创建、初始化和销毁。在prototype作用域中, Spring容器只负责创建Bean实例,不会管理其生命周期。

  • 初始化容器:1.创建对象 -> 2.执行构造方法 -> 3.执行属性注入(set操作) -> 4.执行Bean初始化操作
  • 执行Bean的具体方法
  • 执行关闭Bean

在Bean的生命周期中,有两个时间节点尤为重要,这两个时间节点分别是Bean实例初始化后和Bean实例销毁前,在这两个时间节点通常需要完成一些指定操作。因此,常常需要对这两个节点进行监控。

  • Spring容器提供了@PostConstruct用于监控Bean对象初始化节点
  • 提供了@PreDestroy用于监控Bean对象销毁节点。
@Component("student")
public class Student {
    @Value("1")
    private String id;
    @Value("张三")
    private String name; 	// 省略getter/setter方法,以及toString()方法
    @PostConstruct
    public void init(){System.out.println("Bean的初始化完成,调用init()方法");	}
    @PreDestroy
    public void destroy(){System.out.println("Bean销毁前调用destroy()方法");		}}

public class StudentTest {
    public static void main(String[] args){
    ApplicationContext applicationContext=new 
            ClassPathXmlApplicationContext("applicationStudent.xml");
    Student student=(Student)applicationContext.getBean("student");
    System.out.println(student);
    //销毁Spring容器中的所有Bean
    AbstractApplicationContext ac=(AbstractApplicationContext) applicationContext;
    ac.registerShutdownHook();
}}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值