Spring学习笔记

Spring


春天

一、概述

1. 简介

  • 1)spring出生于2002左右,解决企业开发的难度。
    • spring做了什么:减轻对项目模块之间、类和类之间的管理,帮助开发人员创建对象,管理对象之间的关系。
    • 官网:spring.io,( project — spring framework — learn ,可以查看spring的官方文档)
    • 欧陆词典,看官方文档必备
    • 下载文档:https://repo.spring.io/libs-release-local/org/springframework/spring/
  • 2)spring全家桶:spring、springmvc、spring boot、spring cloud
  • 3)spring核心技术:ioc(控制反转)、aop(面向切面编程),能够实现模块、类之间解耦合。
  • 4)依赖:类A中使用类B中的方法属性,叫做类A依赖类B

2. 优点

  • 1)轻量
    • spring使用的jar包很小,一般1M以下。核心功能所需jar包总共3M左右
    • spring运行占用资源小,运行效率高,不依赖其他jar包
  • 2)针对接口编程,解耦合
    • Spring 提供loc控制反转,由容器管理对象,对象的依赖关系。原来在程序代码中的对象创建方式,现在由容器完成。对象之间的依赖解耦合。
  • 3)AOP编程
  • 4)方便继承各种优秀框架

3. 体系结构

在这里插入图片描述

二、IoC控制反转

1. 简介

  • 1)IoC控制反转:是一个概念,一种思想、理论。描述的是把对象的创建、复制、管理工作都交给代码之外的容器实现。也就是说,对象的创建是由其他的外部资源完成的。
  • 2)控制:创建对象,对象的属性赋值,对象之间的关系管理。
  • 3)反转:把原来的开发人员管理对象的权限,转移给容器实现。由容器代替开发人员管理、创建对象。
    • 正转:由开发人员在代码中使用new构造方法创建对象,开发人员主动管理对象。
  • 4)容器:可以是一个服务器软件,也可以是一个框架(spring)
  • 5)使用IoC的目的:减少对代码的改动,也能实现不同的功能。实现解耦合。
  • 6)java中创建对象的方式
    • 构造方法
    • 反射
    • 序列化
    • 克隆
    • 动态代理
    • ioc
  • 7)ioc的体现
    • servlet
      • 创建类,继承HttpServlet
      • 在web.xml中注册servlet
      • 我们没有创建Servlet对象。但是可以使用它的属性、方法
      • Servlet是Tomcat服务器帮我们创建的 。Tomcat里存放着Servlet、Listener、Filter对象
  • 8)IoC的技术实现:DI
    • DI:依赖注入,只需要在程序中提供要使用的对象的名称就可以了,至于对象如何在容器中创建、赋值、查找都由容器内部实现
    • spring是使用DI实现了ioc的功能,spring底层是反射机制

2. 入门

  • 1)ioc使用步骤

    • ① 创建maven项目,导入依赖

      • <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.3.3</version>
        </dependency>
        
    • ② 创建类(接口和实现类)

      • 因为spring是反射机制创建对象,必须使用类
    • ③ 创建spring需要的配置文件,spring把java对象称为bean

      • 	<!--
                告诉spring创建对象
                1. 声明bean,就是告诉spring要创建某个类的对象
                    id:对象自定义名称,唯一值。spring通过id找到对象
                    class:类的全限定名称(不能是接口,因为spring是反射机制创建对象,必须使用类)
                2. spring把创建好的对象放入map中,spring有个map存放对象
                    springMap.put(id值,对象);
                    例如:springMap("someService", "new SomeServiceImpl()");
                3. 一个bean标签,声明一个对象
            -->
        <bean id="someService" class="cn.zhangm.service.impl.SomeServiceImpl" />
        <bean id="someService2" class="cn.zhangm.service.impl.SomeServiceImpl" />
        
        	<!--
                spring创建非自定义类的对象,创建一个存在的某个类的对象
            -->
        <bean id="mydate" class="java.util.Date" />
        
    • ④ 测试

      • 	/**
             * 使用框架创建对象
             * spring容器创建时,会创建所有的bean对象
             * spring容器创建对象时,使用无参构造方法
             */
        @Test
        public void test02(){	
            //1.指定spring配置文件的名称
            String config = "beans.xml";
        
            //2.创建表示spring容器的对象,ApplicationContext
            //ApplicationContext表示spring容器,通过该容器就可以获取对象了
            //ApplicationContext实现类:ClassPathXmlApplicationContext:表示从类路径加载配置文件
            ApplicationContext ac = new ClassPathXmlApplicationContext(config);
        
            //3.从容器获取对象
            //getBean(“配置文件中bean的id值”)
            SomeService someService = (SomeService)ac.getBean("someService");
        
            someService.doSome();
        }
        
        /**
        * 获取spring容器中jvav对象的信息
        *方法:getBeanDefinitionCount(),获取对象数量
        * 	  getBeanDefinitionNames(),获取对象id值
        */
        @Test
        public void test03(){
            String config = "beans.xml";
            ApplicationContext ac = new ClassPathXmlApplicationContext(config);
        
            //spring容器中对象的数量
            int beanDefinitionCount = ac.getBeanDefinitionCount();
            System.out.println("对象数量:" + beanDefinitionCount);
        
            //spring容器中对象的名称
            String[] beanDefinitionNames = ac.getBeanDefinitionNames();
            for(String name : beanDefinitionNames){
                System.out.println(name);
            }
        }
        
  • 2)注意:

    • ① spring容器创建时,会创建所有的bean对象
    • ② spring容器创建对象时,使用对象的无参构造方法
    • ③ spring把创建好的对象放入map中,spring有个map存放对象

3. 基于XML的DI

3.1 注入分类

① set注入

  • 1)set注入(设值注入):spring调用类的setXxx()方法

  • 2)参数为简单类型(java基本数据类型 + string)

    • <!--语法:
      	<bean id="xx" class="xx" >
              <property name="属性名字" value="属性值" />
              一个property只能给一个属性赋值
      	</bean>
      -->
      <bean id="mystu" class="cn.zhangm.di01.Student" >
          <property name="name" value="李四" /> <!--setName("李四")-->
          <property name="age" value="20" />
      </bean>
      
  • 3)参数为引用类型(java对象)

    • <!--语法:
      	<bean id="xx" class="xx">
            <property name="属性名" ref="bean的id(对象的名称)" />
      	</bean>
      -->
      <bean id="mystu" class="cn.zhangm.di02.Student" >
          <property name="name" value="李四" /> 
          <property name="age" value="20" />
          <!--引用类型赋值-->
          <property name="school" ref="myschool" />
      </bean>
      
      <bean id="myschool" class="cn.zhangm.di02.School">
          <property name="name" value="北京大学" />
          <property name="address" value="北京" />
      </bean>
      

