淘先锋技术网

首页 1 2 3 4 5 6 7

文章目录


Author:宇哥 WeChat/QQ: Warmheart170712 / 939638113
整理不易,重在分享, 欢迎转发收藏,助力面试上岸,学编程找宇哥

1、Spring框架介绍

1、Spring是轻量级的开源的JavaEE框架
2、Spring可以解决企业应用开发的复杂性
3、Spring有两个核心部分,IOC和AOP
      IOC:控制反转,把创建对象的过程交给spring容器进行管理
      AOP:面向切面,不修改源代码的基础上进行功能的增强
4、Spring的特点
     方便解耦,简化开发
     支持AOP编程
     方便程序测试
     方便和其他框架进行整合
     方便进行事务操作
     降低API开发难度

2、IOC容器

2.1、IOC的底层原理

什么是IOC:
    控制反转:inversion of control
    把对象的创建和对象之间的调用都交给spring管理
    使用IOC的目的,为了降低耦合度
    入门案例就是IOC的实现
IOC的底层原理:
     底层技术:XML解析技术,设计模式,反射
     见图过程

在这里插入图片描述

2.2、IOC接口-BeanFactory

IOC思想基于IOC容器实现,IOC容器底层就是对象工厂
Spring提供IOC容器实现两种方式-两个接口
   BeanFactory:
     概念:是IOC容器的基本实现,是Spring内部的接口,
     	  一般不提供给开发人员使用
     特点:在加载配置文件的时候,不会创建对象,
     	  在获取对象或者使用对象的时候才会创建
   ApplicationContext:
     概念:是BeanFactory的子接口,提供了更多更强大的功能,
     	  一般开发人员使用
     特点:在加载配置文件的时候就会配置文件中的对象进行创建

2.3、IOC操作bean管理

什么是bean管理
    bean管理指的是两个操作:Spring创建对象和Spring注入属性
    Spring创建对象两种方式:基于xml配置文件和基于注解方式

2.4、Bean管理操作有两种方式

