Spring框架

简介: Spring框架

Spring框架

在软件开过程中,我们需要使用到实例。我们又期望这些实例以单例的形式存在。现在我们要获得单例没有一个统一的入口,这时候就有了Spring。

我们使用Spring将这些实例统一的管理起来了,如果要获得某个类型的实例,可以通过Spring来获得

使用Spring容器来管理实例,并且从Spring容器中取出实例

组件:Spring容器管理的实例叫组件

注册:注册组件,Spring容器管理实例

引入依赖

Spring的依赖5+1

spring-core\context\aop\expression\beans + jcl(日志包)

junit

Spring依赖的特征:spring-xxx(官方)、 xxx-spring(非官方)

<!--5+1-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.2.15.RELEASE</version>
</dependency>
<!--junit-->
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    <scope>test</scope>
</dependency>

配置文件

为啥要使用配置文件。使用配置文件将需要管理的实例做统一的管理

Spring配置文件是xml格式的 → 使用xml需要规范,xml中可以使用哪一些标签、可以使用哪一些属性,标签又有什么样的子标签 → 约束,同时也提供了解析的方式

我们需要去引入约束(头)

  • 已有的项目复制(×)
  • 文件模板(×)
  • Spring官方文档 →
    → schema约束
<?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:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/util https://www.springframework.org/schema/util/spring-util.xsd">
    <!-- bean definitions here -->
    <bean id="userService" class="proxy.service.UserServiceImpl"/>
</beans>

注册组件

我们要使用Spring容器管理组件

我们需要有对应的类,才能管理它的实例。

<!--注册组件 → UserServiceImpl-->
<!--id属性:组件在容器中唯一标识-->
<!--class属性:全限定类名 ctrl + alt + shift + c-->
 <bean id="userService" class="proxy.service.UserServiceImpl"/>

测试

@Test
public void test1(){
    ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext(
            "application.xml");//初始化容器
    //获得对象,建议用接口接收,方便后期修改维护
    UserService  userService = (UserService) classPathXmlApplicationContext.getBean("userService");
    userService.say();
}

取出组件的方式

getBean

//getBean(String)  → 按照组件id取出组件
//getBean(Class)   → 按照组件类型取出组件(该类型的组件只有一个)
//applicationContext.getBean(UserService.class)
//getBean(String,Class)  → 按照组件id和类型   
@Test
public void mytest() {
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml");
    //按照组件id取出
    UserService userService1 = (UserService) applicationContext.getBean("userService");
    //按照类型取出
    UserService userService2 = applicationContext.getBean(UserService.class);
    //按照组件id和组件类型
    UserService userService3 = applicationContext.getBean("userService", UserService.class);
    userService1.sayHello("world");
}

最好使用id+组件类型共同配合,这样可以避免类型强转出现的问题

当使用按照类型取出的时候,如果相同类型的组件不止一个,那么就会出现问题

<bean id="userService" class="com.cskaoyan.service.UserServiceImpl"/>
<bean id="userService2" class="com.cskaoyan.service.UserServiceImpl2"/>

会出现以下的异常

org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type ‘com.cskaoyan.service.UserService’ available: expected single matching bean but found 2: userService,userService2

维护多个组件之间的关系

即,在一个组件中维护其他组件中的对象

<bean id="userService" class="com.cskaoyan.service.UserServiceImpl">
    <!--name属性:给成员赋值,使用的是set方法赋值的-->  
    <!--ref属性:引用了容器中的组件的id-->
    <property name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" class="com.cskaoyan.dao.UserDaoImpl"/>

核心接口

容器接口:ApplicationContext、BeanFactory(ApplicationContext的父接口)

ApplicationContext:ClassPathXmlApplicationContext、AnnotationConfigApplicationContext、WebApplicationContext

Spring底层就是维护了一个HashMap

BeanFactory和FactoryBean之间的关系?

联系:都是工厂设计模式,都是向容器中注册组件

区别:BeanFactory注册的全部组件;FactoryBean注册的是特定的组件

  1. BeanFactory:负责生产和管理Bean的一个工厂接口,提供一个Spring Ioc容器规范,
  2. FactoryBean: 一种Bean创建的一种方式,对Bean的一种扩展。对于复杂的Bean对象初始化创建使用其可封装对象的创建细节。

Bean的实例化

构造方法

无参构造和有参构造皆可,默认使用的是无参构造,可以通过property来进行赋值

<!--默认使用的是无参构造-->
<bean id="noParamConstructorBean" class="com.cskaoyan.bean.NoParamConstructorBean">
    <property name="username" value="songge"/>
