Spring--开源的轻量级的Java开发框架

2023-05-16

目录:

    • 一、Spring 简介
        • 1. 什么是Spring
        • 2. Spring 框架的优点
        • 3. Spring 体系结构
    • 二、Spring 容器
        • 1. 什么是Spring容器
        • 2. Spring 容器的实例化
        • 3. Spring 容器的使用
    • 三、Spring 容器对Bean的管理
        • 1. Bean的实例化
        • 2. Bean的作用域
        • 3. Bean 的生命周期(and 回调)
        • 4. Bean 延迟实例化
    • 四、Spring IoC
        • 1. IoC(控制反转)概念
        • 2. DI(依赖注入)
        • 3. Spring Bean的装配
    • 五、Spring AOP
        • 1. AOP 简介
        • 2. AOP与OOP之间的联系和区别
        • 3. Spring通知类型和创建AOP代理
        • 4. 基于XML和注解开发AOP
    • 六、Spring 拦截器
    • 七、Spring对JDBC Template的支持
    • 八、Spring 事务
        • 1. 事务的特性
        • 2. Spring事务的配置方式
        • 3.事务的传播机制
        • 4. 事务的隔离级别

一、Spring 简介

Spring 是另一个主流的 Java Web 开发框架,该框架是一个轻量级的应用框架,具有很高的凝聚力和吸引力。Spring 框架因其强大的功能以及卓越的性能而受到众多开发人员的喜爱。

1. 什么是Spring

Spring就像是整个项目中装配bean的大工厂,在配置文件中可以指定使用特定的参数去调用实体类的构造方法来实例化对象。

在实际开发中,通常服务器端采用三层体系架构,分别为表现层(web)、业务逻辑层(service)、持久层(dao)。
Spring对每一层都提供了技术支持,在表现层提供了与Struts2框架的整合,在业务逻辑层可以管理事务和记录日志等,在持久层可以整合Hibernate和Mybaties、JdbcTemplate等技术。

总结:
1)Spring的核心是一个轻量级(Lightwegit)的容器(Container)。
2)Spring是实现IoC(Inversion of Control 控制反转)容器和非入侵性(No intrusive)的框架。
3)Spring提供AOP(Aspect-oriented programming 面向切面编程)概念的实现方式。
4)Spring提供对持久层(Persistence)、事务(Transcation)的支持。
5)Spring提供MVC Web框架的实现,并对一些常用的企业服务API(Application Interface)提供一致的模型封装。
6)Spring提供了对现存的各种框架(Structs、JSF、Hibernate、Mybatis、Webwork等)相整合的方案。

2. Spring 框架的优点

1)方便解耦,简化开发
Spring 就是一个大工厂,可以将所有对象的创建和依赖关系的维护交给Spring管理。
2)方便集成各种优秀框架
Spring 不排斥各种优秀的开源框架,其内部提供了对各种优秀框架(如 Struts2、Hibernate、MyBatis 等)的直接支持。
3)降低 Java EE API 的使用难度
Spring 对 Java EE 开发中非常难用的一些API(JDBC、JavaMail、远程调用等)都提供了封装,使这些API应用难度大大降低。
4)方便程序的测试
Spring 支持 JUnit4,可以通过注解方便的测试Spring程序。
5)AOP编程的支持
Spring提供面向切面编程,可以方便地实现对程序进行权限拦截和运行监控等功能。
6)声明式事务的支持
只需要通过配置就可以完成对事务的管理,而无须手动编程。

3. Spring 体系结构

Spring 框架采用分层架构,根据不同的功能呗划分为了多个模块。

1) Data Access/Integration(数据访问/集成)
数据访问/集成层包括JDBC、ORM、OXM、JMS和Transaction模块,具体介绍如下。

  • JDBC 模块:提供了一个JDBC的抽象层,大幅度减少了再开发过程中对数据库操作的代码。
  • ORM 模块:对流行的对象关系映射API,包括JPA、JDO、Hibernate和Mybaties提供了的集成层。
  • OXM 模块,指Java消息服务,包含的功能为生成和消费的信息。
  • Transactions 事务模块:支持编程和声明式事务管理实现特殊接口类,并为所有的POJO。

2) Web 模块
Spring 的Web层包括Web、Servlet、Struts和Portlet组件,具体介绍如下。

  • Web 模块:提供了基本的Web开发集成特性,例如多文件上传功能、使用的Servlet监听器的IoC容器初始化以及Web应用上下文。
  • Servlet 模块:包括Spring模型、视图、控制器(MVC)实现Web应用程序。
  • Struts 模块:包含支持类内的Spring应用程序,集成了经典的Struts Web层。
  • Portlet 模块:提供了在Portlet环境中使用MVC实现,类似Web-Servlet模块的功能。

3) Core Container(核心容器)
Spring的核心容器是其他模块建立的基础,由Beans模块、Core核心模块、Context上下文模块和Expression Language表达式语言模块组成,具体介绍如下。

  • Beans 模块:提供了BeanFactory,是工厂模式的经典实现,Spring 将管理对象成为Bean。
  • Core 核心模块:提供了Spring 框架的基本组成部分,包括IoC和DI功能。
  • Context 上下文对象:建立在核心和Beans 模块的基础之上,它是访问定义和配置任何对象的媒介。ApplicationContext接口是上下文对象的焦点。
  • Expression Language 模块:是运行时查询和操作对象图的强大的表达式语言。

4) 其他模块
Spring的其他模块还有AOP、Aspects、Instrumentation以及Test模块,具体介绍如下。

  • AOP 模块:提供了面向切面编程实现,允许定义方法拦截器和切入点,将代码按照功能进行分离,以降低耦合性。
  • Aspects 模块:提供与AspectJ的集成,是一个功能强大且成熟的面向切面编程(AOP)框架。
  • Instrumentation 模块:提供了类工具的支持和类加载器的实现,可以在特定的应用服务器中使用。
  • Test 模块:支持Spring组件,使用JUnit或TestNG框架的测试。

二、Spring 容器

1. 什么是Spring容器

