spring学习笔记

思维导图:spring思维导图

spring全家桶:spring,springMVC,spring boot,spring cloud
spring:出现在2002年左右,解决企业开发的难度。减轻对项目模块之间的管理,类和类之间的管理,帮助开发人员创建对象,管理对象之间的关系。
spring核心技术:ioc,aop。能实现模块之间,类之间的解耦合。

依赖:class A中使用class B的属性或者方法,叫做class A依赖class B

spring的优点

(1)轻量
  • Spring框架使用的jar都比较小,一般都在1M以下,或者几百KB。Spring核心功能所需的jar总共在3M左右。
  • Spring框架运行占用的资源少,运行效率高。不依赖其他jar。
(2)针对接口编程,解耦合

Spring提供了ioc控制反转,由容器管理对象,对象的依赖关系。原来在程序代码中的对象创建方式,现在由容器完成。对象之间依赖解耦合。

(3)AOP编程的支持
  • 通过Spring提供的AOP功能,方便进行面向切面编程,许多不容易用传统OOP(面向对象的设计)实现的功能可以通过AOP轻松应付
  • 在Spring中,开发人员可以从繁杂的事务管理代码中解脱出来,通过声明方式灵活地进行事务的管理,提高开发效率和质量。
(4)方便集成各种优秀框架

Spring不排斥各种优秀的开源框架,相反Spring可以降低各种框架的使用难度,Spring提供了对各种优秀框架(如Struts,Hibernate,MyBatis)等的直接支持。简化框架的使用。Spring像插线板一样,其他框架是插头,可以容易的组合到一起。需要使用哪个框架,就把这个插头放入插线板。不需要可以轻松移除。

Spring体系结构

Spring由20多个模块组成,它们可以分为数据访问/集成(Data Access/Integration),Web,面向切面编程(AOP,Aspects),提供JVM的代理(Instrumentation),消息发送(Messaging),核心容器(Core Container)和测试(Test)。

Spring的第一个核心功能 ioc

IoC (Inversion of Control):控制反转,是一个理论,概念,思想。

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

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

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

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

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

为什么要使用ioc:目的就是减少对代码的改动,也能实现不同的功能。实现解耦合

java中创建对象有哪些方式:

  1. 构造方法
  2. 反射
  3. 序列化
  4. 克隆
  5. ioc:容器创建对象
  6. 动态代理

ioc的体现:

​ servlet

  1. 创建类继承HttpServlet

  2. 在web.xml注册servlet,使用

    <servlet-name>myservlet</servlet-name>
    <servlet-class>com.test.controller.MyServlet</servlet-class>
    
  3. 没有创建Servlet对象,没有MyServlet myServlet = new MyServlet();

  4. Servlet 是Tomcat服务器创建的。Tomcat也成为容器 Tomcat作为容器:里面存放的有Servlet对象,Listener,Filter对象

IoC的技术实现

DI是ioc的技术实现

DI:依赖注入(Dependency Injection):依赖注入,只需要在程序中提供提供要用的对象名称就可以,至于对象如何在容器中创建,赋值,查找都由容器内部实现。

Spring是使用的DI实现了ioc的功能,Spring底层创建对象,使用的是反射机制。

spring读取xml文件

spring很智能,spring会把遇到的所有bean标签,都先创建出来,如果在执行过程中发现有的对象找不到(ref中),它会二次扫描文件,找到创建好的对象,再赋值。所以不需要担心顺序问题

使用的ioc,由spring创建对象

实现步骤:
1.创建maven项目
2.加入maven的依赖
  spring的依赖
  junit依赖
3.创建类(接口和它的实现类)
  和没有使用框架一样,就是普通类
4.创建spring需要使用的配置文件
  声明类的信息,这些类由spring创建和管理
5.测试Spring创建的对象
maven的pom.xml配置文件
<?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.mytest</groupId>
    <artifactId>ch01-hello-spring</artifactId>
    <version>1.0-SNAPSHOT</version>

    <name>ch01-hello-spring</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.2.5.RELEASE</version>
        </dependency>
    </dependencies>

    <build>
        
    </build>
</project>
创建类(接口和它的实现类)
package com.mytest.service;

public interface SomeService {
    void doSome();
}
-------------------------------------
package com.mytest.service.impl;

import com.mytest.service.SomeService;

public class SomeServiceImpl implements SomeService {
    public SomeServiceImpl(){
        System.out.println("SomeServiceImpl的无参构造方法");
    }
    @Override
    public void doSome() {
        System.out.println("执行了SomeServiceImpl的doSome方法");
    }
}

创建spring需要使用的配置文件

一般配置文件放在src/main/resource目录下,一般命名为applicationContext.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">

    <!--告诉spring创建对象
        声明bean,就是告诉spring要创建某个类的对象
        id:对象的自定义名称,唯一值。spring通过这个名称找到对象
        class:类的全限定名称(不能是接口,因为spring是反射机制创建对象,必须使用类)

        spring就完成  SomeService someService = new SomeServiceImpl();
        spring把创建好的对象放入到map中,spring框架中有一个map存放对象
            springMap.put(id的值,对象);
            例如 springMap.put("someService","new SomeServiceImpl()");

        一个bean标签声明一个对象。
    -->
    <bean id="someService" class="com.mytest.service.impl.SomeServiceImpl"/>
    <bean id="mydate" class="java.util.Date"/>
</beans>
<!--
    spring的配置文件
    1.beans :是根标签,spring把java对象称为bean
    2.spring-beans.xsd 是约束文件,和mybatis指定的dtd是一样的
-->
测试Spring创建的对象
public class MyTest {
    @Test
    public void test01() {
        SomeService service = new SomeServiceImpl();
        service.doSome();
    }

    /**
     * spring默认创建对象的时间:在创建spring的容器时,会创建配置文件中的所有的对象。
     * spring创建对象:默认调用的是无参构造方法
     */
    @Test
    public void test02() {
        //使用Spring容器创建的对象
        //1.指定spring配置文件的名称
        String config = "beans.xml";
        //2.创建spring容器的对象,ApplicationContext
        //ApplicationContext表示Spring容器,通过容器获取对象
        //ClassPathXmlApplicationContext:表示从类路径中去加载spring的配置文件
        ApplicationContext ac = new ClassPathXmlApplicationContext(config);

        //从容器中获取某个对象,你要调用对象的方法
        //getBean("配置文件中bean的id值");
        SomeService service = (SomeService) ac.getBean("someService");

        //使用spring创建好的对象
//        service.doSome();
    }

    /**
     * 获取spring容器中java对象的信息
     */
    @Test
    public void test03() {
        String config = "beans.xml";
        ApplicationContext ac = new ClassPathXmlApplicationContext(config);
        //使用spring提供的方法,获取容器中定义的对象的数量
        int nums = ac.getBeanDefinitionCount();
        System.out.println("容器中定义的对象的数量:" + nums);
        //容器中每个定义的对象的名称
        String[] names = ac.getBeanDefinitionNames();
        for (String name : names) {
            System.out.println(name);
        }
    }

    //获取一个非自定义的类对象
    @Test
    public void test04() {
        String config = "beans.xml";
        ApplicationContext ac = new ClassPathXmlApplicationContext(config);
        //使用getBean();获取对象
        Date my = (Date) ac.getBean("mydate");
        System.out.println("Date:"+my);
    }

    @Test
    public void test05(){
        String config = "beans.xml";
        ApplicationContext ac = new ClassPathXmlApplicationContext(config);
        //使用getBean();获取对象
        ac.getBean("someService");
        ac.getBean("someService");
        ac.getBean("someService");
    }
}

依赖注入(di)

表示创建对象,给属性赋值

di的语句分类:

1.set注入(设值注入):spring调用类的set方法,在set方法可以实现属性赋值。
80%左右都是使用set注入
2.构造注入,spring调用类的有参构造方法,创建对象。在构造方法中完成赋值。

在spring的配置文件中,使用标签和属性完成,叫做基于XML的di实现
实现步骤:
1.创建maven项目
2.加入maven的依赖
  spring的依赖
  junit依赖
3.创建类(接口和它的实现类)
  和没有使用框架一样,就是普通类