</bean>

如果要使用有参构造,那么需要在子标签中写入构造方法的形参名

<bean id="hasParamConstructorBean" class="com.cskaoyan.bean.HasParamConstructorBean">
    <!--name属性:有参构造方法的形参名-->
    <constructor-arg name="username" value="ligenli"/>
</bean>

工厂

静态工厂和实例工厂

静态工厂注册:

<bean id="carFromStaticFactory" class="com.cskaoyan.factory.CarStaticFactory" factory-method="create"/>

指明调用静态工厂中的方法名即可,会自动调用并产生实例,使用factory-method标签

实例工厂注册:首先要用无参构造完成工厂的实例化,然后再调用实例工厂的工厂方法

<!--工厂的实例化 → 无参构造先完成工厂的实例化-->
<bean id="instanceFactory" class="com.cskaoyan.factory.CarInstanceFactory"/>
<!--使用工厂实例调用生产方法:唯一一个没有使用class属性的bean标签-->
<bean id="carFromInstanceFactory" factory-bean="instanceFactory" factory-method="create"/>
***FactoryBean

将方法的返回值作为容器中的组件

工厂方法:定义工厂接口,来实现工厂接口

public interface FactoryBean<T> {
    String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType";
    //getObject是其核心方法 → 返回值作为容器中的组件
    //类定义的泛型体现在返回值类型
    @Nullable
    T getObject() throws Exception;
    //组件类型
    @Nullable
    Class<?> getObjectType();
    default boolean isSingleton() {
        return true;
    }
}

在注册组件的时候有个命名的规则xxxFactoryBean,注册的组件就是xxx

/**
 * FactoryBean
 * XXXFactoryBean → 注册的组件就是XXX
 * @author stone
 * @date 2021/12/25 11:31
 */
public class CarFactoryBean implements FactoryBean<Car> {
    @Override
    public Car getObject() throws Exception {
        Car car = new Car();
        car.setSource("factory bean");
        return car;
    }
    @Override
    public Class<?> getObjectType() {
        return Car.class;
    } 
}

组件注册

只需要将FactoryBean注册到容器中即可

<!--FactoryBean只需要注册到容器中-->
<bean id="carFromFactoryBean" class="com.cskaoyan.factory.CarFactoryBean"/>

FactoryBean是Spring框架中非常特殊的一个接口,我们注册组件的时候,我们写的是其其实现类,而最终取出的组件是其getObject方法的返回值

Srping容器在取出bean的时候,会先判断这个bean有没有实现FactoryBean,如果有就返回getObject中的返回值,如果没有就返回bean

Scope作用域

singleton 单例:每一次取出来是同一个

prototype 原型:每一次取出来是一个新的 → 每一次取出组件的时候才会开始生命周期

配置作用域

<bean id="singletonBean" class="com.cskaoyan.scope.SingletonBean" scope="singleton"/>
<bean id="prototypeBean" class="com.cskaoyan.scope.PrototypeBean" scope="prototype"/>
<bean id="defaultBean" class="com.cskaoyan.scope.DefaultBean"/>

默认的作用域是单例,绝大多数情况下我们使用的也是单例

生命周期

组件从实例化开始到组件可用状态这个时间经过哪些方法,在容器关闭的时候也会影响

Scope对生命周期的影响:

1. Prototype组件:每一次取出组件的时候才会开始生命周期 → 懒加载,容器关闭不会执行方法
2. Singleton组件:容器初始化的时候就已经开始生命周期 → 立即加载,容器关闭会执行方法

组件在容器中默认以单例的形式存在,单例组件在容器初始化时开始生命周期,生命周期的起点是Bean的实例化

Aware接口

如果你需要Aware提供的值,才去实现对应的接口才会执行到对应的set方法

使用的是set方法进行赋值,需要使用一个成员变量接收。

设置BeanName

public interface BeanNameAware extends Aware {
    void setBeanName(String var1);
}

设置BeanFactory

public interface BeanFactoryAware extends Aware {
    void setBeanFactory(BeanFactory var1) throws BeansException;
}

设置上下文

public interface ApplicationContextAware extends Aware {
    void setApplicationContext(ApplicationContext var1) throws BeansException;
}

***BeanPostProcessor接口

BeanPostProcessor接口提供的方法是给容器中的所有的组件使用的,相当于是一个通用性的方法,容器中的所有组件都会执行到BeanPostProcessor的postProcessBeforeInitialization和postProcessAfternitialization方法

对所有的组件进行通用性的处理,也可以根据传入的参数来进行判断,对某一类或者某一个组件进行单独的处理