Spring容器是Spring的核心,一切Spring bean都存储在Spring容器内,并由其通过IoC技术管理。Spring容器也就是一个bean工厂(BeanFactory)。应用中bean的实例化,获取,销毁等都是由这个bean工厂管理的。

Spring 容器实现了IoC和AOP机制,这些可以简化Bean对象的创建和Bean对象之间的解耦。
Spring 提供了两种IoC容器,分别为BeanFactory和ApplicationContext两种类型。

2. Spring 容器的实例化

1)BeanFactory
BeanFactory是基础类型的IoC容器,它由org.springframework.beans.facytory.BeanFactory 接口定义,并提供了完整的 IoC 服务支持。简单来说,BeanFactory就是一个管理Bean的工厂,它主要负责初始化各种Bean,并调用它们的生命周期方法。

BeanFactory 接口有多个实现类,最常见的是 org.springframework.beans.factory.xml.XmlBeanFactory,它是根据 XML 配置文件中的定义装配 Bean 的。

创建BeanFactory实例时,需要提供Spring所管理容器的详细配置信息,这些信息通常采用XML文件形式管理。其加载配置信息的代码如下所示:

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

2)ApplicationContext
ApplicationContext是BeanFactory的子接口,也被称为应用上下文。该接口的全路径为org.springframework.context.ApplicationContext,它不仅提供了BeanFactory的所有功能,还添加了对i18n(国际化)、资源访问、事件传播等方面的良好支持。
ApplicationContext接口有两个常用的实现类,具体如下。
ClassPathXmlApplicationContext
该类从 类路径ClassPath 中寻找指定的XML配置文件,找到并装载完成ApplicationContext的实例化工作,具体如下所示。

ApplicationContext applicationContext = new ClassPathXmlApplicationContext(String configLocation);
//configLocation 参数用于指定 Spring 配置文件的名称和位置,如 applicationContext.xml。

FileSystemXmlApplicationContext
该类从 指定的文件系统路径 中寻找指定的XML配置文件,找到并装载完成ApplicationContext的实例化工作,具体如下所示。

ApplicationContext applicationContext = new FileSystemXmlApplicationContext(String configLocation);
/**
 *它与 ClassPathXmlApplicationContext 的区别是:在读取 Spring 的配置文件时,FileSystemXmlApplicationContext不再从类路径中读取配置文件,
 *而是通过参数指定配置文件的位置,它可以获取类路径之外的资源,如“F:/workspaces/applicationContext.xml”。
*/

通常在 Java 项目中,会采用通过 ClassPathXmlApplicationContext 类实例化 ApplicationContext 容器的方式,而在 Web 项目中,ApplicationContext 容器的实例化工作会交由 Web 服务器完成。Web 服务器实例化 ApplicationContext 容器通常使用基于 ContextLoaderListener 实现的方式,它只需要在 web.xml 中添加如下代码:

<!--指定Spring配置文件的位置,有多个配置文件时,以逗号分隔-->
<context-param>
    <param-name>contextConfigLocation</param-name>
    <!--spring将加载spring目录下的applicationContext.xml文件-->
    <param-value>
        classpath:spring/applicationContext.xml
    </param-value>
</context-param>
<!--指定以ContextLoaderListener方式启动Spring容器-->
<listener>
    <listener-class>
        org.springframework.web.context.ContextLoaderListener
    </listener-class>
</listener>

3. Spring 容器的使用

从本质上讲,BeanFactory 和 ApplicationContext 仅仅是一个维护 Bean 的定义以及相互
依赖关系的高级工厂接口。我们可以通过 BeanFactory 和 ApplicationContext 访问 Bean
的定义。
1)首先要在容器配置文件中 applicationContext.xml 添加 Bean 定义。

<bean id="标识符" class="Bean 类型">
	<property name="属性名" value="属性值"/>
</bean>

2)在创建 BeanFactory 和 ApplicationContext 容器对象之后,调用 getBean()方法获取 Bean
的实例

ApplicationContext容器对象.getBean("标识符")

三、Spring 容器对Bean的管理

Spring 配置文件支持两种不同的格式,分别是 XML 文件格式和 Properties 文件格式。

通常情况下,Spring 会以 XML 文件格式作为 Spring 的配置文件,这种配置方式通过 XML 文件注册并管理 Bean 之间的依赖关系。

XML 格式配置文件的根元素是 ,该元素包含了多个 子元素,每一个 子元素定义了一个 Bean,并描述了该 Bean 如何被装配到 Spring 容器中。

1. Bean的实例化

在 Spring 中,实例化 Bean 有三种方式,分别是构造器实例化、静态工厂方式实例化和实例工厂方式实例化。
1)构造器实例化

<bean id="calendar01" class="java.util.GregorianCalendar"/>
<bean name="calendar02" class="java.util.GregorianCalendar"/>
  • id 或 name 属性用于指定 Bean 的名称,用于从 Spring 容器中查找这个 Bean 对象。
  • class 属性用于指定 Bean 的类型,会自动调用无参构造器创建对象。

2)静态工厂方式实例化

<bean id="calendar03" class="java.util.Calendar" factory-method="getInstance"/>
  • id 属性用于指定 Bean 的名称。
  • class 属性用于指定工厂类型。
  • factory-method 属性用于指定工厂中创建 Bean 对象的方法,必须用 static 修饰的方法。

3)实例化工厂方式实例化

<bean id="calendar03" class="java.util.GregorianCalendar"/>
<bean id="dateObj" factory-bean="calendar03" factory-method="getTime"/>
//将对象的创建规则告诉 Spring,Spring 会帮你创建对象,基于配置和默认规则,减少代码的书写。
  • id 属性用于指定 Bean 的名称
  • factory-bean 属性用于指定工厂 Bean 对象。
  • factory-method 属性用于指定工厂中创建 Bean 对象的方法。

2. Bean的作用域