4.创建spring需要使用的配置文件
  声明类的信息,这些类由spring创建和管理
  通过spring的语法,完成属性的赋值
5.测试Spring创建的对象
创建java类
package com.mytest.ba01;

public class Student {
    private String name;
    private int age;

    public Student() {
        System.out.println("student的无参构造方法");
    }

    /**
     * 需要有set方法,没有就报错
     * 如果set方法没有这个属性,也会调用这个set方法,但是不会赋值也不会报错
     * 例如:setEmail,会调用这个方法,但是Student对象没有Email属性,所以不会赋值
     * 如果set方法中没有 this.age = age;
     * 对象的属性会赋一个空值
     */
    public void setAge(int age) {
        this.age = age;
        System.out.println("setAge:"+age);
    }

    public void setName(String name) {
        this.name = name;
        System.out.println("setName:"+name);
    }

    public void setEmail(String email){
        System.out.println("email:"+email);
    }
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
创建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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--声明一个student对象
        注入:就是赋值的意思
        简单类型:spring中规定java的基本数据类型和string都是简单类型
        di:给属性赋值
        1.set注入(设值注入):spring调用类的set方法,可以在set方法中完成赋值
            1)简单类型的set注入
            <bean id="XX" class="YY">
                <property name="属性名称" value="属性的值">
                    一个property只能给一个属性赋值
                </property>
            </bean>
    -->
    <bean id="mystudent" class="com.mytest.ba01.Student">
        <property name="name" value="李四"></property><!--setName("李四")-->
        <property name="age" value="20"></property><!--setAge("20")-->
        <property name="email" value="lisi@qq.com"></property><!--setEmail("lisi@qq.com")-->
    </bean>

    <bean id="mydate" class="java.util.Date">
        <property name="time" value="10000000"></property><!--setTime("10000000")-->
    </bean>
</beans>
引用数据类型的赋值
<?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">
    <!--声明一个student对象
        注入:就是赋值的意思
        简单类型:spring中规定java的基本数据类型和string都是简单类型
        di:给属性赋值
        1.set注入(设值注入):spring调用类的set方法,可以在set方法中完成赋值
            1)简单类型的set注入
            <bean id="XXX" class="YYY">
                <property name="属性名称" value="属性的值">
                    一个property只能给一个属性赋值
                </property>
            </bean>
            2)引用类型的set注入:spring调用类的set方法
            <bean id="XXX" class="YYY">
                <property name="属性名称" ref="bean的id(对象的名称)"/>
            </bean>
    -->
    <bean id="mystudent" class="com.mytest.ba02.Student">
        <property name="name" value="李四"></property><!--setName("李四")-->
        <property name="age" value="20"></property><!--setAge("20")-->
        <!--引用类型-->
        <property name="school" ref="myschool"></property><!--setSchool("myschool")-->
    </bean>

    <bean id="mystudent1" class="com.mytest.ba02.Student">
        <constructor-arg name="myname" value="王五"/>
        <constructor-arg name="age" value="20"/>
        <constructor-arg name="school" ref="myschool1"/>
    </bean>

    <bean id="mystudent2" class="com.mytest.ba02.Student">
        <constructor-arg index="0" value="张三"/>
        <constructor-arg index="1" value="20"/>
        <constructor-arg index="2" ref="myschool1"/>
    </bean>

    <bean id="mystudent3" class="com.mytest.ba02.Student">
        <constructor-arg value="张三啊"/>
        <constructor-arg value="20"/>
        <constructor-arg ref="myschool1"/>
    </bean>

    <bean id="mystudent4" class="com.mytest.ba02.Student">
        <constructor-arg name="myname" value="李四啊"/>
        <constructor-arg name="age" value="20"/>
        <constructor-arg name="school" ref="myschool1"/>
        <constructor-arg name="age" value="22"/>
    </bean>

    <!--声明school对象-->
    <bean id="myschool" class="com.mytest.ba02.School">
        <property name="name" value="北京大学"></property>
        <property name="address" value="北京市"></property>
    </bean>

    <bean id="myschool1" class="com.mytest.ba02.School">
        <property name="name" value="清华大学"></property>
        <property name="address" value="北京市"></property>
    </bean>
</beans>
构造注入
<?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">
    <!--声明一个student对象
           注入:就是赋值的意思
           简单类型:spring中规定java的基本数据类型和string都是简单类型
           di:给属性赋值
           1.set注入(设值注入):spring调用类的set方法,可以在set方法中完成赋值
               1)简单类型的set注入
               <bean id="XXX" class="YYY">
                   <property name="属性名称" value="属性的值">
                       一个property只能给一个属性赋值
                   </property>
               </bean>
               2)引用类型的set注入:spring调用类的set方法
               <bean id="XXX" class="YYY">
                   <property name="属性名称" ref="bean的id(对象的名称)"/>
               </bean>
          2.构造注入:spring调用类有参数构造方法,在创建对象的同时,在构造方法中给属性赋值
            构造注入使用 <constructor-arg> 标签
            <constructor-arg> 标签:一个<constructor-arg>表示构造方法一个参数
            <constructor-arg> 标签属性:
                name:表示构造方法的形参名
                index:表示构造方法的参数的位置,参数从左往右的位置是0,1,2的顺序
                value:构造方法的形参简单类型的,使用value赋值
                ref:构造方法的形参引用类型的,使用ref赋值
       -->
    <!--使用name属性实现构造注入-->
    <bean id="student" class="com.mytest.ba03.Student">
        <constructor-arg name="myname" value="张三"/>
        <constructor-arg name="age" value="20"/>
        <constructor-arg name="school" ref="myschool"/>
    </bean>

    <!--使用index属性实现构造注入-->
    <!--index顺序可以随意排列,单index的值,必须和构造器中的形参顺序对应-->
    <bean id="student1" class="com.mytest.ba03.Student">
        <constructor-arg index="0" value="张三"/>
        <constructor-arg index="1" value="20"/>
        <constructor-arg index="2" ref="myschool"/>
    </bean>

    <!--省略index,顺序必须和构造器中的形参对应-->
    <bean id="student2" class="com.mytest.ba03.Student">
        <constructor-arg value="张三"/>
        <constructor-arg value="20"/>
        <constructor-arg ref="myschool"/>
    </bean>

    <!--创建File,使用构造器注入-->
    <bean id="myfile" class="java.io.File">
        <constructor-arg name="parent" value="D:\MyTest\ideaWorkSpace\JavaProjects\spring-course\ch02-di-xml"/>
        <constructor-arg name="child" value="readme.txt"/>
    </bean>

    <bean id="myschool" class="com.mytest.ba03.School">
        <property name="name" value="北京大学"/>
        <property name="address" value="北京市"/>
    </bean>
</beans>
自动注入(仅引用类型)

有两种方式,byName,byType两种方式。

autowire=“byName”;autowire=“byType”

<?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">

    <!--
        引用类型的自动注入:spring框架根据某些规则可以给引用类型赋值。
        使用的常用规则byName,byType.
        1.byName(按名称注入):java类中引用类型的属性名和spring容器中(配置文件)<bean>的id名称一样,
                             且数据类型是一致的,这样容器中的bean,spring能够赋值给引用类型。
        语法:
        <bean id="XXX" class="YYY" atutowire="byName">
            简单类型属性赋值
        </bean>

        2.byType(按类型注入):java类中引用类型的数据类型和spring容器中(配置文件)<bean>的class属性
                              是同源关系的,这样的bean能够赋值给引用类型
        同源就是一类的意思:
            1.java类中的引用类型的数据类型和bean的class的值是一样的。
            2.java类中的引用类型的数据类型和bean的class的值是父子类关系的。
            3.java类中的引用类型的数据类型和bean的class的值接口和实现类关系的。
        语法:
        <bean id="XXX" class="YYY" atutowire="byType">
            简单类型属性赋值
        </bean>

        注意:在byType中,在xml配置文件中声明bean只能有一个符合条件的,多余一个是错误的
        当有多个值匹配时,报错:期望的单个匹配bean
    -->
    <bean id="student" class="com.mytest.ba05.Student">
        <property name="name" value="张三"/>
        <property name="age" value="20"/>
        <!--引用类型-->
        <property name="school" ref="school"/>
    </bean>
    <!--byName-->
    <bean id="student1" class="com.mytest.ba05.Student" autowire="byName">
        <property name="name" value="张三"/>
        <property name="age" value="20"/>
        <!--引用类型-->