2.4.1、IOC操作bean管理-基于xml方式(demo01、demo02

2.4.1.1、IOC操作bean管理–基于xml方式创建对象

<bean id="user" class="cn.diautowried.User"></bean>
在Spring配置文件中:
	使用bean标签,标签里面添加对应属性,就可以实现对象的创建
    bean标签中的常用属性
           id属性:唯一标识,表示给对象其标识或者别名
           class属性:类的全路径(包类路径)
    创建对象的时候,默认使用的是无参构造

2.4.1.2、IOC操作bean管理–基于xml方式注入属性

DI:依赖注入,就是注入属性
2.4.1.2.1、第一种方式:使用set方法进行属性注入
在spring配置文件中配置对象创建,配置属性注入
创建book对象,set方法进行属性注入:如下
		property:表示属性的标签
		name:表示实体类中的属性名
		value:表示为属性名赋值

<bean id="book" class="cn.diautowried.Book">
 <!--在bean标签中使用property标签进行属性注入-->
   <property name="bookName" value="围城"></property>
   <property name="bookAauthor" value="钱钟书"></property>
</bean>
2.4.1.2.2、第二种方法:使用有参构造进行属性注入
在spring配置文件中进行配置
使用有参构造注入属性:
	constructor-arg:构造-参数
	name:表示实体类中的属性名
	value:表示为属性名赋值

<bean id="order" class="cn.diautowried.Order">
	<constructor-arg name="orderName" value="华为手机"></constructor-arg>
	<constructor-arg name="orderAdddress" value="山西太原"></constructor-arg>
</bean>
2.4.1.2.3、第三种注入方式:p名称空间注入—set注入的简化版
1 配置文件中添加p名称空间约束
	xmlns:p="http://www.springframework.org/schema/p"
	
2:进行属性注入,在bean标签中进行操作
<!--p名称空间注入 set注入简化版-->
<bean id="book1" class="cn.diautowried.Book" p:bookName="我们仨" p:bookAauthor="杨绛"></bean>

3 属性说明:
	p名称空间属性注入--set注入简化版本
	p:属性名=“属性值”

2.4.1.3、IOC操作bean管理–注入属性–外部bean

1 创建UserService 和 UserDao 对象
2 将UserDao注入到UserService中
	name属性值:类中的属性的名称
    ref属性值:是UserDao对象的bean标签的属性值

                          
<bean id="userService" class="cn.zzz.service.UserService">
     <property name="userDao" ref="userDaoImpl"></property>
</bean>

外部bean:
<bean id="userDaoImpl" class="cn.jdkproxy.UserDaoImpl"></bean>

2.4.1.4、IOC操作bean管理–注入属性–内部bean

1 创建Dept类 和 Emp类,一对多关系:部门和员工,
  一个部门有多个员工,一个员工属于一个部门
  将部门对象注入到员工员工对象中


<bean id="emp" class="cn.innertbean.Emp">
  <!--设置两个普通属性-->
  <property name="eName" value="Anny"></property>
  <property name="eGender" value=""></property>
   <!--设置对象属性-->
  <property name="dept">
       <bean id="dept" class="cn.innertbean.Dept">
           <property name="dName" value="开发部"></property>
       </bean>
   </property>
</bean>

2.4.1.5、IOC操作bean管理–内部bean的级联赋值

1 创建bean:emp

<bean id="emp" class="cn.innertbean.Emp">
   <!--设置两个普通属性-->
   <property name="eName" value="Jack"></property>
   <property name="eGender" value=""></property>
   <!--级联赋值-->
   <property name="dept" ref="dept"></property>
 </bean>
 
<!--创建bean dept-->
<bean id="dept" class="cn.innertbean.Dept">
   <property name="dName" value="理发部"></property>
</bean>

2.4.1.6、IOC操作bean管理–注入集合属性

2.4.1.6.0、注入集合属性
1 注入数组、注入List、注入Map、注入Set
	创建类,定义数组,list、map、set类型属性,生成的set方法
2 创建实体类Stu,生成属性的set方法

public class Stu {
    //1、数组类型属性
    private String[] courses;

    //2、List集合类型属性
    private List<String> list;

    //3、map集合类行书
    private Map<String,String> map;

    //4、set集合的属性
    private Set<String> set;

    //学生学习多门课程
    private List<Course> courseList;
3 配饰spring.xml

在spring配置文件中进行配置

<!--完成集合类型属性的注入-->
<bean id="stu" class="cn.pojo.Stu">

         <!--数组类型的属性注入-->
         <property name="courses">
               <array>
                   <value>java课程</value>
                   <value>数据库课程</value>
               </array>
         </property>

         <!--list类型属性注入-->
         <property name="list">
              <list>
                  <value>IDEA</value>
                  <value>Eclispse</value>
                  <value>HbuilderX</value>
              </list>
         </property>

         <!--map类型属性注入-->
         <property name="map">
              <map>
                 <entry key="mysql" value="免费"></entry>
                 <entry key="oracle" value="收费"></entry>
              </map>
          </property>

           <!--set类型属性注入-->
           <property name="set">
              <set>
              	<value>SpringAlibaba</value>	
              	<value>SpringCloud</value>
              </set>
          </property>
</bean>
输出属性结果
        数组:    [java课程, 数据库课程]
        List集合:[IDEA, Eclispse, HbuilderX]
        Map集合: {mysql=免费, oracle=收费}
        Set集合   [SpringAlibaba, SpringCloud]
2.4.1.6.1、在集合里面设置对象类型值
List集合设置对象类型
    private List<Course> courseList;
使用Stu和Course
配置spring的配置文件

需求:在集合list里面设置对象类型值
<bean id="stu" class="cn.pojo.Stu">
   <!--注入list集合类型,值是对象-->
   <property name="courseList">
        <list>
           <ref bean="course1"></ref>
           <ref bean="course2"></ref>
           <ref bean="course3"></ref>
        </list>
   </property>
</bean>

创建多个course对象:
<bean id="course1" class="cn.pojo.Course">
      <property name="courseName" value="前端课程"></property>
</bean>
<bean id="course2" class="cn.pojo.Course">
      <property name="courseName" value="后端课程"></property>
</bean>
<bean id="course3" class="cn.pojo.Course">
      <property name="courseName" value="数据库课程"></property>
</bean>

2.4.1.7、IOC操作bean管理-FactoryBean

1 Spring有两种类型的bean
    一种是普通bean
    一种是工厂bean,也就是FactoryBean
2 普通bean:在spring配置文件中定义bean的类型,
		返回类型就是此类型
  工厂bean:在spring配置文件中定义bean的类型,
  		返回类型可以不一致(可以返回其他类型)
3、工厂bean案例
   步骤1:创建类,设置这个类为工厂bean。实现接口FactoryBean
   步骤2:实现接口中的方法,在此方法中定义返回的bean的类型
4 自定义的工厂bbean,定义返回的bean对象是Course类型对象

public class MyBean implements FactoryBean<Course> {

    //定义返回的bean对象是其他对象
    @Override
    public Course getObject() throws Exception {
        Course course = new Course();
        course.setCourseName("社会与科学");
        return course;
    }

    @Override
    public Class<?> getObjectType() {
        return null;
    }

    @Override
    public boolean isSingleton() {
        return false;
    }
}
5 配置文件配置myBean对象
<bean id="myBean" class="cn.factorybean.MyBean"></bean>
6 工厂bean - 测试bean的返回类型是其他类型--FactoryBean
	应该返回MyBean类型的对象,实际返回时Course类型对象

    @Test
    public void testMyBean(){
        //1、创建Spring的核心容器,并加载配置文件
        ApplicationContext context = new ClassPathXmlApplicationContext("spring4.xml");
        //2、获取对象
        Course course = context.getBean("myBean", Course.class);
        System.out.println(course);
    }

7 返回Course类型对象
在这里插入图片描述

2.4.1.8、IOC操作bean管理–bean的作用域

在spring中,设置创建bean实例是单实例还是多实例
   在默认情况下,spring容器创建的对象是单实例
   如何设置单实例和多实例:
   在spring配置文件中bean标签里有属性scope,用于设置单实例和多实例
      第一个值:默认值singleton,表示的是单实例对象
      第二个值:设置protoType。表示的是多实例对象
          <!--测试作用域 单实例 和 多实例-->
          <bean id="user1" class="cn.pojo.User" scope="singleton">
                <property name="username" value="张三"></property>
          </bean>
          <bean id="user2" class="cn.pojo.User" scope="prototype">
                <property name="username" value="张三"></property>
          </bean>
                   
singleton和protoType的区别:
       singleton是单实例对象,
        	设置scope的属性值是singleton,
        	单实例对象会在spring配置文件加载的时候就创建
       prototy是多实例对象
            设置scope的属性值是prototype,
            不是在加载spring配置文件的时候创建对象,
            而是在调用getBean方法的时候创建多实例对象

拓展:scope的其他属性值:
     request:将创建好的对象放在request域对象中,
              一次请求共享数据
     session:将创建好的对象放在session域对象中,
              一次会话共享数据

2.4.1.9、IOC操作bean管理–bean的生命周期

1、生命周期:
    从对象创建到对象销毁的过程
2、bean的生命周期---简化五步
    (1)、通过构造器创建bean实例,也就是执行无参构造
    (2)、为bean的属性设置值,以及对其他bean的调用
    	(调用set方法)
    (3)、调用bean的初始化方法,(初始化的方法需要进行配置)
    (4)、bean可以使用,对象可以获取
    (5)、当容器在关闭的时候,会调用bean的销毁方法
    	 (销毁的方法需要进行配置)
3、bean的生命周期代码案例
      步骤1:创建Phone类,
            设置属性名,
            生成set方法、
            构造方法,
            配置初始化、
            销毁方法。
            以及一个普通方法
/**
 * 测试bean的生命周期
 */
public class Phone {

    private String phoneName;
    //第一步:无参构造执行,创建对象
    public Phone() {
        System.out.println("第一步:创建手机的无参构造执行了");
    }
    //第二步:调用set方法设置对象的属性值
    public void setPhoneName(String phoneName) {
        this.phoneName = phoneName;
        System.out.println("第二步:调用set方法设置对象的属性值");
    }
    //第三步:创建初始化方法 初始化方法需要自行配置
    public void initBean(){
        System.out.println("第三步:执行初始化的方法");
    }
    //第四步:创建bean实例创建完成并且调用方法
    public void actionBean(){
        System.out.println("第四步:创建bean实例创建完成并且调用方法");
    }
    //第五步:关闭容器执行销毁beam方法
    public void destoryBean(){
        System.out.println("第五步:容器关闭bean实例被销毁");
    }
}
4 配置spring配置文件
	属性:
	init-method="initBean" :配置初始化方法
	destroy-method="destoryBean" :配置销毁方法

<!--测试bean的生命周期-->
<bean id="phone" class="cn.beanlife.Phone" init-method="initBean" destroy-method="destoryBean">
      <property name="phoneName" value="华为mate40"></property>
</bean>
5 测试方法

    @Test
    public void testLife(){
        //1、创建spring的核心容器。并加载配置文件
        ClassPathXmlApplicationContext context = 
        		new ClassPathXmlApplicationContext("spring6.xml");
        //2、通过容器获取对象
        Phone phone = context.getBean("phone", Phone.class);
        //3、调用方法
        phone.actionBean();
        //4、关闭容器,手动让bean实例销毁
        context.close();
    }

6 执行结果
在这里插入图片描述

2.4.1.10、IOC操作bean管理–bean的生命周期的后置处理器

1 后置处理器介绍
	Bean的后置处理器的作用:
		Bean的设计模式用到了模板方法设计模式。
		在Bean的各个阶段都会有一些功能增强,
		这些功能都是由Bean后处理器提供的
		
	实现接口BeanPostProcessor,
	重写方法
		在初始化方法执行之前执行:
		postProcessBeforeInitialization
		在初始化方法执行之后执行:
		postProcessAfterInitialization
2 bean的生命周期---详细七步
     (1)、通过构造器创建bean实例,也就是执行无参构造
     (2)、为bean的属性设置值,以及对其他bean的调用
     		(调用set方法)
     (3)、把bean实例传递到bean后置处理器的方法
     	  即:postProcessBeforeInitialization()
     (4)、调用bean的初始化方法,(初始化的方法需要进行配置)
     (5)、把bean实例传递到bean后置处理器的方法
     	  即:postProcessAfterInitialization()
     (6)、bean可以使用,对象可以获取
	 (7)、当容器在关闭的时候,会调用bean的销毁方法
	 		(销毁的方法需要进行配置)
3 bean的后置处理器方法

public class MyBeanPost implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("在初始化执行之前执行的方法,把bean实例传递到bean后置处理器的方法");
        return bean;
    }
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("在初始化执行之后执行的方法,把bean实例传递到bean后置处理器的方法");
        return bean;
    }
}
5 spring.xml文件中配置自定义的后置处理器