Spring 容器在初始化一个Bean的实例时,同时会指定该实例的作用域。作用域如下所示:
1)singleton
单例模式,使用 singleton 定义的 Bean 在 Spring 容器中只有一个实例,这也是 Bean 默认的作用域。
2)prototype
原型模式,每次通过 Spring 容器获取 prototype 定义的 Bean 时,容器都将创建一个新的 Bean 实例。
3)request
在一次 HTTP 请求中,容器会返回该 Bean 的同一个实例。而对不同的 HTTP 请求,会返回不同的实例,该作用域仅在当前 HTTP Request 内有效。
4)session
在一次 HTTP Session 中,容器会返回该 Bean 的同一个实例。而对不同的 HTTP 请求,会返回不同的实例,该作用域仅在当前 HTTP Session 内有效。
5)global Session
在一个全局的 HTTP Session 中,容器会返回该 Bean 的同一个实例。该作用域仅在使用 portlet context 时有效。

<bean id="person" class="com.mengma.scope.Person" scope="singleton"/>
//通过<bean>的 scope 属性来指定 Bean 作用域。

3. Bean 的生命周期(and 回调)


实例化 》 属性赋值 》 初始化 》 销毁

1)根据配置情况调用 Bean 构造方法或工厂方法实例化 Bean。

2)利用依赖注入完成 Bean 中所有属性值的配置注入。

3)如果 Bean 实现了 BeanNameAware 接口,则 Spring 调用 Bean 的 setBeanName() 方法传入当前Bean 的 id 值。

4)如果 Bean 实现了 BeanFactoryAware 接口,则 Spring 调用 setBeanFactory()方法传入当前工厂实例的引用。

5)如果 Bean 实现了 ApplicationContextAware 接口,则 Spring 调用setApplicationContext() 方法传入当前 ApplicationContext 实例的引用。

6)如果 BeanPostProcessor 和 Bean 关联,则 Spring 将调用该接口的预初始化方法postProcessBeforeInitialzation() 对 Bean 进行加工操作,此处非常重要,Spring 的 AOP就是利用它实现的。

7)如果 Bean 实现了 InitializingBean 接口,则 Spring 将调用 afterPropertiesSet()方法。

8)如果在配置文件中通过 init-method 属性指定了初始化方法,则调用该初始化方法。

9)如果 BeanPostProcessor 和 Bean 关联,则 Spring 将调用该接口的初始化方法postProcessAfterInitialization()。此时,Bean 已经可以被应用系统使用了。

10)如果在 中指定了该 Bean 的作用范围为 scope=“singleton”,则将该 Bean 放入 SpringIoC 的缓存池中,将触发 Spring 对该 Bean 的生命周期管理;如果在 中指定了该 Bean 的作用范围为scope=“prototype”,则将该 Bean 交给调用者,调用者管理该 Bean 的生命周期,Spring 不再管理该 Bean。

11)如果 Bean 实现了 DisposableBean 接口,则 Spring 会调用 destory() 方法将 Spring 中的Bean 销毁;如果在配置文件中通过 destory-method 属性指定了 Bean 的销毁方法,则 Spring 将调用该方法对Bean 进行销毁。

指定初始化回调方法

<bean id="exampleBean" class="com.entity.User" init-method="init"/>

指定销毁回调方法
适用于 singleton 模式 Bean。

<bean id="exampleBean" class="com.entity.User" destroy-method="destroy"/>

为容器中所有 Bean 指定初始化回调方法
在顶级的元素中的 default-init-method 属性设置。

<beans default-init-method="init">
	<bean id="exampleBean" class="com.entity.User"/>
	...
</beans>

为容器中所有的 Bean 指定销毁回调方法
在顶级的元素中的 default-destroy-method 属性设置。

<beans default-destroy-method="destroy">
	<bean id="exampleBean" class="com.entity.User"/>
	...
</beans>

4. Bean 延迟实例化

  • Bean 延迟实例化适用于频率很低的单例对象。
  • 在 ApplicationContext 实现的默认行为就是在启动时将所有 singleton Bean 提前进行实例化。 如果不想让一个 singleton Bean 在 ApplicationContext 初始化时被提前实例化,可以使用<bean/>元素的 lazy-init="true"属性改变。
<bean id="exampleBean" class="com.entity.User" lazy-init="true"/>

为容器中所有的 Bean 指定延迟实例化特性。
在顶级的元素中设置 default-lazy-init 属性。

<beans default-lazy-init="true">
	<bean id="exampleBean" class="com.entity.User"/>
	...
</beans>

四、Spring IoC

1. IoC(控制反转)概念

IOC:Inversion of Control,控制反转。
IOC 是指程序中对象的获取方式发生反转,由最初的 new方式创建转变为由第三方框架创建、注入(DI),降低了对象之间耦合度。
Spring 容器是采用 DI 方式实现 IOC 控制,IOC 是Spring 框架的基础和核心。

2. DI(依赖注入)

DI:Dependency Injection,依赖注入。
DI的基本原理就是将一起工作的具有关系的对象,通过构造方法或方法参数传入建立关联,因此容器的工作就是创建 Bean 时注入依赖关系。
IOC是一种思想,而 DI 是实现 IOC 的主要技术途径。 DI 主要有 2 种注入方式,即 Setter 注入和构造器注入。

1)Setter注入

指 IoC 容器使用 setter 方法注入被依赖的实例。通过调用无参构造器或无参 static 工厂方法实例化 bean 后,调用该 bean 的 setter 方法,即可实现基于 setter 的 DI。

//applicationContext.xml 中添加配置信息
<bean id="personService" class="com.mengma.ioc.PersonServiceImpl">
    <!-- 将personDao实例注入personService实例中 -->
    <property name="personDao" ref="personDao"/>
</bean>

2)构造器注入
指 IoC 容器使用构造方法注入被依赖的实例。基于构造器的 DI 通过调用带参数的构造方法实现,容器在实例化 Bean 时,根据参数类型执行相应的构造器。构造器注入可以强制给 Bean 注入某些参数,比 Setter 注入更严格。

3. Spring Bean的装配

1)基于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" xmlns:p="http://www.springframework.org/schema/p"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.2.xsd">
    <!-- 使用设值注入方式装配Person实例 -->
    <bean id="person1" class="com.mengma.assembly.Person">
        <property name="name" value="zhangsan" />
        <property name="age" value="20" />
    </bean>
    <!-- 使用构造方法装配Person实例 -->
    <bean id="person2" class="com.mengma.assembly.Person">
        <constructor-arg index="0" value="lisi" />
        <constructor-arg index="1" value="21" />
    </bean>
