Spring学习笔记

Spring

  • IOC容器(IOC inversion of control 控制反转)

    • 什么是IOC

      • 控制反转,把对象创建和对象之间的调用过程,交给Spring进行管理
      • 使用IOC的目的:降低耦合度
    • IOC底层实现原理:

      • XML解析、工厂模式、反射
    • IOC接口(BeanFactory)

      • IOC思想基于IOC容器实现,IOC容器底层就是对象工厂
      • Spring提供IOC容器实现两方式(两个接口):
        • BeanFactory:IOC容器基本实现,是Spring内部的使用接口,不提供开发人员使用,加载配置文件时,不会创建对象,在获取对象时才会取创建对象
        • ApplicationContext:BeanFactory接口的子接口,功能比父接口更加强大,一般由开发人员使用。加载配置 文件时就会把配置文件对象进行创建
        • ApplicationContext的两个主要实现类:
          • FileSystemXmlApplicationContext需要绝对路径
          • ClassPathXmlApplicationContext需要相对路径
    • IOC操作Bean管理(基于xml):

    • <bean id="book" class="com.atguigu.spring5.Book">
          <property name="bname"value="val1"></property>
          <property name="cname"value="val2"></property>
          // 字面值设置空值
          <property name="dname">
              <null/>
          </property>
          //属性值包含特殊字符(可以转义&gt;&lt;或者CDATA)
          <property name="ename">
              <value><![CDATA[内容]]></value>
          </property>
      </bean>
      
    • 数组类型属性的注入(list map set等类似)

    • <bean id="book" class="com.atguigu.spring5.Book">
          <property name="array">
              <array>
                  <value>book1</value>
                  <value>book2</value>
              </array>
          </property>
      </bean>
      
  • spring全家桶:spring springmvc springboot springcloud

  • IOC (Inversion of control) : 控制反转,是一个理论、概念、思想

    • 描述:把对象的创建,赋值,管理工作都交给代码之外的容器实现,也就是对象的创建是由其他外部资源完成

    • 控制:创建对象,对象的属性赋值,对象之间的关系管理

    • 反转:把原来的开发人员管理,创建对象的权限交给代码之外的容器实现,由容器代替开发人员管理对象,创建对象,给对象赋值。

    • 正转:由开发人员在代码中,使用new构造方法创建对象,开发人员主动管理对象

    • 容器:是一个服务器软件,一个框架(spring)

    • 为什么要使用ioc?

      • 目的就是减少代码的改动,也能实现不同的共能,实现解耦合
    • java中创建对象的方式有?

      • 构造方法
      • 反射
      • 序列化
      • 克隆
      • ioc:容器创建对象
      • 动态代理
    • 什么样的对象放入到容器中?

      • dao类、 service类、controller类、工具类
        • 使用xml配置文件,使用
        • 注解
      • spring中的对象都是单例的,在容器中叫这个名称的对象只有一个
    • 不放到spring容器中的对象

      1. 实体类对象,实体类对象来自数据库
      2. servlet、listener、fliter等
    • IOC的技术实现:

      • DI(Dependency Injection):依赖注入,只需要在程序中提供要使用的对象名称就行,至于对象如何在容器中创建,赋值,查找都交给容器实现
      • spring使用di实现了ioc的功能,spring底层创建对象,使用的是反射机制。
    • spring的依赖:在maven的pom.xml中加入对应的依赖

      <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-context</artifactId>
          <version>5.2.5.RELEASE</version>
      </dependency>
      
    • 在resources目录下的bean.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"
             xsi:schemaLocation="
          http://www.springframework.org/schema/beans
          http://www.springframework.org/schema/beans/spring-beans.xsd">
          <bean id="hello" class="com.javase.pojo.impl.HelloImpl"></bean>
      </beans>
      <!--
      告诉spring创建对象
      声明bean,就是告诉spring要创建某个对象	
      id:对象的自定义名称,唯一值,spring要通过这个名称找到要创建的对象
      class:类的全限定名称(不能是接口,因为spring是反射机制创建对象,必须使用类)
       -->
      
    • 用spring创建java对象

      public void test01() {
              String config = "beans.xml";
              ApplicationContext applicationContext = new ClassPathXmlApplicationContext(config);
              Hello hello = (Hello) applicationContext.getBean("hello");
              hello.dosome();
       }
      
    • spring默认创建对象的时间:在创建spring容器的时候,会创建配置文件中所有的对象

          public void test02() {
              String config = "beans.xml";
              ApplicationContext ac = new ClassPathXmlApplicationContext(config);
              // 获取容器中对象的数量
              int count = ac.getBeanDefinitionCount();
              System.out.println("spring容器中对象的数量为" + count);
              // 获取容器中所有对象的名字
              String[] names = ac.getBeanDefinitionNames();
              System.out.println("spring容器中对象的名字有:");
              System.out.println(Arrays.toString(names));
      
          }
      
    • 在spring中规定java的基本数据类型和String都是简单类型

    • bean对象分为普通bean和工厂bean(可以改变xml中name定义的类型,需要对应的类实现Factory接口,更改对应的getbean方法)

    • 在spring中默认时单实例对象,配置文件中配置的bean,一个id对用一个实例,多次创建获得的对象地址一样

      • 如何设置单实例和多实例
        1. 在spring配置文件bean标签里面有属性scope用于设置单实例还是多实例
        2. 第一个值:singleton,单实例(默认)
        3. 第二个值:prototype,多实例
        4. 注意:设置scope值是singleton时候,加载sprng配置文件时就会创建单实例对象;设置scope值是prototype时,不是在加载配置文件时创建对象,而是在调用getBean方法的时候创建多实例对象
    • bean的生命周期

      • 概念:从对象创建到销毁的过程
      • bean的生命周期:(若加上后置处理器,有7步)
        1. 通过构造器创建bean实例(无参数构造)
        2. 为bean的属性设置值和对其他bean引用(调用set方法)
        3. 调用bean的初始化的方法(需要进行配置初始化的方法)xml文件bean的一个属性init-method中配置
        4. bean可以使用了(对象获取到了)
        5. 当容器关闭的时候,调用bean的销毁方法(需要进行配置销毁的方法)xml文件bean的一个属性destroy-method中配置,需要调用context.close()方法销毁实例
    • 如何给对象赋值:

      • di:依赖注入,表示创建对象,给对象赋值

      • di的实现方法:

        1. 在spring的配置文件中,使用标签和属性完成,叫做基于XML的di实现
        2. 使用spring中的注解,完成属性赋值,叫做基于注解的di实现
      • di的语法分类:

        1. set注入:spring调用类的set方法,在set方法可以实现属性的赋值
        2. 构造注入:spring调用类的有参构造方法,创建对象,在构造方法中完成赋值
      • 简单类型的set注入:

        <bean id="student" class="com.javase.pojo.impl.Student">
                <property name="age" value="18"></property>
                <property name="name" value="张三"></property>
        </bean>
        
      • 引用类型的set注入:

            <bean id="student" class="com.javase.pojo.impl.Student" >
                <property name="age" value="18"></property>
                <property name="name" value="张三"></property>
                <property name="home" ref="home"></property>
            </bean>
            <bean id="home" class="com.javase.pojo.impl.Home">
                <property name="address" value="河南省登封市颖阳镇"></property>
            </bean>
        
      • 数组、List、Map的注入

        <bean id="student" class="com.javase.demo.Student">
                <property name="courses">
                    <array>
                        <value>""</value>
                    </array>
                </property>
                <property name="list">
                    <ref bean=""></ref>
                </property>
                <property name="map">
                    <map>
                        <entry key="" value=""></entry>
                    </map>
                </property>
        </bean>
        
      • 构造注入

        <!--使用name属性进行赋值-->
        <bean id="student" class="com.javase.pojo.impl.Student">
                <constructor-arg name="age" value="18"></constructor-arg>
                <constructor-arg name="name" value="李四"></constructor-arg>
                <constructor-arg name="home" ref="home"></constructor-arg>
        </bean>
        <bean id="home" class="com.javase.pojo.impl.Home">
                <property name="address" value="河南省登封市颖阳镇"></property>    
        </bean>
        
            <!--使用index属性赋值-->
            <bean id="student" class="com.javase.pojo.impl.Student">
                <constructor-arg index="0" value="18"></constructor-arg>
                <constructor-arg index="1" value="李四"></constructor-arg>
                <constructor-arg index="2" ref="home"></constructor-arg>
            </bean>
            <bean id="home" class="com.javase.pojo.impl.Home">
                <property name="address" value="河南省登封市颖阳镇"></property>
            </bean>
        
        <!--如果index和构造方法顺序一致,可以省略index-->
        <bean id="student" class="com.javase.pojo.impl.Student">
                <constructor-arg  value="18"></constructor-arg>
                <constructor-arg  value="李四"></constructor-arg>
                <constructor-arg  ref="home"></constructor-arg>
        </bean>    
        <bean id="home" class="com.javase.pojo.impl.Home">
                <property name="address" value="河南省登封市颖阳镇"></property>    
        </bean>
        
      • 引用类型的自动注入:spring框架根据某些规则可以给引用类型赋值,不用开发人员手动给引用类型赋值

        • byName(按名称注入):java类中引用类型的属性名和spring容器中(配置文件)的id名称一样,且数据类型是一致的,这样容器中的bean,spring能赋值给引用类型
        <!--byName-->
        <bean id="student" class="com.javase.pojo.impl.Student" autowire="byName">
                <property name="age" value="18"></property>
                <property name="name" value="张三"></property>
            	<!--<constructor-arg name="home" ref="home"></constructor-arg>-->
        </bean>
            <!--使用index属性赋值-->   
        <bean id="home" class="com.javase.pojo.impl.Home">
                <property name="address" value="河南省登封市颖阳镇"></property>
        </bean>
        
        • byType(按类型注入):java类中引用类型的数据类型和spring容器中(配置文件)的class属性是同源关系,这样的bean能够赋值给引用类型

          • 同源:
            1. java类中引用类型的数据类型和bean的class的值是一样的
            2. java类中引用类型的数据类型和bean的class的是父子类关系
            3. java类中引用类型的数据类型和bean的class的值是接口和实现类关系
              <bean id="student" class="com.javase.pojo.impl.Student" autowire="byType">
                  <property name="age" value="18"></property>
                  <property name="name" value="张三"></property>
              </bean>
              <!--使用index属性赋值-->
              <bean id="myHome" class="com.javase.pojo.impl.Home">
                  <property name="address" value="河南省登封市颖阳镇"></property>
              </bean>
          

          注意:在byType中,在xml配置文件中声明bean只能有一个符合条件的,多于一个是错误的

    • 当项目的规模过于庞大时,建议使用多个配置文件来注入对象。

      • 可以使用一个主配置文件并引入其他配置文件,来管理配置文件

      • 在包含关系的配置文件中也可以使用通配符*(表示任意字符)

      • <import resource="profile.xml"></import>
        <import resource="classpath:ba06/spring-*"></import>
        
    • 基于注解的DI

      • 使用注解的步骤:

        1. 加入maven的依赖spring-context,在你加入spring-context的同时,间接加入spring-aop的依赖,使用注解必须使用spring-aop
        2. 在类中加入spring的注解(多个不同功能的注解)
        3. 在spring的配置文件中,加入一个组件扫描器的标签,说明注解在你的项目中的位置
      • 主要学习的7个注解:

        1. @Component
        2. @Respotory
        3. @Service
        4. @Controller
        5. @Value
        6. @Autowired
        7. @Resource
      • <!--
         声明组件扫描器(component-scan),组件就是java对象
        base-package:指定注解在你的项目中的包名
        component-scan工作方式:spring会扫描遍历base-package指定的包,把包和子包中的所有类,找到类的注解,按照注解的功能创建对象,或给属性赋值
        -->
        <context:component-scan base-package="com.javase.pojo"/>
        
      • @Compent(value = "user")
        等同于:<bean id="user" class="com.javese.pojo.User"/>
        也可以省略value @Compent("user")
        
      • spring中和@Component功能一致,创建对象的注解还有:

        1. @Repository(用在持久层类的上面):放在dao的实现类上面表示创建dao对象,dao对象是能访问数据库的。
        2. @Service(用在业务层上面):放在service的实现类上面,创建service对象,service对象是做业务处理的,可以有事务等功能
        3. @Controller(用在控制器的上面):放在控制器(处理器)类的上面,创建控制器对象的,控制器对象,能够接收用户提交的参数,显示请求处理的结果。
      • 以上三个注解的使用语法和@Component一样,都能创建对象,但是这三个注解还有额外的功能,@Repository,@Service, @Controller都是给对象分层的

      • 什么时候使用@Component呢?

        • 当你无法区分这个类是属于业务层,持久层,控制层的时候使用
      • 指定多个包的三种方式:

        • 在组件扫描器的属性中可以配置use-default-filters来指定扫描的注解类型比如只扫秒@Controller
            <!--第一种方式:使用多次组件扫描器,指定不同的包-->
            <context:component-scan base-package="com.javase.pojo.impl"/>
            <context:component-scan base-package="com.javase.dao"/>
            <!--第二种方式:使用分隔符;或者,分隔多个包-->
            <context:component-scan base-package="com.javase.pojo.impl;com.javase.dao"/>
            <!--第三种方式:指定父包-->
            <context:component-scan base-package="com.javase"/>
        
      • @Value 简单类型的属性赋值:

        属性:value 是String类型的,表示简单类型的属性值
        位置:1.在属性定义的上面,无需set方法,推荐使用
             2.在set方法的上面
        
      • 引用类型:

        @Autowired:spring框架提供的注解,实现引用类型的赋值
        spring中通过给引用类型赋值,使用的是自动注入原理,支持byName,byType
        @Autowired:默认使用的是byType
        位置:1.在属性定义的上面,无需set方法,推荐使用
             2.在set方法上使用
        属性:required,是一个boolean类型的,默认是true
        	required=true:表示引用类型赋值失败,程序报错,并终止执行
        	required=false:引用类型如果赋值失败,程序正常执行,引用类型是null
        	推荐设为true
        ----------------------------------------------------
        如果使用byName的方式;
        	1.在属性上面加入@Autowired
        	2.在属性上面加入@Qualilfier(value=“bean的id”):表示使用指定名称的bean完成赋值
        	3.两个注解没有先后顺序
        -----------------------------------------------------
        @Resource标签:(jdk中的标签)
        	1.先使用byName自动注入,如果byName赋值失败,再使用byType
        	2.如果只使用byName,则再标签的name属性指定对应类的name值
        
      • 加载属性配置文件:

        <context:property-placeholder location="classpath:test.properties"/>
        加载配置文件后可以在类中使用${name}来获取对应的value值,除了类中配置文件中也可以
        
      • ioc实现解耦合:ioc能够实现业务对象之间的解耦合,例如service和dao对象之间的解耦合

  • 动态代理:可以在程序执行过程中,创建代理对象

    • 通过代理对象执行方法,给目标类的方法增加额外功能
    • jdk动态代理实现步骤:
      1. 创建目标类,SomeServiceImpl目标类,给它的dosome方法,doother方法增加输出时间,事务
      2. 创建InvocationHandler接口的实现类,在这个类实现给目标方法增加功能
      3. 使用jdk类Proxy,创建代理对象,实现创建对象的能力
  • AOP(Aspect Orient Programming)面向切面编程

    • 动态代理:

      • 实现方式:jdk动态代理,使用jdk中的Proxy,Method, InvocationHandler创建代理对象, jdk动态代理要求目标类必须实现接口
      • cglib动态代理:第三方的工具库,创建代理对象,原理是继承,通过继承目标类,创建子类。子类就是代理对象,要求目标类不能被final修饰,方法也不能被final修饰
    • 动态代理的作用:

      1. 在目标类源代码不改变的情况下,增加功能
      2. 减少代码的重复
      3. 专注业务代码
      4. 解耦合,让业务功能和日志、事务等非业务功能分离
    • Aop:面向切面编程,基于动态代理的,可以使用jdk,cglib两种代理方式。Aop就是动态代理的规范化,把动态代理的实现步骤,方式都定义好了,让开发人员用一种统一的方式,使用动态代理。

    • 切面

      • 理解:给你的目标类增加的功能就是切面,想上面的日志、事务都是切面。
      • 特点:一般都是非业务方法,独立使用的
    • 怎么理解切面编程?

      1. 需要在分析项目功能时,找出切面
      2. 合理的安排切面的执行时间(在目标方法之前还是目标方法之后)
      3. 合理的安排切面执行的位置,在哪个类,哪个方法增加功能
    • 术语:

      1. Aspect:切面,表示增强的功能,就是一堆代码,完成某一个功能,非业务功能,常见的切面功能有日志、事务、统计信息、参数检查、权限验证
        • 是一个动作
        • 把通知应用到切入点的过程
      2. JoinPoint:连接点,连接业务方法和切面的位置,就是某个业务方法(类里面哪些方法可以被增强,这些方法称为连接点)
      3. Pointcut:切入点,指多个连接点的方法的集合,多个方法(实际被真正增强的方法,称为切入点)
      4. 目标对象:给哪个类的方法增加功能,这个类就是目标对象
      5. Advice:通知,通知表示切面功能执行的时间
        • 实际增强的逻辑部分称为通知(增强)
        • 通知的类型:
          1. 前置通知
          2. 后置通知
          3. 环绕通知
          4. 异常通知
          5. 最终通知
    • 切面的三个关键要素:

      1. 切面的功能代码,切面干什么
      2. 切面的执行位置,使用poingcut表示切面执行的位置
      3. 切面的执行时间,使用Advice表示时间,在目标方法之前,还是目标方法之后
    • Aop的实现

      • aop是一个规范,是动态代理的规范化,是一个标准

      • aop的技术实现框架:

        1. spring:spring在内部实现了aop规范,能做aop的工作,spring主要在事务处理的时候用aop。我们在项目开发中很少使用spring的aop实现,因为它比较笨重
        2. aspectJ:一个开源的专门做aop的框架,spring框架集成了aspectJ框架,通过spring就能使用aspectJ的功能
      • 切入点表达式

        1. 切入点表达式作用:知道对哪个类的哪个方法进行增强
        2. 语法结构:execution (【权限修饰符】【返回值类型】【类全路径】【方法名称】【参数列表】)
        3. 返回值类型可以忽略

        [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-elWZLBPi-1683813062609)(C:\Users\12086\AppData\Roaming\Typora\typora-user-images\image-20221024082419847.png)]

      • aspectJ框架实现aop有两种方式:

        1. 使用xml的配置文件:配置全局事务
        2. 使用注解,我们在项目中要做aop的功能,一般都使用注解,aspectJ有5个注解
      • aspectJ框架的使用:

        • 切面的执行时间(Advice 通知,增强)

          • 在aspectJ框架中使用注解表示,也可以使用xml配置文件中的标签
          • 常用的注解:
            1. @Before
            2. @AfterReturning
            3. @Around
            4. @AfterThrowing
            5. @After
        • 使用aspectJ实现aop的基本步骤:

          1. 新建maven项目

          2. 加入依赖: spring 依赖 aspect依赖 junit单元测试依赖

          3. 创建目标类:接口和他的实现类

            要做的是给类中的方法增减功能

          4. 创建切面类:普通类

            • 在类的上面加入@Aspect
            • 在类中定义方法,方法就是切面要执行的功能代码,在方法上面加入aspectj中的注解,例如@Before,还需要指定切入表达式execution()
          5. 创建spring的配置文件:声明对象,把对象交给容器统一管理,声明对象可以使用注解或者xml配置文件

            • 声明目标对象(创建业务对象)

            • 声明切面类对象(创建切面对象)

            • 声明aspectJ框架中的自动代理生成器标签。

              自动代理生成器:用来完成代理对象的自动创建功能的(绑定)

          6. 创建测试类,从spring容器中获取目标对象(实际上就是代理对象),通过代理执行方法,实现aop的功能增强

        • aspectJ的依赖:

              <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-aspects</artifactId>
                <version>5.0.5.RELEASE</version>
              </dependency>
          
        • 相关代码实现:

          //连接点
          @Component(value = "someService")
          public class SomeServiceImpl implements SomeService {
              @Override
              public void doSome() {
                  System.out.println("-------------doSome方法执行了!----------------");
              }
          }
          -------------------------------------------------------------------------
          // 定义切面类
          @Component(value = "myAspect")
          @Aspect
          public class MyAspect {
              @Before(value = "execution(public void com.javase.service.SomeServiceImpl.doSome())")
              public void aspect01() {
                  System.out.println("切面方法执行了,现在时间是" + new Date());
          
              }
          }
          --------------------------------------------------------------------------
              // 配置文件
              <context:component-scan base-package="com.javase.service"/>
          
              <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
          -------------------------------------------------------------------------
            	//测试类
              @Test
              public void test05() {
                  String config = "beans.xml";
                  ApplicationContext applicationContext = new ClassPathXmlApplicationContext(config);
                  SomeService proxy = (SomeService)applicationContext.getBean("someService");
                  proxy.doSome();
              }
          
          
        • 有多个增强类对同一个方法进行增强,设置增强优先级

          • 在增强类上面添加注解@Order(数字类型值),数字类型越小优先级越高
        • 什么时候考虑使用aop技术?

          1. 当你要给一个系统中存在的类修改功能,但是原有类的功能完善,但是你没有源代码,使用aop就能增加功能
          2. 你要给项目中的多个类,添加同一个功能,使用aop
          3. 给业务方法添加事务、日志输出等
        • 通知指定方法的参数:JoinPoint

          • JoinPoint:业务方法,要加入切面方法的业务方法

          • 作用:可以在通知方法中获取方法的执行信息,例如方法的名称、方法的实参

          • 如果你的切面功能中需要用到方法的信息,就加入JoinPoint

          • 这个JoinPoint参数的值是由框架赋予,必须是第一个位置的参数

            @Test
            public void test05() {
                String config = "beans.xml";
                ApplicationContext applicationContext = new ClassPathXmlApplicationContext(config);
                SomeService proxy = (SomeService)applicationContext.getBean("someService");
                proxy.doSome("jack", 18);
            }
            
        • @AfterReturning

          • 后置通知

          • 属性:

            1. value 切入表达式
            2. returning 自定义变量,表示目标方法的返回值的
          • 位置:在方法定义的上面

          • 特点:

            1. 在目标方法之后执行的
            2. 能够获取到目标方法的返回值,可以根据这个返回值做不同的处理功能
            3. 可以修改这个返回值(Object = doOther(); aspect02(res) ;)
          • 代码:

                @AfterReturning(returning = "res", value = "execution(public * com.javase.service.SomeServiceImpl.doOther(..))")
                public void aspect02(Object res) {
                    User user = (User) res;
                    user.setName("胡一帆");
                    user.setId(8);
                    user.setWork("湖北省咸宁市");
                }
            
        • @Around

          • 环绕通知:

            1. public
            2. 必须有一个返回值,推荐使用Object
            3. 方法名称自定义
            4. 方法有参数,固定的参数ProceedingJoinPoint
          • 特点:

            1. 它是功能最强的通知
            2. 在目标方式的前和后都能增强功能
            3. 控制目标方法是否被调用执行
            4. 修改原来的目标方法的执行结果,影响最后的调用结果
          • 环绕通知等同于jdk动态代理的InvocationHandler接口

          • 参数:ProceedingJoinPoint等同于Method(继承了JoinPoint)

            作用:执行目标方法

          • 返回值:就是目标方法的执行结果,可以被修改

          • 环绕通知经常做事务:在目标方法之前开启事务,执行目标方法,在目标方法之后提交事务

          • 代码:

            @Override
                public User doOther(String name, int id, String work) {
                    System.out.println("----------------doOther方法执行了!------------------");
                    return new User(name, id, work);
                }
            ----------------------------------------------------------------------------
                @Around(value = "myCut()")
                public void aspect03(ProceedingJoinPoint pjp) throws Throwable {
                    System.out.println("around在目标方法调用之前执行!");
                    Object[] args = pjp.getArgs();
                    String name = (String) args[0];
                    if (name.equals("黄豪杰")) {
                        User user = (User) pjp.proceed();
                        user.setName("黄豪杰真帅!");
                        System.out.println(user);
                    }
                    System.out.println("around在目标方法之后执行!");
                }
            ----------------------------------------------------------------------------
                    @Test
                public void test06() {
                    String config = "beans.xml";
                    ApplicationContext applicationContext = new ClassPathXmlApplicationContext(config);
                    SomeService proxy = (SomeService) applicationContext.getBean("someService");
                    proxy.doOther("黄豪杰", 7, "四川内江");
                    System.out.println("test方法结束!");
            
                }
            
          • @Pointcut:

            • 定义和管理切入点,如果你的项目中有多个切入点表达式是重复的,可以复用的,可以使用@Pointcut

            • 属性:value 切入点表达式

            • 位置:在自定义的方法上

            • 特点:

              • 当使用@Pointcut定义在一个方法的上面,此时这个方法的名称就是切入点表达式的别名。其他的通知中,value属性就可以使用这个方法名称,代替切入点表达式了
            • 代码:

                  @Pointcut(value = "execution(public * com.javase.service.SomeServiceImpl.do*(..))")
                  public void myCut() {
                      // 方法内不需要任何代码
                  }
               @Around(value = "myCut()")
                  public void aspect03(ProceedingJoinPoint pjp) throws Throwable {
                     // 相关代码
                  }
              
          • cglib动态代理:目标类没有接口(没有上面代码上的SomeServiceImpl),使用cglilb动态规划,spring框架会自动应用cglib动态代理

            • 在已经有接口的情况下使用cglib动态代理:在applicationContext.xml文件配置

              <aop:aspectj-autoproxy proxy-target-class="true"/>
              
    • Spring和MyBatis集成使用

      • 使用的技术:ioc

      • 为什么使用ioc:能把mybatis和spring集成在一起,像一个框架,是因为ioc能创建对象。可以把mybatis框架中的对象交给spring统一创建,开发人员从spring中获取对象,开发人员就不用同时面对两个或多个框架了,就面对一个spring

      • mybatis的使用步骤:

        1. 定义dao接口,StudentDao

        2. 在接口同一个目录下创建Dao对应的mapper文件

        3. 定义mybatis的主配置文件,mybatis.xml

        4. 创建dao的代理对象,StudentDao dao = SqlSession.getMapper(StudentDao.class);

          List students = dao.selectStudents();

      • 使用getMapper方法需要的条件:

        1. 获取SqlSession对象,需要使用SqlSessionFactory的openSession()方法
        2. 通过读取mybatis.xml 主配置文件,创建SqlSessionFactory对象
      • 关于连接池:我们会使用独立的连接池类替换mybatis默认自己带的,把连接池类也交给spring创建

      • spring需要创建的对象:

        1. 独立的连接池对象,使用阿里的druid连接池
        2. SqlSessionFactory对象
        3. 创建对应的dao对象
    • 步骤:

      1. 新建maven项目
      2. 加入maven依赖
        1. spring依赖
        2. mybatis依赖
        3. mysql驱动
        4. spring的事务依赖
        5. mybatis和spring集成的依赖:mybatis官方提供的,用来在spring项目中创建mybatis的sqlsessionfactory,dao对象
      3. 创建实体类
      4. 创建dao接口和mapper文件
      5. 创建mybatis主配置文件
      6. 创建service接口和实现类,属性是dao
      7. 创建spring的配置文件:声明mybatis的对象交给spring创建
        1. 数据源(数据库连接池)
        2. SqlSessionFactory
        3. dao对象
        4. 声明自定义的service
      8. 创建测试类:获取service对象,通过service调用dao完成数据的访问
    • 需要加入的依赖:

      <?xml version="1.0" encoding="UTF-8"?>
      
      <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
      
        <groupId>com.javase</groupId>
        <artifactId>spring01</artifactId>
        <version>1.0</version>
      
        <name>spring01</name>
        <!-- FIXME change it to the project's website -->
        <url>http://www.example.com</url>
      
        <properties>
          <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
          <maven.compiler.source>1.8</maven.compiler.source>
          <maven.compiler.target>1.8</maven.compiler.target>
        </properties>
      
        <dependencies>
          <!--单元测试依赖-->
          <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11</version>
            <scope>test</scope>
          </dependency>
          <!--spring核心依赖-->
          <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.0.5.RELEASE</version>
          </dependency>
          <!--下面两个是spring事务需要的依赖-->
          <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>5.0.5.RELEASE</version>
          </dependency>
          <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.0.5.RELEASE</version>
          </dependency>
      
          <!--mybatis依赖-->
          <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.1</version>
          </dependency>
      
          <!--mybatis和spring集成的依赖-->
          <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>1.3.1</version>
          </dependency>
      
          <!--mysql驱动依赖-->
          <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.9</version>
          </dependency>
      
          <!--阿里数据库连接池需要的依赖-->
          <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.12</version>
          </dependency>
        </dependencies>
      
        <build>
          <resources>
            <resource>
              <directory>src/main/java</directory>
              <includes>
                <include>**/*.properties</include>
                <include>**/*.xml</include>
              </includes>
              <filtering>false</filtering>
            </resource>
          </resources>
        </build>
      
      </project>
      
    • 配置连接池对象:

          <bean id="myDataSource" class="com.alibaba.druid.pool.DruidDataSource"
                init-method="init" destroy-method="close">
              <property name="url" value="jdbc:mysql://localhost:3306/books"/>
              <property name="username" value="root"/>
              <property name="password" value="root"/>
              <property name="maxActive" value="20"/>
          </bean>
      
    • 配置selSession对象

      <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
              <!--把数据库连接池赋给dataSource属性-->
              <property name="dataSource" ref="myDataSource"/>
              <!--mybatis主配置文件的位置
                  configLocation属性时Resource属性,读取配置文件
                  它的赋值,使用value,指定文件的路径,使用classpath:表示文件的位置-->
              <property name="configLocation" value="classpath:mybatis.xml"/>
          </bean>
      
    • 创建dao对象,使用sqlSession的getMapper(UserDao.class)MapperScannerConfigurer:在内部调用getMapper()生成每个dao接口的代理对象

      <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
          <!--指定SqlSessionFactory对象的id -->
          <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
          <!--指定包名,包名是dao接口所在的包名。
      		MapperScannerConfigurer会扫描这个包中的所有接口,把每一个接口都执行一次getMapper		()方法,得到每一个接口的dao对象
      		创建好的dao对象放到spring的容器中,dao的默认名称是dao名字的首字母小写
      		多个包可以使用逗号分隔
      	-->
          <property name="basePackage" value="com.javase.dao"/>
      </bean>
      
      • spirng和mybatis整合使用的时候,事务是自动提交的
    • 将jdbc.properties配置文件导入spring文件中

      头部的beans标签内需要加入context相关的三个内容
      <beans xmlns="http://www.springframework.org/schema/beans"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xmlns:context="http://www.springframework.org/schema/context"
             xsi:schemaLocation="http://www.springframework.org/schema/beans
              http://www.springframework.org/schema/beans/spring-beans.xsd
               http://www.springframework.org/schema/context
               http://www.springframework.org/schema/context/spring-context-4.0.xsd">
      
      
      <context:property-placeholder location="classspath:jdbc.properties"/>
      用${}获取对应属性值
      
    • jdbc.properties(注意要加上jdbc.这个前缀避免与mysql冲突)

      jdbc.url=jdbc:mysql://localhost:3306/books
      jdbc.username=root
      jdbc.password=root
      driverClassName=com.mysql.jdbc.Driver
      initialSize=10
      jdbc.maxActive=50
      minIdle=5
      maxWait=6000
      
  • spring的事务

    • 什么是事务?

      • 事务指的是一组sql语句的集合,集合中有多条sql语句,可能时insert、update、select、delete等,我们希望这些sql语句同时成功,或者同时失败,这些sql语句的执行是一致的,作为一个整体来执行
    • 在java代码中写程序,控制事务,此时事务应该放在哪里?

      • service类的业务方法上,因为业务方法会调用多个dao方法,执行多个sql语句
    • 通常使用jdbc访问数据库和mybatis访问数据库,是怎么处理事务的?

      • jdbc:connection.commit(); connection.rollback();
      • mybatis:SqlSession.commit(); SqlSession.rollback();
    • 事务的处理方式,有什么不足?

      1. 不同的数据库访问技术,处理事务的对象,方法不同

        需要了解不同数据库访问技术使用事务的原理

      2. 掌握多种数据库中事务的处理逻辑,什么时候提交事务,什么时候回滚事务

      3. 处理事务的多种方法

      • 总结:就是数据库的访问技术,有不同的事务处理机制,对象和方法,增加了开发难度。
  • spring是如何处理数据的?

    • spring处理事物的模型,使用的步骤都是固定的,把事务使用的信息提供给spring就可以了

      1. spring内部提交、回滚事务,使用的是事务管理对象,代替你完成commit、rollback,事务管理器是一个接口和它的众多实现类。接口:PlatfromTransactionManager,定义了事务的重要方法commit、rollback

        实现类:spring把每一种数据库访问技术对应的事务处理类都创建好了。

        • mybatis访问数据库—DataSourceTransactionManager
        • hibrenate访问数据库—HibrenateTransactionManager
      2. 如何使用:你需要告诉spring,你使用的是哪一种数据库访问技术,

        声明数据库访问技术对应的事务管理器实现类,在spring的配置文件中使用声明就可以了

    • 你的业务方法需要什么样的事务,说明需要事务的类型

      • 说明方法需要的事务:

        1. 事务的隔离级别:

          default:采用db默认的事务隔离级别,mysql的默认为读已提交,Oracle默认为可重复读

          • READ_UNCOMMITTED:读未提交,未解决任何并发问题
          • READ_COMMITED:读已提交,解决脏读,存在不可重复读和幻读
          • REPEATABLE_READ:可重复读,解决脏读、不可重复读,存在幻读
          • SERIALIZARBLE:串行化,不存在并发问题
        2. 事务的超时时间:表示一个方法最长的执行时间,如果方法执行时间超过设定时间,事务就回滚。单位是秒,整数值,默认是-1

        3. 事务的传播行为:控制业务方法是不是有事务,是什么类型的事务,7个传播行为,表示你的业务方法调用时,事务在方法之间是如何使用的

          PROPAGATION_REQUIERED

          PROPAGATION_REQUIRES_NEW

          PROPAGATION_SUPPORTS

    • 提交事务和回滚事务的时机:

      • 当你的业务方法,执行成功,没有异常抛出,当方法执行完毕,spring在方法执行后提交事务,事务管理器commit
      • 当你的业务方法抛出运行时异常或者error,spring执行回滚,调用事务管理器的rooback,运行时异常:RuntimeException
      • 但你的业务方法抛出非运行时异常时,提交异常
    • 总结spring事务:

      1. 管理事物的是:事务管理器及其实现类
      2. spring的事务是一个统一模型
        1. 指定要使用的事务管理器实现类,使用
        2. 指定哪些类,哪些方法需要加入事物的功能
        3. 指定方法需要的隔离级别,传播行为,超时
  • spring框架中提供的事务处理方案:

    1. 适合中小项目使用的:注解方案

      spring框架自己用aop实现给业务方法增加事务的功能,使用@Transactional注解增加事务。@Transactionnl注解是spring框架自己使用注解,放在public方法的上面,表示当前方法具有事务。可以给注解的属性赋值,表示具体的隔离界别,传播行为,异常信息等等

    • 使用@Transactional的步骤:

      1. 需要声明事务管理器对象

      2. 开启事务注解驱动,告诉spring框架,我要使用注解的方式管理事务

        spring使用aop机制,创建@Transactional所在的类代理对象,给方法加入事物的功能

        spring给业务方法加入事务:

        • 在你的业务方法执行之前,先开启事务,在业务方法之后提交或回滚事务,使用aop的环绕通知

          @Around("你要增加的事务功能的业务方法")
          Object myAround() {
              //开启事务,spring帮你开启
              try {
                  buy(1002, 10);
                  spring的事务管理.commit();
              } catch (Exception e) {
                  spring的事务管理.rollback();
              }
          }
          
          <!--使用spring的事务处理-->
              <!--1.声明事务管理器-->
             
              <bean id="=transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
                  <!--连接数据库,指定数据源-->
                  <property name="dataSource" ref="myDataSource"/>
              </bean>
              <!--2.开启事务注解,告诉spring使用注解管理事务,创建代理对象
                  transaction-manager:事务管理器对象的id
              -->
          	<!--注意下面这个dirven要使用tx的-->
              <tx:annotation-driven transaction-manager="=transactionManager"/>
          
          @Transactional(
                      // 传播行为
                      propagation = Propagation.REQUIRED,
                      // 隔离级别
                      isolation = Isolation.DEFAULT,
                      // 设置只读
                      readOnly = false,
                      // 发生指定异常时回滚
                      rollbackFor = {
                              NullPointerException.class
                      }
              )
          	/** 上面的值都是默认的,可以只使用@Transactional,也可以达到相同的效果,下面的方法必须时
          	*	rollback的处理逻辑:
          	*   1)spring框架会首先检查方法抛出的异常是不是在rollbackFor的属性中,如果异常在				rollbackFor列表中,不管时什么类型的异常,一定回滚
          		2)如果你抛出的异常不在rollbackFor列表中,spring会判断异常是不是RuntimeException,		  如果是,一定回滚。
          	*/
          public
              @Override
              public void buy(Integer id, Integer account) { }
          
      3. 适合大型项目,有很多的类,需要大量的配置事务,使用aspectj框架功能,在spring配置文件中声明类,方法需要的事务。这种方式业务方法和事务配置完全分离。

        • 实现步骤:

          • 要使用aspectj框架,需要加入依赖(spring-aspects)
          • 声明事务管理器对象
          • 声明方法需要的事务类型(配置方法的事务属性【隔离级别、传播行为、超时】)
          • 配置aop:指定哪些类的方法要创建代理
        • 声明式的事务

        •     <bean id="=transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
                  <!--连接数据库,指定数据源-->
                  <property name="dataSource" ref="myDataSource"/>
              </bean>
              <!--声明事务方法和事务属性(隔离级别,传播途径,超时时间)
                  id:自定义名称,表示<tx:advice> 和 <tx:advice >之间的配置内容的
                  transaction-manager:事务管理器对象的id
              -->
              <tx:advice id="myAdvice" transaction-manager="=transactionManager">
                  <!--tx:attributes:配置事务属性-->
                  <tx:attributes>
                      <!-- 
          			tx:method:给具体方法配置事务属性,method可以有多个,分别给不同的方法设置事务属性
          			name:方法名称:1)完整的方法名称,不带有包和类,2)方法可以使用通配符,* 表示任意字符
          			propagation:传播行为,枚举值
          			isolation:隔离级别
          			rollback-for:你指定的异常类名,全限定类名
          			可以有多个tx:method标签
          			-->
                     <tx:method name="buy" isolation="DEFAULT" propagation="REQUIRED" 
                                 read-only="false" rollback-for="java.lang.NullPointerException"/>
                  </tx:attributes>
              </tx:advice>
          <aop:config>
              <!--
           	配置切入点表达式:指定哪些包中的类,要使用事务
          	id:切入点表达式的名称,唯一值
          	expression:切入点表达式,指定那些类要使用事务,aspectj会创建对象
          	
          	-->
              <aop:point id="servicePt" expression="execution(* *..service..*.*(..))"/>
              <!--
           	配置增强器:关联advice和pointcut
          	advice-ref:通知,上面tx:advice那里的配置
          	pointcut-ref:切入点表达式的id
          	-->
              <aop:adviser advice-ref:"myAdvice" pointcut-ref="servicePt"/>
               
          </aop:config>
          
  • 事务传播特性的介绍

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mZvO7GMQ-1683813062609)(C:\Users\12086\AppData\Roaming\Typora\typora-user-images\image-20221024121517859.png)]

  • web项目中怎么使用容器对象(在spirng-mybatis项目的依赖基础上加入jsp和servlet的依赖,还要解决jsp版本较老的问题)

    1. 做javase项目由main方法,执行代码是执行main方法的,在main方法中创建容器对象

    2. web项目是在tomcat服务器上运行的,tomcat一启动,项目一直运行

      • 需求:web项目中容器对象只需要创建一次,把容器对象放入到全局作用域servletContext中

      • 怎么实现?

        • 监听器的作用:

          1. 创建容器对象 ApplicationContext ctx = new ClassPathXmlApplicationContext(config);
          2. 把容器对象放到servletContext中,ServletContext.setAttribute(key, ctx);
        • 监听器可以自己创建,也可以使用框架中提供好的ContextLoaderLilstener

        • 如何使用spring中提供的监听器?

          1. maven的pom文件中加入依赖

            <denpendency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-web</artifactId>
                <version>5.2.5.REALEASE</version>
            </denpendency>
            
            <!--在web.xml文件中配置ContextLoaderListener
            	监听器创建对象后,会读取/WEB-INF/applicationContext.xml
            	为什么要读取文件:因为监听器中要创建ApplicationContext对象,需要加载配置文件
            	/WEB-INF/applicationContext.xml就是监听器默认读取的spring配置文件路径
            	可以修改默认的文件位置,使用context-param重新指定文件的位置
            -->
            <context-param>
                <!--contextConfigLocation:表示配置文件的路径-->
                <param-name>contextConfigLocation</param-name>
                <!--为了避免冲突,将原来的applicationContext.xml改名为spring.xml-->
                <!--自定义的文件路径-->
                <param-value>classpath:spring.xml</param-value>
            </context-param>
            <listener>
                <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
            </listener>
            

            在servlet中使用框架的方法获取容器对象

            WebApplicationContext ctx = null;
            ServletContext sc = getServletContext();
            ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(sc);
            // 这样就获取了容器对象ApplicationContext的子类WebApplicationContext
            