<!--        <property name="school" ref="school"/>-->
    </bean>

    <!--byType-->
    <bean id="student2" class="com.mytest.ba05.Student" autowire="byType">
        <property name="name" value="张三"/>
        <property name="age" value="20"/>
        <!--引用类型-->
<!--        <property name="school" ref="school"/>-->
    </bean>

    <bean id="school" class="com.mytest.ba05.School">
        <property name="name" value="北京大学"/>
        <property name="address" value="北京市"/>
    </bean>

<!--    <bean id="school1" class="com.mytest.ba05.School">-->
<!--        <property name="name" value="北京大学"/>-->
<!--        <property name="address" value="北京市"/>-->
<!--    </bean>-->
</beans>
spring多个配置文件的优点
  1. 每个文件的大小比一个文件要小很多,效率高
  2. 避免多人竞争带来冲突

如果你的项目有多个模块(相关的功能在一起),一个模块一个配置文件,例如

  • 学生考勤模块一个配置文件, 张三负责
  • 学生成绩一个配置文件, 李四负责

多文件的分配方式:

  • 按功能模块,一个模块一个配置文件
  • 按类的功能,数据库相关的配置一个文件配置文件, 做事务的功能一个配置文件, 做service功能的一个配置文件等

spring-student.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">

    <!--
        student模块所有的bean声明
    -->
    <bean id="student" class="com.mytest.ba06.Student" autowire="byType">
        <property name="name" value="张三"/>
        <property name="age" value="30"/>
    </bean>

</beans>

spring-school.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">

    <!--
        school模块所有的bean声明,school模块的配置文件
    -->
    <bean id="school" class="com.mytest.ba06.School">
        <property name="name" value="清华大学"/>
        <property name="address" value="北京市"/>
    </bean>
</beans>

total.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">

    <!--
        包含关系的配置文件:
        spring-total表示主配置文件:包含其它的配置文件,主配置文件一般是不定义对象的。
        语法:<import resouce="其它配置文件的路劲"/>
        关键字:"classpath:"表示类路劲(class文件所在的目录),在spring的配置文件中要指定其他文件的位置,
        需要使用classpath,告诉spring到哪儿去加载读取文件。
    -->
    <!--加载的是文件列表-->
    <!--<import resource="classpath:ba06/spring-school.xml"/>-->
    <!--<import resource="classpath:ba06/spring-student.xml"/>-->
    <!--
        在包含关系的配置文件中,可以使用通配符(*:表示任意字符)
        注意:主配置文件名称不能包含在通配符的范围内(不能叫做spring-total.xml)会死循环报错
        用通配符,配置文件必须放在一个目录中(规定)
    -->
    <import resource="classpath:ba06/spring-*.xml"/>
</beans>

测试类

public class MyTest06 {

    @Test
    public void test01(){
        // 加载的是总文件
        String config = "ba06/total.xml";
        ApplicationContext ac = new ClassPathXmlApplicationContext(config);
        // 从容器中获取Student对象
        Student student = (Student) ac.getBean("student");
        System.out.println(student);
    }
}
使用spring中的注解,完成属性赋值,叫做基于注解的di实现
学习的注解:

1.@Component
2.@Respotory
3.@Service
4.@Controller
5.@Value
6.@Autowired
7.@Resource

使用注解的步骤:
  1. 加入maven的依赖 spring-context ,在你加入spring-context的同时, 间接加入spring-aop的依赖。
    使用注解必须使用spring-aop依赖
  2. 在类中加入spring的注解(多个不同功能的注解)
  3. 在spring的配置文件中,加入一个组件扫描器的标签,说明注解在你的项目中的位置
pom.xml
<?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.mytest</groupId>
  <artifactId>ch04-di-anno</artifactId>
  <version>1.0-SNAPSHOT</version>

  <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.2.5.RELEASE</version>
    </dependency>
  </dependencies>

  <build>

  </build>
</project>
applicationContext.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"
       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">

    <!--声明组件扫描器(component-scan),组件就是java对象
        base-package:指定注解在你项目中的包名。
        component-scan工作方式:spring会扫描遍历base-package指定的包,把包中和子包中的所有类,
                              找到类中的注解,按照注解的功能创建对象,或给属性赋值

        加入了component-scan标签,配置文件的变化:
            1.加入了一个新的约束文件spring-context.xsd
            2.给这个新的约束文件起个命名空间的名称 xmlns:context="http://www.springframework.org/schema/context"
            context代表着http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context代表着https://www.springframework.org/schema/context/spring-context.xsd


    -->
    <context:component-scan base-package="com.mytest.ba01"/>


    <!--指定多个包的三种方式-->
    <!--第一种方式:使用多次组件扫描器,指定不同的包-->
    <context:component-scan base-package="com.mytest.ba01"/>
    <context:component-scan base-package="com.mytest.ba02"/>

    <!--第二种方式:使用分隔符(;或,)分隔多个包名-->
    <context:component-scan base-package="com.mytest.ba01;com.mytest.ba02"/>

    <!--第三种方式:指定父包-->
    <context:component-scan base-package="com.mytest"/>

</beans>
通过注解创建对象
package com.mytest.ba01;

import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;

/**
 * @Component: 创建对象的,等同于 <bean> 的功能
 *   属性:value就是对象的名称,也就是bean的id值,value的值是唯一的,创建的对象在整个spring容器中就一个
 *   位置:在类的上面
 *
 *   @Component(value = "mystudent")等同于
 *   <bean id="mystudent" class="com.mytest.ba01.Student"/>
 *
 *   spring中和@Component 功能一致,创建对象的注解还有:
 *   1.@Repository(用在持久层的上面):放在dao的实现类上面,表示创建dao对象,访问数据库
 *   2.@Service(用在业务层类的上面):放在service的实现类上面,创建service对象,做业务处理,可以有事务等功能
 *   3.@Controller(用在控制器的上面):放在控制器(处理器)类的上面,创建控制器对象的,接受用户提交的参数,
 *                                  显示请求的处理结果
 *   以上三个注解的使用语法和Component一样的。都能够创建对象,但是这三个注解还有额外的功能。
 *   这三个注解是给项目的对象分层的。
 */
// 使用value属性,指定对象名称
// @Component(value = "mystudent")

// 省略value,最常用的方法
@Component("mystudent")

// 不指定对象的名称,由spring提供默认名称:类的首字母小写
//@Component
public class Student {

    private String name;
    private Integer age;