</beans>

参数值注入:
① 注入基本值

<bean id="msg" class="com.entity.MessageBean">
	<property name="name" value="李四"/>
</bean>

②注入Bean对象

<bean id="computer" class="com.entity.Computer">
	<property name="mainBoard" value="华硕"/>
	<property name="ram" value="金士顿"/>
</bean>
<bean id="msg" class="com.entity.MessageBean">
	<property name="computer" ref="computer"/>
</bean>

③List集合注入&引用List集合

<bean id="msg" class="com.entity.MessageBean">
	<property name="langs">
		<list>//<set/>、<map/>和<props/>
			<value>Java</value>
			<value>SQL</value>
			...
		</list>
	</property>
</bean>
<util:list id="langList">
	<value>Java</value>
	<value>SQL</value>
	<value>Python</value>
</util:list>
<bean id="msg2" class="com.entity.MessageBean">
	<property name="langs" ref="langList"/>
</bean>
同理:Set、Map、Properties 都可以采用引用方式注入。
使用<util:set/><util:map/><util:properties/>声明 Set、Map 和 Properties 集合。

④注入 Spring 表达式

<util:properties id="const" location="classpath:const.properties">
<bean id="demo" class="com.entity.DemoBean">
	<property name="name" value="#{msg.name}"/>
	<property name="lang" value="#{msg.langs[0]}"/>
	<property name="score" value="#{msg.score.c1}"/>
	<property name="pageSize" value="#{const.PAGE_SIZE}"
</bean>

⑤注入 null 或者空字符串

<bean id="msg" class="com.entity.MessageBean">
	<property name="name>
		<null/>	
	</property>
</bean>
<bean id="msg" class="com.entity.MessageBean">
	<property name="name" value=""/>
</bean>

2)基于注解的装配

开启组件扫描

<context:component-scan base-package="com.entity"/>

@Component
可以使用此注解描述 Spring 中的 Bean,但它是一个泛化的概念,仅仅表示一个组件(Bean),并且可以作用在任何层次。使用时只需将该注解标注在相应类上即可。
@Repository
用于将数据访问层(DAO层)的类标识为 Spring 中的 Bean,其功能与 @Component 相同。
@Service
通常作用在业务层(Service 层),用于将业务层的类标识为 Spring 中的 Bean,其功能与 @Component 相同。
@Controller
通常作用在控制层(如 Struts2 的 Action),用于将控制层的类标识为 Spring 中的 Bean,其功能与 @Component 相同。
@Autowired
用于对 Bean 的属性变量、属性的 Set 方法及构造函数进行标注,配合对应的注解处理器完成 Bean 的自动配置工作。默认按照 Bean 的类型进行装配。
@Resource
其作用与 Autowired 一样。其区别在于 @Autowired 默认按照 Bean 类型装配,而 @Resource 默认按照 Bean 实例名称进行装配。
@Resource 中有两个重要属性:name 和 type。

Spring 将 name 属性解析为 Bean 实例名称,type 属性解析为 Bean 实例类型。如果指定 name 属性,则按实例名称进行装配;如果指定 type 属性,则按 Bean 类型进行装配。

如果都不指定,则先按 Bean 实例名称装配,如果不能匹配,则再按照 Bean 类型进行装配;如果都无法匹配,则抛出 NoSuchBeanDefinitionException 异常。
@Qualifier
与 @Autowired 注解配合使用,会将默认的按 Bean 类型装配修改为按 Bean 的实例名称装配,Bean 的实例名称由 @Qualifier 注解的参数指定。
⑧**@PostConstruct** 和@PreDestroy 注解标记分别用于指定初始化和销毁回调方法。

3)自动装配
自动装配就是指 Spring 容器可以自动装配(autowire)相互协作的 Bean 之间的关联关系,将一个 Bean 注入其他 Bean 的 Property 中。

要使用自动装配,就需要配置 元素的 autowire 属性。

名称说明
byName根据 Property 的 name 自动装配,如果一个 Bean 的 name 和另一个 Bean 中的 Property 的 name 相同,则自动装配这个 Bean 到 Property 中。
byType根据 Property 的数据类型(Type)自动装配,如果一个 Bean 的数据类型兼容另一个 Bean 中 Property 的数据类型,则自动装配。
constructor根据构造方法的参数的数据类型,进行 byType 模式的自动装配。
autodetect根据构造方法的参数的数据类型,进行 byType 模式的自动装配。
no根据构造方法的参数的数据类型,进行 byType 模式的自动装配。

五、Spring AOP

1. AOP 简介

面向切面编程(AOP)和面向对象编程(OOP)类似,也是一种编程模式。Spring AOP 是基于 AOP编程模式的一个框架,它的使用有效减少了系统间的重复代码,达到了模块间的松耦合目的。

AOP 的全称是“Aspect Oriented Programming”,即面向切面编程,它将业务逻辑的各个部分进行隔离,使开发人员在编写业务逻辑时可以专心于核心业务,从而提高了开发效率。

AOP 采取横向抽取机制,取代了传统纵向继承体系的重复性代码,其应用主要体现在事务处理、日志管理、权限控制、异常处理等方面。

AOP的优点:
使用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高开发效率。

相关术语:

名称说明
Joinpoint(连接点)指那些被拦截到的点,在Spring中,可以被动态代理拦截目标类的方法
Pointcut(切入点)指要对哪些Joinpoint进行拦截,即被拦截的连接点
Advice(通知)指拦截到Joinpoint之后要做的事情,即对切入点增强的内容
Target(目标)指代理的目标对象
Weaving(植入)指把增强代码应用到目标上,生成代理对象的过程
Proxy(代理)指生成的代理对象
Aspect(切面)切入点和通知的结合

2. AOP与OOP之间的联系和区别

区别:
1)面向目标不同:简单来说OOP是面向名词领域,AOP面向动词领域。
2)思想结构不同:OOP是纵向结构,AOP是横向结构。
3)注重方面不同:OOP注重业务逻辑单元的划分,AOP偏重业务处理过程的某个步骤或阶段。