<!--测试bean的生命周期 后置处理器执行-->
<bean id="myBeanPost" class="cn.beanlife.MyBeanPost"></bean>

6 测试代码输出结果
在这里插入图片描述

2.4.1.11、IOC操作bean管理-自动装配

1、什么是自动装配?什么是手动装配
	手动装配:
		是程序员在配置文件中手动的进行属性的赋值,
		即value属性值和ref属性值
	自动装配:
		根据指定的装配规则,根据属性名称或者属性类型,
		Spring自动将匹配的属性值进行注入
2 实体类用于自动装配

	public class Emp {
	    //部门属性
	    private Dept dept;
	{
3、演示自动装配: 两种方式:根据名称注入,根据类型注入

2.1、自动装配 通过属性名称 注入属性
	 根据属性名称自动注入:autowire="byName",
	 注意:特点:注入值-的bean的id值和类中的属性名称一致
	<bean id="emp1" class="cn.autowire.Emp" autowire="byName"></bean>
	<bean id="dept" class="cn.autowire.Dept"></bean>

2.2、自动装配 通过属性类型 注入属性
	 根据属性类型自动注入:autowire="byType"
	<bean id="emp2" class="cn.autowire.Emp" autowire="byType"></bean>
	<bean id="dept" class="cn.autowire.Dept"></bean>
4 配置spring.xml
    <!--1、原始方式 手动装配属性-->
    <bean id="emp" class="cn.autowire.Emp">
        <!--手动装配属性-->
        <property name="dept" ref="dept"></property>
    </bean>
    <!--2、自动装配 通过属性类型 注入属性-->
    <bean id="emp1" class="cn.autowire.Emp" autowire="byName"></bean>
    <!--3、自动装配 通过属性名称 注入属性-->
    <bean id="emp2" class="cn.autowire.Emp" autowire="byType"></bean>
    
    <!--创建Dept的bean对象-->
    <bean id="dept" class="cn.autowire.Dept"></bean>
5 测试代码
    @Test
    public void TestAuto(){
        //1、创建spring的核心容器。并加载配置文件
        ApplicationContext context = new ClassPathXmlApplicationContext("spring7.xml");
        //2、通过容器获取对象
        //手动装配
        Emp emp = context.getBean("emp", Emp.class);
        //自动装配-通过属性名称注入
        Emp emp1 = context.getBean("emp1", Emp.class);
        //自动装配-通过属性类型进行注入
        Emp emp2 = context.getBean("emp2", Emp.class);
        //3、输出
        System.out.println(emp);
        System.out.println(emp1);
        System.out.println(emp2);
    }

6 输出结果
在这里插入图片描述

2.4.2、IOC操作bean管理-基于注解方式(demo03 demo04)

2.4.2.1、常用创建bean对象的注解

 1、什么是注解
		注解是代码的特殊标记:
			格式:@注解名(属性名=属性值,属性名=属性值)
		使用注解:注解作用在类上面,方法上面,属性上面
		使用注解的目的:简化xml设置
		
2、spring针对Bean管理中创建对象提供注解
        @Component:笔试普通的bean
        @Service:表示业务层
        @Controller:表示控制层
        @Repository:表示持久层
        
     以上四个注解功能是一样的,都可以用来创建bean实例
     注解中的value值可以自定义,相当于bean标签中的id属性值
		<bean id="userService" class=""/>
		value值可以省略不写,默认值是类名称的首字母小写

2.4.2.2、基于注解方式–实现对象创建

1 创建类,在类上添加创建对象的注解
	@Service(value = "userService")
	public class UserService {
	        public void add(){
	              System.out.println("service add...");
	        }
	}
2 配置spring.xml

开启组件扫描
        1、如果扫描多个包,多个包之间使用逗号隔开
        2、扫描包的上层目录
<context:component-scan base-package="cn.anno"></context:component-scan>

设置组件扫描-设置包含哪些注解
	use-default-filters="false" : 
			表示不使用默认的filter,自行配置filter
	context:include-filter  : 
			设置扫描哪些些内容
	            type属性:注解
	            expression:只扫描对应表达式的注解,
<context:component-scan base-package="cn.anno" use-default-filters="false">
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Service"/>
</context:component-scan>

设置组件扫描-设置排除哪些注解
	默认扫描所有注解,排除execlude
		context:exclude-filter  :设置不扫描哪些些内容
           type:注解
           expression:不扫描对应表达式的注解,

<context:component-scan base-package="cn.anno">
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
3 测试代码
	@Test
    public void testComponent(){
        ApplicationContext context = 
        		new ClassPathXmlApplicationContext("spring1.xml");
        UserService userService = 
        		context.getBean("userService", UserService.class);
        System.out.println(userService);
        userService.add();
    }

测试结果
在这里插入图片描述

2.4.2.3、基于注解方式–实现属性注入

2.4.2.3.1、@AutoWired :根据属性类型进行注入
@AutoWired :根据属性类型进行注入
   @Autowired注解是按照类型(byType)装配依赖对象,
   默认情况下它要求依赖对象必须存在,
   	如果允许 null 值,可以设置它的required 属性为 false。
2.4.2.3.2、@Qualifier:根据属性名称进行注入
@Qualifier:根据属性名称进行注入
	在@AutoWired注解通过类型注入的前提下,
		如果被注入的接口有多个实现类对象,
			那么就可以按照名称(byName)来装配,
			可以结合@Qualifier 注解一起使用
		如果不使用@Qualifier注解进行value属性的标识,
			那么容器会为此接口的多个实现类创建bean对象
			此时,容器不知道改注入哪个实现类的bean对象,
			会报异常
			NoUniqueBeanDefinitionException: 
			No qualifying bean of type 'cn.anno.dao.UserDao'
			 available: expected single matching bean 
			 but found 3: userDaoImpl1,userDaoImpl2,userDaoImpl3
2.4.2.3.3、@Resource: 可以根据类型注入,可以根据名称注入
@Resource: 可以根据类型注入,可以根据名称注入
	@Resource不是spring的注解,而是javax的注解。
	@Resource默认按照byName自动注入
	@Resource 有两个重要的属性:name和type
        Spring将@Resource注解的:
        		name属性解析为bean的名字,
        	    type属性则解析为bean的类型。
        	    
		如果使用 name 属性,则使用 byName 的自动注入策略,
		如果使用 type 属性时则使用 byType 自动注入策略。
		如果既不制定 name 也不制定 type 属性,
			这时将通过反射机制使用 byName 自动注入策略。
2.4.2.3.4、@Value 注入普通类型属性
@Value注解:
		 注入普通类型属性,为普通属性赋值
		 属性value的值,会赋值给name
		 
          @Value(value = "武磊")
          private String name;

2.4.2.4、基于注解方式–实现完全注解开发

1 创建配置类,替代spring.xml文件
	@Configuration:注解 
			表示标识此类是配置类
	@ComponentScan("cn.spring")注解:
			表示开启注解扫描
			括号中的值,表示开启扫描此包下的所有注解
			
@Configuration//作为配置类。替代xml核心配置文件
@ComponentScan("cn.spring")
public class SpringConfig {}
2 编写测试类
	
	@Test
    public void test1(){
        //1、创建核心容器,加载配置类
        AnnotationConfigApplicationContext ccontex =
                new AnnotationConfigApplicationContext(SpringConfig.class);
        //2、通过容器获取bean对象
        UserService userService = ccontex.getBean("userService", UserService.class);
        //3、调用方法
        userService.add();
        System.out.println(userService);
    }

2.4.2.5 容器的不同表现形式

1 BeanFactory 
2 Application
3 ClassPathXmlApplicationContext
4 AnnotationConfigApplicationContext
5 FactoryBean

3、AOP

3.1 什么是AOP(概念)

什么是AOP(概念)
	官方:面向切面编程,利用AOP可以对业务逻辑的各个部分进行隔离
		 从而使得业务逻辑各部分之间的耦合度降低,
		 提高程序的可重用性,同时提高开发效率
    通俗:不修改源代码的方式,在主干功能里添加新的功能

在这里插入图片描述

3.2、AOP底层原理

3.2.1、AOP底层使用动态代理,有两种情况的动态代理

AOP底层使用动态代理,有两种情况的动态代理
  第一种:有接口情况--使用JDK动态代理
         创建接口实现类的代理对象,增强类的方法
  第二种:没有接口情况--使用CGLIB动态代理
         创建子类的代理对象,增强类的方法

JDK动态代理在这里插入图片描述
CGLIB动态代理
在这里插入图片描述

3.2.2、JDK动态代理和CGLIB动态代理的区别

JDK动态代理和CGLIB动态代理的区别

概念:
   JDK代理:使用的是反射机制生成一个实现代理接口的匿名类,
           在调用具体方法前调用InvokeHandler来处理。
   CGLIB代理:使用字节码处理框架asm,
   			 对代理对象类的class文件加载进来,
   			 通过修改字节码生成子类。
   			 
效率:
     JDK创建代理对象效率较高,执行效率较低;
     CGLIB创建代理对象效率较低,执行效率高。
     
关系:
   JDK动态代理机制是委托机制,只能对实现接口的类生成代理,
   		通过反射动态实现接口类;
   CGLIB则使用的继承机制,针对类实现代理,
   		被代理类和代理类是继承关系,
   		所以代理类是可以赋值给被代理类的,
   		因为是继承机制,不能代理final修饰的类。
   		
实现:
    JDK代理是不需要依赖第三方的库,只要JDK环境就可以进行代理,
    	需要满足以下要求:
         1.实现InvocationHandler接口,重写invoke()
         2.使用Proxy.newProxyInstance()产生代理对象
         3.被代理的对象必须要实现接口
    CGLIB 必须依赖于CGLIB的类库,需要满足以下要求:
         1.实现MethodInterceptor接口,重写intercept()
         2.使用Enhancer对象.create()产生代理对象
         
使用场景:
	1)如果目标对象实现了接口,
		默认情况下会采用JDK的动态代理实现AOP,
		可以强制使用CGLIB实现AOP
	2)如果目标对象没有实现了接口,
		必须采用CGLIB库,
		spring会自动在JDK动态代理和CGLIB之间转换