    public void setName(String name) {
        this.name = name;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
通过注解给对象赋值
package com.mytest.ba02;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component("mystudent")
public class Student {
    /**
     * @Value:简单类型的属性赋值
     *  属性:value是String类型的,表示简单类型的属性值
     *  位置:1.在属性定义的上面,无需set方法,推荐使用。
     *       2.在set方法的上面
     */
//    @Value(value = "张三")  value可以省略
    @Value("张三")
    private String name;
//    @Value(value = "29")  value可以省略
//    @Value("29")
    private Integer age;

//    public void setName(String name) {
//        this.name = name;
//    }
//
    @Value("20")
    public void setAge(Integer age) {
        this.age = age;
        System.out.println("age = "+age);
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
引用类型的赋值
package com.mytest.ba03;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

//@Component("mySchool")
public class School {

    @Value("北京大学")
    private String name;
    @Value("北京市")
    private String address;

    public void setName(String name) {
        this.name = name;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    @Override
    public String toString() {
        return "School{" +
                "name='" + name + '\'' +
                ", address='" + address + '\'' +
                '}';
    }
}
----------------------------------------------
package com.mytest.ba03;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component("mystudent")
public class Student {
    /**
     * @Value:简单类型的属性赋值
     *  属性:value是String类型的,表示简单类型的属性值
     *  位置:1.在属性定义的上面,无需set方法,推荐使用。
     *       2.在set方法的上面
     */

    @Value("张三")
    private String name;
    @Value("29")
    private Integer age;

    /**
     * 引用类型
     * @Autowired: spring框架提供的注解,实现引用类型的赋值。
     * spring中通过注解给引用类型赋值,使用的是自动注入原理,支持byName,byType
     * @Autowired: 默认使用的是byType自动注入
     *
     */
    @Autowired
    private School school;

/*
    public void setName(String name) {
        this.name = name;
    }
*/

/*

    @Value("20")
    public void setAge(Integer age) {
        this.age = age;
        System.out.println("age = "+age);
    }
*/

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", school=" + school +
                '}';
    }
}

byName注入方式

package com.mytest.ba04;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component("mystudent")
public class Student {

    @Value("张三")
    private String name;
    @Value("29")
    private Integer age;

    /**
     * 引用类型
     * @Autowired: spring框架提供的注解,实现引用类型的赋值。
     * spring中通过注解给引用类型赋值,使用的是自动注入原理,支持byName,byType
     * @Autowired: 默认使用的是byType自动注入
     *
     * 如果要使用byName方式,需要做的是:
     *  1.在属性上面加入@Autowired
     *  2.在属性上面加入@Qualifier(value="bean的id"):表示使用指定名称的bean完成赋值
     */
    @Autowired
    @Qualifier(value = "myXueXiao")
    private School school;

/*
    public void setName(String name) {
        this.name = name;
    }
*/

/*

    @Value("20")
    public void setAge(Integer age) {
        this.age = age;
        System.out.println("age = "+age);
    }
*/

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", school=" + school +
                '}';
    }
}

required属性的作用

package com.mytest.ba05;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component("mystudent")
public class Student {

    @Value("张三")
    private String name;
    @Value("29")
    private Integer age;

    /**
     * 引用类型
     * @Autowired: spring框架提供的注解,实现引用类型的赋值。
     * spring中通过注解给引用类型赋值,使用的是自动注入原理,支持byName,byType
     * @Autowired: 默认使用的是byType自动注入
     *
     *  属性:required,是一个boolean类型的,默认true
     *  required = true:表示引用类型赋值失败,程序报错,并终止执行。
     *  required = false:引用类型如果赋值失败,程序正常执行,引用类型是null
     *
     * 如果要使用byName方式,需要做的是:
     *  1.在属性上面加入@Autowired
     *  2.在属性上面加入@Qualifier(value="bean的id"):表示使用指定名称的bean完成赋值
     */
    @Autowired(required = false)
    @Qualifier(value = "mySchool-1")
    private School school;

/*
    public void setName(String name) {
        this.name = name;
    }
*/

/*

    @Value("20")
    public void setAge(Integer age) {
        this.age = age;
        System.out.println("age = "+age);
    }
*/

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", school=" + school +
                '}';
    }
}

Resource属性的作用

package com.mytest.ba06;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;

@Component("mystudent")
public class Student {

    @Value("张三")
    private String name;
    @Value("29")
    private Integer age;

    /**
     * 引用类型
     * @Resource:来自jdk中的注解,spring框架提供了对这个注解的功能支持,可以使用它给引用类型赋值
     *            使用的也是自动注入的原理,支持byName,byType。默认是byName
     * 位置:1.在属性定义的上面,无需set方法,推荐使用。
     *      2.在set方法的上面
     */
    // 默认是byName:先使用byName自动注入,如果byName赋值失败,再使用byType
    @Resource
    private School school;

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", school=" + school +
                '}';
    }
}
package com.mytest.ba07;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;

@Component("mystudent")
public class Student {

    @Value("张三")
    private String name;
    @Value("29")
    private Integer age;

    /**
     * 引用类型
     * @Resource:来自jdk中的注解,spring框架提供了对这个注解的功能支持,可以使用它给引用类型赋值
     *            使用的也是自动注入的原理,支持byName,byType。默认是byName
     * 位置:1.在属性定义的上面,无需set方法,推荐使用。
     *      2.在set方法的上面
     *
     * @Resource只使用byName方式,需要增加一个属性name
     * name的值是bean的id(名称)
     */
    // 默认是byName:先使用byName自动注入,如果byName赋值失败,再使用byType
    //只使用byName,如果没有匹配的属性,会报错
    @Resource(name = "mySchool")
    private School school;

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", school=" + school +
                '}';
    }
}

通过外部文件,来实现赋值

test.properties文件

myname=zhangsan
myage=20

spring文件中添加的配置信息

<!--加载属性配置文件-->
<context:property-placeholder location="classpath:test.properties" />
package com.mytest.ba08;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component("mystudent")
public class Student {
    /**
     * @Value:简单类型的属性赋值
     *  属性:value是String类型的,表示简单类型的属性值
     *  位置:1.在属性定义的上面,无需set方法,推荐使用。
     *       2.在set方法的上面
     */
//    @Value(value = "张三")  value可以省略
//    @Value("张三")
    @Value("${myname}")
    private String name;
//    @Value(value = "29")  value可以省略
//    @Value("29")
    @Value("${myage}")
    private Integer age;

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

动态代理

动态代理:可以在程序的执行过程中,创建代理对象。
通过代理对象执行方法,给目标类的方法增加额外的功能(功能增强)

jdk动态代理实现步骤:
1.创建目标类,SomeServiceImpl目标类,给它的doSome,doOther增加输出时间,事务。
2.创建InvocationHandler接口的实现类,在这个类实现给目标方法增加功能。
3.在jdk中 类Proxy,创建代理对象。实现创建对象的能力。

package com.mytest.service;

public interface SomeService {
    void doSome();
    void doOther();
}
package com.mytest.service.impl;

import com.mytest.service.SomeService;

// service类的代码不修改,也能够增加 输出时间,提交事务
public class SomeServiceImpl implements SomeService {
    @Override
    public void doSome() {
        System.out.println("执行业务方法doSome");
    }

    @Override
    public void doOther() {
        System.out.println("执行业务方法doOther");
    }
}
public class ServiceTools {

    public static void doLog(){
        System.out.println("非业务方法,方法的执行时间:"+new Date());
    }

    public static void doTreans(){
        // 方法的最后,提交事务
        System.out.println("非业务方法,方法执行完毕后,提交事务");
    }
}
package com.mytest.handler;

import com.mytest.util.ServiceTools;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class MyInvocationHandler implements InvocationHandler {

    private Object target;

    public MyInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("执行了MyInvocationHandler中的invoke()");
        // 通过代理对象执行方法时,会调用执行这个invoke()
        Object res = null;
        ServiceTools.doLog();
        // 执行目标类的方法,通过Method类实现
        res = method.invoke(target,args);
        ServiceTools.doTreans();
        return res;
    }
}
package com.mytest.service;

import com.mytest.handler.MyInvocationHandler;
import com.mytest.service.impl.SomeServiceImpl;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

public class MyApp {
    public static void main(String[] args) {
//        调用doSome和doOther
//        SomeService service = new SomeServiceImplMyInvocationHandlerMyInvocationHandlerMyInvocationHandlerMyInvocationHandlerMyInvocationHandler();
//        service.doSome();
//        System.out.println("===========================");
//        service.doOther();

        // 使用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.doOther();
    }
}

AOP

Aop:面向切面编程, 基于动态代理的,可以使用jdk,cglib两种代理方式。
Aop就是动态代理的规范化, 把动态代理的实现步骤,方式都定义好了,让开发人员用一种统一的方式,使用动态代理。

AOP(Aspect Orient Programming)面向切面编程

Aspect: 切面,给你的目标类增加的功能,就是切面。 像上面用的日志,事务都是切面。
切面的特点: 一般都是非业务方法,独立使用的。
Orient:面向, 对着。
Programming:编程

怎么理解面向切面编程 ?

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

术语:

1)Aspect:切面,表示增强的功能, 就是一堆代码,完成某个一个功能。非业务功能,
常见的切面功能有日志, 事务, 统计信息, 参数检查, 权限验证。
2)JoinPoint:连接点 ,连接业务方法和切面的位置。 就某类中的业务方法
3)Pointcut : 切入点 ,指多个连接点方法的集合。多个方法
4)目标对象: 给哪个类的方法增加功能, 这个类就是目标对象
5)Advice:通知,通知表示切面功能执行的时间。