② 构造注入

  • 1)构造注入:spring调用类的构造方法

  • 2)构造注入使用<constructor-arg>标签

    • name:表示构造方法形参名
    • index:构造方法参数位置,从左往右 0,1,2…
    • value:构造方法的形参类型是简单类型时,使用value
    • ref:构造方法的形参类是引用类型时,使用ref
  • 3)示例

    • <bean id="mystu" class="cn.zhangm.di03.Student" >
          <!--构造方法注入-->
          <constructor-arg name="name" value="小牛" />
          <constructor-arg name="age" value="13" />
          <constructor-arg name="school" ref="myschool" />
      </bean>
      
      <bean id="myschool" class="cn.zhangm.di03.School">
          <property name="name" value="北京大学" />
          <property name="address" value="北京" />
      </bean>
      

3.2 自动注入

  • 自动注入:spring根据某些规则,可以给引用类型赋值**(自动注入只针对引用类型)**
  • 使用规则:byName、byType

① byName方式自动注入

  • 1)byName(按名称注入):java类中引用类型的属性名(被赋值)和spring中<bean>的id一样,且数据类型一致,这样的容器中的bean,spring能够赋值给引用类型

  • 2)语法:

    • <bean id"xx" class="xx" autowire="byName">
          简单类型赋值
      </bean>
      
  • 3)示例

    • javabean

      • //在Student对象中声明引用数据类型
        private School myschool;
        
    • xml配置文件

      • <!--byname,
        	Student对象中的School类型的属性名为myschool与bean标签的id:myschool一样
        -->
        <bean id="mystu" class="cn.zhangm.di04.Student" autowire="byName" >
            <property name="name" value="李四" />
            <property name="age" value="20" />
        </bean>
        
        <bean id="myschool" class="cn.zhangm.di04.School">
            <property name="name" value="清华大学" />
            <property name="address" value="北京" />
        </bean>
        

② byType方式自动注入

  • 1)byType(按类型注入):java类中引用类型的数据类型和spring容器中声明的某个<bean>的class属性是同源关系,这样的bean能够赋值给引用类型。

    • 同源:一类的意思,三种同源关系:
    • java类中引用类型的数据类型和bean的class值是一样的
    • java类中引用类型的数据类型和bean的class值是父子类关系
    • java类中引用类型的数据类型和bean的class值是接口和实现类关系
  • 2)语法

    • <bean id"xx" class="xx" autowire="byType">
          简单类型赋值
      </bean>
      
  • 3)示例

    • //声明引用数据类型
      private School school;
      
    • <!--bytype-->
      <bean id="mystu" class="cn.zhangm.di05.Student" autowire="byType" >
          <property name="name" value="李四" />
          <property name="age" value="20" />
      </bean>
      
      <bean id="myschool" class="cn.zhangm.di05.School">
          <property name="name" value="郑州大学" />
          <property name="address" value="郑州" />
      </bean>
      
  • 4)注意:注意:在byType中,xml配置文件中声明的bean只能有一个符合条件,否则报错

3.3 多个Spring配置文件

  • 1)为什么使用多个spring配置文件

    • 开发中创建的类比较多,写在一个文件中不便于管理。文件加载慢
  • 2)优势

    • 每个文件大小不是很大,效率高
    • 避免多人竞争带来的冲突
  • 3)多文件分配方式

    • 按功能模块:一个模块一个配置文件
    • 按类的功能:…
  • 4)语法

    • 	<!--
              total表示主配置文件,包含其他配置文件,主配置文件一般不定义对象
              语法:
              <import resource="其他配置文件" />
              关键字:"classpath":表示类路径(class文件所在目录),在spring的配置文件中指定其他文件的位置,
                      需要使用classpath,告诉spring到哪儿去读取文件
          -->
          <!--<import resource="classpath:di06/spring-school.xml" />
          <import resource="classpath:di06/spring-student.xml" />-->
        
          <!--
              在包含关系的配置文件中,可以使用通配符(*:任意字符)
              注意:主配置文件名称不能在通配符范围内
          -->
      <import resource="classpath:di06/spring-*.xml" />
      

4. 基于注解的DI

  • 1)使用步骤

    • ① 加入依赖spring-context,在加入spring-context的同时,间接加入spring-aop。

      • 使用注解,必须使用spring-aop依赖
    • ② 在类中加入spring注解

      • /**
         * @ Component注解:创建对象的,等同于<bean>的功能
         *      属性:value:对象的名称,就是bean的id,唯一。创建的对象在整个spring容器中就一个
         *      位置:在类的上面
         *      @ Component等同于:<bean id="myStudent" class="cn.zhangm.anno01.Student" />
         */
        @Component(value = "myStudent")
        public class Student {
            //...
        }
        
        
    • ③ 在spring配置文件中,加入一个组件扫描器的标签,说明注解在项目中的位置

      • <?xml version="1.0" encoding="UTF-8"?>
        <beans xmlns="http://www.springframework.org/schema/beans"
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xmlns: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 https://www.springframework.org/schema/context/spring-context.xsd">
        
        
            <!--声明组件扫描器
                base-package:指定注解在项目中的包名
                component-scan工作方式:扫描遍历base-package指定的包,扫描包中、子包中所有的类,
                                        找到类中的注解,按照注解的功能创建对象,或给属性赋值
            -->
            <context:component-scan base-package="cn.zhangm.anno01" />
        </beans>
        
    • ④ 测试对象是否创建

      • @Test
        public void test01(){
            String config = "applicationContext.xml";
            ApplicationContext ac = new ClassPathXmlApplicationContext(config);
        
            Student myStudent = ac.getBean("myStudent", Student.class);
            System.out.println(myStudent);
        }
        
  • 2)组件扫描器,指定多个包的三种方式

    • ① 使用多次扫描器
    • ② 使用分隔符,;,分割多个包名
    • ③ 指定父包
4.1 创建对象的注解

① @Component

  • 1)@Component注解:创建对象的,等同于<bean>的功能

    • 属性:value:对象的名称,就是bean的id,唯一。创建的对象在整个spring容器中就一个
    • 位置:在类的上面
    • @ Component等同于:<bean id=“myStudent” class=“cn.zhangm.anno01.Student” />
  • 2)使用方法

    • //@Component(value = "myStudent")
      @Component("myStudent") //省略value
      //@Component //不使用名称,有spring提供默认名称:类名首字母小写
      public class Student {
          //...
      }
      