生效条件:首先定义一个类实现BeanPostProcessor接口,并且将其注册为容器中的组件。

针对于BeanPostProcessor而言,作用范围是:除了BeanPostProcessor这个组件之外,其他的所有组件

public interface BeanPostProcessor {
    /**
    * bean: 正在执行生命周期的组件
    * beanName: 正在执行生命周期的组件id
    * 返回值:Object类型,需要注册到容器中的实例
    */
    @Nullable
    default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }
    @Nullable
    default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }
}

针对于返回值,如果返回值为null那么就返回原先的bean,如果不是null,就返回自定义的值并注册到组件中

个性化处理:

@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
    System.out.println("组件" + beanName +"的BeanPostProcessor的before方法");
    //针对于容器中的某一类或某一个组件可以在这里做通用性的处理
    // 能否找到所有的DataSource组件做处理
    if (bean instanceof DataSource) {
        //个性化的处理
        // 数据库的密码是123456
        // 我这里可以获得配置文件中的abcdef
        // 把abcdef转换为你需要的123456
    }
    // 我能否找到lifeCycleBean这个组件
    if ("lifeCycleBean".equals(beanName)) {
        //原先的username是songge → 组件到达可用状态之前变成ligenli
        LifeCycleBean temp = (LifeCycleBean) bean;
        temp.setUsername("ligenli");
        return temp;
    }
    // 传入的是委托类的实例,返回的是代理对象
    return bean;
}

主要关注的是通用性,在通用性里也可以自己写代码完成个性化的处理

before和after之间的过程

InitializingBean接口提供的afterPropertiesSet方法

@Override
public void afterPropertiesSet() throws Exception { //在processor的before和after之间生效
    System.out.println(beanName+":afterPropertiesSet方法");
}

自定义的init方法

public void myinit() {  //需要使用init-method的标签告诉容器这是init方法
    System.out.println("自定义init");
}

使用自定义的init中的方法的时候需要在配置文件中加上init-method属性,告知容器这是这个实例的init方法

<bean id="lifeCycleBean" class="ioc1.lifeCycle.LifeCycleBean" init-method="myinit" >
    <property name="username" value="zs"/>
</bean>

注意

对于作用域是prototype的组件,每一次取出组件(getBean方法)的时候,才会开始生命周期,因为他是懒加载,这样就不用维护多余的内存空间。同时因为不是单例,那么每次加载的时候都是新的对象,也就会执行新的生命周期

容器关闭

容器在关闭的时候,会执行对应的方法,一般是为了方便资源的释放

一定要注意prototype类型的组件在容器关闭时不会执行对应的方法

DisposableBean提供的destroy方法

public interface DisposableBean {
    void destroy() throws Exception;
}

自定义的destroy方法

public void myDestroy(){
    System.out.println("自定义的destroy方法");
}

如果需要调用自定义的destroy方法,那么就需要在标签中指明该方法

<bean id="lifeCycleBean" class="ioc1.lifeCycle.LifeCycleBean" init-method="myinit" destroy-method="myDestroy">
    <property name="username" value="zs"/>
</bean>

如果自定义的destroy和DisposableBean提供的destroy方法同时都有,那么两个都会执行

这里要注意destroy方法必须要容器关闭才会执行,因此需要调用容器的close方法

applicationContext.close();

完整的生命周期的流程

1.实例化

2.参数设置–使用set方法–关联propert标签

3.如果组件实现了Aware接口,那么就会执行到Aware接口中提供的方法

4.执行BeanPostProcessor中的方法

4.1先执行beforeinit再执行afterinit

4.2如果在这中间有接口提供的或者自定义的init方法,那么就会执行

5.最后到达可用状态

**注解 **

组件注册功能的注解

在注册的时候,使用注解和全限定类名耦合起来,并通过配置文件配置一个包目录,Spring可以自动找到这个包下的所有包含注册功能注解的类

1.限定范围

2.注册功能注解

扫描包的配置文件

<!--扫描包目录:扫描的是当前包以及它的所有的子包-->
<context:component-scan base-package="com.cskaoyan"/>

常用的注册组件的注解

@Component,  通用型
@Repository,  通常是dao层的组件
@Service,  通常是service层的组件
@Controller,  SpringMVC阶段的
@RestController,  SpringMVC阶段的
@ControllerAdvice,  SpringMVC阶段的
@Configuration   Spring的配置类使用的 → JavaConfig

通过注解注册的组件,组件的id是什么?

1.默认的id值是类名的首字母小写的形式

2.自行指定:使用注解的value属性来指定