说一个切面有三个关键的要素:

1)切面的功能代码,切面干什么
2)切面的执行位置,使用Pointcut表示切面执行的位置
3)切面的执行时间,使用Advice表示时间,在目标方法之前,还是目标方法之后。

以切面为核心,分析出在整个项目中什么样的功能可以用切面的形式去实现它(找出切面)

需要知道切面在什么地方执行

aop的实现

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

aop的技术实现框架:

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

​ spring主要在事务处理时使用aop。

​ 我们项目开发中很少使用spring的aop实现。因为spring的aop比较笨重。

​ 2.aspectJ:一个开源的专门做aop的框架。spring框架中集成了aspectJ框架,通过spring就能使用aspectJ的功 能。

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

​ 1.使用xml配置文件:配置全局事务

​ 2.使用注解,我们在项目中要做aop功能,一般都使用注解,aspectJ有5个注解。

学习aspectJ框架的使用

1)切面的执行时间, 这个执行时间在规范中叫做Advice(通知,增强)
在aspectj框架中使用注解表示的。也可以使用xml配置文件中的标签
1)@Before 前置通知
2)@AfterReturning 后置通知
3)@Around 环绕通知
4)@AfterThrowing 异常通知
5)@After 最终通知

2)表示切面执行的位置,使用的是切入点表达式。

​ AspectJ 定义了专门的表达式用于指定切入点。表达式的原型是:

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 抛出异常类型

​ ?表示可选的部分

以上表达式共 4 个部分。

execution(访问权限 方法返回值 方法声明(参数) 异常类型)

切入点表达式要匹配的对象就是目标方法的方法名。所以,execution 表达式中明显就

是方法的签名。注意,表达式中黑色文字表示可省略部分,各部分间用空格分开。在其中可

以使用以下符号:

在这里插入图片描述

举例:

*execution(public * (…))

指定切入点为:任意公共方法。

execution( set(…))**

指定切入点为:任何一个以“set”开始的方法。

execution( com.xyz.service..(…))*

指定切入点为:定义在 service 包里的任意类的任意方法。

execution( com.xyz.service….(…))*

指定切入点为:定义在 service 包或者子包里的任意类的任意方法。“…”出现在类名中时,后

面必须跟“*”,表示包、子包下的所有类。

execution( …service..*(…))*

指定所有包下的 serivce 子包下所有类(接口)中所有方法为切入点

execution( .service..*(…))*

指定只有一级包下的 serivce 子包下所有类(接口)中所有方法为切入点

execution( .ISomeService.(…))*

指定只有一级包下的 ISomeSerivce 接口中所有方法为切入点

execution( …ISomeService.(…))*

指定所有包下的 ISomeSerivce 接口中所有方法为切入点

execution( com.xyz.service.IAccountService.*(…))*

指定切入点为:IAccountService 接口中的任意方法。

execution( com.xyz.service.IAccountService+.*(…))*

指定切入点为:IAccountService 若为接口,则为接口中的任意方法及其所有实现类中的任意

方法;若为类,则为该类及其子类中的任意方法。

execution( joke(String,int)))*

指定切入点为:所有的 joke(String,int)方法,且 joke()方法的第一个参数是 String,第二个参

数是 int。如果方法中的参数类型是 java.lang 包下的类,可以直接使用类名,否则必须使用

全限定类名,如 joke( java.util.List, int)。

execution( joke(String,*)))*

指定切入点为:所有的 joke()方法,该方法第一个参数为 String,第二个参数可以是任意类

型,如joke(String s1,String s2)和joke(String s1,double d2)都是,但joke(String s1,double d2,String

s3)不是。

execution( joke(String,…)))*

指定切入点为:所有的 joke()方法,该方法第一个参数为 String,后面可以有任意个参数且

参数类型不限,如 joke(String s1)、joke(String s1,String s2)和 joke(String s1,double d2,String s3)

都是。

execution( joke(Object))*

指定切入点为:所有的 joke()方法,方法拥有一个参数,且参数是 Object 类型。joke(Object ob)

是,但,joke(String s)与 joke(User u)均不是。

execution( joke(Object+)))*

指定切入点为:所有的 joke()方法,方法拥有一个参数,且参数是 Object 类型或该类的子类。

不仅 joke(Object ob)是,joke(String s)和 joke(User u)也是。

aspectJ框架的使用
使用aop:目的是给已经存在的一些类和方法,增加额外的功能。前提是不改变原来的类的源码。

使用aspectj实现aop的基本步骤:
1.新建maven项目
2.加入依赖
    1)spring依赖
    2)aspectj依赖
    3)junit单元测试
3.创建目标类:接口和他的实现类。
    要做的是给类中的方法增加功能

4.创建切面类:普通类
    1)在类的上面加入 @Aspect
    2)在类中定义方法, 方法就是切面要执行的功能代码
       在方法的上面加入aspectj中的通知注解,例如@Before
       有需要指定切入点表达式execution()

5.创建spring的配置文件:声明对象,把对象交给容器统一管理
  声明对象你可以使用注解或者xml配置文件<bean>
    1)声明目标对象
    2)声明切面类对象
    3)声明aspectj框架中的自动代理生成器标签。
       自动代理生成器:用来完成代理对象的自动创建功能的。

6.创建测试类,从spring容器中获取目标对象(实际就是代理对象)。
  通过代理执行方法,实现aop的功能增强。
pom.xml配置文件
<?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.test</groupId>
    <artifactId>ch06-aop-aspectj</artifactId>
    <version>1.0-SNAPSHOT</version>

    <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.2.5.RELEASE</version>
        </dependency>
        <!--aspectj依赖-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>5.2.5.RELEASE</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>
创建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">

    <!--声明目标类对象-->
    <bean id="someService" class="com.test.ba08.SomeServiceImpl" />

    <!--声明切面类对象-->
    <bean id="myAspect" class="com.test.ba08.MyAspect" />

    <!--声明自动代理生成器:使用aspectj框架内部的功能,创建目标对象的代理对象。
        创建代理对象是在内存中实现的,修改目标对象的内存中的结构。创建为代理对象
        所以目标对象就是被修改后的代理对象

        aspectj-autoproxy:会把spring容器中所有的目标对象,一次性都生成代理对象。
    -->
    <aop:aspectj-autoproxy />

    <!--
       如果你期望目标类有接口,使用cglib代理
       proxy-target-class="true":告诉框架,要使用cglib动态代理
    -->
    <!--<aop:aspectj-autoproxy proxy-target-class="true" />-->
</beans>
前置通知 @Before
目标类接口
package com.test.ba01;

public interface SomeService {
    void doSome(String name,Integer age);
}
目标类实现类
package com.test.ba01;

public class SomeServiceImpl implements SomeService {
    @Override
    public void doSome(String name, Integer age) {
        // 给doSome方法增加一个功能,在doSome()方法执行之前,输出方法的执行时间
        System.out.println("===目标方法doSome()===");
    }
}
切面类
package com.test.ba01;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

import java.util.Date;

/**
 * @Aspect:是aspect框架中的注解。 作用:表示当前类是切面类。
 * 切面类:是用来给业务方法增加功能的类,在这个类中有切面的功能代码
 * 位置:在类定义的上面
 */
@Aspect
public class MyAspect {
    /**
     * 定义方法,方法是实现切面功能的。
     * 方法定义要求:
     * 1.公共方法public
     * 2.方法没有返回值
     * 3.方法名称自定义
     * 4.方法可以有参数,也可以没有参数。
     *   如果有参数,参数不是自定义的,有几个参数类型可以使用。
     */

    /**
     * @Before(value = )前置通知注解
     *  属性:value,是切入点表达式,表示切面功能执行的位置。
     *  位置:在方法的上面
     *  特点:
     *      1.在目标方法之前执行的
     *      2.不会改变目标方法的执行结果
     *      3.不会影响目标方法的执行
     */
    /*@Before(value = "execution(public void com.test.ba01.SomeServiceImpl.doSome(String,Integer))")
    public void myBefore(){
        //切面执行的功能代码
        System.out.println("前置通知,切面功能:在目标方法执行之前输出执行时间:"+new Date());
    }*/