② @Repository、@Service、@Controller

  • 1)@Component@Repository、@Service、@Controller注解的区别(★)
    • @Repository(用在持久层):放在dao的实现类上面,表示创建dao对象,dao对象是能访问数据库的
    • @Service(用在业务层):放在service的实现类上面,service对象是做业务处理,有事物功能
    • @Controller(用在控制器上面):放在控制器(处理器)上面,创建控制器对象,即servlet
  • 2)注意:以上三个注解的使用语法,和@Component的语法一样。但是这三个注解有额外的功能
    • @Repository、@Service、@Controller这三个注解是给项目对象分层的
4.2 赋值的注解

① @Value

  • 1)@Value:给简单类型的属性赋值

    • 属性:value="xx",是string类型的,表示简单类型的属性值。value一般可以省略直接写值
    • 位置:
      • 在属性定义的上面,无需set方法,推荐
      • 在set方法上面
  • 2)示例

    • @Component("myStudent") //省略value
      public class Student {
          @Value("张飞")
          private String name;
          @Value("20")
          private Integer age;
      
          public Student() {
          }
      
      }
      

② @Autowired

  • 1)@Autowired:spring框架提供的注解,实现引用类型赋值。spring中通过注解给引用类型赋值,使用的是自动注入原理,支持byName、byType。

  • 2)@Autowired默认使用的时byType方式的自动注入。需要有同源对象(见基于XML的DI)。

    • 示例

    • @Autowired
      private School school;
      
  • 3)@Autowired使用byName方式,需要@Qualifier注解配合

    • 示例

    • @Autowired
      @Qualifier("mySchool")
      private School school;
      
  • 4)@Autowired的属性

    • required:是一个boolean类型,默认true
      • required=true:表示引用类型赋值失败,程序报错,并终止执行。一般使用该方式
      • required=false:表示引用类型如果赋值失败,程序正常进行,引用类型赋值null

③ @Resource

  • 1)@Resource:针对引用类型。来自jdk的注解,spring提供了对该注解的支持。使用的也是自动注入的原理,支持byType、byName。默认byName。

  • 2)@Resource默认使用byName的方式注入,如果byName方式注入失败,会使用byType方式

    • 示例

    • @Resource
      private School school;
      
  • 3)@Resource只使用byName方式

    • 使用属性:name="bean的id值",示例

    • @Resource(name="myschool")
      private School school;
      

5.XML和注解的选择

  • 1)经常改变的用XML
  • 2)不经常改变的用注解

三、AOP面向切面编程

1. 动态代理

① 简介

  • 1)动态代理能干什么?
    • 能创建对象
    • 在程序原有代码不改动的情况下,增强功能
  • 2)**「★」**JDK动态代理
    • 使用jdk中的Proxy、Method、InovcationHandler创建代理对象
    • 要求目标类必须实现接口
  • 3)**「★」**CGLIB动态代理
    • 第三方的工具库,创建代理对象。原理是继承。通过继承目标类,创建子类。子类就是代理对象。
    • 要求目标不能时final的,方法也不能时final的。
  • 4)**「★」**动态代理的作用
    • 在目标类源代码不改变的情况下,增加功能
    • 减少代码重复
    • 专注业务逻辑代码
    • 解耦合,让业务功能和日志、事物、非业务功能分离

② JDK动态代理步骤

  • 1)创建目标类,SomeServiceImpl实现类,给它的doSome(),doOther()增添输出时间,事物

    • public interface SomeService {
          public void doSome();
          public void doOhter();
      }
      
    • public class SomeServiceImpl implements SomeService {
          @Override
          public void doSome() {
              System.out.println("dosome...");
          }
      
          @Override
          public void doOhter() {
              System.out.println("doother...");
          }
      }
      
  • 2)创建InvocationHandler接口的实现类,在这个类实现目标方法的增强功能

    • public class MyInvocationHandler implements InvocationHandler {
          //目标对象
          private Object target; //SomeServiceImpl类
      
          public MyInvocationHandler(Object target) {
              this.target = target;
          }
      
          @Override
          public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
              //通过代理对象执行方法时,会调用这个invoke方法
              System.out.println("执行了MyInvocationHandler的invoke方法");
              String methodName = method.getName();
              System.out.println("方法名:" + methodName);
      
              Object res = null;
      
              if ("doSome".equals(methodName)){
                  System.out.println("方法执行时间:" + new Date()); //增强功能1
                  //执行目标类的方法,通过Method类实现
                  res = method.invoke(target ,args); //SomeServiceImpl类的doSome()
                  System.out.println("方法执行完毕后,提交事物");//增强功能2
              }else {
                  res = method.invoke(target ,args); //SomeServiceImpl类的doOther()
              }
      
              //目标方法执行结果
              return  res;
          }
      }
      
  • 3)使用jdk中类Proxy,创建代理对象。实现创建对象的能力。

    • public class MyApp {
          public static void main(String[] args) {
              //使用jdk的proxy创建代理对象
              //创建目标对象
              SomeService target = new SomeServiceImpl();
      
              //创建invocationHandler
              InvocationHandler handler = new MyInvocationHandler(target);
      
              //使用proxy创建代理
              SomeService proxy = (SomeService)Proxy.newProxyInstance(
                      target.getClass().getClassLoader(),
                      target.getClass().getInterfaces(), handler);
      
              //通过代理执行方法,会调用handler中的invoke方法
              proxy.doSome();
              System.out.println("==================================================");
              proxy.doOhter();
          }
      }
      

2. AOP概述

  • 1)AOP(Aspect Orient Programming):面前切面编程,是从动态角度考虑程序运行过程。
    • AOP底层就是采用动态代理模式实现的。采用两种动态代理:jdk的动态代理CGLIB的动态代理
    • AOP就是动态代理规范化。把动态代理的实现步骤、方式都定义好了,让开发人员用统一的方式使用动态代理。
  • 2)**「★」**如何理解面向切面编程?
    • OOP:面向对象编程,就是将项目的各种功能封装为对象
    • AOP:面向切面编程
      • 需要在分析项目功能时,找出切面
      • 合理安排切面的执行时间(在目标方法之前,还是在目标方法之后)
      • 合理的安排切面执行的位置,在哪个类,在哪个方法增强功能
  • 3)一个切面的三个要素
    • 切面的功能代码,切面干什么
    • 切面的执行位置,使用Pointcut表示切面执行位置
    • 切面的执行时间,使用Advice表示时间在目标方法之前还是之后执行

3. AOP编程术语「掌握」

  • 1)Aspect:切面,给目标类添加的功能,就是切面。
    • 比如以上示例的日志、事物。统计信息、参数检查、权限验证。
    • 切面特点:一般都是非业务方法,独立使用。
  • 2)Orient:面向
  • 3)Programming:编程
  • 4)JoinPoint:连接点,连接业务方法和切面的位置。
    • 就是某个类中的业务方法
  • 5)Pointcut:切入点,指多个连接点方法的集合。多个方法
  • 6)目标对象:给哪个类的方法增强功能,它就是目标对象
  • 7)Advice:通知,表示切面的功能执行的时间