3.2.3、AOP的JDK动态代理实现案例

1 创建接口 UserDao 

public interface UserDao {
    public int add(int a,int b);
    public String update(String id);
}
2 创建接口的实现类 

public class UserDaoImpl implements UserDao{
    @Override
    public int add(int a, int b) {
        return a+b;
    }

    @Override
    public String update(String id) {
        return id;
    }
}
3 创建UserDaoImpl的代理类,实现功能增强

创建代理对象代码,做功能增强
public class UserDaoProxy implements InvocationHandler {

    被代理对象是userDaoImpl
    创建的是谁的代理对象,就需要把谁传递过来
    通过有参构造进行传递
    
    private UserDaoImpl userDaoImpl;
    public UserDaoProxy(UserDaoImpl userDaoImpl) {
        this.userDaoImpl = userDaoImpl;
    }


    增强逻辑
    @Override          proxy代理对象本身  被增强的方法  执行增强方法传递的参数
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //在方法执行之前执行
        System.out.println("在方法执行之前执行......"+"被增强的方法的名称:"+method.getName()+"......传递的参数"+ Arrays.toString(args));
        //被增强的方法add执行,
        Object result =method.invoke(userDaoImpl, args);
        System.out.println("add方法执行了..."+result);
        //在方法执行之后执行
        System.out.println("方法之后执行");
        return result;
    }
}
4 创建测试代码