@scope

  • singleton:默认的,Spring会采用单例模式创建这个对象。关闭工厂 ,所有的对象都会销毁。
  • prototype:多例模式。关闭工厂 ,所有的对象不会销毁。内部的垃圾回收机制会回收

动态代理与静态代理

  • 动态代理的角色和静态代理的一样 .

  • 动态代理的代理类是动态生成的 . 静态代理的代理类是我们提前写好的
    为什么要读取文件:因为监听器中要创建ApplicationContext对象,需要加载配置文件
    /WEB-INF/applicationContext.xml就是监听器默认读取的spring配置文件路径
    可以修改默认的文件位置,使用context-param重新指定文件的位置
    –>


    contextConfigLocation


    classpath:spring.xml


    org.springframework.web.context.ContextLoaderListener

    ```

          在servlet中使用框架的方法获取容器对象
    
          ```java
          WebApplicationContext ctx = null;
          ServletContext sc = getServletContext();
          ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(sc);
          // 这样就获取了容器对象ApplicationContext的子类WebApplicationContext
          ```
    

@scope

  • singleton:默认的,Spring会采用单例模式创建这个对象。关闭工厂 ,所有的对象都会销毁。
  • prototype:多例模式。关闭工厂 ,所有的对象不会销毁。内部的垃圾回收机制会回收

动态代理与静态代理

  • 动态代理的角色和静态代理的一样 .
  • 动态代理的代理类是动态生成的 . 静态代理的代理类是我们提前写好的
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值