    /**
     * 指定通知方法中的参数:JoinPoint(注意P是大写的)
     * JoinPoint:业务方法,要加入切面功能的业务方法
     * 作用是:可以在通知方法中获取方法执行的信息,例如方法的名称,方法的实参。
     * 如果你的切面功能中需要用到方法的信息,就加入JoinPoint。
     * 这个JoinPoint参数的值是由框架赋予,必须是第一个位置的参数
     */
    @Before(value = "execution(void *..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("2前置通知,切面功能:在目标方法执行之前输出执行时间:" + new Date());
    }

    /*@Before(value = "execution(void com.test.ba01.SomeServiceImpl.doSome(String,Integer))")
    public void myBefore(){
        //切面执行的功能代码
        System.out.println("1前置通知,切面功能:在目标方法执行之前输出执行时间:"+new Date());
    }*/

    /*@Before(value = "execution(void *..SomeServiceImpl.doSome(String,Integer))")
    public void myBefore(){
        //切面执行的功能代码
        System.out.println("2前置通知,切面功能:在目标方法执行之前输出执行时间:"+new Date());
    }*/

    /*@Before(value = "execution(* *..SomeServiceImpl.do*(..))")
    public void myBefore(){
        //切面执行的功能代码
        System.out.println("3前置通知,切面功能:在目标方法执行之前输出执行时间:"+new Date());
    }

    @Before(value = "execution(* do*(..))")
    public void myBefore2(){
        //切面执行的功能代码
        System.out.println("4前置通知,切面功能:在目标方法执行之前输出执行时间:"+new Date());
    }*/

    /*//异常情况
    @Before(value = "execution(void *..SomeServiceImpl.doSome(Integer))")
    public void myBefore(){
        //切面执行的功能代码
        System.out.println("2前置通知,切面功能:在目标方法执行之前输出执行时间:"+new Date());
    }*/
}
测试类
package com.test;

import com.test.ba01.SomeService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest01 {

    @Test
    public void test01(){
        String config = "applicationContext.xml";
        ApplicationContext ctx = new ClassPathXmlApplicationContext(config);
        SomeService proxy = (SomeService) ctx.getBean("someService");
        System.out.println("proxy:"+proxy.getClass().getName());
        proxy.doSome("lisi",20);
    }
}
后置通知@AfterReturning
目标类实现类
package com.test.ba02;

public class SomeServiceImpl implements SomeService {
    @Override
    public void doSome(String name, Integer age) {
        // 给doSome方法增加一个功能,在doSome()方法执行之前,输出方法的执行时间
        System.out.println("===目标方法doSome()===");
    }

    @Override
    public String doOther(String name, Integer age) {
        System.out.println("===目标方法doOther()===");
        return "abcd";
    }

    @Override
    public Student doStu(String name, Integer age) {
        Student stu = new Student();
        System.out.println("===doStu()===");
        stu.setName(name);
        stu.setAge(age);
        return stu;
    }
}
package com.test.ba02;

public class Student {
    private String name;
    private int age;

    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
切面类
package com.test.ba02;

import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;

/**
 * @Aspect:是aspect框架中的注解。 作用:表示当前类是切面类。
 * 切面类:是用来给业务方法增加功能的类,在这个类中有切面的功能代码
 * 位置:在类定义的上面
 */
@Aspect
public class MyAspect {
    /**
     * 后置通知定义方法,方法是实现切面功能的。
     * 方法定义要求:
     * 1.公共方法public
     * 2.方法没有返回值
     * 3.方法名称自定义
     * 4.方法有参数的,推荐使用Object,参数名自定义
     */

    /**
     *  @AfterReturning:后置通知
     *      属性:1.value 切入点表达式
     *           2.returning 自定义的变量,表示目标方法的返回值的。
     *              自定义的变量名必须和通知方法的形参名一样。
     *      位置:在方法定义的上面
     *      特点:
     *          1.在目标方法之后执行
     *          2.能够获取到目标方法的返回值,可以根据这个返回值做不同的处理功能
     *              相当于Object res = doOther();
     *          3.可以修改这个返回值
     *
     *      后置通知的执行顺序
     *          Object res = doOther();
     *          myAfterReturing(res);
     */
    @AfterReturning(value = "execution(* *..SomeServiceImpl.doOther(..))",returning = "res")
    public void myAfterReturing(Object res){
        //Object res:是目标方法执行后的返回值,根据返回值做你的切面的功能处理
        System.out.println("后置通知:在目标方法之后执行的,获取的返回值是:"+res);

        //修改目标方法的返回值,看一下是否会影响 最后的方法调用结果
        if(res != null){
            res = "Hello";
        }
        System.out.println("res = "+res);
    }

    @AfterReturning(value = "execution(* *..SomeServiceImpl.doStu(..))",returning = "res")
    public void myAfterReturing2(Object res){
        //Object res:是目标方法执行后的返回值,根据返回值做你的切面的功能处理
        System.out.println("后置通知:在目标方法之后执行的,获取的返回值是:"+res);
        //修改目标方法的返回值,看一下是否会影响 最后方法调用结果
        //如果修改了res的内容,属性值等,是不是会影响最后的调用结果

//        res = new Student("李四",30);//这样不会影响
        ((Student) res).setAge(30);
        ((Student) res).setName("王五");
    }
}

注意影响调用结果的情况

环绕通知@Around
切面类
package com.test.ba03;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;

import java.util.Date;

/**
 *  @Aspect : 是aspectj框架中的注解。
 *     作用:表示当前类是切面类。
 *     切面类:是用来给业务方法增加功能的类,在这个类中有切面的功能代码
 *     位置:在类定义的上面
 */
@Aspect
public class MyAspect {

    /**
     * 环绕通知方法的定义格式
     *  1.public
     *  2.必须有一个返回值,推荐使用Object
     *  3.方法名称自定义
     *  4.方法有参数,固定的参数 ProceedingJoinPoint
     */

    /**
     * @Around: 环绕通知
     *    属性:value 切入点表达式
     *    位置:在方法的定义什么
     * 特点:
     *   1.它是功能最强的通知
     *   2.在目标方法的前和后都能增强功能。
     *   3.控制目标方法是否被调用执行
     *   4.修改原来的目标方法的执行结果。 影响最后的调用结果
     *
     *  环绕通知,等同于jdk动态代理的,InvocationHandler接口
     *
     *  参数:  ProceedingJoinPoint 就等同于 Method
     *         作用:执行目标方法的
     *  返回值: 就是目标方法的执行结果,可以被修改。
     *
     *  环绕通知: 经常做事务, 在目标方法之前开启事务,执行目标方法, 在目标方法之后提交事务
     */
    @Around(value = "execution(* *..SomeServiceImpl.doFirst(..))")
    public Object myAround(ProceedingJoinPoint pjp) throws Throwable{
        //获取
        //实现环绕通知
        Object result = null;
        //1.目标方法调用
        System.out.println("环绕通知:在目标方法之前,输出时间:"+ new Date());
        result = pjp.proceed();
        System.out.println("环绕通知:在目标方法之后,提交事务");
        //2.在目标方法的前或者后加入功能

        //修改目标方法的执行结果, 影响方法最后的调用结果
        if( result != null){
            result = "Hello AspectJ AOP";
        }

        //返回目标方法的执行结果
        return  result;
    }
}
异常通知@AfterThrowing
切面类
package com.test.ba04;

import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;

/**
 *  @Aspect : 是aspectj框架中的注解。
 *     作用:表示当前类是切面类。
 *     切面类:是用来给业务方法增加功能的类,在这个类中有切面的功能代码
 *     位置:在类定义的上面
 */
@Aspect
public class MyAspect {
    /**
     * 异常通知方法的定义格式
     *  1.public
     *  2.没有返回值
     *  3.方法名称自定义
     *  4.方法有个一个Exception, 如果还有是JoinPoint,
     */