//@Component //userServiceImpl → 组件id的默认值是类名的首字母小写的形式
//@Component(value = "userService")//使用注解的value属性指定
@Component("userService")//如果注解中只有value属性,‘value=’这一部分可以省略掉
public class UserServiceImpl implements UserService{
}
作用域

@Scope 直接写在类上,在value属性中直接写作用域即可,同样的,value属性可以省略,直接写值即可

@Scope("prototype") //value属性里直接写作用域就可以了
public class UserServiceImpl implements UserService{
}
生命周期中的init和destroy方法

@PostConstruct 表示的是init方法

@PreDestroy表示的是destroy方法

public class UserServiceImpl implements UserService{
    @PostConstruct //init-method
    public void myinit() {
        System.out.println("自定义init方法");
    }
    @PreDestroy //destroy-method
    public void mydestroy() {
        System.out.println("自定义destroy方法");
    }
}

注入功能的注解

组件的注入

在组件的成员变量中引入容器中已经注册的组件,维护组件之间的依赖关系

其实就是从容器中取出组件给成员变量赋值。取出组件的方式和getBean方法是类似的

1.按照组件类型取出

2.按照组件id取出

3.按照id和类型取出

@Autowired
@Qualifier("userDaoImpl1") //按照类型和id引入
UserDao userDao1;
@Resource(name = "userDaoImpl2") // 使用@Resource注解的name属性
UserDao userDao2;
@Autowired   //按照类型
OrderDao orderDao;

常用的方法是按照类型取出

值的注入

注入组件的成员变量:接收字符串、基本类型这样的一些值

可以使用@Value注解使用SpEL,需要首先加载properties配置文件 如果要加载classpath路径,需要进行指定

<context:property-placeholder location="classpath:param.properties"/>

接下来在使用的时候直接用${}的形式来引入key,就可以自动获取key中的value的值

@Value("${userservice.username}") //${}引入key → SpEL → Spring Expression Language
String username;

这里要注意写key的时候一定要增加一个自定义的前缀名,这样可以更方便的分辨属性,并且也可以避免引入系统变量

注意事项

@Autowired、@Autowired+@Qualifier、@Resource、@Value

这些注解,要在容器中的组件才能使用,因为只有组件注册了,才可以取出

单元测试

Spring单元测试:方便从容器中取出实例

直接在单元测试类使用注入功能的注解来取出组件

在单元测试类中需要维护容器 → ApplicationContext → application.xml

单元测试类中会帮我们维护好容器

引入依赖

spring-test

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>5.2.15.RELEASE</version>
    <scope>test</scope>
</dependency>

单元测试类上的注解

@Runwith(SpringJUnit4ClassRunner.class) 加载测试类的注解

@ContextConfiguration(“classpath:application.xml”) 加载配置文件的注解,一定要带上classpath

@RunWith(SpringJUnit4ClassRunner.class)//注解是junit提供的,class是spring-test
@ContextConfiguration("classpath:application.xml")
public class AnnotationTest {
    @Autowired
    OrderDao orderDao;
    @Autowired
    UserService userService;
    @Autowired
    ApplicationContext applicationContext;
    @Test
    public void mytest1() {
        userService.serviceMethod();
    }
}

ork

spring-test

5.2.15.RELEASE

test