OOP和AOP的联系:
AOP需要以OOP为前提和基础,是对OOP的补充,两者之间是一个相互补充和完善的关系。

3. Spring通知类型和创建AOP代理

1)Spring通知类型

名称说明
org.springframework.aop.MethodBeforeAdvice(前置通知)在方法之前自动执行的通知称为前置通知,可以应用于权限管理等功能。
org.springframework.aop.AfterReturningAdvice(后置通知)在方法之后自动执行的通知称为后置通知,可以应用于关闭流、上传文件、删除临时文件等功能。
org.aopalliance.intercept.MethodInterceptor(环绕通知)在方法前后自动执行的通知称为环绕通知,可以应用于日志、事务管理等功能。
org.springframework.aop.ThrowsAdvice(异常通知)在方法抛出异常时自动执行的通知称为异常通知,可以应用于处理异常记录日志等功能。
org.springframework.aop.IntroductionInterceptor(引介通知)在目标类中添加一些新的方法和属性,可以应用于修改旧版本程序(增强类)。

2)声明式Spring AOP
Spring 创建一个 AOP 代理的基本方法是使用org.springframework.aop.framework.ProxyFactoryBean,这个类对应的切入点和通知提供了完整的控制能力,并可以生成指定的内容。

属性名称描述
target代理的目标对象
proxyInterfaces代理要实现的接口,如果有多个接口,则可以使用以下格式赋值:<list> <value></value> </list>
proxyTargetClass是否对类代理而不是接口,设置为 true 时,使用 CGLIB 代理
interceptorNames需要植入目标的 Advice
singleton返回的代理是否为单例,默认为 true(返回单实例)
optimize当设置为 true 时,强制使用 CGLIB
<?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 id="customerDao" class="com.mengma.dao.CustomerDaoImpl" />
    <!-- 通知 advice -->
    <bean id="myAspect" class="com.mengma.factorybean.MyAspect" />
    <!--生成代理对象 -->
    <bean id="customerDaoProxy"
     class="org.springframework.aop.framework.ProxyFactoryBean">
     <!--代理实现的接口 -->
        <property name="proxyInterfaces" value="com.mengma.dao.CustomerDao" />
        <!--代理的目标对象 -->
        <property name="target" ref="customerDao" />
        <!--用通知增强目标 -->
        <property name="interceptorNames" value="myAspect" />
        <!-- 如何生成代理,true:使用cglib; false :使用jdk动态代理 -->
        <property name="proxyTargetClass" value="true" />
    </bean>
</beans>

4. 基于XML和注解开发AOP

使用 AspectJ 开发 AOP 通常有两种方式:
基于 XML 的声明式。
基于 Annotation 的声明式。
1)基于XML的声明式
基于 XML 的声明式是指通过 Spring 配置文件的方式定义切面、切入点及声明通知,而所有的切面和通知都必须定义在 aop:config 元素中。

<?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:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="  
            http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans-2.5.xsd  
            http://www.springframework.org/schema/aop
            http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
    <!--目标类 -->
    <bean id="customerDao" class="com.mengma.dao.CustomerDaoImpl" />
    <!--切面类 -->
    <bean id="myAspect" class="com.mengma.aspectj.xml.MyAspect"></bean>
    <!--AOP 编程 -->
    <aop:config>
        <aop:aspect ref="myAspect">
            <!-- 配置切入点,通知最后增强哪些方法 -->
            <aop:pointcut expression="execution ( * com.mengma.dao.*.* (..))"
                id="myPointCut" />
            <!--前置通知,关联通知 Advice和切入点PointCut -->
            <aop:before method="myBefore" pointeut-ref="myPointCut" />
            <!--后置通知,在方法返回之后执行,就可以获得返回值returning 属性 -->
            <aop:after-returning method="myAfterReturning"
                pointcut-ref="myPointCut" returning="returnVal" />
            <!--环绕通知 -->
            <aop:around method="myAround" pointcut-ref="myPointCut" />
            <!--抛出通知:用于处理程序发生异常,可以接收当前方法产生的异常 -->
            <!-- *注意:如果程序没有异常,则不会执行增强 -->
            <!-- * throwing属性:用于设置通知第二个参数的名称,类型Throwable -->
            <aop:after-throwing method="myAfterThrowing"
                pointcut-ref="myPointCut" throwing="e" />
            <!--最终通知:无论程序发生任何事情,都将执行 -->
            <aop:after method="myAfter" pointcut-ref="myPointCut" />
        </aop:aspect>
    </aop:config>
</beans>

2)基于注解的声明式
在 Spring 中,尽管使用 XML 配置文件可以实现 AOP 开发,但是如果所有的相关的配置都集中在配置文件中,势必会导致 XML 配置文件过于臃肿,从而给维护和升级带来一定的困难。

为此,AspectJ 框架为 AOP 开发提供了另一种开发方式——基于 Annotation 的声明式。AspectJ 允许使用注解定义切面、切入点和增强处理,而 Spring 框架则可以识别并根据这些注解生成 AOP 代理。
在 applicationContext.xml 中开启 AOP 注解扫描。
<aop:aspectj-autoproxy proxy-target-class="true"/>

<!-- 开启注解扫描 -->
<context:component-scan base-package="com. . ."/>

import org.springframework.stereotype.Component;

@Component("admin")
public class BraveAdmin {
    public void saying(){
        System.out.println("我是(切点方法)");
    }
}

切点类:

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
/**
 * 注解方式声明aop
 * 1.用@Aspect注解将类声明为切面
 * (如果用@Component("")注解注释为一个bean对象,那么就要在spring配置文件中开启注解扫描,<context:component-scan base-package="com.."/>
 * 否则要在spring配置文件中声明一个bean对象)
 * 2.在切面需要实现相应方法的前面加上相应的注释,也就是通知类型。
 * 3.此处有环绕通知,环绕通知方法一定要有ProceedingJoinPoint类型的参数传入,然后执行对应的proceed()方法,环绕才能实现。
 */