4. AspectJ对AOP的实现「掌握」

4.1 简介
  • aop是动态的一个规范,一个标准

  • aop的技术实现框架

    1. spring:spring内部实现了aop规范,能做aop的工作。

      • spring主要在处理事物时使用aop
      • 我们项目开发中很少使用spring的aop实现。因为其比较笨重
    2. aspectJ:一个开源的专业做aop的框架。Esclise的开源项目。spring中集成了aspectJ框架

      • aspectJ框架实现aop有两种方式
        • xml:配置全局事物
        • 注解:「一般使用」,有5个注解
4.2 AspectJ的aop的要素
  • 1)切面的执行时间,这个执行时间,在规范中叫做Advice(通知,增强)

    • @Before
    • @AfterReturning
    • @Around
    • @AfterThrowing
    • @After
  • 2)切面的执行位置,使用的时切入点表达式

    • ① 表达式原型

      • execution(modifiers-pattern? ret-type-pattern
        		declaring-type-pattern?name-pattern(param-pattern)
                 throws-pattern?)
        
    • ② 解释

      • modifiers-pattern:访问权限类型
      • ret-type-pattern:返回值类型,不可省略
      • declaring-type-pattern:包名类名
      • name-pattern(param-pattern):方法名(参数类型和参数个数),不可省略
      • throws-pattern:抛出异常类型
      • ?:表示可选部分
    • ③ 总结:execution([访问权限] 方法返回值 方法声明(参数) [异常类型]),("[…]"表示可省略部分)

    • ④ 通配符:表达式中可以使用通配符

      • *:0至多个任意字符
      • ..:用在方法参数中,表示任意多个参数;用在包名后,表示当前包及其子包路径
      • +:用在类名后,表示当前类及其子类;用在接口后,表示当前接口及其实现类
    • ⑤ 举例

      • execution(public * *(..)):指定切入点为:任意公共方法
      • execution(* set*(..)):指定切入点为:任何一个以"set"开始的方法
      • execution(* cn.zhangm.service.*.*(..)):指定切入点为:该包中的任意类的任意方法
      • execution(* cn.zhangm.service..*.*(..)):指定切入点为:该包以及子包中的任意类的任意方法
      • execution(* *..service.*.*(..)):指定切入点为:所有包含service包的service包的任意类的任意方法
4.3 使用步骤
  • 1)创建maven项目

  • 2)导入依赖

    • spring依赖
    • aspectJ框架依赖
    • junit依赖
  • 3)创建目标类:接口及其实现类

    • public interface SomeService {
          void doSome(String name, Integer age);
      }
      
      
    • //目标类
      public class SomeServiceImpl implements SomeService {
          @Override
          public void doSome(String name, Integer age) {
              //给doSome方法增强一个功能,在doSome执行之前,输出方法执行时间
              System.out.println("目标方法doSome...");
          }
      }
      
      
  • 4)创建切面类:普通类

    • 在类的上面添加注解:@Aspect

    • 在类中定义方法,方法就是切面要执行的代码。在方法上面加入aspect中的通知注解,例如@Before。还需要指定切入点表达式

    • /**
       * @Aspect:是aspectj框架中的注解
       *      作用:表示当前类时切面类
       *      切面类:是用来给业务方法增加功能的类,在这个类中有切面的功能代码
       *      位置:类上名
       */
      @Aspect
      public class MyAspect {
          /**
           * 定义方法,方法是实现切面功能的
           * 方法定义的要求:
           *      1.公共方法
           *      2.没有返回值
           *      3.方法名称自定义
           *      4.方法可以有参数,也可以没有。
           *          如果有参数,参数不是自定义的。有几个参数类型可以使用
           */
      
          /**
           * @Before:前置通知注解
           *      属性
           *          value,是切入点表达式,表示切面的功能执行的位置
           *      位置:方法上
           *       特点:
           *          1.在目标方法执行之前执行
           *          2.不会改变目标方法的执行结果
           *          3.不会影响目标方法执行
           */
          //切面表达式完整写法
          @Before("execution(public void cn.zhangm.aop01.SomeServiceImpl.doSome(String,Integer))")
          public void myBefore(){
              //切面执行功能代码
              System.out.println("前置通知,切面功能:在目标方法之前输出执行时间:" + new Date());
          }
      }
      
      
  • 5)创建spring配置文件:声明对象,把对象交给容器管理

    • <?xml version="1.0" encoding="UTF-8"?>
      <beans xmlns="http://www.springframework.org/schema/beans"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
             xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
      
      
          <!--把对象交给spring容器-->
          <!--声明目标对象-->
          <bean id="someService" class="cn.zhangm.aop01.SomeServiceImpl" />
      
          <!--声明切面对象-->
          <bean id="myAspect" class="cn.zhangm.aop01.MyAspect" />
      
          <!--声明自动代理生成器:使用aspectj框架内部的功能,创建目标对象的代理对象
              创建代理对象是在内存中实现的,修改目标对象的内存中的结构,创建为代理对象
              所以目标对象就是一个被修改后的代理对象
      
              aspectj-autoproxy:会把spring容器中的所有的目标对象,一次性都生成代理对象。
          -->
          <aop:aspectj-autoproxy />
      </beans>
      
  • 6)创建测试类,从spring容器中获取目标对象(实际就是代理对象)。通过代理执行方法,实现方法增强

    • //测试aop之@before注解
      @Test
      public void test01(){
          String config = "applicationContext.xml";
          ApplicationContext ac = new ClassPathXmlApplicationContext(config);
      
          //从容器中获取目标对象
          SomeService proxy = (SomeService) ac.getBean("someService");
          //com.sun.proxy.$Proxy8  ---jdk的动态代理
          System.out.println(proxy.getClass().getName());
      
          //通过代理的对象执行方法,实现目标方法执行时,增强了功能
          proxy.doSome("lisi", 20);
      }
      
4.4 注解详解

〇 JoinPoint参数

  • 1)所有通知注解所注解的方法中都有JoinPoint参数

    • 作用:可以在通知方法中获取方法执行的信息,例如方法名、方法的实参
    • JoinPoint参数的值是由框架赋予,必须是第一个位置的参数
  • 2)示例

    • @Before("execution(public void cn.zhangm.aop01.SomeServiceImpl.doSome(String,Integer))")
      public void myBefore(JoinPoint jp){
          //获取方法完整定义
          System.out.println("方法的签名(定义) = " + jp.getSignature());
          System.out.println("方法的名称 = " + jp.getSignature().getName());
          Object[] args = jp.getArgs();
          for (Object arg:args){
              System.out.println("参数:" + arg);
          }
      
          //切面执行功能代码
          System.out.println("前置通知,切面功能:在目标方法之前输出执行时间:" + new Date());
      }
      