单元测试类上的注解
@Runwith(SpringJUnit4ClassRunner.class) 加载测试类的注解
@ContextConfiguration("classpath:application.xml") 加载配置文件的注解,一定要带上classpath
```java
@RunWith(SpringJUnit4ClassRunner.class)//注解是junit提供的,class是spring-test
@ContextConfiguration("classpath:application.xml")
public class AnnotationTest {
    @Autowired
    OrderDao orderDao;
    @Autowired
    UserService userService;
    @Autowired
    ApplicationContext applicationContext;
    @Test
    public void mytest1() {
        userService.serviceMethod();
    }
}
目录
相关文章
|
24天前
|
数据采集 监控 前端开发
二级公立医院绩效考核系统源码,B/S架构,前后端分别基于Spring Boot和Avue框架
医院绩效管理系统通过与HIS系统的无缝对接,实现数据网络化采集、评价结果透明化管理及奖金分配自动化生成。系统涵盖科室和个人绩效考核、医疗质量考核、数据采集、绩效工资核算、收支核算、工作量统计、单项奖惩等功能,提升绩效评估的全面性、准确性和公正性。技术栈采用B/S架构,前后端分别基于Spring Boot和Avue框架。
|
1月前
|
Java API 数据库
构建RESTful API已经成为现代Web开发的标准做法之一。Spring Boot框架因其简洁的配置、快速的启动特性及丰富的功能集而备受开发者青睐。
【10月更文挑战第11天】本文介绍如何使用Spring Boot构建在线图书管理系统的RESTful API。通过创建Spring Boot项目,定义`Book`实体类、`BookRepository`接口和`BookService`服务类,最后实现`BookController`控制器来处理HTTP请求,展示了从基础环境搭建到API测试的完整过程。
42 4
|
1月前
|
JavaScript 安全 Java
如何使用 Spring Boot 和 Ant Design Pro Vue 实现动态路由和菜单功能,快速搭建前后端分离的应用框架
本文介绍了如何使用 Spring Boot 和 Ant Design Pro Vue 实现动态路由和菜单功能,快速搭建前后端分离的应用框架。首先,确保开发环境已安装必要的工具,然后创建并配置 Spring Boot 项目,包括添加依赖和配置 Spring Security。接着,创建后端 API 和前端项目,配置动态路由和菜单。最后,运行项目并分享实践心得,包括版本兼容性、安全性、性能调优等方面。
152 1
|
1月前
|
Java API 数据库
Spring Boot框架因其简洁的配置、快速的启动特性及丰富的功能集而备受开发者青睐
本文通过在线图书管理系统案例,详细介绍如何使用Spring Boot构建RESTful API。从项目基础环境搭建、实体类与数据访问层定义,到业务逻辑实现和控制器编写,逐步展示了Spring Boot的简洁配置和强大功能。最后,通过Postman测试API,并介绍了如何添加安全性和异常处理,确保API的稳定性和安全性。
38 0
|
28天前
|
前端开发 Java 数据库连接
Spring 框架:Java 开发者的春天
Spring 框架是一个功能强大的开源框架,主要用于简化 Java 企业级应用的开发,由被称为“Spring 之父”的 Rod Johnson 于 2002 年提出并创立,并由Pivotal团队维护。
43 1
Spring 框架:Java 开发者的春天
|
20天前
|
JavaScript 安全 Java
如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个前后端分离的应用框架,实现动态路由和菜单功能
本文介绍了如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个前后端分离的应用框架,实现动态路由和菜单功能。首先,确保开发环境已安装必要的工具,然后创建并配置 Spring Boot 项目,包括添加依赖和配置 Spring Security。接着,创建后端 API 和前端项目,配置动态路由和菜单。最后,运行项目并分享实践心得,帮助开发者提高开发效率和应用的可维护性。
37 2
|
19天前
|
消息中间件 NoSQL Java
springboot整合常用中间件框架案例
该项目是Spring Boot集成整合案例,涵盖多种中间件的使用示例,每个案例项目使用最小依赖,便于直接应用到自己的项目中。包括MyBatis、Redis、MongoDB、MQ、ES等的整合示例。
77 1
|
27天前
|
Java 数据库连接 开发者
Spring 框架:Java 开发者的春天
【10月更文挑战第27天】Spring 框架由 Rod Johnson 在 2002 年创建,旨在解决 Java 企业级开发中的复杂性问题。它通过控制反转(IOC)和面向切面的编程(AOP)等核心机制,提供了轻量级的容器和丰富的功能,支持 Web 开发、数据访问等领域,显著提高了开发效率和应用的可维护性。Spring 拥有强大的社区支持和丰富的生态系统,是 Java 开发不可或缺的工具。
|
1月前
|
人工智能 Java API
阿里云开源 AI 应用开发框架:Spring AI Alibaba
近期,阿里云重磅发布了首款面向 Java 开发者的开源 AI 应用开发框架:Spring AI Alibaba(项目 Github 仓库地址:alibaba/spring-ai-alibaba),Spring AI Alibaba 项目基于 Spring AI 构建,是阿里云通义系列模型及服务在 Java AI 应用开发领域的最佳实践,提供高层次的 AI API 抽象与云原生基础设施集成方案,帮助开发者快速构建 AI 应用。本文将详细介绍 Spring AI Alibaba 的核心特性,并通过「智能机票助手」的示例直观的展示 Spring AI Alibaba 开发 AI 应用的便利性。示例源
|
1月前
|
人工智能 开发框架 Java
总计 30 万奖金,Spring AI Alibaba 应用框架挑战赛开赛
Spring AI Alibaba 应用框架挑战赛邀请广大开发者参与开源项目的共建,助力项目快速发展,掌握 AI 应用开发模式。大赛分为《支持 Spring AI Alibaba 应用可视化调试与追踪本地工具》和《基于 Flow 的 AI 编排机制设计与实现》两个赛道,总计 30 万奖金。
下一篇
无影云桌面