@Component("annotationTest")
@Aspect
public class AnnotationTest {
    //定义切点
    @Pointcut("execution(* *.saying(..))")
    public void sayings(){}
   
    @Before("sayings()")
    public void sayHello(){
        System.out.println("注解类型前置通知");
    }
    //后置通知
    @After("sayings()")
    public void sayGoodbey(){
        System.out.println("注解类型后置通知");
    }
    //环绕通知。注意要有ProceedingJoinPoint参数传入。
    @Around("sayings()")
    public void sayAround(ProceedingJoinPoint pjp) throws Throwable{
        System.out.println("注解类型环绕通知..环绕前");
        pjp.proceed();//执行方法
        System.out.println("注解类型环绕通知..环绕后");
    }
}

测试类:

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {
    public static void main(String[] args) {
        ApplicationContext ac = new ClassPathXmlApplicationContext("com/xx/xx/application.xml");
        BraveAdmin br = (BraveAdmin) ac.getBean("knight");
        br.saying();
    }
}
@Component注解标注当前类是一个组件,在扫描时会被扫描进来

@Pointcut 注解为了避免相同的匹配规则被定义多处,专门定义该方法设置执行的匹配规则,各个自行调用即可
名称说明
@Aspect用于定义一个切面。
@Before用于定义前置通知,相当于 BeforeAdvice。
@AfterReturning用于定义后置通知,相当于 AfterReturningAdvice。
@Around用于定义环绕通知,相当于MethodInterceptor。
@AfterThrowing用于定义抛出通知,相当于ThrowAdvice。
@After用于定义最终final通知,不管是否异常,该通知都会执行。
@DeclareParents用于定义引介通知,相当于IntroductionInterceptor(不要求掌握)。

六、Spring 拦截器

Spring 的 HandlerMapping 处理器支持拦截器应用。当需要为某些请求提供特殊功能时,例如对用户的身份进行验证,就非常适用。

拦截器的接口
拦截器必须实现 HandlerInterceptor 接口,这个接口中含有 3 个方法:
preHandle()
处理器执行前被调用。方法返回 true 表示会继续调用其它拦截器和处理器,返回 false 表示中断流程,不会执行后续拦截器和处理器。
postHandle()
处理器执行后,视图处理前调用。此时可以通过 ModelAndView 对象对模型数据进行处理或对视图进行处理。
afterCompletion()
整个请求处理完毕后调用。如性能监控中我们可以在此记录结束时间并输出消耗时间,还可以进行一些资源清理。只有 preHandle()返回 true 时才会执行afterCompletion()方法。

拦截器的使用
自定义拦截器示例:

public class SomeInterceptor implements HandlerInterceptor {
	public boolean preHandle(HttpServletRequest req,HttpServletResponse res,
		Object handler) throws Exception {
		//处理器执行前调用
		return true;
	}
	public void postHandle(HttpServletRequest req,HttpServletResponse res,
		Object handler,ModelAndView mav) throws Exception {
		//处理器执行后调用
	}
	public void afterCompletion(HttpServletRequest req,HttpServletResponse res,
		Object handler,Exception e) throws Exception {
		//请求完成处理后调用
	}
}

自定义拦截器时,实现 HandlerInterceptor 接口需要实现接口中定义的所有方法,但是如果只需要某一个方法,可以继承 HandlerInterceptorAdapter。

自定义拦截器需要在 Spring 的 xml 配置文件配置:

<mvc:interceptors>
	<mvc:interceptor>
		<!--拦截所有的请求,必须要写在前面,也就是写在"不拦截"的请求上面-->
		<mvc:mapping path="/**"/>
		<!--排除"不拦截"的请求-->
		<mvc:exclude-mapping path="/login/*"/>
		<mvc:exclude-mapping path="/regist/*"/>
		<bean class="com.web.interceptor.SomeInterceptor"/>
		<!-- 注意:如果有多个拦截器拦截处理器的请求,则依据配置的先后顺序来执行。 -->
	</mvc:interceptor>
</mvc:interceptors>

七、Spring对JDBC Template的支持

Spring 框架针对数据库开发中的应用提供了 JDBCTemplate 类,该类是 Spring 对 JDBC 支持的核心,它提供了所有对数据库操作功能的支持。

Spring 框架提供的JDBC支持主要由四个包组成,分别是 core(核心包)、object(对象包)、dataSource(数据源包)和 support(支持包),org.springframework.jdbc.core.JdbcTemplate 类就包含在核心包中。
1)DataSource
其主要功能是获取数据库连接,具体实现时还可以引入对数据库连接的缓冲池和分布式事务的支持,它可以作为访问数据库资源的标准接口。
2)SQLExceptionTranslator
org.springframework.jdbc.support.SQLExceptionTranslator 接口负责对 SQLException 进行转译工作。通过必要的设置或者获取 SQLExceptionTranslator 中的方法,可以使 JdbcTemplate 在需要处理 SQLException 时,委托 SQLExceptionTranslator 的实现类完成相关的转译工作。

JdbcOperations 接口定义了在 JdbcTemplate 类中可以使用的操作集合,包括添加、修改、查询和删除等操作。

<?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 id="dataSource" class="org.springframework.jdbc.dataSource.DriverManagerDataSource">
        <!--数据库驱动-->
        <property name="driverClassName" value="com.mysql.jdbc.Driver" /> 
        <!--连接数据库的url-->
        <property name= "url" value="jdbc:mysql://localhost/spring" />
        <!--连接数据库的用户名-->
        <property name="username" value="root" />
        <!--连接数据库的密码-->
        <property name="password" value="root" />
    </bean>
    <!--配置JDBC模板-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.jdbcTemplate">
        <!--默认必须使用数据源-->
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <!--配置注入类-->
    <bean id="xxx" class="xxx">
        <property name="jdbcTemplate" ref="jdbcTemplate"/>
    </bean>
    ...
</beans>

八、Spring 事务

1. 事务的特性