① @Before

  • 1)@Before:前置通知注解
    • 前置通知的方法定义格式
      • 公共方法
      • 没有返回值
      • 方法名称自定义
      • 方法可以有参数,也可以没有。如果有参数,参数不是自定义的。有几个参数类型可以使用
    • @Before的属性:value,是切入点表达式,表示切面的功能执行的位置
    • 特点
      • 在目标方法执行之前执行
      • 不会改变目标方法的执行结果
      • 不会影响目标方法执行
  • 2)示例:见4.3代码示例

② @AfterReturning

  • 1)@AfterReturning:后置通知

    • 后置通知的方法定义格式

      • 公共方法
      • 没有返回值
      • 方法名称自定义
      • 有参数,推荐使用Object,参数名自定义。该参数为目标方法的返回值
    • @AfterReturning的属性

      • value:切入点表达式
      • returning:自定义变量,表示目标方法的返回值。(自定义变量名必须和通知方法的形参名一样)
    • 特点

      • 在目标方法之后执行
      • 能够获取目标方法的返回值,可以根据返回值做不同的处理功能
      • 可以修改返回值。(但无法修改目标方法的执行结果?)
    • 后置通知的执行顺序

      • Object res = doOther(..)
        myAfterReturning(res);
        
  • 2)示例

    • @AfterReturning(value = "execution(* *..SomeServiceImpl.doOther(..))",
                      returning = "res")
      public void myAfterReturning(Object res){
          //Object res:是目标方法执行后的返回值
          System.out.println("后置通知:在目标方法之后执行,获取的返回值是:" + res);
      
          //修改目标方法的返回值,看看是否会影响最后方法调用结果---不会
          if (res != null) {
              res = "hello";
          }
      }
      

③ @Around

  • 1)@Around环绕通知,相当于jdk的动态代理。经常做事物,在目标方法之前开启事物,在目标方法之后提交事物

    • 环绕通知的方法定义格式
      • public公共方法
      • 必须有一个返回值,推荐Object。(影响目标方法的执行结果)
      • 方法名自定义
      • 方法有参数,是固定的参数:ProceedingJoinPoint,继承了JoinPoint
    • @Around的属性:value,切入点表达式
    • 特点
      • 在目标方法的前、后都能增强功能
      • 可以控制目标方法是否执行
      • 会修改原来的目标方法的执行结果,影响最后的调用结果
    • 方法的参数:ProceedingJoinPoint
      • 等同于jdk动态代理的 Method。ProceedingJoinPoint继承了JoinPoint
      • 作用:执行目标方法、获取方法参数等
    • 方法的返回值:目标方法执行结果返回值,可以被修改
  • 2)示例

    • @Around(value = "execution(* *..SomeServiceImpl.doFirst(..))")
      public Object myAround(ProceedingJoinPoint pjp) throws Throwable {
          String name = "";
          //获取参数
          Object[] args = pjp.getArgs();
          if (args != null && args.length > 1) {
              Object arg = args[0];
              name = (String)arg;
          }
      
          //环绕通知
          Object result = null;
          System.out.println("环绕通知:在目标方法之前输出时间:" + new Date()); //新增功能1
          //1.目标方法调用
          if ("zhangsan".equals(name)){
              //符合条件,调用目标方法
              result = pjp.proceed(); //=method.invoke();
          }
          System.out.println("环绕通知:在目标方法之后提交事物:");//新增功能2
          //2.在目标方法的前或者后加功能
      
          //3.修改目标方法的执行结果,影响方法最后的调用结果
          if (result != null) {
              result = "hello aspect";
          }
      
          //返回目标方法的执行结果
          return result;
      }
      

④ @AfterThrowing

  • 1)@AfterThrowing:异常通知

    • 异常通知方法定义格式
      • public
      • 没有返回值
      • 方法名自定义
      • 方法有一个参数Exception
    • @AfterThrowing的属性
      • value:切入点表达式
      • throwing:自定义的变量,表示目标方法抛出的异常对象。变量名必须和方法参数名一样
    • 特点:
      • 在目标方法抛出异常时,执行
      • 可以做异常的监控程序,监控目标方法执行时是不是有异常,如果有,可以发送邮件、短信通知
  • 2)示例

    • AfterThrowing(value = "execution(* *..SomeServiceImpl.doSecond(..))",
                    throwing = "ex")
          public void myAfterThrowing(Exception ex){
          System.out.println("异常通知:在方法发生异常时,执行。" + ex.getMessage());
          //发送邮件、短信通知开发人员
      }
      

⑤ @After

  • 1)@After:最终通知

    • 方法定义格式
      • public
      • 没有返回值
      • 方法名自定义
      • 没有参数
    • 属性:value
    • 特点
      • 总是会执行,相当于finaly{…}里面的代码
      • 目标方法之后执行
  • 2)示例

    • @After(value = "execution(* *..SomeServiceImpl.doThird(..))")
      public void myAfter(){
          System.out.println("执行最终通知,总是执行");
          //一般做资源清除
      }
      

⑥ @Pointcut

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

    • 属性:value
    • 特点
      • 当使用@Pointcut定义在一个方法上时,该方法的名称就是切入点表达式的别名。其他的通知中的value属性,就可以使用这个别名
  • 2)示例

    • @After(value = "mypt()")
         public void myAfter(){
              System.out.println("执行最终通知,总是执行");
              //一般做资源清除
         }
      
          @Before(value = "mypt()")
          public void myBefore(){
              System.out.println("前置通知,在目标方法之前执行");
          }
      
          @Pointcut(value = "execution(* *..SomeServiceImpl.doThird(..))")
          private void mypt(){
              //无需代码
          }
      

5. 总结

  • 1)通过以上案例我们可以发现aspectJ框架底层实现aop技术是使用的jdk的动态代理。

    • 使用jdk的动态代理要求,必须有接口和其实现类
  • 2)若无接口,使用动态代理,使用的是cglib的动态代理

    • 如何使用?:在有接口及其实现类的基础上去掉接口即可。
  • 3)如有接口,想用cglib的动态代理怎么做?

    • 在有接口及其实现类的基础上,在spring配置文件中添加:

    • <aop:aspectj-autoproxy proxy-target-class="true" />
      

四、srping集成mybatis

0. 简介

  • 1)spring和mybatis的集成,使用的是ioc技术
    • ioc能创建对象,可以把mybatis框架中的对象,交给spring统一创建,开发人员从spring中获取对象。开发人员就不用同时面对多个框架了,就面对spring。
  • 2)我们需要spring帮我们创建哪些对象?
    • 独立的连接池类的对象,Druid(srping内置连接池不好用,舍弃)
    • SqlSessionFactory对象
    • 创建dao对象

