一篇让你读懂Spring基础

Spring 简介

Spring 是分层的 full-stack(全栈) 轻量级开源框架,以 IoC 和 AOP 为内核,提供了展现层 Spring MVC 和业务层事务管理等众多的企业级应⽤技术,还能整合开源世界众多著名的第三⽅框架和类库,已经成为使⽤最多的 Java EE 企业应⽤开源框架。

Spring 官⽅⽹址:http://spring.io/

我们经常说的 Spring 其实指的是Spring Framework(spring 框架)。

Spring 优势

整个 Spring 优势,传达出⼀个信号,Spring 是⼀个综合性,且有很强的思想性框架,每学习⼀

天,就能体会到它的⼀些优势。

  • 方便解耦,简化开发
    通过Spring提供的IoC容器,可以将对象间的依赖关系交由Spring进行控制,避免硬编码所造成的过度程序耦合。用户也不必再为单例模式类、属性文件解析等这些很底层的需求编写代码,可以更专注于上层的应用。

  • AOP编程的支持
    通过Spring的AOP功能,方便进行面向切面的编程,许多不容易用传统OOP(面向对象,Object Oriented Programming)实现的功能可以通过AOP轻松应付。

  • 声明式事务的支持
    @Transactional
    可以将我们从单调烦闷的事务管理代码中解脱出来,通过声明式灵活的进行事务的管理,提高开发效率和质量。

  • 方便程序的测试
    可以yoghurt非容器依赖的编程方式进行几乎所有的测试工作,测试不再是昂贵的操作,而是随手可做的事情。

  • 方便集成各种优秀框架
    Spring可以降低各种框架的使⽤难度,提供了对各种优秀框架(Struts、Hibernate、Hessian、Quartz等的直接⽀持。

  • 降低JavaEE API的使用难度
    Spring对JavaEE API(如JDBC、JavaMail、远程调⽤等)进⾏了薄薄的封装层,使这些API的使⽤难度⼤为降低。

  • 源码是经典的Java学习范例
    Spring的源代码设计精妙、结构清晰、匠⼼独⽤,处处体现着⼤师对Java设计模式灵活运⽤以及对Java技术的⾼深造诣。它的源代码⽆意是Java技术的最佳实践的范例。

Spring 核心架构

Spring是⼀个分层⾮常清晰并且依赖关系、职责定位⾮常明确的轻量级框架,主要包括⼏个⼤模块:数据处理模块、Web模块、AOP(Aspect Oriented Programming)/Aspects模块、Core Containe模块和 Test 模块,如下图所示,Spring依靠这些基本模块,实现了⼀个令⼈愉悦的融合了现有解决方案的零侵⼊的轻量级框架。

image.png

  • Spring核⼼容器(Core Container)
    Spring核⼼容器(Core Container) 容器是Spring框架最核⼼的部分,它管理着Spring应⽤中bean的创建、配置和管理。在该模块中,包括了Spring bean⼯⼚,它为Spring提供了DI的功能。基于bean⼯⼚,我们还会发现有多种Spring应⽤上下⽂的实现。所有的Spring模块都构建于核⼼容器之上。

  • ⾯向切⾯编程(AOP)/Aspects
    Spring对⾯向切⾯编程提供了丰富的⽀持。这个模块是Spring应⽤系统中开发切⾯的基础,与DI⼀样,AOP可以帮助应⽤对象解耦。

  • 数据访问与集成
    Spring的JDBC和DAO模块封装了⼤量样板代码,这样可以使得数据库代码变得简洁,也可以更专注于我们的业务,还可以避免数据库资源释放失败⽽引起的问题。 另外,Spring AOP为数据访问提供了事务管理服务,同时Spring还对ORM进⾏了集成,如Hibernate、MyBatis等。该模块由JDBC、Transactions、ORM、OXM 和 JMS 等模块组成。

  • Web模块
    Web 该模块提供了SpringMVC框架给Web应⽤,还提供了多种构建和其它应⽤交互的远程调⽤⽅案。 SpringMVC框架在Web层提升了应⽤的松耦合⽔平。

  • Test
    Test 为了使得开发者能够很⽅便的进⾏测试,Spring提供了测试模块以致⼒于Spring应⽤的测试。 通过该模块,Spring为使⽤Servlet、JNDI等编写单元测试提供了⼀系列的mock对象实现。
    注:Mock 对象就是真实对象在调试期的替代品

在许多情况下,mock对象都可以给我们带来帮助:

  • 真实对象具有不可确定的行为(产生不可预测的结果, 如股票行情);
  • 真实对象很难被创建;
  • 真实对象的某些行为很难触发(如网络错误);
  • 真实对象令程序的运行速度很慢;
  • 真实对象有(或者是)用户界面;
  • 测试需要询问真实对象是如何被调用的(例如测试可能需要验证某个回调函数是否被调用);
  • 真实对象实际上并不存在(当需要和其他开发小组或者新的硬件系统打交道的时候, 这是一个普遍问题)

借助 Mock 对象,我们就可以解决上面提到的所有问题;在使用 Mock 对象进行测试的时候, 总共有3个关键步骤, 分别是:

  • 使用一个接口来描述这个对象;
  • 为产品代码实现这个接口;
  • 以测试为目的, 在 Mock 对象中实现这个接口。

Spring 核心思想

IoC

1、什么是IoC?

IoC:Inversion of Control(控制反转/反转控制),注意它是⼀个技术思想,不是⼀个技术实现

控制:指的是对象创建(实例化、管理)的权利

反转:控制权交给外部环境了(spring框架、IoC容器)

  • 传统开发⽅式:⽐如类A依赖于类B,往往会在类A中new⼀个B的对象
  • IoC思想下开发⽅式:我们不⽤⾃⼰去new对象了,⽽是由IoC容器(Spring框架)去帮助我们实例化对象并且管理它,我们需要使⽤哪个对象,去问IoC容器要即可

image.png

2、IoC解决了什么问题

IoC解决对象之间的耦合问题

image.png

3、IoC和DI的区别

DI:Dependancy Injection(依赖注⼊)

IOC和DI描述的是同⼀件事情,只不过⻆度不⼀样罢了

image.png

AOP

1、什么是AOP?

AOP: Aspect oriented Programming ⾯向切⾯编程/⾯向⽅⾯编程

AOP是OOP的延续,从OOP说起。

OOP三⼤特征:封装、继承和多态

oop是⼀种垂直继承体系

image.png

OOP编程思想可以解决⼤多数的代码重复问题,但是有⼀些情况是处理不了的,⽐如下⾯的在顶级⽗类 Animal 中的多个⽅法中相同位置出现了重复代码,OOP就解决不了

public class Animal {
    private String height;
    private float weight;
    
    public void eat() {
        // 假设性性能监控代码
        long start = System.currentTimeMillis();
        
        // 假设业务逻辑代码
        System.out.println("I can eat...");
        
        // 假设性性能监控代码
        long end = System.currentTimeMillis();
        System.out.println("执行时长:" + (end-start)/1000f + "s");
    }
    
    public void run() {
        // 假设性性能监控代码
        long start = System.currentTimeMillis();
        
        // 假设业务逻辑代码
        System.out.println("I can run...");
        
        // 假设性性能监控代码
        long end = System.currentTimeMillis();
        System.out.println("执行时长:" + (end-start)/1000f + "s");
    }
}

横切逻辑代码

image.png

横切逻辑代码存在什么问题:

  • 横切代码重复问题
  • 横切逻辑代码和业务代码混杂在⼀起,代码臃肿,维护不⽅便

AOP出场,AOP独辟蹊径提出横向抽取机制,将横切逻辑代码和业务逻辑代码分析

将横切代码跟业务代码拆分:

image.png

代码拆分容易,那么如何在不改变原有业务逻辑的情况下,悄⽆声息的把横切逻辑代码应⽤到原有的业务逻辑中,达到和原来⼀样的效果,这个是⽐较难的

2、AOP在解决什么问题

在不改变原有业务逻辑情况下,增强横切逻辑代码,根本上解耦合,避免横切逻辑代码重复

3、为什么叫做⾯向切⾯编程

「切」:指的是横切逻辑,原有业务逻辑代码我们不能动,只能操作横切逻辑代码,所以⾯向横切逻辑

「⾯」:横切逻辑代码往往要影响的是很多个⽅法,每⼀个⽅法都如同⼀个点,多个点构成⾯,有⼀个⾯的概念在⾥⾯

手写实现IoC和AOP:https://www.yuque.com/sc30/kuoicx/yvzynz

4、AOP与OOP的关系

引用其他博主

  • OOP(面向对象编程)针对业务处理过程的实体及其属性和行为进行抽象封装,以获得更加清晰高效的逻辑单元划分。但是也有它的缺点,最明显的就是关注点聚焦时,面向对象无法简单的解决这个问题,一个关注点是面向所有而不是单一的类,不受类的边界的约束,因此OOP无法将关注点聚焦来解决,只能分散到各个类中。
  • AOP(面向切面编程)则是针对业务处理过程中的切面进行提取,它所面对的是处理过程中的某个步骤或阶段,以获得逻辑过程中各部分之间低耦合性的隔离效果。这两种设计思想在目标上有着本质的差异。
  • AOP并不是与OOP对立的,而是为了弥补OOP的不足。OOP解决了竖向的问题,AOP则解决横向的问题。因为有了AOP我们的调试和监控就变得简单清晰。它们之间的关系如下图所示:

image

Spring IoC基础及源码剖析

1、BeanFactory与ApplicationContext区别

手写实现IoC和AOP:https://www.yuque.com/sc30/kuoicx/yvzynz

通过上面手写的IoC可以知道beans.xml的关系,

  • beans.xml:定义需要实例化对象的类的全限定类名以及类之间依赖关系描述
<?xml version="1.0" encoding="UTF-8" ?>
<!--跟标签beans,里面配置一个又一个的bean子标签,每一个bean子标签都代表一个类的配置-->
<beans>
    <!--id标识对象,class是类的全限定类名-->
    <bean id="accountDao" class="com.lagou.edu.dao.impl.JdbcTemplateDaoImpl">
        <property name="ConnectionUtils" ref="connectionUtils"/>
    </bean>
    <bean id="transferService" class="com.lagou.edu.service.impl.TransferServiceImpl">
        <!--set+ name 之后锁定到传值的set方法了,通过反射技术可以调用该方法传入对应的值-->
        <property name="AccountDao" ref="accountDao"></property>
    </bean>


    <!--配置新增的三个Bean-->
    <bean id="connectionUtils" class="com.lagou.edu.utils.ConnectionUtils"></bean>

    <!--事务管理器-->
    <bean id="transactionManager" class="com.lagou.edu.utils.TransactionManager">
        <property name="ConnectionUtils" ref="connectionUtils"/>
    </bean>

    <!--代理对象工厂-->
    <bean id="proxyFactory" class="com.lagou.edu.factory.ProxyFactory">
        <property name="TransactionManager" ref="transactionManager"/>
    </bean>
</beans>
  • BeanFactory:通过反射技术来实例化对象并维护对象之间的依赖关系
public class BeanFactory {

    /**
     * 任务一:读取解析xml,通过反射技术实例化对象并且存储待用(map集合)
     * 任务二:对外提供获取实例对象的接口(根据id获取)
     */

    private static Map<String,Object> map = new HashMap<>();  // 存储对象


    static {
        // 任务一:读取解析xml,通过反射技术实例化对象并且存储待用(map集合)
        // 加载xml
        InputStream resourceAsStream = BeanFactory.class.getClassLoader().getResourceAsStream("beans.xml");
        // 解析xml
        SAXReader saxReader = new SAXReader();
        try {
            Document document = saxReader.read(resourceAsStream);
            Element rootElement = document.getRootElement();
            List<Element> beanList = rootElement.selectNodes("//bean");
            for (int i = 0; i < beanList.size(); i++) {
                Element element =  beanList.get(i);
                // 处理每个bean元素,获取到该元素的id 和 class 属性
                String id = element.attributeValue("id");        // accountDao
                String clazz = element.attributeValue("class");  // com.lagou.edu.dao.impl.JdbcAccountDaoImpl
                // 通过反射技术实例化对象
                Class<?> aClass = Class.forName(clazz);
                Object o = aClass.newInstance();  // 实例化之后的对象

                // 存储到map中待用
                map.put(id,o);

            }

            // 实例化完成之后维护对象的依赖关系,检查哪些对象需要传值进入,根据它的配置,我们传入相应的值
            // 有property子元素的bean就有传值需求
            List<Element> propertyList = rootElement.selectNodes("//property");
            // 解析property,获取父元素
            for (int i = 0; i < propertyList.size(); i++) {
                Element element =  propertyList.get(i);   //<property name="AccountDao" ref="accountDao"></property>
                String name = element.attributeValue("name");
                String ref = element.attributeValue("ref");

                // 找到当前需要被处理依赖关系的bean
                Element parent = element.getParent();

                // 调用父元素对象的反射功能
                String parentId = parent.attributeValue("id");
                Object parentObject = map.get(parentId);
                // 遍历父对象中的所有方法,找到"set" + name
                Method[] methods = parentObject.getClass().getMethods();
                for (int j = 0; j < methods.length; j++) {
                    Method method = methods[j];
                    if(method.getName().equalsIgnoreCase("set" + name)) {  // 该方法就是 setAccountDao(AccountDao accountDao)
                        method.invoke(parentObject,map.get(ref));
                    }
                }

                // 把处理之后的parentObject重新放到map中
                map.put(parentId,parentObject);

            }


        } catch (DocumentException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }

    }


    // 任务二:对外提供获取实例对象的接口(根据id获取)
    public static  Object getBean(String id) {
        return map.get(id);
    }

}

具体关系如下图:

image.png

BeanFactory是Spring框架中IoC容器的顶层接口,它只是用来定义一些基础功能,定义一些基础规范,而ApplicationContext是它的一个子接口,所以Application是具备BeanFactory提供的全部功能的。
通常,我们称BeanFactory为Spring IoC的基础容器,Application是容器的高级接口,比BeanFactory要拥有更多的功能,比如说国际化支持和资源访问(xml,java配置类)等等

image.png

启动IoC的方式

  • Java环境下启动:

    • ClassPathXmlApplicationContext:从类的根路径下加载配置⽂件(推荐使⽤)
    • FileSystemXmlApplicationContext:从磁盘路径上加载配置⽂件
    • AnnotationConfigApplicationContext:纯注解模式下启动Spring容器
  • Web环境下启动IoC容器

    • 从xml启动容器
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
 <web-app>
  <display-name>Archetype Created Web Application</display-name>
  <!--配置Spring ioc容器的配置⽂件-->
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:applicationContext.xml</param-value>
  </context-param>
  <!--使⽤监听器启动Spring的IOC容器-->
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>
</web-app>
    • 从配置类启动容器
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" 
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
  <display-name>Archetype Created Web Application</display-name>
  <!--告诉ContextloaderListener知道我们使⽤注解的⽅式启动ioc容器-->
  <context-param>
    <param-name>contextClass</param-name>
    <param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
  </context-param>
  <!--配置启动类的全限定类名-->
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>com.lagou.edu.SpringConfig</param-value>
  </context-param>
  <!--使⽤监听器启动Spring的IOC容器-->
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>
</web-app>

2、重写IoC

2.1、纯xml模式
a、实例化Bean的三种方式
  • 方式一:使用无参构造函数
<!--方式一:使用无参构造器(推荐)-->
<bean id="connectionUtils" class="com.lagou.edu.utils.ConnectionUtils"></bean>

在默认情况,它会通过反射调用无参构造函数来创建对象。注:如果类中没无参构造函数则构造失败

  • 方式二:使用静态方法创建
<!--方式二:静态方法-->
<bean id="connectionUtils" class="com.lagou.edu.factory.CreateBeanFactory" factory-method="getInstanceStatic"/>
public class CreateBeanFactory {
    public static ConnectionUtils getInstanceStatic() {
        return new ConnectionUtils();
    }
    
    public ConnectionUtils getInstance() {
        return new ConnectionUtils();
    }
}

在实际开发中,我们使用的对象并不是都直接通过构造函数就可以创建出来的,它可能在创建过程中会做额外的其余操作。此时会提供一个创建对象的方法,恰好这个方法是static修饰的方法,即是方式二。

举例:

我们在做JDBC操作时,会用到java.sql.Connection接⼝的实现类,如果是mysql数据库,那么用的就是JDBC4Connection,但是我们不会去写 JDBC4Connection connection = new JDBC4Connection() ,因为我们要注册驱动,还要提供URL和凭证信息,⽤DriverManager.getConnection ⽅法来获取连接。

  • 方式三:使用实例化方法创建
<!--方式三:实例化方法-->
<bean id="createBeanFactory" class="com.lagou.edu.factory.CreateBeanFactory"></bean>
<bean id="connectionUtils" factory-bean="createBeanFactory" factory-method="getInstance"/>
public class CreateBeanFactory {
    public static ConnectionUtils getInstanceStatic() {
        return new ConnectionUtils();
    }
    public ConnectionUtils getInstance() {
        return new ConnectionUtils();
    }
}

与方式二类似,区别只在于不是static修饰。

b、Bean的X及生命周期
  • 作用范围的改变

在Spring框架管理Bean对象的创建时,Bean对象默认都是单例的,但是它支持配置的方式改变作用范围。

作用范围官方说明如下图:

image.png

在上图提供的这些选项中,我们实际开发中用到最多的作用范围就是singleton(单例模式)和prototype(原型模式,也叫多例模式)。参考代码如下:

<!--配置service对象-->
<bean id="transferService" class="com.lagou.service.impl.TransferServiceImpl" scope="singleton">
</bean>
  • 不同作用范围的生命周期

    • singleton(单例模式)
      对象出⽣:当创建容器时,对象就被创建了。
      对象活着:只要容器在,对象⼀直活着。
      对象死亡:当销毁容器时,对象就被销毁了。
      ⼀句总结:单例模式的bean对象⽣命周期与容器相同。

    • prototype(原型模式,也叫多例模式)
      对象出⽣:当使⽤对象时,创建新的对象实例。
      对象活着:只要对象在使⽤中,就⼀直活着。
      对象死亡:当对象⻓时间不⽤时,被java的垃圾回收器回收了。
      ⼀句总结:多例模式的bean对象,spring框架只负责创建,不负责销毁。

  • Bean标签属性
    在基于xml的IoC配置中,bean标签是最基础的标签。它表示了IoC容器中的⼀个对象。换句话说,如果⼀个对象想让spring管理,在XML的配置中都需要使⽤此标签配置,Bean标签的属性如下:

    • id属性: ⽤于给bean提供⼀个唯⼀标识。在⼀个标签内部,标识必须唯⼀。
    • class属性:⽤于指定创建Bean对象的全限定类名。
    • name属性:⽤于给bean提供⼀个或多个名称。多个名称⽤空格分隔。
    • factory-bean属性:⽤于指定创建当前bean对象的⼯⼚bean的唯⼀标识。当指定了此属性之后,class属性失效。
    • factory-method属性:⽤于指定创建当前bean对象的⼯⼚⽅法,如配合factory-bean属性使⽤,则class属性失效。如配合class属性使⽤,则⽅法必须是static的。
    • scope属性:⽤于指定bean对象的作⽤范围。通常情况下就是singleton。当要⽤到多例模式时,可以配置为prototype。
    • init-method属性:⽤于指定bean对象的初始化⽅法,此⽅法会在bean对象装配后调⽤。必须是⼀个⽆参⽅法。
    • destory-method属性:⽤于指定bean对象的销毁⽅法,此⽅法会在bean对象销毁前执⾏。它只能为scope是singleton时起作⽤。
  • DI 依赖注⼊的xml配置

    • 依赖注⼊分类

      • 按照注入的方式分类
        构造函数注⼊:顾名思义,就是利⽤带参构造函数实现对类成员的数据赋值。
        set⽅法注⼊:它是通过类成员的set⽅法实现数据的注⼊。(使⽤最多的)

      • 按照注⼊的数据类型分类
        基本类型和String
        注⼊的数据类型是基本类型或者是字符串类型的数据。
        其他Bean类型
        注⼊的数据类型是对象类型,称为其他Bean的原因是,这个对象是要求出现在IoC容器中的。 那么针对当前Bean来说,就是其他Bean了。
        复杂类型(集合类型)
        注⼊的数据类型是Aarry,List,Set,Map,Properties中的⼀种类型。

    • 依赖注⼊的配置实现之构造函数注⼊

      • 顾名思义,就是利⽤构造函数实现对类成员的赋值。它的使⽤要求是,类中提供的构造函数参 数个数必须和配置的参数个数⼀致,且数据类型匹配。同时需要注意的是,当没有⽆参构造时,则必须提供构造函数参数的注⼊,否则Spring框架会报错。
      • 在使用构造函数注入时,涉及的标签是constructor-arg,该标签属性如下:
        name:⽤于给构造函数中指定名称的参数赋值。
        index:⽤于给构造函数中指定索引位置的参数赋值。
        value:⽤于指定基本类型或者String类型的数据。
        ref:⽤于指定其他Bean类型的数据。写的是其他bean的唯⼀标识。
public class JdbcTemplateDaoImpl implements AccountDao {

    private ConnectionUtils connectionUtils;
    private String name;
    private int sex;
    private float money;
    
    //省略get、set方法

}
<?xml version="1.0" encoding="UTF-8"?>
<!--省略请求头-->
<beans>
    <bean id="accountDao" class="com.lagou.edu.dao.impl.JdbcTemplateDaoImpl" scope="singleton" init-method="init" destroy-method="destory">

      
       <!--name:按照参数名称注入,index按照参数索引位置注入-->
      
        <!--<constructor-arg index="0" ref="connectionUtils"/>
        <constructor-arg index="1" value="zhangsan"/>
        <constructor-arg index="2" value="1"/>
        <constructor-arg index="3" value="100.5"/>-->
       
        <constructor-arg name="connectionUtils" ref="connectionUtils"/>
        <constructor-arg name="name" value="zhangsan"/>
        <constructor-arg name="sex" value="1"/>
        <constructor-arg name="money" value="100.6"/>
        </bean>
  
    <bean id="connectionUtils" class="com.lagou.edu.utils.ConnectionUtils"></bean>
</beans>
  • 依赖注⼊的配置实现之set⽅法注⼊
    顾名思义,就是利⽤字段的set⽅法实现赋值的注⼊⽅式。此种⽅式在实际开发中是使⽤最多的注⼊⽅式。
<?xml version="1.0" encoding="UTF-8"?>
<!--省略请求头-->
<beans>
    <bean id="accountDao" class="com.sc.edu.dao.impl.JdbcTemplateDaoImpl" scope="singleton" init-method="init" destroy-method="destory">
            <!--set注入使用property标签,如果注入的是另外一个bean那么使用ref属性,如果注入的是普通值那么使用的是value属性-->
        <property name="ConnectionUtils" ref="connectionUtils"/>
        <property name="name" value="zhangsan"/>
        <property name="sex" value="1"/>
        <property name="money" value="100.3"/>
        </bean>
  
    <bean id="connectionUtils" class="com.sc.edu.utils.ConnectionUtils"></bean>
</beans>
在使⽤set⽅法注⼊时,需要使⽤ property 标签,该标签属性如下:
	name:指定注⼊时调⽤的set⽅法名称。(注:不包含set这三个字⺟,druid连接池指定属性名称)
	value:指定注⼊的数据。它⽀持基本类型和String类型。
	ref:指定注⼊的数据。它⽀持其他bean类型。写的是其他bean的唯⼀标识。
  • 复杂数据类型注⼊
    • 复杂类型数据,它指的是集合类型数据。集合分为两类,⼀类是List结构(数组结构),⼀类是Map接⼝(键值对) 。
    • 复杂数据类型注入方式只能在构造函数和set⽅法中选择,下面实例为set方法注入:
private String[] myArray;
private Map<String, String> myMap;
private Set<String> mySet;
private Properties myProperties;
<?xml version="1.0" encoding="UTF-8"?>
<!--省略请求头-->
<beans>
  <bean id="PoJo" class="com.sc.edu.pojo.PoJo" scope="singleton" init-method="init" destroy-method="destory">
    <!--set注入注入复杂数据类型-->
        <property name="myArray">
            <array>
                <value>array1</value>
                <value>array2</value>
                <value>array3</value>
            </array>
        </property>

        <property name="myMap">
            <map>
                <entry key="key1" value="value1"/>
                <entry key="key2" value="value2"/>
            </map>
        </property>

        <property name="mySet">
            <set>
                <value>set1</value>
                <value>set2</value>
            </set>
        </property>

        <property name="myProperties">
            <props>
                <prop key="prop1">value1</prop>
                <prop key="prop2">value2</prop>
            </props>
        </property>
    </bean>
</beans>
总结:
在List结构的集合数据注⼊时, array , list , set 这三个标签通⽤,另外注值的 value 标签内部可以直接写值,也可以使⽤ bean 标签配置⼀个对象,或者⽤ ref 标签引⽤⼀个已经配合的bean的唯⼀标识。
在Map结构的集合数据注⼊时, map 标签使⽤ entry ⼦标签实现数据注⼊, entry 标签可以使⽤key和value属性指定存⼊map中的数据。使⽤value-ref属性指定已经配置好的bean的引⽤。同时 entry 标签中也可以使⽤ ref 标签,但是不能使⽤ bean 标签。⽽ property 标签中不能使⽤ ref 或者 bean 标签引⽤对象
2.2、xml与注解相结合模式
a、xml中标签与注解的对应(IoC)
xml形式对应的注解形式
标签@Component(“accountDao”),注解加在类上bean的id属性内容直接配置在注解后⾯如果不配置,默认定义个这个bean的id为类的类名⾸字⺟⼩写;另外,针对分层代码开发提供了@Componenet的三种别名@Controller、@Service、@Repository分别⽤于控制层类、服务层类、dao层类的bean定义,这四个注解的⽤法完全⼀样,只是为了更清晰的区分⽽已
标签的scope属性@Scope(“prototype”),默认单例,注解加在类上
标签的init-method属性@PostConstruct,注解加在⽅法上,该⽅法就是初始化后调⽤的⽅法
标签的destory-method属性@PreDestory,注解加在⽅法上,该⽅法就是销毁前调⽤的⽅法
b、DI 依赖注⼊的注解实现⽅式
  • @Autowired(推荐使⽤)
    @Autowired采取的策略为按照类型注⼊。
public class TransferServiceImpl {
    @Autowired
    private AccountDao accountDao;
}

如上代码所示,这样装配回去spring容器中找到类型为AccountDao的类,然后将其注⼊进来。这样会产⽣⼀个问题,当⼀个类型有多个bean值的时候,会造成⽆法选择具体注⼊哪⼀个的情况,这个时候我们需要配合着**@Qualifier**使⽤。
@Qualifier告诉Spring具体去装配哪个对象。

public class TransferServiceImpl {
    @Autowired
    @Qualifier(name="jdbcAccountDaoImpl")
    private AccountDao accountDao;
}


@Repository("accountDao")
public class JdbcAccountDaoImpl implements AccountDao {}
  • @Resource
    @Resource 默认按照 ByName ⾃动注⼊。
public class TransferService {
    @Resource
    private AccountDao accountDao;
    @Resource(name="studentDao")
    private StudentDao studentDao;
    @Resource(type="TeacherDao")
    private TeacherDao teacherDao;
    @Resource(name="manDao",type="ManDao")
    private ManDao manDao;
}
  • 如果同时指定了 name 和 type,则从Spring上下⽂中找到唯⼀匹配的bean进⾏装配,找不到则抛出异常。
  • 如果指定了 name,则从上下⽂中查找名称(id)匹配的bean进⾏装配,找不到则抛出异常。
  • 如果指定了 type,则从上下⽂中找到类似匹配的唯⼀bean进⾏装配,找不到或是找到多个,都会抛出异常。
  • 如果既没有指定name,⼜没有指定type,则⾃动按照byName⽅式进⾏装配;
    注意:@Resource 在 Jdk 11中已经移除,如果要使⽤,需要单独引⼊jar包
<dependency>
  <groupId>javax.annotation</groupId>
  <artifactId>javax.annotation-api</artifactId>
  <version>1.3.2</version>
</dependency>
2.3、纯注解模式
  • @Configuration:表名当前类是⼀个配置类
  • @ComponentScan:替代 context:component-scan
  • @PropertySource:引⼊外部属性配置⽂件
  • @Import :引⼊其他配置类
  • @Value:对变量赋值,可以直接赋值,也可以使⽤ ${} 读取资源配置⽂件中的信息
  • @Bean:将⽅法返回对象加⼊ SpringIOC 容器

3、IoC高级特性

3.1、lazy-Init 延迟加载

ApplicationContext 容器的默认⾏为是在启动服务器时将所有 singleton bean 提前进⾏实例化。提前实例化意味着作为初始化过程的⼀部分,ApplicationContext 实例会创建并配置所有的singleton bean。

<bean id="testBean" class="cn.sc.LazyBean" />
  
  <!-- 该bean默认为 -->
  <bean id="testBean" class="cn.sc.LazyBean" lazy-init="true" />

如果将lazy-init设置为true,bean 将不会在 ApplicationContext 启动时提前被实例化,⽽是第⼀次向容器通过 getBean 索取 bean 时实例化的。
注:

  • 如果⼀个设置了⽴即加载的 bean1,引⽤了⼀个延迟加载的 bean2 ,那么 bean1 在容器启动时被实例化,⽽ bean2 由于被 bean1 引⽤,所以也被实例化,这种情况也符合延时加载的 bean 在第⼀次调⽤时才被实例化的规则。
  • 如果⼀个 bean 的 scope 属性为 scope=“pototype” 时,即使设置了 lazy-init=“false”,容器启动时也不会实例化bean,⽽是调⽤ getBean ⽅法实例化的。也可以在容器层次设置延迟加载
<beans default-lazy-init="true">
    <!-- no beans will be eagerly pre-instantiated... -->
</beans>

应用场景

  1. 开启延迟加载一定程度提高容器启动和运转性能
  2. 对于不常使用的Bean设置延迟加载,这样偶尔使⽤的时候再加载,不必要从⼀开始该 Bean 就占⽤资源
3.2、FactoryBean 和 BeanFactory
  • BeanFactory是容器的顶级接⼝,定义了容器的⼀些基础⾏为,负责⽣产和管理Bean的⼀个⼯⼚,具体使⽤它下⾯的⼦接⼝类型,⽐如ApplicationContext;
  • FactoryBean:工厂Bean
// 可以让我们⾃定义Bean的创建过程(完成复杂Bean的定义)
public interface FactoryBean<T> {
    @Nullable
    // 返回FactoryBean创建的Bean实例,如果isSingleton返回true,则该实例会放到Spring容器的单例对象缓存池中Map
    T getObject() throws Exception;
    
    @Nullable
    // 返回FactoryBean创建的Bean类型
    Class<?> getObjectType();
    
    // 返回作⽤域是否单例
    default boolean isSingleton() {
        return true;
    }
}

Spring中Bean有两种,⼀种是普通Bean,⼀种是⼯⼚Bean(FactoryBean),FactoryBean可以⽣成某⼀个类型的Bean实例(返回给我们),也就是说我们可以借助于它⾃定义Bean的创建过程。

实例:
Company类

public class Company {
    private String name;
    private String address;
    private int scale;
}

CompanyFactoryBean类

public class CompanyFactoryBean implements FactoryBean<Company> {
    
    private String companyInfo; // 公司名称,地址,规模
    
    public void setCompanyInfo(String companyInfo) {
        this.companyInfo = companyInfo;
    }
    
    @Override
    public Company getObject() throws Exception {
        // 模拟创建复杂对象Company
        Company company = new Company();
        String[] strings = companyInfo.split(",");
        company.setName(strings[0]);
        company.setAddress(strings[1]);
        company.setScale(Integer.parseInt(strings[2]));
            return company;
    }
    
    @Override
    public Class<?> getObjectType() {
        return Company.class;
    }
    @Override
    public boolean isSingleton() {
        return true;
    }
}

xml配置

<bean id="companyBean" class="com.sc.edu.factory.CompanyFactoryBean">
    <property name="companyInfo" value="华为,深圳,500"/>
</bean> 
3.3、后置处理器

在BeanFactory初始化之后可以使⽤BeanFactoryPostProcessor进⾏后置处理做⼀些事情,在Bean对象实例化(并不是Bean的整个⽣命周期完成)之后可以使⽤BeanPostProcessor进⾏后置处理做⼀些事情。

Spring提供了两种后处理Bean的扩展接口:

  • BeanPostProcessor
    BeanPostProcessor是针对Bean级别的处理,可以针对某个具体的Bean。

image.png 接口提供了两个方法,分别在Bean的初始化⽅法前和初始化⽅法后执⾏,具体这个初始化⽅法指的是什么⽅法,类似我们在定义bean时,定义了init-method所指定的⽅法。
两个类型参数分别为Object和String,第⼀个参数是每个bean的实例,第⼆个参数是每个bean的name或者id属性的值。所以我们可以通过第⼆个参数,来判断我们将要处理的具体的bean。
注意:处理是发⽣在Spring容器的实例化和依赖注⼊之后。

  • BeanFactoryPostProcessor
    BeanFactory级别的处理,是针对整个Bean的⼯⼚进⾏处理,典型应⽤:PropertyPlaceholderConfigurer

image.png

image.png
此接口只提供了一个方法,方法参数为ConfigurableListableBeanFactory,该参数类型定义了⼀些⽅法。
其中有个⽅法名为getBeanDefinition的⽅法,我们可以根据此⽅法,找到我们定义bean 的BeanDefinition对象。然后我们可以对定义的属性进⾏修改,以下是BeanDefinition中的⽅法:

image.png⽅法名字类似我们bean标签的属性,setBeanClassName对应bean标签中的class属性,所以当我们拿到BeanDefinition对象时,我们可以⼿动修改bean标签中所定义的属性值。
BeanDefinition对象:我们在 XML 中定义的 bean标签,Spring 解析 bean 标签成为⼀个 JavaBean,这个JavaBean 就是 BeanDefinition
注意:调⽤ BeanFactoryPostProcessor ⽅法时,这时候bean还没有实例化,此时 bean 刚被解析成BeanDefinition对象

4、IoC源码解析

https://www.yuque.com/sc30/kuoicx/rb7s1s

Spring AOP基础及源码剖析

AOP本质:在不改变原有业务逻辑的情况下增强横切逻辑,横切逻辑代码往往是权限校验代码、⽇志代码、事务控制代码、性能监控代码。

1、AOP相关术语

在讲解AOP术语之前,我们先来看⼀下下⾯这两张图,它们就是第三部分案例需求的扩展(针对这些扩展的需求,我们只进⾏分析,在此基础上去进⼀步回顾AOP,不进⾏实现)

image.png

上图描述的就是未采⽤AOP思想设计的程序,当我们红⾊框中圈定的⽅法时,会带来⼤量的重复劳动。程序中充斥着⼤量的重复代码,使我们程序的独⽴性很差。⽽下图中是采⽤了AOP思想设计的程序,它把红框部分的代码抽取出来的同时,运⽤动态代理技术,在运⾏期对需要使⽤的业务逻辑⽅法进⾏增强。image.png

AOP术语

名词解释
Joinpoint(连接点)它指的是那些可以⽤于把增强代码加⼊到业务主线中的点,那么由上图中我们可以看出,这些点指的就是⽅法。在⽅法执⾏的前后通过动态代理技术加⼊增强的代码。在Spring框架AOP思想的技术实现中,也只⽀持⽅法类型的连接点。
Pointcut(切⼊点)它指的是那些已经把增强代码加⼊到业务主线进来之后的连接点。由上图中,我们看出表现层transfer⽅法就只是连接点,因为判断访问权限的功能并没有对其增强。
Advice(通知/增强)它指的是切⾯类中⽤于提供增强功能的⽅法。并且不同的⽅法增强的时机是不⼀样的。⽐如,开启事务肯定要在业务⽅法执⾏之前执⾏;提交事务要在业务⽅法正常执⾏之后执⾏,⽽回滚事务要在业务⽅法执⾏产⽣异常之后执⾏等等。那么这些就是通知的类型。其分类有:前置通知后置通知异常通知最终通知环绕通知。
Target(⽬标对象)它指的是代理的⽬标对象。即被代理对象。
Proxy(代理)它指的是⼀个类被AOP织⼊增强后,产⽣的代理类。即代理对象。
Weaving(织⼊)它指的是把增强应⽤到⽬标对象来创建新的代理对象的过程。spring采⽤动态代理织⼊,⽽AspectJ采⽤编译期织⼊和类装载期织⼊。
Aspect(切⾯)它指定是增强的代码所关注的⽅⾯,把这些相关的增强代码定义到⼀个类中,这个类就是切⾯类。例如,事务切⾯,它⾥⾯定义的⽅法就是和事务相关的,像开启事务,提交事务,回滚事务等等,不会定义其他与事务⽆关的⽅法。我们前⾯的案例中TrasnactionManager就是⼀个切⾯。
  • 连接点:⽅法开始时、结束时、正常运⾏完毕时、⽅法异常时等这些特殊的时机点,我们称之为连接点,项⽬中每个⽅法都有连接点,连接点是⼀种候选点
  • 切⼊点:指定AOP思想想要影响的具体⽅法是哪些,描述感兴趣的⽅法
  • Advice增强:
    • 第⼀个层次:指的是横切逻辑
    • 第⼆个层次:⽅位点(在某⼀些连接点上加⼊横切逻辑,那么这些连接点就叫做⽅位点,描述的是具体的特殊时机)
  • Aspect切⾯:切⾯概念是对上述概念的⼀个综合
    Aspect切⾯= 切⼊点+增强
    = 切⼊点(锁定⽅法) + ⽅位点(锁定⽅法中的特殊时机)+ 横切逻辑

2、AOP的代理选择

Spring 实现AOP思想使⽤的是动态代理技术默认情况下,Spring会根据被代理对象是否实现接⼝来选择使⽤JDK还是CGLIB。

  • 当被代理对象没有实现任何接⼝时,Spring会选择CGLIB。
  • 当被代理对象实现了接⼝,Spring会选择JDK官⽅的代理技术,不过我们可以通过配置的⽅式,让Spring强制使⽤CGLIB。

3、AOP的配置方式

在Spring的AOP配置中,也和IoC配置⼀样,⽀持3类配置⽅式。

  • 第⼀类:使⽤XML配置
  • 第⼆类:使⽤XML+注解组合配置
  • 第三类:使⽤纯注解配置

4、Spring中AOP实现

4.1、XML模式
  • AOP jar包
<dependency> 
  <groupId>org.springframework</groupId> 
  <artifactId>spring-aop</artifactId> 
  <version>5.1.12.RELEASE</version>
</dependency>
<dependency> 
  <groupId>org.aspectj</groupId> 
  <artifactId>aspectjweaver</artifactId> 
  <version>1.9.4</version>
</dependency>
  • AOP 核心配置
<!-- Spring基于XML的AOP配置前期准备: 在spring的配置⽂件中加⼊aop的约束 xmlns:aop="http://www.springframework.org/schema/aop" http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd   Spring基于XML的AOP配置步骤:细节关于切⼊点表达式上述配置实现了对 TransferServiceImpl 的 updateAccountByCardNo ⽅法进⾏增强,在其执⾏之前,输出了记录⽇志的语句。这⾥⾯,我们接触了⼀个⽐较陌⽣的名称:切⼊点表达式,它是做什么的呢?我们往下看。概念及作⽤切⼊点表达式,也称之为AspectJ切⼊点表达式,指的是遵循特定语法结构的字符串,其作⽤是⽤于对符合语法格式的连接点进⾏增强。它是AspectJ表达式的⼀部分。关于AspectJAspectJ是⼀个基于Java语⾔的AOP框架,Spring框架从2.0版本之后集成了AspectJ框架中切⼊点表达式的部分,开始⽀持AspectJ切⼊点表达式。切⼊点表达式使⽤示例 第⼀步:把通知Bean交给Spring管理 第⼆步:使⽤aop:config开始aop的配置 第三步:使⽤aop:aspect配置切⾯ 第四步:使⽤对应的标签配置通知的类型 ⼊⻔案例采⽤前置通知,标签为aop:before--><!--把通知bean交给spring来管理--><beanid="logUtil"class="com.lagou.utils.LogUtil"></bean>
<!--开始aop的配置-->
<aop:config>
  <!--配置切⾯--> 
  <aop:aspect id="logAdvice"ref="logUtil"> 
    <!--配置前置通知--> 
    <aop:before method="printLog" pointcut="execution(public*com.lagou.service.impl.TransferServiceImpl.updateAccountByCardNo(com.lagou.pojo.Account))">
      </aop:before> 
    </aop:aspect>
</aop:config>
  • 细节

    • 关于切⼊点表达式
      上述配置实现了对 TransferServiceImpl 的 updateAccountByCardNo ⽅法进⾏增强,在其执⾏之前,输出了记录⽇志的语句。这⾥⾯,我们接触了⼀个⽐较陌⽣的名称:切⼊点表达式,它是做什么的呢?我们往下看。

      • 概念及作⽤
        切⼊点表达式,也称之为AspectJ切⼊点表达式,指的是遵循特定语法结构的字符串,其作⽤是⽤于对符合语法格式的连接点进⾏增强。它是AspectJ表达式的⼀部分。

      • 关于AspectJ
        AspectJ是⼀个基于Java语⾔的AOP框架,Spring框架从2.0版本之后集成了AspectJ框架中切⼊点表达式的部分,开始⽀持AspectJ切⼊点表达式。

      • 切⼊点表达式使⽤示例

        • 全限定⽅法名 访问修饰符 返回值 包名.包名.包名.类名.⽅法名(参数列表)

        • 全匹配⽅式:
          public void com.lagou.service.impl.TransferServiceImpl.updateAccountByCardNo(com.lagou.pojo.Account)

        • 访问修饰符可以省略:
          void com.lagou.service.impl.TransferServiceImpl.updateAccountByCardNo(com.lagou.pojo.Account)

        • 返回值可以使⽤*,表示任意返回值:
          * c om.lagou.service.impl.TransferServiceImpl.updateAccountByCardNo(com.lagou.pojo.Account)

      • 包名可以使⽤.表示任意包,但是有⼏级包,必须写⼏个:
        * …TransferServiceImpl.updateAccountByCardNo(com.lagou.pojo.Account)

      • 包名可以使⽤…表示当前包及其⼦包:
        * …TransferServiceImpl.updateAccountByCardNo(com.lagou.pojo.Account)

      • 类名和⽅法名,都可以使⽤.表示任意类,任意⽅法:
        * …(com.lagou.pojo.Account)

      • 参数列表可以使⽤*,表示任意参数类型,但是必须有参数:
        * .()

      • 参数列表可以使⽤…,表示有⽆参数均可。有参数可以是任意类型:
        * .*(…)

      • 全通配⽅式:
        *** *…*.*(…)**

    • 改变代理⽅式的配置
      在前⾯我们已经说了,Spring在选择创建代理对象时,会根据被代理对象的实际情况来选择的。被代理对象实现了接⼝,则采⽤基于接⼝的动态代理。当被代理对象没有实现任何接⼝的时候,Spring会⾃动切换到基于⼦类的动态代理⽅式。但是我们都知道,⽆论被代理对象是否实现接⼝,只要不是final修饰的类都可以采⽤cglib提供的⽅式创建代理对象。所以Spring也考虑到了这个情况,提供了配置的⽅式实现强制使⽤基于⼦类的动态代理(即cglib的⽅式),配置的⽅式有两种:

      • 使⽤aop:config标签配置
<aop:configproxy-target-class="true">
      • 使⽤aop:aspectj-autoproxy标签配置
<!--此标签是基于XML和注解组合配置AOP时的必备标签,表示Spring开启注解配置AOP的⽀持-->
<aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>
    • 五种通知类型

      • 前置通知
        配置⽅式:aop:before标签
<!-- 作⽤: 
                ⽤于配置前置通知。 
            出现位置: 它只能出现在aop:aspect标签内部 

     属性: 
                method:⽤于指定前置通知的⽅法名称 
                pointcut:⽤于指定切⼊点表达式 
                pointcut-ref:⽤于指定切⼊点表达式的引⽤
-->
<aop:beforemethod="printLog"pointcut-ref="pointcut1"></aop:before>

执行时机
前置通知永远都会在切⼊点⽅法(业务核⼼⽅法)执⾏之前执⾏。
细节:前置通知可以获取切⼊点⽅法的参数,并对其进⾏增强。

  • 正常执⾏时通知
    配置方式
<!-- 
    作⽤: 
            ⽤于配置正常执⾏时通知 
        出现位置: 
            它只能出现在aop:aspect标签内部 

    属性: 
        method:⽤于指定后置通知的⽅法名称 
        pointcut:⽤于指定切⼊点表达式 
        pointcut-ref:⽤于指定切⼊点表达式的引⽤ 
-->
<aop:after-returning method="afterReturningPrintLog"pointcutref="pt1"></aop:after-returning>
      • 异常通知
        配置⽅式
<!-- 
    作⽤: 
        ⽤于配置异常通知。 
    出现位置: 
        它只能出现在aop:aspect标签内部 

    属性: 
        method:⽤于指定异常通知的⽅法名称 
        pointcut:⽤于指定切⼊点表达式
        pointcut-ref:⽤于指定切⼊点表达式的引⽤  
-->
<aop:after-throwing method="afterThrowingPrintLog"pointcut-ref="pt1"></aop:after-throwing>

执⾏时机
异常通知的执⾏时机是在切⼊点⽅法(业务核⼼⽅法)执⾏产⽣异常****之后,异常通知执⾏。如果切⼊点⽅法执⾏没有产⽣异常,则异常通知不会执⾏。
细节:异常通知不仅可以获取切⼊点⽅法执⾏的参数,也可以获取切⼊点⽅法执⾏产⽣的异常信息。

  • 最终通知
    配置⽅式
<!-- 
    作⽤: 
        ⽤于指定最终通知。 
    出现位置: 
        它只能出现在aop:aspect标签内部 

    属性: 
        method:⽤于指定最终通知的⽅法名称 
        pointcut:⽤于指定切⼊点表达式 
        pointcut-ref:⽤于指定切⼊点表达式的引⽤
-->
<aop:after method="afterPrintLog"pointcut-ref="pt1"></aop:after>

执⾏时机
最终通知的执⾏时机是在切⼊点⽅法(业务核⼼⽅法)执⾏完成之后,切⼊点⽅法返回之前执⾏。换句话说,⽆论切⼊点⽅法执⾏是否产⽣异常,它都会在返回之前执⾏。
细节:最终通知执⾏时,可以获取到通知⽅法的参数。同时它可以做⼀些清理操作。

  • 环绕通知
    配置⽅式
<!-- 
    作⽤: 
        ⽤于配置环绕通知。 
    出现位置: 
        它只能出现在aop:aspect标签的内部 

    属性: 
        method:⽤于指定环绕通知的⽅法名称 
        pointcut:⽤于指定切⼊点表达式 
        pointcut-ref:⽤于指定切⼊点表达式的引⽤  
-->
<aop:around method="aroundPrintLog"pointcut-ref="pt1"></aop:around>

特别说明:
环绕通知,它是有别于前⾯四种通知类型外的特殊通知。前⾯四种通知(前置,后置,异常和最终)它们都是指定何时增强的通知类型。⽽环绕通知,它是Spring框架为我们提供的⼀种可以通过编码的⽅式,控制增强代码何时执⾏的通知类型。它⾥⾯借助的ProceedingJoinPoint接⼝及其实现类,实现⼿动触发切⼊点⽅法的调⽤。

4.2、XML+注解模式
  • XML 中开启 Spring 对注解 AOP 的⽀持:aop:aspectj-autoproxy/
4.3、注解模式
  • 在配置类中使⽤如下注解进⾏替换上述配置(aop:aspectj-autoproxy/
@Configuration@ComponentScan("com.lagou")
@EnableAspectJAutoProxy
//开启spring对注解AOP的⽀持
publicclassSpringConfiguration {
}

5、Spring声明式事务的支持

PlatformTransactionManager
此接⼝是Spring的事务管理器核⼼接⼝。Spring本身并不⽀持事务实现,只是负责提供标准,应⽤底层⽀持什么样的事务,需要提供具体实现类。此处也是策略模式的具体应⽤。

在Spring框架中,也为我们内置了⼀些具体策略,例如:DataSourceTransactionManager,HibernateTransactionManager等等。DataSourceTransactionManager 归根结底是横切逻辑代码,声明式事务要做的就是使⽤Aop(动态代理)来将事务控制逻辑织⼊到业务代码

publicinterfacePlatformTransactionManager {
    
    
    /**
    * 获取事务状态信息
    */
    TransactionStatusgetTransaction(@NullableTransactionDefinitiondefinition)throwsTransactionException;
    
    /**
    * 提交事务
    */
    void commit(TransactionStatusstatus) throwsTransactionException;
    
    /**
    * 回滚事务
    */
    void rollback(TransactionStatusstatus) throwsTransactionException;

}
5.1、纯xml模式
<tx:advice id="txAdvice" transaction-manager="transactionManager"> 
  <!--定制事务细节,传播⾏为、隔离级别等--> 
  <tx:attributes> 
    <!--⼀般性配置--> 
    <tx:method name="*" read-only="false" propagation="REQUIRED" isolation="DEFAULT" timeout="-1"/> 
    <!--针对查询的覆盖性配置--> 
    <tx:method name="query*" read-only="true" propagation="SUPPORTS"/> 
  </tx:attributes> 
  </tx:advice> 
<aop:config> 
  <!--advice-ref指向增强=横切逻辑+⽅位--> 
  <aop:advisor advice-ref="txAdvice" pointcut="execution(*com.lagou.edu.service.impl.TransferServiceImpl.*(..))"/> 
</aop:config>
5.2、基于XML+注解
  • xml配置
<!--配置事务管理器-->
<bean id="transactionManager"class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> 
  <property name="dataSource"ref="dataSource">
  </property>
</bean>
<!--开启spring对注解事务的⽀持-->
<tx:annotation-driven transaction-manager="transactionManager"/>
  • 在接⼝、类或者⽅法上添加@Transactional注解
5.3、基于纯注解

Spring基于注解驱动开发的事务控制配置,只需要把 xml 配置部分改为注解实现。

只是需要⼀个注解替换掉xml配置⽂件中的<tx:annotation-driven transactionmanager=“transactionManager”/>配置。

在 Spring 的配置类上添加 @EnableTransactionManagement 注解即可

@EnableTransactionManagement
//开启spring注解事务的⽀持
publicclassSpringConfiguration {
}

6、AOP源码深度剖析

https://www.yuque.com/sc30/kuoicx/nks5zg













我只是觉得自己不仅仅如此,所以在努力呀!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值