1、脏读(Dirty read)
脏读发生在一个事务读取了被另一个事务改写但尚未提交的数据时。如果这些改变在稍后被回滚了,那么第一个事务读取的数据就会是无效的。
2、不可重复读(Nonrepeatable read)【不可重复读重点在修改。】
不可重复读发生在一个事务执行相同的查询两次或两次以上,但每次查询结果都不相同时。这通常是由于另一个并发事务在两次查询之间更新了数据。
3、幻读(Phantom reads)【幻读重点在新增或删除。】
幻读和不可重复读相似。当一个事务(T1)读取几行记录后,另一个并发事务(T2)插入了一些记录时,幻读就发生了。在后来的查询中,第一个事务(T1)就会发现一些原来没有的额外记录。

1)原子性(Atomicity)
事务是一个原子操作,由一系列动作组成。事务的原子性确保动作要么全部完成,要么完全不起作用。
2)一致性(Consistency)
一旦事务完成(不管成功还是失败),系统必须确保它所建模的业务处于一致的状态,而不会是部分完成部分失败。在现实中的数据不应该被破坏。
3)隔离性(Isolation)
可能有许多事务会同时处理相同的数据,因此每个事务都应该与其他事务隔离开来,防止数据损坏。
4)持久性(Durability)
一旦事务完成,无论发生什么系统错误,它的结果都不应该受到影响,这样就能从任何系统崩溃中恢复过来。通常情况下,事务的结果被写到持久化存储器中。

2. Spring事务的配置方式

1)编程式事务

编程式事务管理是侵入性事务管理,使用TransactionTemplate或者直接使用PlatformTransactionManager,对于编程式事务管理,Spring推荐使用TransactionTemplate。

TransactionTemplate 应用示例:

public class SimpleService implements Service {
	private final TransactionTemplate transactionTemplate;
	public SimpleService(PlatformTransactionManager tm) {
		this.transactionTemplate = new TransactionTemplate(tm);
	}
	public Object someServiceMethod() {
		return transactionTemplate.execute(new TransactionCallback(){
			public Object doInTransaction(TransactionStatus status){
				updateOperation1();
				return resultOfUpdateOperation2();
			}
		});
	} 
}

如果不需要返回值,那么可以创建一个 TransactionCallbackWithoutResult 的匿名类:

transactionTemplate.execute(
	new TransactionCallbackWithoutResult() {
		protected void doInTransactionWithoutResult(TransactionStatus status) {
			try{
				updateOperation1();
				updateOperation2();
			}catch(SomeBusinessException e){
				status.setRollbackOnly();
			}
		} 
	} 
)

2)声明式事务

声明式事务管理建立在AOP之上,其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,执行完目标方法之后根据执行的情况提交或者回滚。

Spring 的声明式事务管理是通过 Spring AOP 实现的。使用时不需要修改原有的业务代码,只需要通过简单的配置就可以追加事务控制功能。
大多数 Spring 用户选择声明式事务管理。这是对程序代码影响最小的选择,因此也最符合非侵入式的理念。

注解实现声明式事务:

<!--声明事务管理组件-->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
	<property name="datasSource" ref="ds"/>
</bean>
<!--开启事务注解扫描-->
<tx:annotation-driven transation-manager="txManager" proxy-target-class="true"/>

使用@Transactional 注解声明事务

事务的传播性:
@Transactional(propagation=Propagation.REQUIRED)

事务的隔离级别:
@Transactional(isolation = Isolation.READ_UNCOMMITTED)

只读:
@Transactional(readOnly=true)
该属性用于设置当前事务是否为只读事务,设置为true表示只读,false则表示可读写,默认值为false。

事务的超时性:
@Transactional(timeout=30)

回滚:
指定单一异常类:@Transactional(rollbackFor=RuntimeException.class)
指定多个异常类:@Transactional(rollbackFor={RuntimeException.class, Exception.class})

@Transactional 注解属性默认设置:
@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, readOnly = false)

属性说明
propagation设置事务传播
isolation设置事务隔离级别
readOnly设置为只读还是可读写
rollbackFor设置遇到哪些异常必须回滚
norollbackFor设置遇到哪些异常不回滚

XML实现声明式事务:

<!--声明事务管理组件-->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
	<property name="datasSource" ref="ds"/>
</bean>
<!--配置事务作用的范围及类型-->
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
	<tx:method name="find*" read-only="true"/>
	<tx:method name="add*" propagation="REQUIRED"/>
	<tx:method name="update*" propagation="REQUIRED"/>
	<tx:method name="delete*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<aop:config proxy-target-class="true">
	<aop:advisor advice-ref="txAdvice" pointcut="within(com.web.controller..*)"/>
</aop:config>

3.事务的传播机制

@Transactional(propagation = Propagation.REQUIRED)

属性说明
REQUIREDSpring默认的传播机制,能满足绝大部分业务需求,如果外层有事务,则当前事务加入到外层事务,一块提交,一块回滚。如果外层没有事务,新建一个事务执行
SUPPORT如果外层有事务,则加入外层事务,如果外层没有事务,则直接使用非事务方式执行。完全依赖外层的事务
REQUES_NEW该事务传播机制是每次都会新开启一个事务,同时把外层事务挂起,当当前事务执行完毕,恢复上层事务的执行。如果外层没有事务,执行当前新开启的事务即可
NOT_SUPPORT该传播机制不支持事务,如果外层存在事务则挂起,执行完当前代码,则恢复外层事务,无论是否异常都不会回滚当前的代码
NEVER该传播机制不支持外层事务,即如果外层有事务就抛出异常
MANDATORY与NEVER相反,如果外层没有事务,则抛出异常
NESTED该传播机制的特点是可以保存状态保存点,当前事务回滚到某一个点,从而避免所有的嵌套事务都回滚,即各自回滚各自的,如果子事务没有把异常吃掉,基本还是会引起全部回滚的。

4. 事务的隔离级别

@Transactional(isolation = Isolation.DEFAULT)