1. 创建项目

  • 1)创建maven项目

  • 2)导入依赖

    • <dependency>
          <groupId>junit</groupId>
          <artifactId>junit</artifactId>
          <version>4.11</version>
          <scope>test</scope>
      </dependency>
      
      <!--spring ==ioc -->
      <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-context</artifactId>
          <version>5.3.3</version>
      </dependency>
      
      <!--spring事物用的-->
      <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-tx</artifactId>
          <version>5.3.3</version>
      </dependency>
      <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-jdbc</artifactId>
          <version>5.3.3</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>8.0.22</version>
      </dependency>
      
      <!--阿里数据库连接池druid-->
      <dependency>
          <groupId>com.alibaba</groupId>
          <artifactId>druid</artifactId>
          <version>1.1.12</version>
      </dependency>
      
  • 3)导入插件

    • <build>
          <resources>
              <resource>
                  <directory>src/main/java</directory>  <!--文件所在目录-->
                  <!--包括目录下的properties和xml文件都会扫描到-->
                  <includes>
                      <include>**/*.properties</include>
                      <include>**/*.xml</include>
                  </includes>
                  <!--false不启用过滤,因为*.properties已经起到过滤作用了-->
                  <filtering>false</filtering>
              </resource>
          </resources>
          <plugins>
              <plugin>
                  <artifactId>maven-compiler-plugin</artifactId>
                  <version>3.1</version>
                  <configuration>
                      <source>1.8</source>
                      <target>1.8</target>
                  </configuration>
              </plugin>
          </plugins>
      </build>
      

2. 创建实体类

3. 创建Dao接口及mapper文件

  • 1)dao接口

    • public interface StudentDao {
      
          int insertStudent(Student student);
          List<Student> selectStudents();
      }
      
  • 2)mapper文件

    • <?xml version="1.0" encoding="UTF-8" ?>
      <!DOCTYPE mapper
              PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
              "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
      <mapper namespace="cn.zhangm.dao.StudentDao">
          <select id="selectStudents" resultType="cn.zhangm.domain.Student">
            select * from student
          </select>
      
          <insert id="insertStudent">
              insert into student values(#{id}, #{name}, #{email}, #{age})
          </insert>
      </mapper>
      

4. mybatis核心配置

  • <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE configuration
            PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-config.dtd">
    <configuration>
        <!--开启mybatis内置的日志功能-->
        <settings>
            <setting name="logImpl" value="STDOUT_LOGGING"/>
        </settings>
    
        <!--启别名-->
        <typeAliases>
            <package name="cn.zhangm.domain"/>
        </typeAliases>
    
        <!--sql mapper(sql映射文件)的位置-->
        <mappers>
           <!--扫描该包下的所有xxx.xml文件-->
            <package name="cn.zhangm.dao" />
        </mappers>
    </configuration>
    
  • 因为我们不使用mybatis内置的连接池,所以省略了<environments>的配置

5. 创建service

  • 该步骤意在模拟web开发中的业务层,调用dao方法执行数据库操作

  • //接口
    public interface StudentService {
    
        int addStudent(Student student);
        List<Student> queryStudent();
    }
    
  • //实现类
    public class StudentServiceImpl implements StudentService {
        //引用类型
        private StudentDao studentDao;
        //为了使用set注入,赋值
        public void setStudentDao(StudentDao studentDao) {
            this.studentDao = studentDao;
        }
    
        @Override
        public int addStudent(Student student) {
            int nums = studentDao.insertStudent(student);
            return nums;
        }
    
        @Override
        public List<Student> queryStudent() {
            List<Student> students = studentDao.selectStudents();
            return students;
        }
    }
    

6. spring配置文件

  • 1)创建spring配置文件:声明mybatis对象交给spring创建

    • ① 数据源:DataSource。在spring和mybatis集成中叫做:com.alibaba.druid.pool.DruidDataSource
      • druid的使用在github上有说明。
      • 必要属性:url、username、password、maxActive
    • ② SqlSessionFactory。在spring和mybatis集成中叫做:org.mybatis.spring.SqlSessionFactoryBean
      • dataSource:指明数据源(值为连接池bean的id)
      • configLocation:指明mybatis配置文件
    • ③ Dao对象。在spring和mybatis集成中叫做:org.mybatis.spring.mapper.MapperScannerConfigurer
      • sqlSessionFactoryBeanName:指明由哪个工厂创建
      • basePackage:指明dao接口所在包名。( 因为getMapper(XxxDao.class) )
    • ④ 声明自定义的service
  • 2)示例

    • <!--把数据库信息放在一个单独文件中,方便管理
      	获取文件数据语法:${key}
      -->
      <context:property-placeholder location="classpath:jdbc.properties" />
      
      <!--声明数据源DataSource,作用连接数据库的-->
      <bean id="myDataSource" class="com.alibaba.druid.pool.DruidDataSource"
            init-method="init" destroy-method="close" >
          <!--set注入,给DruidDataSource提供连接数据库的信息。4个重要属性以下:-->
          <property name="url" value="${jdbc.url}" /><!--setUrl()-->
          <property name="username" value="${jdbc.username}" />
          <property name="password" value="${jdbc.password}" />
          <property name="maxActive" value="${jdbc.maxActive}" />
      </bean>
      
      <!--声明mybatis中提供的SqlSessionFactoryBean类,这个类内部创建SqlSessionFactory-->
      <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
          <!--set注入,把数据库连接池赋给了dataSource属性-->
          <property name="dataSource" ref="myDataSource" />
          <!--mybatis主配置文件的位置
                  configLocation的类型是Resource(spring的资源,读取配置文件)
                  它的赋值,使用value,指定文件路径,使用classpath:xxx
              -->
          <property name="configLocation" value="classpath:mybatis.xml" />
      </bean>
      
      <!--创建dao对象,使用SqlSession的getMapper(StudentDao.class)
              MapperScannerConfigurer在内部调用getMapper()生成每个dao接口的代理对象
          -->
      <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
          <!--指定SqlSessionFactory的id-->
          <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
          <!--指定包名,dao接口所在包名
                  MapperScannerConfigurer会扫描这个包中的所有接口,把每个接口都执行一次
                  一个getMapper()方法,得到每个接口的dao对象。
                  创建好的对象放入到spring容器中,对象名称是接口的首字母小写
              -->
          <property name="basePackage" value="cn.zhangm.dao" />
      </bean>
      
      <!--声明service-->
      <bean id="studentService" class="cn.zhangm.service.impl.StudentServiceImpl">
          <property name="studentDao" ref="studentDao" />
      </bean>
      