public class TestJDKProxy {
    public static void main(String[] args) {
        //1、获取被代理实现类的接口UserDao的字节码文件对象
        Class[] interfaces = {UserDao.class};
        //2、创建被代理接口的实现类UserDaoImpl的对象
        UserDaoImpl userDaoImpl = new UserDaoImpl();
        //3、创建UserDao接口的实现类的代理对象
        //下面的代码相当于 UserDao userDao = new UserDaoImpl();
        UserDao userDaoProxy = (UserDao) Proxy.newProxyInstance(TestJDKProxy.class.getClassLoader(), interfaces, new UserDaoProxy(userDaoImpl));
        //执行目标方法
        int total = userDaoProxy.add(1, 2);
        System.out.println("total:"+total);
    }
}
JDK动态代理,调用Proxy类的newProxyInstance()方法
	调用:newProxyInstance(ClassLoader loader, 类< ? >[] interfaces, InvocationHandler h)
	返回指定接口的代理类的实例,该接口将方法调用分派给指定的调用处理程序。
       方法参数1:ClassLoader loader     
       		     --类加载器
       方法参数2:类<?>[] interfaces     
       			 --增强方法所在的类,这个类实现的接口,
       			 	即被代理类的父接口
       方法参数3:InvocationHandler h    
       			--实现InvocationHandler接口,
       				创建代理对象,写增强方法

5 执行结果
在这里插入图片描述

3.3、AOP相关术语

1 AOP的先关术语 
	连接点:类里面的那些方法可以增强,这些方法就被成为连接点
	切入点:    实际被增强的方法,称为切入点
	通知(增强):
          实际增强的逻辑部分,被称为通知(增强)
          通知有多种昂类型:5种类
                           前置通知,
                           后置通知、
                           环绕通知,
                           异常通知,
                           最终通知:
    切面:把通知应用到切入点的过程,就是切面

在这里插入图片描述

3.4、AOP操作准备

3.4.1、Spring和AspectJ的关系

Spring和AspectJ的关系
       1、Spring框架一般基于AspectJ实现AOP操作
       2、什么是AspectJ
          Aspect不是Spring的组成部分,独立的AOP框架,
          一般把Aspect和Spring框架一起使用,进行AOP操作

3.4.2、基于AspectJ实现AOP的操作

3.4.3、在项目中引入AOP的相关依赖

1 步骤1:在项目中引入AOP的相关依赖
	 <!--spring-AOP依赖-->
    <dependency>
      		<groupId>org.springframework</groupId>
      		<artifactId>spring-aop</artifactId>
      		<version>5.2.5.RELEASE</version>
    </dependency>
           
    <!--Aspect切面依赖-->
    <dependency>
			<groupId>org.aspectj</groupId>
			<artifactId>aspectjrt</artifactId>        
			<version>1.9.9.1</version>
	</dependency>

    <dependency>
          <groupId>org.aspectj</groupId>
          <artifactId>aspectjweaver</artifactId>
		  <version>1.9.9.1</version>
	</dependency>

3.4.4、切入点表达式

切入点表达式
     切入点表达式的作用:知道对哪个类里面的哪个方法进行增强
     切入点表达式语法:
 	 execution([权限修饰符][返回类型][类的路径][方法名称][参数列表])
       举例1:对cn.aop.zzy包中的UserDao类里面的add方法做增强
             execution(* cn.aop.zzy.UserDao.add(..))
       举例2:对cn.aop.zzy包中的UserDao类里面的所有方法做增强
             execution(* cn.aop.zzy.UserDao.*(..))
       举例3:对cn.aop.zzy包中的所有类,以及里面的所有方法做增强
             execution(* cn.aop.zzy.*.*(..))

3.6、AOP操作–基于AspectJ注解

3.6.1 基于AspectJ注解

1 创建被增强类

创建被增强类,被增强方法
public class User {
     public void add(){
     	System.out.println("我是被增强的方法,执行了");
     }
}
2  创建增强类,编写增强逻辑