    /**
     * @AfterThrowing:异常通知
     *     属性:1. value 切入点表达式
     *          2. throwinng 自定义的变量,表示目标方法抛出的异常对象。
     *             变量名必须和方法的参数名一样
     * 特点:
     *   1. 在目标方法抛出异常时执行的
     *   2. 可以做异常的监控程序, 监控目标方法执行时是不是有异常。
     *      如果有异常,可以发送邮件,短信进行通知
     *
     *  执行就是:
     *   try{
     *       SomeServiceImpl.doSecond(..)
     *   }catch(Exception e){
     *       myAfterThrowing(e);
     *   }
     */
    @AfterThrowing(value = "execution(* *..SomeServiceImpl.doSecond(..))",
            throwing = "ex")
    public void myAfterThrowing(Exception ex) {
        System.out.println("异常通知:方法发生异常时,执行:"+ex.getMessage());
        //发送邮件,短信,通知开发人员
    }

}
最终通知@After
切面类
package com.test.ba05;

import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;

/**
 *  @Aspect : 是aspectj框架中的注解。
 *     作用:表示当前类是切面类。
 *     切面类:是用来给业务方法增加功能的类,在这个类中有切面的功能代码
 *     位置:在类定义的上面
 */
@Aspect
public class MyAspect {
    /**
     * 最终通知方法的定义格式
     *  1.public
     *  2.没有返回值
     *  3.方法名称自定义
     *  4.方法没有参数,  如果还有是JoinPoint,
     */

    /**
     * @After :最终通知
     *    属性: value 切入点表达式
     *    位置: 在方法的上面
     * 特点:
     *  1.总是会执行
     *  2.在目标方法之后执行的
     *
     *  try{
     *      SomeServiceImpl.doThird(..)
     *  }catch(Exception e){
     *
     *  }finally{
     *      myAfter()
     *  }
     *
     */
    @After(value = "execution(* *..SomeServiceImpl.doThird(..))")
    public  void  myAfter(){
        System.out.println("执行最终通知,总是会被执行的代码");
        //一般做资源清除工作的。
     }

}
Pointcut定义和管理切入点
切面类
package com.test.ba06;

import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;

/**
 *  @Aspect : 是aspectj框架中的注解。
 *     作用:表示当前类是切面类。
 *     切面类:是用来给业务方法增加功能的类,在这个类中有切面的功能代码
 *     位置:在类定义的上面
 */
@Aspect
public class MyAspect {


    @After(value = "mypt()")
    public  void  myAfter(){
        System.out.println("执行最终通知,总是会被执行的代码");
        //一般做资源清除工作的。
     }

    @Before(value = "mypt()")
    public  void  myBefore(){
        System.out.println("前置通知,在目标方法之前先执行的");
    }

    /**
     * @Pointcut: 定义和管理切入点, 如果你的项目中有多个切入点表达式是重复的,可以复用的。
     *            可以使用@Pointcut
     *    属性:value 切入点表达式
     *    位置:在自定义的方法上面
     * 特点:
     *   当使用@Pointcut定义在一个方法的上面 ,此时这个方法的名称就是切入点表达式的别名。
     *   其它的通知中,value属性就可以使用这个方法名称,代替切入点表达式了
     */
    @Pointcut(value = "execution(* *..SomeServiceImpl.doThird(..))" )
    private void mypt(){
        //无需代码,
    }

}
使用CGLIB代理对象
目标类没有实现接口
package com.test.ba07;

//目标类
public class SomeServiceImpl {

    public void doThird() {
        System.out.println("执行业务方法doThird()");
    }
}
切面类
package com.test.ba07;

import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;

/**
 *  @Aspect : 是aspectj框架中的注解。
 *     作用:表示当前类是切面类。
 *     切面类:是用来给业务方法增加功能的类,在这个类中有切面的功能代码
 *     位置:在类定义的上面
 */
@Aspect
public class MyAspect {


    @After(value = "mypt()")
    public  void  myAfter(){
        System.out.println("执行最终通知,总是会被执行的代码");
        //一般做资源清除工作的。
     }

    @Before(value = "mypt()")
    public  void  myBefore(){
        System.out.println("前置通知,在目标方法之前先执行的");
    }

    /**
     * @Pointcut: 定义和管理切入点, 如果你的项目中有多个切入点表达式是重复的,可以复用的。
     *            可以使用@Pointcut
     *    属性:value 切入点表达式
     *    位置:在自定义的方法上面
     * 特点:
     *   当使用@Pointcut定义在一个方法的上面 ,此时这个方法的名称就是切入点表达式的别名。
     *   其它的通知中,value属性就可以使用这个方法名称,代替切入点表达式了
     */
    @Pointcut(value = "execution(* *..SomeServiceImpl.doThird(..))" )
    private void mypt(){
        //无需代码,
    }
}
目标类实现接口,需要在配置文件中做配置

目标类实现了接口,也使用CGLIB代理生成对象

<?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">

    <!--声明目标类对象-->
    <bean id="someService" class="com.test.ba07.SomeServiceImpl" />

    <!--声明切面类对象-->
    <bean id="myAspect" class="com.test.ba07.MyAspect" />

    <!--声明自动代理生成器:使用aspectj框架内部的功能,创建目标对象的代理对象。
        创建代理对象是在内存中实现的,修改目标对象的内存中的结构。创建为代理对象
        所以目标对象就是被修改后的代理对象

        aspectj-autoproxy:会把spring容器中所有的目标对象,一次性都生成代理对象。
    -->
    <!--<aop:aspectj-autoproxy />-->

    <!--
       如果你期望目标类有接口,使用cglib代理
       proxy-target-class="true":告诉框架,要使用cglib动态代理
    -->
    <aop:aspectj-autoproxy proxy-target-class="true" />
</beans>

spring集成mybatis

将 MyBatis 与 Spring 进行整合,主要解决的问题就是将 SqlSessionFactory 对象交由 Spring来管理。所以,该整合,只需要将 SqlSessionFactory 的对象生成器 SqlSessionFactoryBean 注册在 Spring 容器中,再将其注入给 Dao 的实现类即可完成整合。

实现 Spring 与 MyBatis 的整合常用的方式:扫描的 Mapper 动态代理

Spring 像插线板一样,mybatis 框架是插头,可以容易的组合到一起。插线板 spring 插 上 mybatis,两个框架就是一个整体。

spring集成mybatis的实现
maven的pom.xml文件
<?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.test</groupId>
    <artifactId>ch07-spring-mybatis</artifactId>
    <version>1.0-SNAPSHOT</version>

    <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>
        <!--sprin核心ioc-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.5.RELEASE</version>
        </dependency>
        <!--做spring事务用到的-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>5.2.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.2.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>
        <!--目的是把src/main/java目录中的xml文件包含到输出结果中.输出到classes目录中-->
        <resources>
            <resource>
                <directory>src/main/java</directory><!--所在的目录-->
                <includes><!--包括目录下的.properties,.xml 文件都会扫描到-->
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
        </resources>
        <!--指定jdk的版本-->
        <plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>
实体类
package com.test.domain;

public class Student {
    private Integer id;
    private String name;
    private String email;
    private Integer age;

    public Student() {
    }

    public Student(Integer id, String name, String email, Integer age) {
        this.id = id;
        this.name = name;
        this.email = email;
        this.age = age;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", email='" + email + '\'' +
                ", age=" + age +
                '}';
    }
}
dao接口
package com.test.dao;

import com.test.domain.Student;

import java.util.List;

public interface StudentDao {
    int insertStudent(Student student);
    List<Student> selectStudents();
}
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="com.test.dao.StudentDao">
    <insert id="insertStudent">
        insert into student values(#{id},#{name},#{email},#{age})
    </insert>

    <select id="selectStudents" resultType="com.test.domain.Student">
        select id,name,email,age from student order by id desc
    </select>
</mapper>
service接口
package com.test.service;

import com.test.domain.Student;

import java.util.List;

public interface StudentService {

    int addStudent(Student student);
    List<Student> queryStudents();
}
service实现类
package com.test.service.impl;

import com.test.dao.StudentDao;
import com.test.domain.Student;
import com.test.service.StudentService;

import java.util.List;

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> queryStudents() {
        List<Student> students = studentDao.selectStudents();
        return students;
    }
}
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>

    <!--settings:控制mybatis全局行为-->
    <settings>
        <!--设置mybatis输出日志-->
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>