7. 测试

  • //测试service的使用
    @Test
    public void testService(){
        String config = "applicationContext.xml";
        ApplicationContext ac = new ClassPathXmlApplicationContext(config);
    
        //获取spring容器中的dao对象
        StudentService service = ac.getBean("studentService", StudentService.class);
        List<Student> students = service.queryStudent();
        System.out.println("students = " + students);
    }
    

五、spring事物

1. 简介

  • 1)事物:指一组sql语句的集合,这些语句要么同时成功,要么同时失败

  • 2)何时使用事物:涉及多个表等需要sql语句同时成功才执行的地方。例如:转账

    • 在java中,事物放在service类的方法上
  • 3)如何处理事物

    • JDBC:Connection conn = ..; conn.commit(); conn.rollback();
    • Mybatis:sqlSession.commit(); sqlSession().rollback()
  • 4)(3)中处理事物的不足

    • 不同数据库访问技术,处理事物的方法不同。需要了解不同技术的事物实现
    • 需要掌握多种数据库事物的处理逻辑
    • 处理数据库的多种方法
    • 总结:多种数据库访问技术,有不同的事物处理机制、对象、方法
  • 5)如何解决(3)中的不足

    • spring提供一种处理事物的统一模型。能使用统一的方式,完成多种不同数据库访问技术的事物处理。
  • 6)spring如何处理事物

    • ① 事物内部提交、回滚事物,使用的是事务管理对象。

      • 接口:PlatformTransactionManager,定义事物的提交和回滚
    • ② 告诉spring使用哪种数据库访问技术

      • 声明数据库访问技术对于事物管理器的实现类,例如:

      • <bean id="xxx" class="...DataSourceTransactionManger">
        
    • ③ 说明方法需要的事物的类型

      • 事物的隔离级别
      • 事物的超时时间
      • 事物传播行为
    • ④ 事物提交、回滚的时机

      • 当业务方法执行成功,没有异常抛出,spring在方法执行完毕后自动提交事物
      • 当业务方法抛出运行时异常和ERROR时,spring回滚事物,调用事务管理器的rollback
      • 当业务方法抛出非运行时异常,主要是受查异常时,提交事物
        • 受查异常:写代码时,必须处理的异常,例如:IOException

2. 事物管理API

① 事物管理器接口(重点)

  • 1)事物管理器是PlatformTransactionManager接口对象。用于事物提交、回滚、获取事物状态信息
  • 2)接口中的方法
    • void commit(TransactionStatus status)
    • TransactionStatus getTransaction(TransactionDefiniton definition)
    • void rollback(TransactionStatus status)
  • 3)常用的两个实现类
    • DataSourceTransactionManager:使用JDBC或Mybatis操作数据库时使用
    • HibernateTransactionManager:使用Hibernate操作数据库时使用
  • 4)Srping事物的默认回滚方式时:发生运行时异常和error时回滚,发送受查(编译)异常时提交

② 事物定义接口

  • 1)TransactionDefinition接口定义了事物描述相关的三类常量:事物隔离级别、事物传播行为、事物默认超时时限,及对其操作。
  • 2)隔离级别
    • DEFAULT
    • READ_UNCOMMITTED:读未提交,未解决任何并发问题
    • READ_COMMITTED:读已提交,解决脏读,存在不可重复读与幻读。(oracle默认)
    • REPEATABLE_READ:可重复读,解决脏读、不可重复读,存在幻读。(mysql默认)
    • SERIALIZABLE:串行化,不存在并发问题
  • 3)传播行为:控制业务方法是否有事物,是什么样的事物。(掌握前三个)
    • PROPAGATION_REQUIRED:有事物使用事物,没有事物创建事物,默认
    • PROPAGATION_REQUIRED_NEW:总创建一个新事物
    • PROPAGATION_SUPPORTS:支持事物,有没有事物都行
    • PROPAGATION_MANDATORY
    • PROPAGATION_NESTED
    • PROPAGATION_NEVER
    • PROPAGATION_NOT_SUPPORTED
  • 4)超时时限
    • TIMEOUT_DEFAULT常量定义了事物底层默认的超时时限
    • 表示最长执行时间,超过时间,回滚。单位秒,默认 -1

3. 注解管理事物

① 简介

  • 1)注解管理事物:适用于中小项目使用
  • 2)spring框架自己用aop实现给业务方法增加事物的功能,@Transactional注解管理事物。
    • 该注解放在public方法上,表示当前方法具有事物
    • 可以给注解的属性赋值,表示具体的隔离级别、传播行为、异常信息等
  • 3)@Transactional的属性
    • propagation:事物的传播属性,该属性类型为枚举,默认值Propagation.REQUIRED
    • isolation:事物的隔离级别,枚举,默认值Isolation.DEFAULT
    • readOnly:对数据库操作是否是只读的,默认值false
    • timeout:操作与数据库连接超时时限,单位秒,默认值-1即没有时限
    • rollbackFor:指定需要回滚的异常类。类型为Class[],默认值空数组
    • rollbackForClassName:指定需要回滚的异常类类名,类型为String[],默认值空数组
    • noRollbackFor:指定不需要回滚的异常类,类型为Class[],默认值空数组
    • noRollbackForClassName:指定不需要回滚的异常类类名,类型为String[],默认值空数组

② 使用步骤

  • 1)声明事物管理器对象

    • <bead id="xxx" class="DataSourceTransactionManager">

    • <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
          <!--连接哪个数据库。指定数据源-->
          <property name="dataSource" ref="myDataSource" />
      </bean>
      
  • 2)开启事物注解驱动,告诉spring我要使用注解方式管理事物

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

    • spring如何给业务方法加入事物

      • 在业务方法执行之前,先开启事物,在业务方法之后提交或回滚事物,使用aop环绕通知
    • <!-- 2.开启事物注解驱动,告诉spring使用注解开启事物,创建代理对象
          transaction-manager:事物管理器对象的id
      -->
      <tx:annotation-driven transaction-manager="transactionManager" />
      
  • 3)在方法上添加事物注解

    • /**
           * 测试spring事物管理
           *
           * rollbackFor:表示发生指定的异常一定回滚
           *      处理逻辑:
           *      1)spring首先会检查方法抛出的异常是不是在rollbackFro的属性值中
           *          如果异常在该属性值中,不管是什么类型的异常,一定回滚
           *      2)如果抛出的异常不自在rollbackFro列表中,spring会判断异常是不是RuntimeException,
           *          如果是,一定回滚
           */
      //    @Transactional(
      //            propagation = Propagation.REQUIRED,
      //            isolation = Isolation.DEFAULT,
      //            readOnly = false,
      //            rollbackFor = {
      //                    NullPointerException.class,
      //                    NotEnoughException.class
      //            }
      //    )
          //使用的都是默认值,默认抛出运行时异常,回滚事物
          @Transactional
          @Override
          public void buy(Integer goodsId, Integer nums) {...}
      