@Component
@Aspect //生成代理对象
public class UserProxy {

//前置通知:在目标方法执行之前执行
//增强的方法 before注解表示作为前置通知,是在目标方法执行之前执行
@Before(value = "execution(* cn.anno.User.add(..))")
public void before(){
	System.out.println("before。。。在目标方法执行之前执行");
}

//后置通知:在目标方法执行之后执行
@After(value = "execution(* cn.anno.User.add(..))")
public void after(){
	System.out.println("after。。。在目标方法执行之后执行");
}

//返回通知:在目标方法返回值之后执行
@AfterReturning(value = "execution(* cn.anno.User.add(..))")
public void afterReturning(){
	System.out.println("afterReturning。。。在目标方法返回值之后执行");
}

//异常通知:在目标方法执行发生异常执行
@AfterThrowing(value = "execution(* cn.anno.User.add(..))")
public void afterThrowing(){
	System.out.println("afterThrowing。。。在目标方法执行发生异常执行");
}

//环绕通知,在目标方法执行之前和执行之后执行
@Around(value = "execution(* cn.anno.User.add(..))")
public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
	System.out.println("around 前。。。目标方法执行之前执行");
	//被增强的方法执行
	proceedingJoinPoint.proceed();
	System.out.println("around 后。。目标方法执行之后执行");
}
}
3 进行通知配置
1、在spring文件中,开启注解扫描
	<context:component-scan base-package="cn.anno"></context:component-scan>
2、使用注解创建User和UserProxy对象加上@Component
3、在增强类上面添加@Aspect注解@Aspect //生成代理对象
4、在配置文件中开启生成代理对象的配置
	<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
5、配置不同类型的通知在增强类里面,
	在作为通知方法上面添加通知类型的注解,
	并使用切入点表达式配置
4 编写测试类

@Test
public void testBefore(){
	//1.创建IOC容器,加载核心配置文件,
	ApplicationContext context =
			new ClassPathXmlApplicationContext("spring1.xml");
	//2.通过容器获取对象
	User user = context.getBean("user", User.class);
	user.add();
}
4 测试结果

目标方法无异常时,通知的执行顺序:
	around 前。。。目标方法执行之前执行
	before。。。在目标方法执行之前执行
	我是目标方法,也就是被增强的方法。。。。我执行了
	around 后。。目标方法执行之后执行
	after。。。在目标方法执行之后执行
	afterReturning。。。在目标方法执行返回之后执行

目标方法发生异常时,通知的执行顺序:
	around 前。。。目标方法执行之前执行
	before。。。在目标方法执行之前执行
	after。。。在目标方法执行之后执行
	afterThrowing。。。在目标方法执行发生异常执行

3.6.2 相同切入点提取

相同切入点抽取:
	pointCut()作用:
		作为切入点的承载方法,在通知方法上直接引用即可

@Pointcut(value = "execution(* cn.anno.User.add(..))")
public void pointCut(){}

//前置通知
@Before(value = "pointCut()")
public void before(){
	System.out.println("before。。。在目标方法执行之前执行");
}

3.6.3 增强的优先级

@Order(value = 3):
	设置增强的优先级(同一个目标方法有多个增强的情况)
	在增强类上面添加注解@Order(数字类型值),
		数字类型值越小,优先级越高

4、JdbcTemplate

4.1、什么是JdbcTemplate

spring框架对JDBC进行封装,
使用JDBCTemplate方便实现对数据库操作

4.2、准备工作

1 引入JDBCTemplate和事务的依赖

		<!--事务-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>5.2.6.RELEASE</version>
        </dependency>

        <!--spring-jdbc-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.2.6.RELEASE</version>
        </dependency>
2 在spring配置文件中配置数据库连接池

<!--配置德鲁伊连接池-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="clone">
    <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
    <property name="url" value="jdbc:mysql:///user_db"></property>
    <property name="username" value="root"></property>
    <property name="password" value="root"></property>
</bean>
3 配置JDBCTemplate对象,为其注入DataSource

<!--配置JDBCTemplate 查看源码-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
     <!--注入DataSource-->
     <property name="dataSource" ref="dataSource"></property>
</bean>

4.3、基于4.2的准备工作进行CRUD

4.3.1、新增操作

API方法:
	update方法用于增删改的操作
	update(String sql,Object[]... args)
	     有两个参数:
	     第一个参数:sql语句
	     第二个参数:可变参数,设置sql语句的值
@Repository
public class UserDaoImpl implements UserDao {

    //注入JdbcTemplate
    @Autowired
    private JdbcTemplate jdbcTemplate;

    //添加的方法
    @Override
    public void add(User user) {
        //编写SQL语句
        String sql1 = "insert into t_user values(null ,?,?)";
        //获取参数值
        Object[] args = {user.getUserName(), user.getUserStatus()};
        //进行新增操作,返回操作数
        int update = jdbcTemplate.update(sql1,args);
        //输出行记录
        System.out.println(update);
    }
}

4.3.2、修改和删除操作

API方法:
	update方法用于增删改的操作
	update(String sql,Object[]... args)
          有两个参数:
          第一个参数:sql语句
          第二个参数:可变参数,设置sql语句的值
@Repository
public class UserDaoImpl implements UserDao {

    //注入JdbcTemplate
    @Autowired
    private JdbcTemplate jdbcTemplate;
    
 	//修改的方法
    @Override
    public void update(User user) {
        //注意:sql语句中的字段名称要与数据库表中的字段名称一致
        String sql2 = "update t_user set user_name=?,user_status=? where user_id=?";
        Object[] args = {user.getUserName(), user.getUserStatus(),user.getUserId()};
        int update = jdbcTemplate.update(sql2,args);
        System.out.println(update);
    }

    //删除的方法
    @Override
    public void delete(Integer id) {
        String sql3 = "delete from t_user where user_id=?";
        int update = jdbcTemplate.update(sql3,id);
        System.out.println(update);
    }
}

4.3.3、查询操作

4.3.3.1、查询返回某个值—查询所有记录总数