隔离级别含义
DEFAULT使用后端数据库默认的隔离级别
UNCOMMITTED允许读取尚未提交的更改。可能导致脏读、幻读或不可重复读。
READ_COMMITTED(Oracle 默认级别)允许从已经提交的并发事务读取。可防止脏读,但幻读和不可重复读仍可能会发生。
REPEATABLE_READ(MYSQL默认级别)对相同字段的多次读取的结果是一致的,除非数据被当前事务本身改变。可防止脏读和不可重复读,但幻读仍可能发生。
SERIALIZABLE完全服从ACID的隔离级别,确保不发生脏读、不可重复读和幻影读。这在所有隔离级别中也是最慢的,因为它通常是通过完全锁定当前事务所涉及的数据表来完成的。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Spring--开源的轻量级的Java开发框架 的相关文章

随机推荐

  • 第十章 Vxworks嵌入式软件

    1 VxWork2概述和Vxworks特点 最优秀的特点 xff1a 可靠性和实时性 占有率排名第一 应用领域 xff1a 通信 xff0c 军事 xff0c 航空 xff0c 航天 向后兼容 xff0c 兼容5 X 存储保护技术 基于标准
  • Kotlin:如何在PreferenceFragment中自定义视图

    前几天项目开发中需要用kotlin做一个类似Android原生settings的页面 xff0c 只不过ui相较preference自带的控件有些变化 xff0c 特别是ListPreference的数据更新问题 xff0c 困扰了好久 x
  • Codewars 刷题笔记(Python)3.Friend or Foe

    题目 Make a program that filters a list of strings and returns a list with only your friends name in it If a name has exac
  • Xshell调用图形化界面-(用于图像化安装Oracle)

    前言 xff1a 想要安装Oracle数据库 xff0c 但是Xshell无法调起图形化界面 先使用测试工具xclock进行测试 xff0c 提示命令找不到 xff1a root 64 iZm5efqp4uqpnio9zmnp5rZ xcl
  • openEuler 22.03 KVM虚拟机桥接联网

    最近需要将一个系统移植到欧拉上运行 xff0c 实现思路就是在欧拉上搭建KVM平台 xff0c 再在KVM上运行我们的系统 xff0c 让电脑主机 xff08 Windows xff09 可以访问该系统 其间遇到过一些问题 xff0c 但是
  • 非分区表与分区表相互转换

    1 非分区表转分区表 1 普通表 table T1 OBJECT ID NUMBER not null OBJECT NAME VARCHAR2 128 OWNER VARCHAR2 30 STATUS VARCHAR2 7 分区表 cre
  • 【软工】程序编码

    目录 前言正文 程序设计语言 分类 选择原则 程序编码总原则 好程序的标准 结构化程序设计 主要内容 主要原则 程序设计风格 源程序文档化 数据说明 语句结构 输入输出方法 程序设计质量评价 正确性结构清晰性易修改性 易读性 简单性 程序复
  • TP-LINK TL-WDN6200 USB无线网卡驱动程序安装方法

    前一阵在某东入手一个TP LINK的USB无线网卡 xff08 完整型号 xff1a TP LINK TL WDN6200 1200M千兆高速双频无线网卡USB 台式机笔记本随身wifi接收器 xff09 xff0c 由于装了win10 4
  • Windows Server 2019 免密登录

    Windows Server 2019 免密登录
  • CentOS 7 为firewalld添加开放端口及相关资料

    目录 一 运行 停止 禁用firewalld 1 1启动 xff1a 1 2 查看状态 xff1a 1 3 停止 xff1a 1 4禁用 xff1a 二 查看firewall是否运行 下面两个命令都可以 三 查看default zone和a
  • MicroStrategy的面经(from bbs.byr..

    搜了一下论坛 xff0c 发现基本上没有MicroStrategy的面经 xff0c 既然有幸参加了他们的面试 xff0c 这里把面试过程给大家描述一下 xff0c 希望对后来的人有点帮助吧 1 公司介绍 xff1a 中文叫凌策软件 xff
  • 程序员迷茫:30岁以上的“大龄程码农”出路在哪?java码农该怎么办?

    程序员生存 成功 制胜的法则源自IT精英的职业发展秘诀热爱工作 xff0c 享受生活 为什么程序员过了30就不行了 xff1f 我们被固定在 敲代码 的坑里 xff0c 一干就是10年 xff0c 再干别的早已不会 敲代码已经成了一项流水线
  • Ubuntu添加和设置开机自动启动程序的方法

    ubuntu 18 04 设置开机启动脚本 ubuntu 18 04 设置开机启动脚本 参阅下列链接 https askubuntu com questions 886620 how can i execute command on sta
  • Codewars 刷题笔记(Python)5.Disemvowel Trolls

    题目 Trolls are attacking your comment section A common way to deal with this situation is to remove all of the vowels fro
  • 华为笔试题(4)

    一 计算n x m的棋盘格子 xff08 n为横向的格子数 xff0c m为竖向的格子数 xff09 沿着各自边缘线从左上角走到右下角 xff0c 总共有多少种走法 xff0c 要求不能走回头路 xff0c 即 xff1a 只能往右和往下走
  • ubuntu 16.04 LTS + xgboost 0.7 + GPU support

    记录下安装xgboost 踩过的坑 xff0c 也是为了日后万一再需要 xff0c 可以翻看 cpu版的安装倒是很容易的 xff1a sudo pip install xgboost gpu版 xff1a 为了避免import的时候把旧版的
  • mariaDB JSON函数

    官方文档 xff1a https mariadb com kb en json search
  • #最详细# Github Page 个人博客绑定二级域名

    文章目录 1 必要条件 xff1a 2 操作步骤 xff1a 3 操作3 1 在阿里云控制台添加子域名解析记录3 2 在 Github 中修改配置 1 必要条件 xff1a 已申请个人域名已配置好Github Page 2 操作步骤 xff
  • springboot日志输出到文件

    今天来谈一谈日志 xff0c 主要是说一说springboot的日志 xff0c 因为最近在学习springboot 首先在写代码的时候 xff0c 要养成记日志的习惯 xff0c 这点真的很重要 xff0c 因为之前吃了很多亏 过去我对日
  • Spring--开源的轻量级的Java开发框架

    目录 xff1a 一 Spring 简介1 什么是Spring2 Spring 框架的优点3 Spring 体系结构 二 Spring 容器1 什么是Spring容器2 Spring 容器的实例化3 Spring 容器的使用 三 Sprin