spring框架(spring框架介绍)

发布时间:2025-12-11 03:11:30 浏览次数:1

目录
  • Spring 框架概述

  • Spring优点

  • Spring体系结构

  • Spring拓展

    • Spring Boot与Spring Cloud

  • Spring IoC 容器 (IoC 也称为依赖项注入(DI),或DI是实现IoC的一种方法)

    • IoC容器概述

  • Spring入门程序

    • IoC创建对象的三种方式

      • 通过无参构造(要提供set方法)

    • 通过有参构造(要提供get方法)

      • 通过工厂类

        • Spring依赖注入(DI)和Bean的作用域

          • Spring 常用配置及属性

          • Spring自动装配

        • Spring注解开发

          • 环境搭建

          • 使用@ComponentScan自动扫描组件并指定扫描规则

            • 使用@Scope注解设置组件的作用域

            • 注解自动装配组件(@Resource是JDK自带的)

            • 懒加载@Lazy

            • 使用@Import注解给容器中快速导入一个组件

            • Bean生命周期

            • @Value注解为属性赋值

            • 使用@PropertySource加载配置文件

            • 代理模式

            • 静态代理

          • Spring AOP AOP

            • AOP

            • Spring AOP的实现(3种)

            • 通过 Spring API 实现

        • Spring事务管理及Spring整合MyBatis代码示例

          • Spring事务管理

            • Spring结合事务整合MyBatis示例

            • 总结

              Spring 框架概述

              • Spring 使创建 Java 企业应用程序变得容易。它提供了在企业环境中使用 Java 语言所需的一切,并支持 Groovy 和 Kotlin 作为 JVM 上的替代语言,并且可以根据应用程序的需求灵活地创建多种体系结构。从 Spring Framework 5.0 开始,Spring 需要 JDK 8(Java SE 8),并且已经为 JDK 9 提供了现成的支持。

              • Spring 是分层的 Java SE/EE full-stack 轻量级开源框架,以 IoC(Inverse of Control,控制反转)和 AOP(Aspect Oriented Programming,面向切面编程)为内核,使用基本的 JavaBean 完成以前只可能由 EJB 完成的工作,取代了 EJB 臃肿和低效的开发模式。

              • Spring 是开源的。它拥有一个庞大而活跃的社区,可以根据各种实际用例提供持续的反馈。这帮助 Spring 在很长一段时间内成功地 Developing 了。

              Spring优点

              • 方便解耦,简化开发

              • Spring 就是一个大工厂,可以将所有对象的创建和依赖关系的维护交给 Spring 管理。

              • 方便集成各大优秀框架

              • Spring 不排斥各种优秀的开源框架,其内部提供了对各种优秀框架(如 Struts2、Hibernate、MyBatis 等)的直接支持。

              • 方便程序的测试

              • Spring 支持 JUnit4,可以通过注解方便地测试 Spring 程序。

              • AOP 编程的支持

              • Spring 提供面向切面编程,可以方便地实现对程序进行权限拦截和运行监控等功能。

              • 声明式事务的支持

              • 只需要通过配置就可以完成对事务的管理,而无须手动编程。

              Spring体系结构

              Spring 框架采用分层架构,根据不同的功能被划分成了多个模块,这些模块大体可分为 Data Access/Integration、Web、AOP、Aspects、Messaging、Instrumentation、Core Container 和 Test,具体如下图所示:

              Data Access/Integration(数据访问/集成)

              数据访问/集成层包括 JDBC、ORM、OXM、JMS 和 Transactions 模块,具体介绍如下。

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

              Web 模块

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

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

              Core Container(核心容器)

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

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

              其他模块

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

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

              Spring拓展

              Spring Boot与Spring Cloud

              • Spring Boot 是 Spring 的一套快速配置脚手架,可以基于Spring Boot 快速开发单个微服务。

              • Spring Cloud是基于Spring Boot实现的。

              • Spring Boot专注于快速、方便集成的单个微服务个体,Spring Cloud关注全局的服务治理框架。

              • Spring Boot使用了约束优于配置的理念,很多集成方案已经帮你选择好了,能不配置就不配置 , Spring Cloud很大的一部分是基于Spring Boot来实现,Spring Boot可以离开Spring Cloud独立使用开发项目,但是Spring Cloud离不开Spring Boot,属于依赖的关系。

              • SpringBoot在SpringClound中起到了承上启下的作用,如果你要学习SpringCloud必须要学习SpringBoot。

              Spring IoC 容器 (IoC 也称为依赖项注入(DI),或DI是实现IoC的一种方法)

              IoC容器概述

              • 控制反转是一种通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式。在Spring中实现控制反转的是IoC容器,其实现方法是依赖注入。

              • Spring容器在初始化时先读取配置文件,根据配置文件或元数据创建与组织对象存入容器中,程序使用时再从Ioc容器中取出需要的对象。

              • Spring 提供了两种 IoC 容器,分别为 BeanFactory 和 ApplicationContext。

              1.BeanFactory

              beanFactory是一个Factory,用于管理bean的,有了一个Spring的beanFactory,我们就可以从spring中获取注册到其中的bean来使用。

              2.ApplicationContext

              ApplicationContext 是 BeanFactory 的子接口,也被称为应用上下文。该接口的全路径为:

              org.springframework.context.ApplicationContext,它不仅提供了 BeanFactory 的所有功能,还添加了对 i18n(国际化)、资源访问、事件传播等方面的良好支持。

              ApplicationContext 接口有两个常用的实现类:ClassPathXmlApplicationContext和FileSystemXmlApplicationContext。 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 不再从类路径中读取配置文件,而是通过参数指定配置文件的位置,它可以获取类路径之外的资源,如“D:/workspaces/applicationContext.xml”。

              3.BeanFactory 和 ApplicationContext区别:

              BeanFactory在初始化容器时,并未实例化Bean,直到第一次访问某个Bean 时才实例目标Bean;而ApplicationContext 则在初始化应用上下文时就实例化所有单实例的Bean 。

              在实际开发中,通常都选择使用 ApplicationContext,而只有在系统资源较少时,才考虑使用 BeanFactory。(但是,它们都是通过 XML 配置文件加载 Bean 的。)

              Spring入门程序

              1.创建maven项目

              2.在pom.xml导入jar包依赖

              <dependencies><!--导入spring,maven依赖--><dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId><version>5.2.12.RELEASE</version></dependency><!--导入junit--><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version></dependency></dependencies>

              3.编写接口

              packagecom.xxx.mapper;/***@authorshkstart*@create2021-06-1115:50*/publicinterfaceUserMapper{publicvoidhello();}

              4.编写接口实现类

              packagecom.xxx.mapper;/***@authorshkstart*@create2021-06-1115:50*//***@program:springTest*@description:*@author:XieXianXin*@create:2021-06-1115:50*/publicclassUserMapperImplimplementsUserMapper{@Overridepublicvoidhello(){System.out.println("Spring入门程序!");}}

              编写Spring核心配置文件applicationContext.xml

              <?xmlversion="1.0"encoding="UTF-8"?><beansxmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><!--使用Spring来创建对象,在Spring这些都称为Bean类型变量名=new类型();Hellohello=newHello();id=变量名class=new的对象--><beans><beanid="hello"class="com.xxx.mapper.UserMapperImpl"></bean></beans></beans>

              测试

              packagecom.xxx.mapper;/***@authorshkstart*@create2021-06-1115:57*/importorg.junit.Test;importorg.springframework.context.ApplicationContext;importorg.springframework.context.support.ClassPathXmlApplicationContext;/***@program:springTest*@description:*@author:XieXianXin*@create:2021-06-1115:57*/publicclasshelloTest{@TestpublicvoidhelloTest1(){//1.初始化Spring容器,加载配置文件ApplicationContextcontext=newClassPathXmlApplicationContext("applicationContext.xml");//2.通过容器获取userMapper实例UserMapperhello=context.getBean("hello",UserMapper.class);//3.调用实例中的hello()方法hello.hello();}}

              测试结果

              IoC创建对象的三种方式

              通过无参构造(要提供set方法)

              编写实体类User:

              publicclassUser{privateStringname;//set方法publicvoidsetName(Stringname){this.name=name;}publicUser(){System.out.println("无参构造方法执行了!");}publicvoidprint(){System.out.println("学生名字为:"+name);}}

              编写Spring核心配置文件:

              <!--无参构造,但是要有set方法--><beanid="user"class="com.xxx.pojo.User"><propertyname="name"value="小新"/></bean>

              测试以及结果:

              @TestpublicvoidhelloTest2(){//1.初始化Spring容器,加载配置文件ApplicationContextcontext=newClassPathXmlApplicationContext("applicationContext.xml");//2.通过容器获取userMapper实例Useruser=context.getBean("user",User.class);//3.调用实例中的print()方法user.print();}

              通过有参构造(要提供get方法)

              编写实体类User:

              publicclassUser{privateStringname;//get方法publicStringgetName(){returnname;}publicUser(Stringname){System.out.println("有参构造方法执行了!");this.name=name;}publicvoidprint(){System.out.println("学生名字为:"+name);}}

              编写Spring核心配置文件:

              <!--有参构造,但是要有get方法--><beanid="user"class="com.xxx.pojo.User"><constructor-argvalue="小新2"index="0"/></bean>

              测试以及结果:

              @TestpublicvoidhelloTest2(){//1.初始化Spring容器,加载配置文件ApplicationContextcontext=newClassPathXmlApplicationContext("applicationContext.xml");//2.通过容器获取userMapper实例Useruser=context.getBean("user",User.class);//3.调用实例中的print()方法user.print();}

              拓展:Spring核心配置文件有三种写法:

              <!--有参构造,但是要有get方法--><beanid="user"class="com.xxx.pojo.User"><constructor-argindex="0"value="小新-index属性(0开始,按顺序)"/><constructor-argname="name"value="小新-name属性"/><constructor-argtype="java.lang.String"value="小新-参数类型"/></bean>

              结果展示:

              通过工厂类

              编写工厂类:

              publicclassFactory{//方法一,静态方法publicstaticUsergetStaticInstance(){returnnewUser("小新2——静态方法创建对象");}//方法二,实例方法publicUsergetInstance(){returnnewUser("小新3-实例方法创建对象");}}

              编写Spring核心配置文件:

              <!--工厂类创建对象--><!--创建工厂--><beanid="factory"class="com.xxx.mapper.Factory"/><!--静态方法对象--><beanid="staticFactory-user"class="com.xxx.mapper.Factory"factory-method="getStaticInstance"/><!--实例方法对象--><beanid="factory-user"factory-bean="factory"factory-method="getInstance"/>

              测试以及结果:静态方法:

              @TestpublicvoidhelloTest4(){//1.初始化Spring容器,加载配置文件ApplicationContextcontext=newClassPathXmlApplicationContext("applicationContext.xml");//2.通过容器获取userMapper实例Useruser=context.getBean("staticFactory-user",User.class);//3.调用实例中的print()方法user.print();}

              实例方法:

              @TestpublicvoidhelloTest3(){//1.初始化Spring容器,加载配置文件ApplicationContextcontext=newClassPathXmlApplicationContext("applicationContext.xml");//2.通过容器获取userMapper实例Useruser=context.getBean("factory-user",User.class);//3.调用实例中的print()方法user.print();}

              Spring依赖注入(DI)和Bean的作用域

              什么是依赖注入:Spring 容器在创建被调用者的实例时,会自动将调用者需要的对象实例注入给调用者,这样,调用者通过 Spring 容器获得被调用者实例。

              依赖注入主要有两种实现方式,分别是属性 setter 注入和构造方法注入,其中setter注入要求重点掌握。

              • 属性 setter 注入(重点展开讲解)

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

              • 构造方法注入

              • 指 IoC 容器使用构造方法注入被依赖的实例。基于构造器的 DI 通过调用带参数的构造方法实现,每个参数代表一个依赖。

              属性 setter 注入讲解:

              环境搭建:(创建一个Student和Book类):

              Student

              packagecom.xxx.pojo;/***@authorshkstart*@create2021-06-1117:45*/importjava.util.*;/***@program:Spring_study*@description:*@author:XieXianXin*@create:2021-06-1117:45*/publicclassStudent{privateStringname;privateBookbook;privateString[]course;privateList<String>hobbies;privateMap<String,String>card;privateSet<String>fruit;privateStringmarriage;privatePropertiesinfo;publicStudent(){}publicStudent(Stringname,Bookbook,String[]course,List<String>hobbies,Map<String,String>card,Set<String>fruit,Stringmarriage,Propertiesinfo){this.name=name;this.book=book;this.course=course;this.hobbies=hobbies;this.card=card;this.fruit=fruit;this.marriage=marriage;this.info=info;}@OverridepublicStringtoString(){return"Student{"+"name='"+name+'\''+",book="+book+",course="+Arrays.toString(course)+",hobbies="+hobbies+",card="+card+",fruit="+fruit+",marriage='"+marriage+'\''+",info="+info+'}';}publicStringgetName(){returnname;}publicvoidsetName(Stringname){this.name=name;}publicBookgetBook(){returnbook;}publicvoidsetBook(Bookbook){this.book=book;}publicString[]getCourse(){returncourse;}publicvoidsetCourse(String[]course){this.course=course;}publicList<String>getHobbies(){returnhobbies;}publicvoidsetHobbies(List<String>hobbies){this.hobbies=hobbies;}publicMap<String,String>getCard(){returncard;}publicvoidsetCard(Map<String,String>card){this.card=card;}publicSet<String>getFruit(){returnfruit;}publicvoidsetFruit(Set<String>fruit){this.fruit=fruit;}publicStringgetMarriage(){returnmarriage;}publicvoidsetMarriage(Stringmarriage){this.marriage=marriage;}publicPropertiesgetInfo(){returninfo;}publicvoidsetInfo(Propertiesinfo){this.info=info;}}

              Book

              packagecom.xxx.pojo;/***@authorshkstart*@create2021-06-1117:45*//***@program:Spring_study*@description:*@author:XieXianXin*@create:2021-06-1117:45*/publicclassBook{privateStringname;privateintid;publicBook(){}@OverridepublicStringtoString(){return"Book{"+"name='"+name+'\''+",id="+id+'}';}publicStringgetName(){returnname;}publicvoidsetName(Stringname){this.name=name;}publicintgetId(){returnid;}publicvoidsetId(intid){this.id=id;}publicBook(Stringname,intid){this.name=name;this.id=id;}}

              常量注入:

              <beanclass="com.xxx.pojo.Student"id="student"><!--常量注入--><propertyname="name"value="小新"/></bean>

              Bean注入:

              <beanclass="com.xxx.pojo.Book"id="book"><propertyname="name"value="Java放弃"/><propertyname="id"value="100"/></bean><beanclass="com.xxx.pojo.Student"id="student"><!--Bean注入--><propertyname="book"ref="book"/></bean>

              数组注入:

              <propertyname="course"><array><value>高数</value><value>计算机网络</value><value>数据库</value></array></property>

              List注入:

              <propertyname="hobbies"><list><value>唱</value><value>跳</value><value>Rap</value></list></property>

              Map注入:

              <propertyname="card"><map><entrykey="银行卡:"value="2501314"/><entrykey="身份证:"value="1314520"/></map></property>

              Set注入:

              <propertyname="fruit"><set><value>香蕉</value><value>苹果</value><value>雪梨</value></set></property>

              Null注入:

              <propertyname="marriage"><null/></property>

              Properties注入:

              <propertyname="info"><props><propkey="username">小新</prop><propkey="password">520</prop></props></property>

              测试及结果展示:

              publicclassBeanTest{@TestpublicvoidbeanTest(){ApplicationContextcontext=newClassPathXmlApplicationContext("applicationContext.xml");Studentstudent=context.getBean("student",Student.class);System.out.println(student);}}

              Student{name=‘小新', book=Book{name=‘Java放弃', id=100}, course=[高数, 计算机网络, 数据库], hobbies=[唱, 跳, Rap], card={银行卡:=2501314, 身份证:=1314520}, fruit=[香蕉, 苹果, 雪梨], marriage=‘null', info={password=520, username=小新}}

              Process finished with exit code 0

              p命名空间(以Book类举例)导入约束 xmlns:p=“http://www.springframework.org/schema/p”

              <beanid="pBook"class="com.xxx.pojo.Book"p:name="Java懵懂"p:id="250"/>

              测试及结果:

              @TestpublicvoidcpTest(){ApplicationContextcontext=newClassPathXmlApplicationContext("applicationContext.xml");BookpBook=context.getBean("pBook",Book.class);System.out.println(pBook);}

              c命名空间导入约束 xmlns:c=“http://www.springframework.org/schema/c”

              <beanid="cBook"class="com.xxx.pojo.Book"c:id="520"c:name="Java入坑"/>

              测试及结果:

              @TestpublicvoidcpTest(){ApplicationContextcontext=newClassPathXmlApplicationContext("applicationContext.xml");BookcBook=context.getBean("cBook",Book.class);System.out.println(cBook);}

              作用域种类

              singleton(以Book举例)单例模式,使用 singleton 定义的 Bean 在 Spring 容器中只有一个实例,这也是 Bean 默认的作用域。

              <beanclass="com.xxx.pojo.Book"id="scopeBook"scope="singleton"><propertyname="id"value="1"/></bean>
              @TestpublicvoidscopeTest(){ApplicationContextcontext=newClassPathXmlApplicationContext("applicationContext.xml");Bookbook1=context.getBean("scopeBook",Book.class);Bookbook2=context.getBean("scopeBook",Book.class);System.out.println(book1.hashCode());System.out.println(book2.hashCode());System.out.println(book1==book2);}}

              prototype 原型模式,每次通过 Spring 容器获取 prototype 定义的 Bean 时,容器都将创建一个新的 Bean 实例,即每次调用getBean()时,相当于执行了一次new XxxBean()。

              <beanclass="com.xxx.pojo.Book"id="scopeBook"scope="prototype"><propertyname="id"value="1"/></bean>
              @TestpublicvoidscopeTest(){ApplicationContextcontext=newClassPathXmlApplicationContext("applicationContext.xml");Bookbook1=context.getBean("scopeBook",Book.class);Bookbook2=context.getBean("scopeBook",Book.class);System.out.println(book1.hashCode());System.out.println(book2.hashCode());System.out.println(book1==book2);}}
              • request

              在一次 HTTP 请求中,容器会返回该 Bean 的同一个实例。而对不同的 HTTP 请求,会返回不同的实例,该作用域仅在当前 HTTP Request 内有效。

              • session

              在同一个 HTTP Session 中,容器会返回该 Bean 的同一个实例。而对不同的 HTTP 请求,会返回不同的实例,该作用域仅在当前 HTTP Session 内有效。

              • global Session

              在一个全局的 HTTP Session 中,容器会返回该 Bean 的同一个实例。该作用域仅在使用 portlet context 时有效。

              Spring 常用配置及属性

              Spring自动装配

              • Bean 的装配可以理解为依赖关系注入,Bean 的装配方式也就是 Bean 的依赖注入方式。Spring 容器支持多种形式的 Bean的装配方式,如基于 XML 的 Bean 装配、基于Annotation 的 Bean 装配和自动装配等。之前的举例是通过XML的Bean装配的。接下来讲解自动装配。

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

              • Spring的自动装配需要从两个角度来实现:

              1.组件扫描(component scanning):spring会自动发现应用上下文中所创建的bean;

              2.自动装配(autowiring):spring自动满足bean之间的依赖,也就是我们说的IoC/DI;

              autowire 的属性和作用

              • 环境搭建:(分别创建一个Student和Student2类,再创建一个Teacher类)

              publicclassStudent{publicvoidstudy(){System.out.println("Student类的方法study执行了");}}
              publicclassStudent2{publicvoidstudy(){System.out.println("Student2类的方法study执行了");}}
              publicclassTeacher{privateStudentstudent;privateStudent2student2;privateStringteach;publicTeacher(){}@OverridepublicStringtoString(){return"Teacher{"+"student="+student+",student2="+student2+",teach='"+teach+'\''+'}';}publicStudentgetStudent(){returnstudent;}publicvoidsetStudent(Studentstudent){this.student=student;}publicStudent2getStudent2(){returnstudent2;}publicvoidsetStudent2(Student2student2){this.student2=student2;}publicStringgetTeach(){returnteach;}publicvoidsetTeach(Stringteach){this.teach=teach;}publicTeacher(Studentstudent,Student2student2,Stringteach){this.student=student;this.student2=student2;this.teach=teach;}}

              配置Spring核心配置文件

              使用autowire=“byName”:

              <beanclass="com.xxx.pojo.Student"id="student"/><beanclass="com.xxx.pojo.Student"id="student"/><beanclass="com.xxx.pojo.Student2"id="student2"/><beanclass="com.xxx.pojo.Teacher"id="teacher"autowire="byName"><propertyname="teach"value="Java"/></bean>

              测试及结果:

              publicclassBeanTest{@TestpublicvoidbeanTest(){ApplicationContextcontext=newClassPathXmlApplicationContext("applicationContext.xml");Teacherteacher=context.getBean("teacher",Teacher.class);teacher.getStudent().study();teacher.getStudent2().study();}}

              若修改Student的bean id值不为student,如:<bean class="com.xxx.pojo.Student" id="s"/>则会报空指针异常java.lang.NullPointerException at BeanTest.beanTest(BeanTest.java:24)。因为按byName规则找不对应set方法,真正的setStudent就没执行,对象就没有初始化,所以调用时就会报空指针错误。

              当一个bean节点带有 autowire byName的属性时:

              1.将查找其类中所有的set方法名,例如setStudent,获得将set去掉并且首字母小写的字符串,即student。

              2.去spring容器中寻找是否有此字符串名称id的对象,如果有,就取出注入;如果没有,就报空指针异常。

              Spring注解开发

              环境搭建

              1.在spring配置文件中引入context文件头

              xmlns:context="http://www.springframework.org/schema/context"http://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd

              开启属性注解支持!

              <context:annotation-config/>

              编写一个 Student类

              publicclassStudent{privateStringname;@OverridepublicStringtoString(){return"Student{"+"name='"+name+'\''+'}';}publicStringgetName(){returnname;}publicvoidsetName(Stringname){this.name=name;}publicStudent(){}publicStudent(Stringname){this.name=name;}}

              编写Spring核心配置文件:

              <beanclass="com.xxx.pojo.Student"id="student"><propertyname="name"value="小新"/></bean>

              测试及结果:

              @TestpublicvoidbeanTest(){ApplicationContextcontext=newClassPathXmlApplicationContext("applicationContext.xml");Studentstudent=context.getBean("student",Student.class);System.out.println(student);

              使用@Configuration和@Bean给容器中注册组件 编写一个配置类

              /***@program:springTest*@description:在类上添加@Configuration注解使得该类成为Spring配置类,通过@Bean注解将该类注入到IoC容器,此时配置类==配置文件*@author:XieXianXin*@create:2021-06-1223:06*///这个配置类也是一个组件@Configuration//告诉Spring这是一个配置类publicclassAnnotationStudent{@Bean//@Bean注解是给IOC容器中注册一个bean,id默认是用方法名作为idpublicStudentstudent(){returnnewStudent("小新");}}

              测试及结果:

              @TestpublicvoidbeanTest(){AnnotationConfigApplicationContextcontext=newAnnotationConfigApplicationContext(AnnotationStudent.class);Studentbean=context.getBean(Student.class);//返回Student类在IoC容器中的id值String[]namesForType=context.getBeanNamesForType(Student.class);for(Strings:namesForType){System.out.println(s);}System.out.println(bean);}}

              若在配置类中给@Bean设置一个value值,如@Bean("stu")则测试结果为:

              则我们在使用注解方式向Spring的IOC容器中注入JavaBean时,如果没有在@Bean注解中明确指定bean的名称,那么就会使用当前方法的名称来作为bean的名称;如果在@Bean注解中明确指定了bean的名称,那么就会使用@Bean注解中指定的名称来作为bean的名称。

              使用@ComponentScan自动扫描组件并指定扫描规则

              开启注解扫描,并删除之前配置文件中的bean

              <context:component-scanbase-package="com.xxx"/>

              在原有环境下创建一个com.xxx.service包,并创建一个Teacher类,并在类上添加一个@Service注解,同时,之前的Student类上也添加一个@Component注解

              @ServicepublicclassTeacher{privateStudentstudent;publicvoidteach(){System.out.println("教授的学生是"+student);}@OverridepublicStringtoString(){return"Teacher{"+"student="+student+'}';}publicStudentgetStudent(){returnstudent;}publicvoidsetStudent(Studentstudent){this.student=student;}publicTeacher(Studentstudent){this.student=student;}publicTeacher(){}}

              测试及结果:

              publicclassBeanTest{@TestpublicvoidbeanTest(){ApplicationContextcontext=newClassPathXmlApplicationContext("applicationContext.xml");String[]beanDefinitionNames=context.getBeanDefinitionNames();for(StringdefinitionName:beanDefinitionNames){System.out.println(definitionName);}}}
              • 使用注解配置XML包扫描

              我们可以在配置类中(前面的AnnotationStudent)使用@ComponentScan注解配置包扫描,由此代替xml中的<context:component-scan base-package="com.xxx"/>。先注释掉之前的xml方式的注解扫描,接着

              @Configuration//告诉Spring这是一个配置类@ComponentScan(value="com.xxx")publicclassAnnotationStudent{@Bean//@Bean注解是给IOC容器中注册一个bean,id默认是用方法名作为idpublicStudentstudent(){returnnewStudent("小新");}}

              测试结果跟之前一样。因此,推荐以后都使用注解扫描就好了,Spring还是尽量用注解开发,MyBatis中还是用xml配置文件。

              • ComponentScan方法使用

              excludeFilters()不包含哪些包、includeFilters()包含哪些包,使用includeFilters时,需要在XML配置文件中先配置use-default-filters="false",即禁用默认的扫描所有包过滤规则才能生效。另外,ComponentScan还是一个可重复注解的注解,因此可以在一个类上重复使用这个注解。

              使用@Scope注解设置组件的作用域

              通过在类中添加注解@scope注解设置作用域,如:

              //这个配置类也是一个组件@Configuration//告诉Spring这是一个配置类publicclassAnnotationStudent{@Scope("prototype")@Bean//@Bean注解是给IOC容器中注册一个bean,id默认是用方法名作为idpublicStudentstudent(){returnnewStudent("小新");}}
              • 结果:

              如果为false。

              • @Scope注解中的取值如下所示:

              注解自动装配组件(@Resource是JDK自带的)
              • @Autowired

              @Autowired注解可以对类成员变量、方法和构造函数进行标注,完成自动装配的工作。@Autowired注解可以放在类、接口以及方法上。等价于<property name="属性名" value=" 属性值"/>@Autowired注解默认是优先按照类型去容器中找对应的组件,即:context.getBean(类名.class);,如果找到多个相同类型的组件,那么是将属性名称作为组件的id,到IOC容器中进行查找,即:context.getBean("组件的id");

              • @Qualifier

              @Autowired是根据类型自动装配的,加上@Qualifier则可以根据byName的方式自动装配,且Qualifier不能单独使用。

              • @Resource

              是JDK自带的注解 可以按名称注入也可以按类型注入,默认是按名称注入,没有显式指定名称时,在spring容器中匹配与需要注入的bean属性名相同的bean,如果还不同,@Resource会找到一个主类型匹配而不是一个特定的命名bean。

              懒加载@Lazy

              懒加载就是Spring容器启动的时候,先不创建对象,在第一次使用(获取)bean的时候Xxx xxx = context.getBean(Xxx.class);再来创建对象,并进行一些初始化。使用时,只需要在配置类的方法上加上@Lazy注解即可。

              publicclassAnnotationStudent{@Lazy@Bean//@Bean注解是给IOC容器中注册一个bean,id默认是用方法名作为idpublicStudentstudent(){System.out.println("在容器中添加对象!");returnnewStudent("小新");}}
              publicclassBeanTest{@TestpublicvoidbeanTest(){ApplicationContextcontext=newClassPathXmlApplicationContext("applicationContext.xml");System.out.println("容器创建完成!");Studentstudent=context.getBean(Student.class);Studentstudent1=context.getBean(Student.class);System.out.println(student==student1);}}
              • 非懒加载模式(默认情况):bean在Spring容器启动的时候ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");就会被创建,并且还加载到Spring容器中去了。

              @Configuration//告诉Spring这是一个配置类publicclassAnnotationStudent{@Bean//@Bean注解是给IOC容器中注册一个bean,id默认是用方法名作为idpublicStudentstudent(){System.out.println("在容器中添加对象!");returnnewStudent("小新");}}
              publicclassBeanTest{@TestpublicvoidbeanTest(){ApplicationContextcontext=newClassPathXmlApplicationContext("applicationContext.xml");System.out.println("容器创建完成!");}}
              使用@Import注解给容器中快速导入一个组件

              注册bean的方式通常有以下几种:

              1.包扫描+给组件标注注解(@Controller、@Servcie、@Repository、@Component

              2.@Bean注解

              3.@Import注解(只作用在类上,可以在实际开发项目中导入别人的类并注册到容器中,这是两外两种无法做到的)例如在AnnotationStudent配置类上导入Teacher类对应的bean实例(id默认是组件的全类名)

              4.使用FactoryBean接口(支持泛式)向Spring容器中注册bean

              //这个配置类也是一个组件@Configuration//告诉Spring这是一个配置类@Import(Teacher.class)publicclassAnnotationStudent{@Bean//@Bean注解是给IOC容器中注册一个bean,id默认是用方法名作为idpublicStudentstudent(){returnnewStudent("小新");}}
              publicclassBeanTest{@TestpublicvoidbeanTest(){/*ApplicationContextcontext=newClassPathXmlApplicationContext("applicationContext.xml");*/AnnotationConfigApplicationContextapplicationContext=newAnnotationConfigApplicationContext(AnnotationStudent.class);String[]beanNamesForType=applicationContext.getBeanDefinitionNames();for(Strings:beanNamesForType){System.out.println(s);}}}

              当去除@Import后,输出结果为:

              Bean生命周期

              常意义上讲的bean的生命周期,指的是bean从创建到初始化,经过一系列的流程,最终销毁的过程,如下图所示。在Spring中,我们可以自己来指定bean的初始化和销毁的方法@Bean(initMethod = "自定义的初始化方法名",destroyMethod = "自定义的销毁方法名")。当容器在bean进行到当前生命周期的阶段时,会自动调用我们自定义的初始化和销毁方法。

              自定义一个Life类:

              publicclassLife{publicLife(){System.out.println("Life构造方法执行了!");}publicvoidinit(){System.out.println("Life初始化方法执行了!");}publicvoiddestroy(){System.out.println("Life销毁方法执行了!");}}

              配置类中注册bean:

              @Configuration//告诉Spring这是一个配置类publicclassAnnotationStudent{@Bean(initMethod="init",destroyMethod="destroy")publicLifelife(){returnnewLife();}}

              测试及结果:

              publicclassBeanTest{@TestpublicvoidbeanTest(){AnnotationConfigApplicationContextapplicationContext=newAnnotationConfigApplicationContext(AnnotationStudent.class);System.out.println("容器创建完成!");Lifebean=applicationContext.getBean(Life.class);}}

              可以看到,对于单实例对象,先执行构造方法,再到初始化方法,而销毁方法执行需要显式关闭容器时候才执行applicationContext.close();

              因此,我们可以自定义初始化方法和销毁方法处理配置数据源问题,在初始化的时候,会对很多的数据源的属性进行赋值操作;在销毁的时候,我们需要对数据源的连接等信息进行关闭和清理。

              @Value注解为属性赋值

              在Student类中的name属性上加上@Value注解,等价于配置文件中的<bean id="student" class="com.xxx.pojo.Student"> <property name="name" value="xiaoxin"/> </bean>里的<property name="name" value="xiaoxin"/>,外面的bean是@Component注解作用。

              @ComponentpublicclassStudent{@Value("xiaoxin")privateStringname;@OverridepublicStringtoString(){return"Student{"+"name='"+name+'\''+'}';}publicStringgetName(){returnname;}publicvoidsetName(Stringname){this.name=name;}publicStudent(){}publicStudent(Stringname){this.name=name;}}

              测试及结果:

              publicclassBeanTest{@TestpublicvoidbeanTest(){ApplicationContextcontext=newClassPathXmlApplicationContext("applicationContext.xml");Studentstudent=context.getBean("student",Student.class);System.out.println(student);}}
              使用@PropertySource加载配置文件
              • 原始xml方式:

              • 在resources包下创建一个applicationContext.properties配置文件,内容为键值对形式:name=xiaoxin password=888888

              • 编写一个Property类,用于测试:

              publicclassProperty{privateStringusername;privateIntegerpassword;@OverridepublicStringtoString(){return"Property{"+"username='"+username+'\''+",password="+password+'}';}publicStringgetUsername(){returnusername;}publicvoidsetUsername(Stringusername){this.username=username;}publicIntegergetPassword(){returnpassword;}publicvoidsetPassword(Integerpassword){this.password=password;}publicProperty(Stringusername,Integerpassword){this.username=username;this.password=password;}publicProperty(){}}

              Spring核心配置文件内容为:

              <context:annotation-config/><context:component-scanbase-package="com.xxx"/><context:property-placeholderlocation="applicationContext.properties"/><beanclass="com.xxx.pojo.Property"id="property"><propertyname="username"value="${name}"/><propertyname="password"value="${password}"/></bean>

              测试及结果:

              publicclassBeanTest{@TestpublicvoidbeanTest(){ApplicationContextcontext=newClassPathXmlApplicationContext("applicationContext.xml");Propertyproperty=context.getBean("property",Property.class);System.out.println(property.toString());}}

              注解方式:

              • 保留原applicationContext.properties配置文件

              • 将Spring核心配置文件内容删除:只保留开启注解:<context:annotation-config />

              • Property类完全使用注解代替:

              @Configuration//表示该类是配置类,等价于核心配置文件@ComponentScan(value="com.xxx")//等价于<context:component-scanbase-package="com.xxx"/>@Component//注册bean,默认id为类名(首字母小写),等价于<beanclass="com.xxx.pojo.Property"id="property"></bean>@PropertySource("classpath:applicationContext.properties")//等价于<context:property-placeholderlocation="applicationContext.properties"/>publicclassProperty{@Value("${name}")//等价于<propertyname="username"value="${name}"/>privateStringusername;@Value("${password}")//等价于<propertyname="password"value="${password}"/>privateIntegerpassword;@OverridepublicStringtoString(){return"Property{"+"username='"+username+'\''+",password="+password+'}';}publicStringgetUsername(){returnusername;}publicvoidsetUsername(Stringusername){this.username=username;}publicIntegergetPassword(){returnpassword;}publicvoidsetPassword(Integerpassword){this.password=password;}publicProperty(Stringusername,Integerpassword){this.username=username;this.password=password;}publicProperty(){}}

              测试及结果:

              publicclassBeanTest{@TestpublicvoidbeanTest(){AnnotationConfigApplicationContextapplicationContext=newAnnotationConfigApplicationContext(Property.class);Propertybean=applicationContext.getBean(Property.class);System.out.println(bean.toString());}}
              代理模式

              代理模式:为其他对象提供一种代理以控制对这个对象的访问。

              静态代理

              案例:男孩相亲,想找女孩结婚,于是男孩找媒婆进行代理,媒婆代理介绍女孩同时,还要收取一定的介绍费。

              接口类

              /***@program:springTest*@description:相亲接口*@author:XieXianXin*@create:2021-06-1320:36*/publicinterfaceMarry{//相亲voidmarry();}

              女孩(目标对象)

              /***@program:springTest*@description:目标对象*@author:XieXianXin*@create:2021-06-1320:32*/publicclassGirl{privateStringname;@OverridepublicStringtoString(){return"Girl{"+"name='"+name+'\''+'}';}publicGirl(Stringname){this.name=name;}publicGirl(){}publicStringgetName(){returnname;}publicvoidsetName(Stringname){this.name=name;}}

              男孩(被代理对象)

              /***@program:springTest*@description:被代理对象*@author:XieXianXin*@create:2021-06-1320:33*/publicclassBoyimplementsMarry{privateGirlgirl;publicBoy(Girlgirl){this.girl=girl;}@Overridepublicvoidmarry(){System.out.println("想跟"+girl.getName()+"认识!");}}

              媒婆(代理对象)

              /***@program:springTest*@description:代理类*@author:XieXianXin*@create:2021-06-1320:33*/publicclassProxyimplementsMarry{privateBoyboy;publicProxy(Girlgirl){boy=newBoy(girl);}@Overridepublicvoidmarry(){boy.marry();}publicvoidearn(){System.out.println("媒婆收取介绍费");}}

              测试及结果

              publicclassProxyTest{@TestpublicvoidproxyTest(){Girlgirl=newGirl();girl.setName("美女!");Proxyproxy=newProxy(girl);proxy.marry();proxy.earn();}}
              • 静态代理的好处:

              可以使得我们的真实角色更加纯粹 . 不再去关注一些公共的事情。

              公共的业务由代理来完成 . 实现了业务的分工。

              公共业务发生扩展时变得更加集中和方便。

              • 静态代理缺点:

              冗余,由于代理对象要实现与目标对象一致的接口,会产生过多的代理类。

              不易维护,一旦接口增加方法,目标对象与代理对象都要进行修改。

              Spring AOP AOP

              AOP

              (Aspect Oriented Programming)意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

              总的来说,AOP是指在程序的运行期间动态地将某段代码切入到指定方法、指定位置进行运行的编程方式。AOP的底层是使用动态代理实现的。

              AOP中相关概念

              横切关注点:跨越应用程序多个模块的方法或功能。即是,与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点。如日志 , 安全 , 缓存 , 事务等等 …

              切面(ASPECT):横切关注点 被模块化 的特殊对象。即,它是一个类。

              通知(Advice):切面必须要完成的工作。即,它是类中的一个方法。

              目标(Target):被通知对象。

              代理(Proxy):向目标对象应用通知之后创建的对象。

              切入点(PointCut):切面通知 执行的 “地点”的定义。

              连接点(JointPoint):与切入点匹配的执行点。

              SpringAOP中支持5种类型的Advice

              Spring AOP的实现(3种)
              • 导入依赖

              在原有的maven的pom.xml文件中加上AOP织入依赖包

              <!--使用Spring实现Aop,使用AOP织入,需要导入一个依赖包!--><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.9.4</version></dependency>
              通过 Spring API 实现

              编写业务接口及其实现类

              /***@authorshkstart第一种,有接口方式,通过SpringAPI实现,要实现Uservice接口,具体看advice包*第二种,通过自定义类实现,运用的是AOP定义,不需要实现接口,具体看diy包*第三种,使用注解实现,具体看annotation包*@create2021-06-0416:12*/publicinterfaceUserService{publicvoidadd();publicvoiddelete();publicvoidupdate();publicvoidselect();}
              /***@program:Spring_study*@description:*@author:XieXianXin*@create:2021-06-0416:14*/publicclassUserServiceImplimplementsUserService{@Overridepublicvoidadd(){System.out.println("增加用户");}@Overridepublicvoiddelete(){System.out.println("删除用户");}@Overridepublicvoidupdate(){System.out.println("更新用户");}@Overridepublicvoidselect(){System.out.println("查询用户");}}

              编写增强类(分别有前置通知、后置通知和环绕通知)

              /***@program:Spring_study*@description:前置通知,在方法前增强,实现MethodBeforeAdvice接口*@author:XieXianXin*@create:2021-06-0416:21*/publicclassBeforeAdviceimplementsMethodBeforeAdvice{//method:要执行的目标对象的方法//args:被调用的方法的参数//target:目标对象@Overridepublicvoidbefore(Methodmethod,Object[]args,Objecttarget)throwsThrowable{System.out.println("前置通知的"+target.getClass().getName()+"的"+method.getName()+"方法被执行了");}}
              /***@program:Spring_study*@description:后置通知,在方法后执行,实现AfterReturningAdvice接口*@author:XieXianXin*@create:2021-06-0417:00*/publicclassAfterAdviceimplementsAfterReturningAdvice{//returnValue返回值//method被调用的方法//args被调用的方法的对象的参数//target被调用的目标对象@OverridepublicvoidafterReturning(ObjectreturnValue,Methodmethod,Object[]args,Objecttarget)throwsThrowable{System.out.println("后置通知的"+target.getClass().getName()+"的"+method.getName()+"执行了,返回值为:"+returnValue);}}
              /***@program:Spring_study*@description:环绕通知,在方法前后执行,实现MethodInterceptor接口*@author:XieXianXin*@create:2021-06-0417:07*/publicclassInterceptAdviceimplementsMethodInterceptor{@OverridepublicObjectinvoke(MethodInvocationinvocation)throwsThrowable{try{System.out.println("环绕通知"+invocation.getMethod().getName()+"——方法前执行的");MethodinvocationMethod=(Method)invocation.proceed();System.out.println("环绕通知"+invocation.getMethod().getName()+"——方法后执行的");returninvocationMethod;}catch(Throwablethrowable){throwable.printStackTrace();}returninvocation;}}

              配置Spring核心配置文件,实现AOP切入

              <!--第一种方式,通过接口实现--><!--1.注册bean--><beanid="userService"class="com.xxx.service.UserServiceImpl"/><beanid="beforeAdvice"class="com.xxx.advice.BeforeAdvice"/><beanid="afterAdvice"class="com.xxx.advice.AfterAdvice"/><beanid="interceptAdvice"class="com.xxx.advice.InterceptAdvice"/><beanid="throwAdvice"class="com.xxx.advice.ThrowAdvice"/><!--2.aop的配置--><aop:config><!--切入点expression:表达式匹配要执行的方法--><aop:pointcutid="pointCut"expression="execution(*com.xxx.service.UserServiceImpl.*(..))"/><!--执行环绕;advice-ref执行方法.pointcut-ref切入点--><!--前置通知--><aop:advisoradvice-ref="beforeAdvice"pointcut-ref="pointCut"/><!--后置通知--><aop:advisoradvice-ref="afterAdvice"pointcut-ref="pointCut"/><!--环绕通知--><aop:advisoradvice-ref="interceptAdvice"pointcut-ref="pointCut"/><!--异常抛出通知--><aop:advisoradvice-ref="throwAdvice"pointcut-ref="pointCut"/></aop:config>

              测试及结果

              publicclassUserServiceImplTest{@TestpublicvoidmyTest(){ApplicationContextcontext=newClassPathXmlApplicationContext("beans.xml");//动态代理的是接口,不是实体类,因此不是UserServiceImpl.classUserServiceuserService=context.getBean("userService",UserService.class);userService.delete();System.out.println("==============================");UserServiceuserService1=context.getBean("userService",UserService.class);userService1.add();System.out.println("==============================");UserServiceuserService2=context.getBean("userService",UserService.class);userService2.select();System.out.println("==============================");UserServiceuserService3=context.getBean("userService",UserService.class);userService3.update();}}

              通过自定义类来实现 保留之前的业务类UserServiceImpl编写自定义类DiyPointcut

              /***@program:Spring_study*@description:自定义类实现AOP,一个类相当于一个切面,类的方法相当于通知*@author:XieXianXin*@create:2021-06-0421:16*/publicclassDiyPointcut{publicvoidbeforeAdvice(){System.out.println("前置通知");}publicvoidafterAdvice(){System.out.println("后置通知");}publicvoidinterceptAdvice(ProceedingJoinPointjoinPoint){//环绕通知要有ProceedingJoinPointjoinPoint参数System.out.println("方法"+joinPoint.getSignature().getName()+"环绕通知前执行的语句");Object[]args=joinPoint.getArgs();try{Objectproceed=joinPoint.proceed(args);}catch(Throwablethrowable){throwable.printStackTrace();}System.out.println("方法"+joinPoint.getSignature().getName()+"环绕通知后执行的语句");}}

              配置Spring核心配置文件

              <!--第二种方式,通过自定义类实现--><!--1.注册bean--><beanid="userService"class="com.xxx.service.UserServiceImpl"/><beanid="diyPointcut"class="com.xxx.diy.DiyPointcut"/><aop:config><!--2.使用AOP标签--><aop:aspectref="diyPointcut"><!--3.切入点--><aop:pointcutid="pointcut"expression="execution(*com.xxx.service.UserServiceImpl.*(..))"/><!--4.通知--><!--前置通知--><aop:beforemethod="beforeAdvice"pointcut-ref="pointcut"/><!--后置通知--><aop:aftermethod="afterAdvice"pointcut-ref="pointcut"/><!--环绕通知--><aop:aroundmethod="interceptAdvice"pointcut-ref="pointcut"/></aop:aspect></aop:config>

              测试及结果

              publicclassUserServiceImplTest{@TestpublicvoidmyTest(){ApplicationContextcontext=newClassPathXmlApplicationContext("beans.xml");//动态代理的是接口,不是实体类,因此不是UserServiceImpl.classUserServiceuserService=context.getBean("userService",UserService.class);userService.delete();System.out.println("==============================");UserServiceuserService1=context.getBean("userService",UserService.class);userService1.add();System.out.println("==============================");UserServiceuserService2=context.getBean("userService",UserService.class);userService2.select();System.out.println("==============================");UserServiceuserService3=context.getBean("userService",UserService.class);userService3.update();}}

              通过自定义类来实现 编写注解实现的增强类AnnotationAdvice

              /***@program:Spring_study*@description:使用注解进行AOP设计*@author:XieXianXin*@create:2021-06-0422:18*/@AspectpublicclassAnnotationAdvice{@Before("execution(*com.xxx.service.UserServiceImpl.*(..))")//表达式中写要被增强的类publicvoidbefore(){System.out.println("前置通知");}@After("execution(*com.xxx.service.UserServiceImpl.*(..))")publicvoidafter(){System.out.println("后置通知");}@Around("execution(*com.xxx.service.UserServiceImpl.*(..))")publicvoidaround(ProceedingJoinPointjoinPoint)throwsThrowable{System.out.println("环绕通知执行前");System.out.println("签名:"+joinPoint.getSignature());//执行目标方法proceedObjectproceed=joinPoint.proceed();System.out.println("环绕通知执行后");System.out.println(proceed);}}

              开启注解扫描和注册bean

              <!--指定要扫描的包,这个包下的注解就会生效--><context:component-scanbase-package="com.xxx.service"/><context:annotation-config/>
              <aop:aspectj-autoproxyproxy-target-class="false"/><!--2.注册bean,只需要注册增强的那个类--><beanclass="com.xxx.service.UserServiceImpl"id="userService"/><beanid="annotationAdvice"class="com.xxx.annotation.AnnotationAdvice"/>

              测试及结果

              publicclassUserServiceImplTest{@TestpublicvoidmyTest(){ApplicationContextcontext=newClassPathXmlApplicationContext("beans.xml");//动态代理的是接口,不是实体类,因此不是UserServiceImpl.classUserServiceuserService=context.getBean("userService",UserService.class);userService.delete();System.out.println("==============================");UserServiceuserService1=context.getBean("userService",UserService.class);userService1.add();System.out.println("==============================");UserServiceuserService2=context.getBean("userService",UserService.class);userService2.select();System.out.println("==============================");UserServiceuserService3=context.getBean("userService",UserService.class);userService3.update();}}

              Spring事务管理及Spring整合MyBatis代码示例

              Spring事务管理

              • 什么是事务:事务就是把一系列的动作当成一个独立的工作单元,这些动作要么都执行,要么都不执行。

              • 事务四个特性-ACID:

              原子性(atomicity)

              事务是原子性操作,由一系列动作组成,事务的原子性确保动作要么全部完成,要么完全不起作用

              一致性(consistency)

              一旦所有事务动作完成,事务就要被提交。数据和资源处于一种满足业务规则的一致性状态中

              隔离性(isolation)

              可能多个事务会同时处理相同的数据,因此每个事务都应该与其他事务隔离开来,防止数据损坏

              持久性(durability)

              事务一旦完成,无论系统发生什么错误,结果都不会受到影响。通常情况下,事务的结果被写到持久化存储器中Spring支持编程

              • 式事务管理和声明式的事务管理:

              声明式事务管理

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

              编程式事务每次实现都要单独实现,但业务量大功能复杂时,使用编程式事务无疑是痛苦的,而声明式事务不同,声明式事务属于无侵入式,不会影响业务逻辑的实现,只需要在配置文件中做相关的事务规则声明或者通过注解的方式,便可以将事务规则应用到业务逻辑中。

              显然声明式事务管理要优于编程式事务管理,这正是Spring倡导的非侵入式的编程方式。唯一不足的地方就是声明式事务管理的粒度是方法级别,而编程式事务管理是可以到代码块的,但是可以通过提取方法的方式完成声明式事务管理的配置。

              使用Spring管理事务,注意头文件的约束导入:

              xmlns:tx="http://www.springframework.org/schema/tx"http://www.springframework.org/schema/txhttp://www.springframework.org/schema/tx/spring-tx.xsd">

              声明式事务配置拓展:

              JDBC事务

              <beanid="transactionManager"class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><propertyname="dataSource"ref="dataSource"/></bean>

              自动代理的配置

              !--Spring事务管理--><beanid="transactionManager"class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><propertyname="dataSource"ref="dataSource"/></bean><!--配置事务的传播特性--><beanid="baseTransactionProxy"class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"abstract="true"><propertyname="transactionManager"ref="transactionManager"/><propertyname="transactionAttributes"><props><propkey="add*">PROPAGATION_REQUIRED</prop><propkey="edit*">PROPAGATION_REQUIRED</prop><propkey="remove*">PROPAGATION_REQUIRED</prop><propkey="insert*">PROPAGATION_REQUIRED</prop><propkey="update*">PROPAGATION_REQUIRED</prop><propkey="del*">PROPAGATION_REQUIRED</prop><propkey="*">readOnly</prop></props></property></bean>

              基于 命名空间的声明式事务管理

              <beans......>......<beanid="bankService"class="footmark.spring.core.tx.declare.namespace.BankServiceImpl"><propertyname="bankDao"ref="bankDao"/></bean><tx:adviceid="bankAdvice"transaction-manager="transactionManager"><tx:attributes><tx:methodname="transfer"propagation="REQUIRED"/></tx:attributes></tx:advice><aop:config><aop:pointcutid="bankPointcut"expression="execution(**.transfer(..))"/><aop:advisoradvice-ref="bankAdvice"pointcut-ref="bankPointcut"/></aop:config>......</beans>
              • @Transactional 的声明式事务管理

              启用tx的annotation:

              <tx:annotation-driventransaction-manager="transactionManager"/>

              @Transactional 可以作用于接口、接口方法、类以及类方法上。当作用于类上时,该类的所有 public 方法将都具有该类型的事务属性,同时,我们也可以在方法级别使用该标注来覆盖类级别的定义。

              编程式事务管理

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

              • Spring事务的传播行为:

              事务的第一个方面是传播行为(propagation behavior)。当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。例如:方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行。Spring定义了七种传播行为:

              • 事务的隔离级别:

              事务的第二个维度就是隔离级别(isolation level)。

              脏读(Dirty reads)——脏读发生在一个事务读取了另一个事务改写但尚未提交的数据时。如果改写在稍后被回滚了,那么第一个事务获取的数据就是无效的。 不可重复读(Nonrepeatable read)——不可重复读发生在一个事务执行相同的查询两次或两次以上,但是每次都得到不同的数据时。这通常是因为另一个并发事务在两次查询期间进行了更新。 幻读(Phantom read)——幻读与不可重复读类似。它发生在一个事务(T1)读取了几行数据,接着另一个并发事务(T2)插入了一些数据时。在随后的查询中,第一个事务(T1)就会发现多了一些原本不存在的记录。

              Spring结合事务整合MyBatis示例

              1.导入相关Jar包

              <!--Spring整合Mybatis需要如下包,都是放在dependencies内--><dependencies><!--junit--><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version><scope>test</scope></dependency><!--mysql驱动--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.15</version></dependency><!--mybatis--><dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.5.2</version></dependency><!--导入spring,maven依赖--><dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId><version>5.2.12.RELEASE</version></dependency><!--使用Spring实现Aop,使用AOP织入--><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.9.4</version></dependency><!--spring操作数据库也需要一个spring-jdbc包--><dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId><version>5.3.7</version></dependency><!--整合必要的一个包,mybatis-spring,使用2.0以上版本--><dependency><groupId>org.mybatis</groupId><artifactId>mybatis-spring</artifactId><version>2.0.5</version></dependency><!--LOG4J--><dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.17</version></dependency><dependency><groupId>org.testng</groupId><artifactId>testng</artifactId><version>RELEASE</version><scope>compile</scope></dependency><dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.17</version><scope>compile</scope></dependency></dependencies><!--需要解决的乱码以及maven静态资源过滤问题等在build内完成--><!--解决单元测试中文乱码--><build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-surefire-plugin</artifactId><version>2.12.4</version><configuration><argLine>-Dfile.encoding=UTF-8</argLine></configuration></plugin></plugins><!--可能出现问题说明:Maven静态资源过滤(导出)问题Cause:org.apache.ibatis.builder.BuilderException:ErrorparsingSQLMapperConfiguration.Cause:java.io.IOException:Couldnotfindresourcecom/xxx/dao/UserMapper.xml原因是idea默认不编译src目录下的xml文件,所以加载不到解决办法在pom文件中的build标签内加入如下配置,则可以找到java和resources下的所有properties和xml文件了--><resources><resource><directory>src/main/resources</directory><includes><include>**/*.properties</include><include>**/*.xml</include></includes><filtering>true</filtering></resource><resource><directory>src/main/java</directory><includes><include>**/*.properties</include><include>**/*.xml</include></includes><filtering>true</filtering></resource></resources></build>

              2.编写配置文件及加入日志

              mybatis-config.xml

              <configuration><!--configuration"里的标签顺序如下:(否则报错如下信息)"(properties?,settings?,typeAliases?,typeHandlers?,objectFactory?objectWrapperFactory?,reflectorFactory?,plugins?,environments?,databaseIdProvider?,mappers?)".--><!--标准的日志工厂实现(常用:STDOUT_LOGGING,LOG4J),下面的value值建议去mybaits文档复制日志就是记录程序的运行轨迹,方便查找关键信息,也方便快速定位解决问题。--><settings><!--下划线驼峰自动转换--><settingname="mapUnderscoreToCamelCase"value="true"/><settingname="logImpl"value="LOG4J"/></settings><!--给这个包下的类起别名--><typeAliases><packagename="com.xxx.pojo"/></typeAliases><mappers><mapperresource="com/xxx/mapper/UserMapper.xml"/></mappers></configuration>

              spring-mybatis.xml

              <!--spring整合mybatis,根据mybatis-spring文档可以,需要一个数据源获取SqlSessionFactory和至少一个数据映射器类具体查看文档:http://mybatis.org/spring/zh/getting-started.html--><!--DataSource:使用Spring的数据源替换Mybatis的配置:druidc3p0,dbcp这里使用Speing提供的JDBC:org.springframework.jdbc.datasource.DriverManagerDataSource前提是要导入:spring-jdbc包--><beanid="dataSource"class="org.springframework.jdbc.datasource.DriverManagerDataSource"><propertyname="driverClassName"value="com.mysql.cj.jdbc.Driver"/><propertyname="url"value="jdbc:mysql://localhost:3306/mybaits?serverTimezone=UTC&amp;allowPublicKeyRetrieval=true&amp;useSSL=false&amp;characterEncoding=UTF-8"/><propertyname="username"value="root"/><propertyname="password"value="123456"/></bean><!--MyBatis-Spring中,可使用SqlSessionFactoryBean来创建SqlSessionFactory--><beanid="sqlSessionFactory"class="org.mybatis.spring.SqlSessionFactoryBean"><propertyname="dataSource"ref="dataSource"/><!--跟在mybatis中学习一样,需要在mybatis核心配置文件绑定xxxmapper.xml文件这里也需要绑定mybatis核心配置文件,绑定后,mybatis核心配置文件可以完成的这里也都可以完成,则mybatis-config文件可以不要也行--><!--绑定mybatis--><propertyname="configLocation"value="classpath:mybatis-config.xml"/><!--注册映射器,等价于mybatis核心配置文件中的:<mappers><mapperresource="com/xxx/mapper/UserMapper.xml"/></mappers>--><!--<propertyname="mapperLocations"value="classpath:com/xxx/mapper/*.xml"/>--></bean><!--注册SqlSessionTemplate,相当于我们使用的sqlSession,因此可将id命名为此好记--><beanid="sqlSession"class="org.mybatis.spring.SqlSessionTemplate"><!--因为sqlSessionTemplate只有构造方法而无set方法,只能使用构造器注入--><constructor-argindex="0"ref="sqlSessionFactory"/></bean><!--配置声明式事务(AOP原理,不改变源代码条件下增加事务),而编程式事务要在源代码上自动trycatch具体可查看文档:http://mybatis.org/spring/zh/transactions.html--><beanid="transactionManager"class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><propertyname="dataSource"ref="dataSource"/></bean><!--结合AOP实现事务的织入--><!--配置事务的通知--><tx:adviceid="transactionAdvice"transaction-manager="transactionManager"><!--给具体方法配置事务和传播新特性(propagation=REQUIRED是默认的,即会自动创建事务)具体查看:https://blog.csdn.net/edward0830ly/article/details/7569954name="*"表示给所有方法配置事务,也可给具体方法,给出方法名即可--><tx:attributes><tx:methodname="*"propagation="REQUIRED"/></tx:attributes></tx:advice><!--配置事务切入--><aop:config><aop:pointcutid="transactionPointcut"expression="execution(*com.xxx.mapper.*.*(..))"/><aop:advisoradvice-ref="transactionAdvice"pointcut-ref="transactionPointcut"/></aop:config>

              applicationContext.xml

              <importresource="spring-mybatis.xml"/><beanid="userMapperImpl_2"class="com.xxx.mapper.UserMapperImpl_2"><propertyname="sqlSessionFactory"ref="sqlSessionFactory"/></bean>

              log4j.properties

              #将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义在下面的代码log4j.rootLogger=DEBUG,console,file#控制台输出的相关设置log4j.appender.console=org.apache.log4j.ConsoleAppenderlog4j.appender.console.Target=System.outlog4j.appender.console.Threshold=DEBUGlog4j.appender.console.layout=org.apache.log4j.PatternLayoutlog4j.appender.console.layout.ConversionPattern=[%c]-%m%n#文件输出的相关设置log4j.appender.file=org.apache.log4j.RollingFileAppenderlog4j.appender.file.File=./log/xxx.loglog4j.appender.file.MaxFileSize=10mblog4j.appender.file.Threshold=DEBUGlog4j.appender.file.layout=org.apache.log4j.PatternLayoutlog4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n#日志输出级别log4j.logger.org.mybatis=DEBUGlog4j.logger.java.sql=DEBUGlog4j.logger.java.sql.Statement=DEBUGlog4j.logger.java.sql.ResultSet=DEBUGlog4j.logger.java.sql.PreparedStatement=DEBUG

              3.编写接口及其实现类和配置对应的mapper.xml文件

              UserMapper接口

              publicinterfaceUserMapper{//查询所有用户publicList<User>queryUser();//添加一个用户intaddUser(Useruser);//根据id删除用户intdeleteUser(intid);}

              UserMapperImpl_2实现类

              /***@program:Spring_study*@description:spring-mybatis整合方式二:继承SqlSessionDaoSupport实现接口*@author:XieXianXin*@create:2021-06-0522:08*/publicclassUserMapperImpl_2extendsSqlSessionDaoSupportimplementsUserMapper{@OverridepublicList<User>queryUser(){returngetSqlSession().getMapper(UserMapper.class).queryUser();}@OverridepublicintaddUser(Useruser){returngetSqlSession().getMapper(UserMapper.class).addUser(user);}@OverridepublicintdeleteUser(intid){returngetSqlSession().getMapper(UserMapper.class).deleteUser(id);}}

              UserMapper.xml

              <!--namespace==绑定一个对应的Dao/Mapper接口,以后Mapper.xml文件都放在resourse下,但是要建立一个跟Mapper接口相对应得包注意!!这里有一个坑,当在resources下建立包时候,不要写为:com.xxx.dao应该为:com/xxx/dao--><!--诡异事件,在学习mabatis适合,写UTF-8没错,但是整合这里的所有XML却报错:1字节的UTF-8序列的字节1无效。解决方法:将所有的XML文件UTF-8改为UTF8即可--><mappernamespace="com.xxx.mapper.UserMapper"><!--last_name已经进行自动驼峰转换,则这里不用resultMap进行不同名的映射resultType中也起了别名,不用再写com.xxx.pojo了--><selectid="queryUser"resultType="User">select*fromuser</select><insertid="addUser"parameterType="User">insertintouser(id,last_name,email)values(#{id},#{lastName},#{email})</insert><deleteid="deleteUser"parameterType="_int">deletefromuserwhereid=#{id}</delete></mapper>

              测试及结果

              手动设置错误,如在插入语句上写错insert为inserts

              <insertid="addUser"parameterType="User">insertsintouser(id,last_name,email)values(#{id},#{lastName},#{email})</insert>
              publicclassUserMapperTest{staticLoggerlogger=Logger.getLogger(UserMapperTest.class);@Test@TestpublicvoiduserMapperImpl_2(){ApplicationContextcontext=newClassPathXmlApplicationContext("applicationContext.xml");UserMapperuserMapperImpl_2=context.getBean("userMapperImpl_2",UserMapper.class);userMapperImpl_2.addUser(newUser(12,"xiaoxin","com@xiaoxin"));userMapperImpl_2.deleteUser(8);for(Useruser:userMapperImpl_2.queryUser()){System.out.println(user);}}}

              如果为插入语句错误,则项目不能正常插入,事务会回滚。

              查看并刷新数据库表,没有变化。

              接着将错误改正后,再次测试结果为:

              成功添加和删除,事务保证了数据的一致性。查看数据库表为:

              到此,相信大家对“spring框架介绍”有了更深的了解,不妨来实际操作一番吧!这里是本站网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

              spring框架
              需要做网站?需要网络推广?欢迎咨询客户经理 13272073477