API方法:
jdbcTemplate.queryForObject(String sql, Class<T> requiredType)
	此方法有连个参数:
	第一个参数:sql语句
	第二个参数:返回类型Class
 //查询所有记录
    @Override
    public int getAll() {
        //编写sql语句
        String sql4 = "select count(*) from t_user";
        //执行查询操作,第一个参数是sql。第二个参数是返回值的类型class
        Integer count =  jdbcTemplate.queryForObject(sql4, Integer.class);
        //返回结果
        return count;
    }

4.3.3.2、查询返回某对象

API方法:
jdbcTemplate.queryForObject(String sql, RowMapper<T> rowMapper ,Object... args )
	此方法有三个参数:
	第一个参数:sql语句
	第二个参数:RowMapper是接口,可以返回不同类型的数据,
			  使用接口中的实现类,可以完成数据的封装
	第三个参数:传递sql语句中问号的值
    //通过id查询
    @Override
    public User selectUserById(Integer id) {
        //编写sql
        String sql5 = "select * from t_user where user_id=?";
        //执行查询操作
        /*
            jdbcTemplate.queryForObject(String sql, RowMapper<T> rowMapper ,Object... args )
            第一个参数:sql语句
            第二个参数:RowMapper是接口,可以返回不同类型的数据,使用接口中的实现类,可以完成数据的封装
            第三个参数:传递sql语句中问号的值
         */
        User user = 
        	jdbcTemplate.queryForObject(sql5, new BeanPropertyRowMapper<User>(User.class), id);
        return user;
    }

4.3.3.3、查询返回某集合

API方法:
jdbcTemplate.query(String sql, RowMapper<T> rowMapper ,Object... args )
	此方法有三个参数:
	第一个参数:sql语句
	第二个参数:RowMapper是接口,可以返回不同类型的数据,
			  使用接口中的实现类,可以完成数据的封装
	第三个参数:传递sql语句中问号的值
//返回集合数据
    @Override
    public List<User> findAllUser() {
        //编写sql
        String sql6 = "select * from t_user";
        //执行查询操作
        List<User> userList = jdbcTemplate.query(sql6, new BeanPropertyRowMapper<User>(User.class));
        return userList;
    }

4.3.3.4、批量操作—新增

API方法:
batchUpdate(String sql,List<Object[]> batchArgs)
	两个参数:
	第一个参数:sql语句
	第二个参数:list集合,添加多条记录数据
//批量添加
    @Override
    public void batchAddUser(List<Object[]> batchArgs) {
        String sql7 = "insert into t_user values(null,?,?)";
        int[] ints = jdbcTemplate.batchUpdate(sql7,batchArgs);
        System.out.println(Arrays.toString(ints));
    }

4.3.3.5、批量操作—修改和删除

API方法:
batchUpdate(String sql,List<Object[]> batchArgs)
	 两个参数:
	第一个参数:sql语句
	第二个参数:list集合,添加多条记录数据
    //批量修改
    @Override
    public void batchUpdateUser(List<Object[]> batchArgs) {
        String sql8 = "update t_user set user_name=?,user_status=? where user_id=?";
        int[] ints = jdbcTemplate.batchUpdate(sql8,batchArgs);
        System.out.println(Arrays.toString(ints));
    }
    
    //批量删除
    @Override
    public void batchDelete(List<Object[]> batchArgs) {
        String sql9 = "delete from t_user where user_id=?";
        int[] ints = jdbcTemplate.batchUpdate(sql9,batchArgs);
        System.out.println(Arrays.toString(ints));
    }

5、事务管理

5.1、什么是事务

什么是事务
	事务是数据库操作的最基本的单元,
		是逻辑上的一组操作,要么都成功,
		如果有一个失败,所有操作都失败
		
	典型场景。银行转账

5.2、事务的四个也行ACID

事务的四个也行ACID
	原子性:
		原子性意味着数据库中的事务执行是作为原子。
		即不可再分,整个语句要么执行,要么不执行
	一致性:
		一致性即在事务开始之前和事务结束以后,
		数据库的完整性约束没有被破坏。
	隔离性:
		事务的执行是互不干扰的,
		一个事务不可能看到其他事务运行时,中间某一时刻的数据
	持久性:
		意味着在事务完成以后,
		该事务所对数据库所作的更改便持久的保存在数据库之中,
		并不会被回滚

5.3、新建事务操作环境

5.4、Spring事务管理操作

事务添加到JavaEE三层架构里的Service层,也就是业务逻辑层

在Spring中进行事务管理--两种方式
        编程时事务:不建议使用,代码臃肿,不易维护
        声明式事务:使用方便
        
声明式事务:
	1、基于注解方式
	2、基于xml配置文件方式
	3、在Spring进行声明式事务管理。底层用到了AOP原理
	4、提供可一个接口,代表事务管理器,
		这个接口针对不同的框架提供了不同的实现类

5.5、Spring声明式事务–注解方式

步骤1、在Spring配置文件中引入事务的名称空间tx
      xmlns:tx="http://www.springframework.org/schema/tx"
      http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
      
步骤2、在Spring配置文件中配置事务管理器,并且注入数据源
     <!--创建事务管理器-->
      <bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <!--指定数据源-->
            <property name="dataSource" ref="dataSource"></property>
      </bean>
      
步骤3:在配置文件中开启事务注解
       <!--开启事务注解 指明事务管理器-->
       <tx:annotation-driven transaction-manager="dataSourceTransactionManager"></tx:annotation-driven>

步骤4、在service类上或者方法上添加事务注解--@Transactional
      1、@Transactional,这个注解可以添加到类上面,也可以添加到方法上面
      2、如果把这个注解添加到类上面,这个类里面的所有方法都添加事务
      3、如果把这个方法添加到方法上,只为这个方法添加事务
      
           @Service
           @Transactional
           public class AccountService {
                  @Autowired
                  private AccountDao accountDao;

                  //转账的方法
                  public void accountMoney(){
                        //luncy少100
                        accountDao.reduceMoney();
                        //模拟意向
                        int a = 1/0;
                        //maey多100
                        accountDao.addMoney();
                   }
		    }

步骤5、测试结果
      测试过程中发生了异常。事务的提交失败,
      那么事务就会进行回滚,数据库中的lucy和mary的money值,
      没有发生变化

5.6、@Transactional注解事务管理的参数配置