4. AOP配置管理事物

① 简介

  • 1)适合大型项目,有很多类、方法需要大量配置事物
  • 2)使用aspectj框架功能,在spring配置文件中声明类、方法需要的事物。

② 使用步骤

  • 1)加入aspectj的依赖

  • 2)声明事物管理器对象

    • <!-- 1.声明事物管理器对象-->
      <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
          <!--连接哪个数据库。指定数据源-->
          <property name="dataSource" ref="myDataSource" />
      </bean>
      
  • 3)声明方法需要的事物的类型(配置方法的事物属性【隔离级别、传播行为、超时】)

    • <!--2.声明业务方法的业务属性(隔离级别、传播行为、超时时间)
              id:自定义名称,表示<tx:advice>和</tx:advice>之间的配置内容的
              transaction-manager:事务管理器对象的id
          -->
      <tx:advice id="myAdvice" transaction-manager="transactionManager">
          <!--配置事物的属性-->
          <tx:attributes>
              <!--给具体的方法配置属性
                      name:方法名称,1)完整的方法名,不带包和类。2)方法可以使用通配符,* = 任意字符
                      propagation:传播行为,
                      isolation:隔离级别
                      rollback-for:指定的异常类名,全限定名。发送异常一定回滚
                  -->
              <tx:method name="buy" propagation="REQUIRED" isolation="DEFAULT"
                         rollback-for="cn.zhangm.excep.NotEnoughException,java.lang.NullPointerException"/>
              <!--使用通配符,指定多个方法-->
              <!--添加方法-->
              <tx:method name="add*" propagation="REQUIRES_NEW"/>
              <!--修改方法-->
              <tx:method name="modify*" />
              <!--删除方法-->
              <tx:method name="remove*" />
              <!--查询方法,query、search、find-->
              <tx:method name="*" propagation="SUPPORTS" read-only="true" />
          </tx:attributes>
      </tx:advice>
      
  • 4)配置aop,指定哪些类要创建代理(即哪些包的哪些类的方法需要配置事物)

    • <!--配置aop-->
      <aop:config>
          <!--配置切入点表达式
              id:切入点表达式的名称
              expression:切入点表达式,指定哪些类要创建事物,aspectj会创建代理对象
          -->
          <aop:pointcut id="servicePt" expression="execution(* *..service..*.*(..))" />
          <!--配置增强器:关联advice和pointcut
              dvice-ref:通知,上面tx:advice
              pointcut-ref:切入点表达式的id
          -->
          <aop:advisor advice-ref="myAdvice" pointcut-ref="servicePt"/>
      </aop:config>
      

六、spring与web

1. 简介

  • 1)需求:web项目中容器对象只需要创建一次。
    • 把容器对象放入到全局作用域ServletContext中
  • 2)如何做?
    • 使用监听器,当全局作用域对象被创建时,创建容器,存入ServletContext中
  • 3)监听器的作用?
    • 创建容器对象ApplicationContext
    • 把容器对象放入到ServletContext
  • 4)监听器可以自己创建,也可以使用框架中提供好的ContextLoaderListener

2. 如何做

  • 1)在web.xml中配置监听器:ContextLoaderListener

    • <context-param>
          <!--contextConfigLocation表示配置文件路径-->
          <param-name>contextConfigLocation</param-name>
          <param-value>classpath:applicationContext.xml</param-value>
      </context-param>
      <listener>
          <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
      </listener>
      
    • 监听器创建后会读取/WEB-INF/applicationContext.xml文件

    • 为什么读取该文件:

      • 因为在监听器中要创建ApplicationContext对象,需要加载配置文件。/WEB-INF/applicationContext.xml是监听器默认读取的spring配置文件路径
    • 可以修改默认路径,使用<context-param>标签

  • 2)在servlet中获取容器

    •  /*
      ========================================================================
      * 创建spring容器对象,获取的容器对象并不是唯一的,每一次进入该servlet就会创建一个该对象,
      * 而创建容器对象时,所有交给spring创建的对象都会创建一次,造成内存浪费
      * */
      //String config = "applicationContext.xml";
      //ApplicationContext ac = new ClassPathXmlApplicationContext(config);
      //=======================================================================
      
      WebApplicationContext ctx = null;
      //获取ServletContext中的容器
      /*
      String key = WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE;
      Object attribute = this.getServletContext().getAttribute(key);
      if (attribute != null) {
          ctx = (WebApplicationContext)attribute;
      }
      */
      //使用工具类获取
      ServletContext servletContext = this.getServletContext();
      ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext);
      

七、总结

  • 1)注入的注解形式需要声明组件扫描器

    • <context:component-scan base-package="cn.zhangm.anno01" />
      
  • 2)aspectj的aop需要声明代理器

    • <aop:aspectj-autoproxy />
      
  • 3)spring的配置文件中读取.properties格式文件的值,需要声明

    • <context:property-placeholder location="classpath:jdbc.properties" />
      
  • 4)spring事物管理需要声明注解驱动

    • <tx:annotation-driven transaction-manager="事物管理器对象的id" />
      

pringframework.web.context.ContextLoaderListener

```

  • 监听器创建后会读取/WEB-INF/applicationContext.xml文件

  • 为什么读取该文件:

    • 因为在监听器中要创建ApplicationContext对象,需要加载配置文件。/WEB-INF/applicationContext.xml是监听器默认读取的spring配置文件路径
  • 可以修改默认路径,使用<context-param>标签

  • 2)在servlet中获取容器

    •  /*
      ========================================================================
      * 创建spring容器对象,获取的容器对象并不是唯一的,每一次进入该servlet就会创建一个该对象,
      * 而创建容器对象时,所有交给spring创建的对象都会创建一次,造成内存浪费
      * */
      //String config = "applicationContext.xml";
      //ApplicationContext ac = new ClassPathXmlApplicationContext(config);
      //=======================================================================
      
      WebApplicationContext ctx = null;
      //获取ServletContext中的容器
      /*
      String key = WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE;
      Object attribute = this.getServletContext().getAttribute(key);
      if (attribute != null) {
          ctx = (WebApplicationContext)attribute;
      }
      */
      //使用工具类获取
      ServletContext servletContext = this.getServletContext();
      ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext);
      

七、总结

  • 1)注入的注解形式需要声明组件扫描器

    • <context:component-scan base-package="cn.zhangm.anno01" />
      
  • 2)aspectj的aop需要声明代理器

    • <aop:aspectj-autoproxy />
      
  • 3)spring的配置文件中读取.properties格式文件的值,需要声明

    • <context:property-placeholder location="classpath:jdbc.properties" />
      
  • 4)spring事物管理需要声明注解驱动

    • <tx:annotation-driven transaction-manager="事物管理器对象的id" />
      
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值