    <!--设置别名-->
    <typeAliases>
        <!--name:实体类所在的包名
            表示com.bjpowernode.domain包中的列名就是别名
            你可以使用Student表示com.bjpowenrode.domain.Student
        -->
        <package name="com.test.domain"/>
    </typeAliases>


    <!-- sql mapper(sql映射文件)的位置-->
    <mappers>
        <!--
          name:是包名, 这个包中的所有mapper.xml一次都能加载
        -->
        <package name="com.test.dao"/>
    </mappers>
</configuration>
数据源的配置
Druid 数据源

Druid 是阿里的开源数据库连接池。是 Java 语言中最好的数据库连接池。Druid 能够提供强大的监控和扩展功能。

jdbc.properties
jdbc.url=jdbc:mysql://localhost:3306/springdb
jdbc.username=root
jdbc.passworld=root
jdbc.max=20
applicationContext.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"
       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">

    <!--
       把数据库的配置信息,写在一个独立的文件,编译修改数据库的配置内容
       spring知道jdbc.properties文件的位置
    -->
    <context:property-placeholder location="classpath:jdbc.properties"/>

    <!--声明数据源,作用是连接数据库的-->
    <bean id="myDateSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
        <!--set注入给DruidDataSource提供连接数据库信息 -->

        <!--<property name="url" value="jdbc:mysql://localhost:3306/springdb"/>&lt;!&ndash;setUrl()&ndash;&gt;-->
        <!--
        <property name="username" value="root"/>
        <property name="password" value="root"/>
        <property name="maxActive" value="20"/>
        -->
        <!--    使用属性配置文件中的数据,语法 ${key} -->
        <property name="url" value="${jdbc.url}"/><!--setUrl()-->
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.passworld}"/>
        <property name="maxActive" value="${jdbc.max}"/>
    </bean>

    <!--声明的是mybatis中提供的SqlSessionFactoryBean类,这个类内部创建SqlSessionFactory的
        SqlSessionFactory  sqlSessionFactory = new ..
    -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!--set注入,把数据库连接池付给了dataSource属性-->
        <property name="dataSource" ref="myDateSource"/>
        <!--mybatis主配置文件的位置
           configLocation属性是Resource类型,读取配置文件
           它的赋值,使用value,指定文件的路径,使用classpath:表示文件的位置
        -->
        <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对象。
            创建好的dao对象放入到spring的容器中的。 dao对象的默认名称是 接口名首字母小写
            如果有多个包,使用逗号分割
        -->
        <property name="basePackage" value="com.test.dao"/>
    </bean>

    <!--声明service-->
    <bean id="studentService" class="com.test.service.impl.StudentServiceImpl">
        <property name="studentDao" ref="studentDao"/>
    </bean>
</beans>
测试类
package com.test;

import com.test.dao.StudentDao;
import com.test.domain.Student;
import com.test.service.StudentService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.util.List;

public class MyTest {
    @Test
    public void test01() {
        String config = "applicationContext.xml";
        ApplicationContext ctx = new ClassPathXmlApplicationContext(config);
        String[] names = ctx.getBeanDefinitionNames();
        for (String name : names) {
            System.out.println("容器中对象名称:"+name+"|"+ctx.getBean(name));
        }
    }

    @Test
    public void test02() {
        String config = "applicationContext.xml";
        ApplicationContext ctx = new ClassPathXmlApplicationContext(config);
        StudentDao dao = (StudentDao) ctx.getBean("studentDao");
        Student stu = new Student(1006,"老六","123@qq.com",30);
        int nums = dao.insertStudent(stu);
        System.out.println("nums"+nums);
    }

    @Test
    public void test03() {
        String config = "applicationContext.xml";
        ApplicationContext ctx = new ClassPathXmlApplicationContext(config);
        StudentService service = (StudentService) ctx.getBean("studentService");
        Student stu = new Student(1007,"老七","123@qq.com",30);
        int nums = service.addStudent(stu);
        //spring和mybatis整合在一起使用,事务是自动提交的。 无需执行SqlSession.commit();
        System.out.println("nums"+nums);
    }

    @Test
    public void testServiceSelect(){

        String config="applicationContext.xml";
        ApplicationContext ctx = new ClassPathXmlApplicationContext(config);
        //获取spring容器中的dao对象
        StudentService service = (StudentService) ctx.getBean("studentService");

        List<Student> students = service.queryStudents();
        for (Student stu:students){
            System.out.println(stu);
        }
    }
}

spring事务

Spring 的事务管理

​ 事务原本是数据库中的概念,在Dao层。但一般情况下,需要将事务提升到业务层,即service层。这样做是为了能够使用事务的特性来管理具体的业务。

​ 在spring中通常可以通过以下两种方式来实现对事务的管理:

​ (1) 使用spring的事务注解管理事务

​ (2) 使用AspectJ的AOP配置管理事务

spring事务管理API

Spring 的事务管理,主要用到两个事务相关的接口。

事务管理器接口

​ 事务管理器是 PlatformTransactionManager 接口对象。其主要用于完成事务的提交、回滚,及获取事务的状态信息。

常用的两个实现类

PlatformTransactionManager 接口有两个常用的实现类:

➢ DataSourceTransactionManager:使用 JDBC 或 MyBatis 进行数据库操作时使用。

➢ HibernateTransactionManager:使用 Hibernate 进行持久化数据时使用。

spring的回滚方式

​ spring事务的默认回滚方式是:发生运行时异常和error时回滚,发生受查(编译)异常时提交。不过,对于受查异常,程序员也可以手工设置其回滚方式。

事务定义的接口

​ 事务定义接口TransactionDefinition 中定义了事务描述相关的三类常量:事务隔离级别,事务传播行为,事务默认超时时限,及对它们的操作。

五个事务隔离级别常量

这些常量均是以 ISOLATION_开头。即形如 ISOLATION_XXX。

➢ DEFAULT:采用 DB 默认的事务隔离级别。MySql 的默认为 REPEATABLE_READ; Oracle

默认为 READ_COMMITTED。

➢ READ_UNCOMMITTED:读未提交。未解决任何并发问题。

➢ READ_COMMITTED:读已提交。解决脏读,存在不可重复读与幻读。

➢ REPEATABLE_READ:可重复读。解决脏读、不可重复读,存在幻读

➢ SERIALIZABLE:串行化。不存在并发问题。

定义了七个事务传播行为常量

​ 所谓事务传播行为是指,处于不同事务中的方法在相互调用时,执行期间事务的维护情况。如,A 事务中的方法 doSome()调用 B 事务中的方法 doOther(),在调用执行期间事务的维护情况,就称为事务传播行为。事务传播行为是加在方法上的。

事务传播行为常量都是以 PROPAGATION_ 开头,形如 PROPAGATION_XXX。

PROPAGATION_REQUIRED

PROPAGATION_REQUIRES_NEW

PROPAGATION_SUPPORTS

PROPAGATION_MANDATORY

PROPAGATION_NESTED

PROPAGATION_NEVER

PROPAGATION_NOT_SUPPORTED

PROPAGATION_REQUIRED

​ 指定的方法必须在事务内执行。若当前存在事务,就加入到当前事务中;若当前没有事务,则创建一个新事务。这种传播行为是最常见的选择,也是 Spring 默认的事务传播行为。

​ 如该传播行为加在 doOther()方法上。若 doSome()方法在调用 doOther()方法时就是在事务内运行的,则doOther()方法的执行也加入到该事务内执行。若 doSome()方法在调用doOther()方法时没有在事务内执行,则 doOther()方法会创建一个事务,并在其中执行。
在这里插入图片描述
PROPAGATION_SUPPORTS

指定的方法支持当前事务,但若当前没有事务,也可以以非事务方式执行。
在这里插入图片描述
PROPAGATION_REQUIRES_NEW

总是新建一个事务,若当前存在事务,就将当前事务挂起,直到新事务执行完毕。
在这里插入图片描述

定义了默认事务超时时限

​ 常量 TIMEOUT_DEFAULT 定义了事务底层默认的超时时限,sql 语句的执行时长。注意,事务的超时时限起作用的条件比较多,且超时的时间计算点较复杂。所以,该值一般就使用默认值即可。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值