@Transactional注解源码

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {
     @AliasFor("transactionManager")
     String value() default "";
     @AliasFor("value")
     String transactionManager() default "";
     Propagation propagation() default Propagation.REQUIRED;
     Isolation isolation() default Isolation.DEFAULT;
     int timeout() default -1;
     boolean readOnly() default false;
     Class<? extends Throwable>[] rollbackFor() default {};
     String[] rollbackForClassName() default {};
     Class<? extends Throwable>[] noRollbackFor() default {};
     String[] noRollbackForClassName() default {};
}

属性1:propagation。事务的传播行为
	多事务方法直接进行调用,这个过程中的事务管理有七种方式,
	前两种行为常用:REQUIRED和REQUIRED_NEW
	@Transactional(propagation = Propagation.REQUIRED)
	
属性2:ioslation、事务的隔离级别
	在多事务操作之间不会互相产生影响,
	不考虑隔离会产生很多问题,问题如下:
		问题:
	        脏读:一个未提交的事务读取到了另一个未提交事务的数据
	        幻读:一个事务读取到另一个提交事务的添加数据
	        不可重复读:一个未提交的事务读取到了另一个已提交事务修改的数据
		解决方案:
            通过设置事务的隔离级别,就能解决读的问题
                 读未提交:
                 读已提交
                 可重复读:
                 串行化:
                 //事务                      事务的隔离级别:可重复度
                 @Transactional(isolation = Isolation.REPEATABLE_READ)
                 
属性3:timeou。超时时间
        事务需要在一定的时间内提交,如果超过设定时间,事务就会回滚
        默认值是-1.设置时间以单位秒计算
属性4:readOnly、是否只读
        读:表示查询,写:表示增删改
        readOnly默认值是false,表示可以增删改查
        设置readOnly值是true,只能进行查询操作
属性5:rollbackFor、回滚
        设置出现纳西异常,进行回滚操作
属性6:norollbackFor、不回滚
        设置出现哪些异常,不进行回滚操作

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

5.7、Spring声明式事务–xml方式

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

    <!--配置德鲁伊连接池-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="clone">
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property>
        <property name="url" value="jdbc:mysql:///user_db"></property>
        <property name="username" value="root"></property>
        <property name="password" value="root"></property>
    </bean>

    <!--配置JDBCTemplate 查看源码-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <!--注入DataSource-->
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!--创建事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--指定数据源-->
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!--配置通知-->
    <tx:advice id="txadvice">
        <!--配置事务参数-->
        <tx:attributes>
            <tx:method name="accountMoney" propagation="REQUIRED"/>
        </tx:attributes>
    </tx:advice>

    <!--配置切入点和切面-->
    <aop:config>
        <!--配置切入点-->
        <aop:pointcut id="pt" expression="execution(* cn.zzz.service.AccountService.*(..))"/>
        <!--配置切面-->
        <aop:advisor advice-ref="txadvice" pointcut-ref="pt"></aop:advisor>
    </aop:config>
</beans>
测试结果
	测试过程中发生了异常。事务的提交失败,
	那么事务就会进行回滚,数据库中的lucy和mary的money值,
	没有发生变化

5.8、Spring声明式事务–完全注解方式

1  创建配置类,配置数据源,JdbcTemplate ,事务管理器
@Configuration//配置类
@ComponentScan(basePackages = "cn.zzy")//注解扫描
@Transactional//开启事务
public class TxConfig {

    //创建DruidDataSource对象
    @Bean
    public DruidDataSource getDruidDataSource(){
        DruidDataSource druidDataSource = new DruidDataSource();
        druidDataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
        druidDataSource.setUrl("jdbc:mysql:///user_db");
        druidDataSource.setUsername("root");
        druidDataSource.setPassword("root");
        return druidDataSource;
    }

    //创建JdbcTemplate对象
    @Bean
    public JdbcTemplate getJdbcTemplate(DruidDataSource druidDataSource){
        JdbcTemplate jdbcTemplate = new JdbcTemplate();
        //在IOC容器中根据数据类型,找到DruidDataSource,并注入
        jdbcTemplate.setDataSource(druidDataSource);
        return jdbcTemplate;
    }

    //创建事务管理器DataSourceTransactionManager对象
    @Bean
    public DataSourceTransactionManager getDataSourceTransactionManager(DruidDataSource druidDataSource){
        DataSourceTransactionManager dataSourceTransactionManager =
                new DataSourceTransactionManager();
        //在IOC容器中根据数据类型,找到DruidDataSource,并注入
        dataSourceTransactionManager.setDataSource(druidDataSource);
        return dataSourceTransactionManager;
    }
}
2 创建Service Dao 以及D奥Impl
@Service
@Transactional
public class AccountService {

    @Autowired
    private AccountDao accountDao;

    //转账的方法
    public void accountMoney(){
        /*注解方式:声明式事务*/
        //luncy少100
        accountDao.reduceMoney();
        //模拟意向
        int a = 1/0;
        //maey多100
        accountDao.addMoney();
    }
}
public interface AccountDao {

    //多钱的方法
    public void addMoney();
    //少钱的方法
    public void reduceMoney();
}
@Repository
public class AccountDaoImpl implements AccountDao{

    @Autowired
    private JdbcTemplate jdbcTemplate;
    @Override
    public void addMoney() {
        String sql = "update t_account set money=money+? where username=?";
        //设置让mary的钱多100
        jdbcTemplate.update(sql, 100,"mary");
    }
    @Override
    public void reduceMoney() {
        String sql = "update t_account set money=money-? where username=?";
        //设置让lucy的钱少100
        jdbcTemplate.update(sql, 100,"lucy");
    }
}
public class test1 {
    @Test
    public void testAnnoTx(){
        //创建核心容器对象
        ApplicationContext context =
                new AnnotationConfigApplicationContext(TxConfig.class);
        //通过容器获取对象
        AccountService accountService = context.getBean("accountService", AccountService.class);
        //调用方法
        accountService.accountMoney();
    }
}

测试结果:
	测试过程中发生了异常。事务的提交失败,
	那么事务就会进行回滚,数据库中的lucy和mary的money值,
	没有发生变化