JavaEE Spring学习记录

197 阅读24分钟

本文已参与 [新人创作礼] 活动 , 一起启动掘金创作之路!


Spring

Spring是主流的 Java Web开发框架 ,该框架是轻量级、性能好、易于测试。

Spring具有控制反转(IoC)和 面向切面(AOP)两大核心。Java Spring 框架通过声明式方式灵活地进 行事务的管理,提高开发效率和质量

Spring优势

  • 方便解耦,简化开发
  • 方便继承各种优秀框架
  • 降低 Java EE API 使用难度
  • 方便程序测试
  • AOP编程支持
  • 声明式事务支持

Spring体系结构

Spring提供了一站式服务,是以模块化形式呈现需要自行选择

  1. 数据访问/集成
    • JDBC模块:提供了JDBC抽象层
    • ORM模块:对对象关系映射API
    • OXM模块:支持 对象/XML 映射的抽象层实现
    • JMS模块:Java消息服务,生产消费信息等
    • Transactions事务模块:支持编程和声明式事务管理实现特殊接口类
  2. Web
    • Web模块:Web开发继承的特性
    • Servlet模块:Spring 模型-视图-控制器 (MVC) 实现Web应用程序
    • Struts模块:支持类内的Spring应用程序
    • Portlet模块:提供在Portlet环境中使用MVC实现
  3. 核心容器
    • Beans模块:将对象以Bean进行管理
    • Core核心模块:提供框架基本部分, IoC 和 DI 功能 等
    • Context上下文模块:是Beans模块基础之上,是访问和配置文件任何对象的媒介
    • Expression Language模块:运行时查询和对象图的强大的表达式语音
  4. 其他
    • AOP模块:面向切面的编程实现,允许定义方法拦截器和切入点,按方法功能进行分离,降低耦合性
    • Aspects模块:提供与AspectJ的集成,是功能强大的AOP框架
    • Instrumentation模块:提供类工具的支持和类加载器的实现,在特定的应用服务器中使用
    • Test模块:支持JUnit 或 TestNG 框架测试

Spring IOC

Ioc—Inversion of Control,即“控制反转”,不是什么技术,而是一种设计思想

Ioc 在开发中,无需自行实例对象,而是有 Spring Ioc容器 创建。Spring容器会负责控制程序之间的关系,而不是由代码直接控制,因此,控制权由 Spring容器 接管,控制权发生了反转,是Ioc设计思想

Spring 提供了两种 IoC 容器,分别为 BeanFactory 和 ApplicationContext

对象说明和获取

BeanFactory

BeanFactory接口 是一个管理Bean的工厂,根据xml配置文件中的定义Bean进行管理,主要负责初始化各种Bean,并调用他们的生命周期方法

ApplicationContext

ApplicationContext 是 BeanFactory 的子接口,也是 应用上下文。支持了 BeanFactory 的所有功能

ApplicationContext接口有两个常用的 实现类,主要用于加载配置文件的

  • ClassPathXmlApplicationContext 通过 类路径上下文加载

    ==new ClassPathXmlApplicationContext(<Spring配置文件的名>)==

    将普通路径解释为包含包路径的类路径资源名称(例如“mypackage/myresource.txt”)

  • FileSystemXmlApplicationContext 通过 文件系统路径/URL 加载指定配置文件

    ==new FileSystemXmlApplicationContext(<文件系统路径/URL>);==

    **注意:**普通路径将始终被解释为相对于当前 VM 工作目录,即使它们以斜杠开头 使用显式的“file:”前缀来强制使用绝对文件路径

对象获取实例

//指定决定绝对路径的文件
new FileSystemXmlApplicationContext(new FileSystemResource("D:\applicationContext.xml"));

//指定相对根路径的文件
new ClassPathXmlApplicationContext("applicationContext.xml");

Bean标签

Spring Bean标签 可以想象成 我们使用的 实例对象 ,Spring 是通过在配置文件中进行调取出来的实例对象,而往常的操作 是通过 硬编码 new 出来的对象 ,因此 我们需要提前 编写好配置文件

Bean标签,因此我们需要自己手动配置Bean对象,以下有两种Spring配置文件支持的格式

  • Properties配置文件 (只能存在键值形式存在
  • XML配置文件

XML配置文件

XML 格式配置文件的根元素是 <beans>,该元素包含了多个 <bean> 子元素,每一个 <bean> 子元素定义了一个 Bean,并描述了该 Bean 如何被装配到 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:p="http://www.springframework.org/schema/p"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.2.xsd">

    <!-- 使用id属性定义person1,其对应的实现类为com.mengma.person1 (类路径及包路径)-->
    <bean id="person1" class="com.mengma.person1 " />
    <!--使用name属性定义person2,其对应的实现类为com.mengma.domain.Person2 (类路径及包路径) -->
    <bean name="Person2" class="com.mengma.domain.Person2"/>
        
</beans>

bean标签属性说明

属性说明
idStringBean 的唯一标识符,Spring IoC 容器对 Bean的 配置和管理都通过该属性完成
nameStringBean 的名称,可通过 name属性 为同一个 Bean同时指定多个名称,每个名之间用 逗号/分号 隔开。Spring容器 可通过 name属性 配置和管理容器中的 Bean
classStringBean 的具体实现类 (全限定名
scopeString定义 的 [Bean作用域](#Bean 作用域) (点击跳转
autowirebyName | byTypeBean自动装配,根据定义的 name/type 进行自动装配
lazy-initboolean延迟加载 (默认false),在getBean()方法调用时才实例
init-methodString初始化对象方法
destroy-methodString销毁对象方法

示例

实体类 People.calss

package com.sans;
public class Person {
    private String name;
    private String msg;

    public Person() {}

    public Person(String name, String msg) {
        this.name = name;
        this.msg = msg;
    }

    // 省略 自动生成的 set/get 方法

    public void init() {
        System.out.println("Person对象初始化: "+this);
    }

    public void destroy() {
        System.out.println("Person对象销毁: "+this);
    }
    
    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", msg='" + msg + '\'' +
                '}';
    }
}

配置文件 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">

    <!-- Bean实例方式
        - 构造器实例
        - 静态工厂实例
        - 实例工厂实例
     -->
    <!-- 无参构造器 -->
    <bean id="person1" class="com.sans.Person"/>
    <!-- 有参构造器 -->
    <!-- Bean属性 初始化/销毁 -->
    <bean id="person2" class="com.sans.Person" init-method="init" destroy-method="destroy">
        <constructor-arg index="0" value="张三"/>
        <constructor-arg index="1" value="Spring挺简单的嘛"/>
    </bean>

    <!-- 静态工厂实例 -->
    <bean id="personFactory1" class="com.sans.MyFactory" factory-method="instanceLisi" />

    <!-- 实例工厂实例 -->
    <bean id="factory" class="com.sans.MyFactory"/>
    <bean id="personFactory2" factory-bean="factory" factory-method="instanceZhangsan"/>
</beans>

测试

@Test
public void beanExampleTest() {
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
    Person person1 = (Person) applicationContext.getBean("person1");
    Person person2 = (Person) applicationContext.getBean("person2");
    Person factory1 = (Person) applicationContext.getBean("personFactory1");
    Person factory2 = (Person) applicationContext.getBean("personFactory2");

    System.out.println("person1 = " + person1);
    System.out.println("person2 = " + person2);
    System.out.println("factory1 = " + factory1);
    System.out.println("factory2 = " + factory2);

    /* 结果:
        Person对象初始化: Person{name='张三', msg='Spring挺简单的嘛'}
        Person对象初始化: Person{name='null', msg='null'}
        person1 = Person{name='null', msg='null'}
        person2 = Person{name='张三', msg='Spring挺简单的嘛'}
        factory1 = Person{name='李四', msg='我也这么觉得!'}
        factory2 = Person{name='张三', msg='Spring挺简单的嘛'}
     */
}

Bean 作用域

Spring Bean作用域,可方便的管理 Bean应用的时期 以及场景

只需在 <bean>.scope属性 配置范围值即可。scope范围值有以下6种:

属性值说明
singleton单例模式,每次在容器获取都是同一 Bean实例 (默认值)
prototype原型模式,每次在容器获取都是新的 Bean实例
request每次request请求,容器都会创建一个 Bean实例。该域只在当前 request 内有效
session每次session会话,不同的会话使用的 Bean实例不同。该域仅在当前 Session 内有效
applicationWeb应用 共享一个 Bean实例。该域在当前 ServletContext 内有效(和单例模式差不多
websocketwebsocket 的作用域是 WebSocket ,即在整个 WebSocket 中有效

以上属性应用自行测试

依赖注入 DI

DI—Dependency Injection,即 依赖注入。需要通过 简单的配置,而无需任何代码就可指定目标需要的资源。而注入的方式有:

  • setter
  • 构造器
  • 接口
  • 注解

Ioc 和 DI 是同一个概念的不同角度描述。IoC是一种思想;DI是实现它的手段 Spring框架使用依 赖注入实现IoC

属性注入

Bean 属性注入,是将属性数据注入到 Bean 中的过程

实现属性注入方式 :

  • 构造函数注入
  • setter 注入

bean标签 代表的是一个实例对象,实例对象中 包含多个 属性/子标签 等。以下说明当中的标签以及作用

constructor-arg标签 构造实例。应用的构造器指定的参数进行实例对象。该标签有以下属性

属性说明
indexString传参的序号(从0开始
valueString指定传递的 常量值
nameString构造方法对应的 形参名称
refString引用配置 Bean实例
typeString传参的属性类型

**ref:**引用可通过 id/name 属性的值 进行引入

property标签 set注入。该标签是通过 属性的set方法进行填充数据,因此属性一定要有set方法。该标签有以下属性

属性说明
nameString属性名称
refString引用配置 Bean实例
valueString配置指定 常量值

注意:

  • name属性 在配置的时候,如果失去高亮,那么很有可能实体类没有配置该属性的 set方法 (前提:需要配置Spring上下文

  • set注入P命名空间,使用前提父标签需要引入以下配置

    ==xmlns:p="www.springframework.org/schema/p"==

集合属性配置 在配置当中难免会存在一些特殊情况,例如集合属性的配置。以下是 集合标签的说明

标签说明
<array>封装数组(可重复
<list>封装 List (可重复
<set>封装 Set(不可重复
<map>封装 Map (k和v均可任意类型
<props>封装 Map (k和v都是字符串类型
  • <array> 封装数组(可重复

    • <ref>.bean:引用对象元素
    • <value>:常量值
  • <list> 封装List(可重复

    • <ref>.bean:引用对象元素
    • <value>:常量值
  • <set> 封装 Set(不可重复

    • <ref>.bean:引用对象元素
    • <value>:常量值
  • <map> 封装 Map (k和v均可任意类型

    • <entry>.key:Map键 K

    • <entry>.value:Map值 V

      键和值可以 都可以 引用实例对象。引用方式 属性名后缀添加 -ref

  • <props> 封装 Map (k和v都是字符串类型

    • <prop>.key:Map键 K
    • <prop>:Map值 V(标签包裹

应用示例

步骤:

  1. 编写好实体类的属性以及get/set方法
  2. 在 xml/Properties 配置上编辑 bean实例
  3. 通过 ==new ClassPathXmlApplicationContext(<配置文件路径>).getBean()==方法获取实例对象

实体类 Person.class

public class Person {
    private String name;
    private String msg;

    // 数组类型
    private Person[] array;

    // 集合 特殊应用
    private List<Person> list;
    private Set<String> set;
    private Map<String, Object> map;
    private Properties properties;

    // 构造方法注入
    public Person() {
    }
    
    public Person(String name, String msg) {
        this.name = name;
        this.msg = msg;
    }

    // 省略 自动生成的 get/set 方法

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

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:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- 无参构造 -->
    <bean id="person1" class="com.sans.bean.Person"/>
    <!-- 全参构造 -->
    <bean id="person2" class="com.sans.bean.Person">
        <constructor-arg index="0" value="张三"/>
        <constructor-arg index="1" value="Spring挺简单的嘛"/>
    </bean>

    <!-- set注入 -->
    <bean id="person3" class="com.sans.bean.Person">
        <property name="name" value="李四"/>
        <property name="msg" value="我也觉得"/>
    </bean>
    <!-- P命名空间 set注入-->
    <bean id="person4" class="com.sans.bean.Person" p:name="王五" p:msg="还行吧!挺好的"/>
    <!-- set方法 集合注入 -->
    <bean id="person5" class="com.sans.bean.Person">

        <!-- 数组类型 -->
        <property name="array">
            <array>
                <ref bean="person1"/>
                <ref bean="person2"/>
                <ref bean="person3"/>
                <ref bean="person4"/>
            </array>
        </property>
        
        <!-- List类型 -->
        <property name="list">
            <list>
                <ref bean="person1"/>
                <ref bean="person2"/>
                <ref bean="person3"/>
                <ref bean="person4"/>
            </list>
        </property>
        
        <!-- Set类型 -->
        <property name="set">
            <set>
                <value>张三</value>
                <value>李四</value>
                <value>王五</value>
            </set>
        </property>

        <!-- Map类型 -->
        <property name="map">
            <map>
                <entry key="No1" value="张三" />
                <entry key="No2" value="李四"/>
                <entry key="No3" value="王五"/>
            </map>
        </property>

        <property name="properties">
            <props>
                <prop key="No1">张三</prop>
                <prop key="No2">李四</prop>
                <prop key="No3">王五</prop>
                <prop key="No4">12</prop>
            </props>
        </property>
    </bean>
</beans>

测试结果

public class Demo {

    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");

    /* 通过构造器 实例 */
    @Test
    public void constructor() {
        Person person1 = (Person) applicationContext.getBean("person1");
        Person person2 = (Person) applicationContext.getBean("person2");
        System.out.println("person1 = " + person1);
        System.out.println("person2 = " + person2);
        /* 结果 toString方法直接打印
            person1 = Person{name='null', msg='null'}
            person2 = Person{name='张三', msg='Spring挺简单的嘛'}
         */
    }

    /* set注入 实例 */
    @Test
    public void setInjection() {
        Person person3 = (Person) applicationContext.getBean("person3");
        Person person4 = (Person) applicationContext.getBean("person4");
        Person person5 = (Person) applicationContext.getBean("person5");
        System.out.println("person3 = " + person3);
        System.out.println("person4 = " + person4);

        // 由于 person4对象 针对了 数组/集合 类型的属性测试
        System.out.println("person5.getArray() = " + Arrays.toString(person5.getArray()));
        System.out.println("person5.getList() = ");
        person5.getList().forEach(person -> System.out.println("\t"+person));
        System.out.println("person5.getSet() = " + person5.getSet());
        System.out.println("person5.getMsg() = ");
        person5.getMap().forEach((key, value) -> System.out.println("\t"+key+" : "+value));
        System.out.println("person5.getProperties() = " + person5.getProperties());
        /* 结果 (PS: 由于有一个是无参的引用因此为null
            person3 = Person{name='李四', msg='我也觉得'}
            person4 = Person{name='王五', msg='还行吧!挺好的'}
            person5.getArray() = [Person{name='null', msg='null'}, Person{name='张三', msg='Spring挺简单的嘛'}, Person{name='李四', msg='我也觉得'}, Person{name='王五', msg='还行吧!挺好的'}]
            person5.getList() = 
            	Person{name='null', msg='null'}
            	Person{name='张三', msg='Spring挺简单的嘛'}
            	Person{name='李四', msg='我也觉得'}
            	Person{name='王五', msg='还行吧!挺好的'}
            person5.getSet() = [张三, 李四, 王五]
            person5.getMsg() = 
            	No1 : 张三
            	No2 : 李四
            	No3 : 王五
            person5.getProperties() = {No2=李四, No1=张三, No4=12, No3=王五}
         */

    }
}

自动注入

Spring 的自动注入功能可以让 Spring容器 依据指定规则,为指定的 Bean 从应用的上下文中查找它所依赖的 Bean 并自动建立 Bean 之间的依赖关系。而这一过程是在完全不使用任何 <constructor-arg>.ref / <property>.ref 形式配置Bean

自动注入 主要作用是 简化 Spring 在 XML配置应用,因此在大工程中降低很多工作量

Spring容器 默认不支持自动装配的,需要在配置文件中的 <bean>.autowire属性应用

<bean>.autowire属性说明

属性值说明
byName根据 名称 自动注入 (id/name均可引用
byType根据 类型 自动注入
constructor根据 构造器参数 的数据类型,进行 byType模式 的自动装配
default默认采用上一级元素 <beans>设置的自动装配规则(default-autowire)进行装配
no默认值,不使用自动注入

byName装配

ByName表示 按属性名称自动装配,配置文件中 <bean>.id/name属性值 必须与 类中属性名称 相同

示例

Person实体对象 (省略

实体工厂对象

public class MyFactory {
    private Person person1;
    private Person person2;
    // 省略 自动生成的 set/get 方法
}

配置文件

<!-- 使用 id/name 属性 标明  -->
<bean id="person1" class="com.sans.bean.Person" p:name="张三" p:msg="Spring挺简单的嘛!"/>
<bean name="person2" class="com.sans.bean.Person" p:name="李四" p:msg="我也觉得"/>

<!--  byName -->
<bean id="Myfactory" class="com.sans.MyFactory" autowire="byName"/>

测试

@Test
public void auotInjection() {
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
    MyFactory factory = (MyFactory) applicationContext.getBean("Myfactory");

    System.out.println(factory.getPerson1());
    System.out.println(factory.getPerson2());
    /* 结果:
        Person{name='张三', msg='Spring挺简单的嘛!'}
        Person{name='李四', msg='我也觉得'}
    */
}

byType

byType表示 按属性类型自动装配,配置文件中 被注入类中的属性类型 与 容器内的Bean类型 相同,则自动注入

注意:

  • 如果类中有一个以上的相同属性类型,那么该类型全部自动注入
  • 在Spring Ioc容器上下文应用中不能存在相同类型的 Bean实例,否则无法引用

示例

Person实体对象 (省略

实体工厂对象 (省略

配置文件

<bean id="person1" class="com.sans.bean.Person" p:name="张三" p:msg="Spring挺简单的嘛!"/>
<!--    不能存在相同类型
<bean name="person2" class="com.sans.bean.Person" p:name="李四" p:msg="我也觉得"/>-->

<!-- byClass -->
<bean id="Myfactory" class="com.sans.MyFactory" autowire="byType"/>

测试

@Test
public void auotInjection() {
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
    MyFactory factory = (MyFactory) applicationContext.getBean("Myfactory");

    System.out.println(factory.getPerson1());
    System.out.println(factory.getPerson2());
    /* 结果:
        Person{name='张三', msg='Spring挺简单的嘛!'}
		Person{name='张三', msg='Spring挺简单的嘛!'}
    */
}

注解装配Bean

在 Spring 中,尽管使用 XML配置文件可实现 Bean 的装配工作,如果应用中 Bean 的数量较多,会导致 XML配置文件过于臃肿,从而给维护和升级带来一定的困难,因此 Spring 提供了注解应用,需要在原有的运行环境基础上做些变化,由此减少过多的Bean

@Component

在类上添加上 @Component注解 表示该类实例的对象的权限交给 Spring容器 。注解的value属性用于指定bean的 id值 或 name值 ,但一般情况可省略 value 属性!(该注解指定id是类名的首字母小写)

以下注解与 @Component注解 用法和功能 相同,表达的含义不同!

  • @Repository dao层实现类的注解(持久层)
  • @Service service层实现类的注解(业务层)
  • @Controller controller层实现类的注解(控制层)

@Value

该 注解 是为指定属性 注入值。该注解用在 类的属性 或 指定set方法上 。 其注解注入 原理 和 set方法 写入是一样,因此 set方法 也可

package com;

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

@Component
public class MyUser {
    //方式1
    @Value("001")
    private int id;
    @Value("张三")
    private String name;
    @Value("23")
    private int age;
    @Value("洛杉矶")
    private String locateion;
    
    ···
    
    //方式2
    @Value("001")
    public void setId(int id) {
        this.id = id;
    }
    @Value("张三")
    public void setName(String name) {
        this.name = name;
    }
    @Value("23")
    public void setAge(int age) {
        this.age = age;
    }
    @Value("洛杉矶")
    public void setLocateion(String locateion) {
        this.locateion = locateion;
    }
    
   	···
}

包扫描

需要在 xml配置文件中配置组件扫描器,用于在指定包中扫描的注解。如果未添加扫描,则对象添加的注解 将无法实例化

xml配置文件 添加扫描

  1. beans标签 配置属性 xmlns:context属性: xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation属性(添加值): http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd

    ···
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context.xsd">
    	···
    </beans>
    
  2. context:component-scan标签 指定包扫描 分隔符可以使用逗号(,)分号(;)还可以使用空格,不建议使用空格。

    <?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
        http://www.springframework.org/schema/context/spring-context.xsd">
    <!--    包扫描 方法1: (包路径 Annotation.dao、Annotation.service、Annotation.controller)-->
    	    <context:component-scan base-package="Annotation.dao"/>
    	    <context:component-scan base-package="Annotation.service"/>
    	    <context:component-scan base-package="Annotation.controller"/>
    <!--    包扫描 方法2: (包路径 Annotation.dao、Annotation.service、Annotation.controller)-->
    	    <context:component-scan base-package="Annotation.dao;Annotation.service;Annotation.dao"/>
    </beans>
    
  3. 添加对应注解即可

自动注入

自动注入指定对象的前提,需要为该对象添加注解,且 上级包 或 指定类 有扫描到!

@Autowired

用于对 Bean 的 属性变量、属性Set方法 及构造方法进行标注(构造方法应用的前提,参数必须指定的是对应的实例对象),配合对应的注解处理器完成 Bean 的自动配置工作。默认按照 Bean 的类型 进行装配

@Autowired 还有一个属性 required,默认值为 true,表示匹配失败后终止程序运行;若值为 false,则匹配失败后 其属性值为 null

@Qualifier

与 @Autowired 注解配合使用,会将默认的按 Bean类型 装配修改为按 Bean实例队形名称 装配,Bean 的实例名称由 @Qualifier 注解的 value参数 指定

@Resource

该注解 在jdk1.6版本以上 支持使用,Bean属性可指定按照 类型(type) 或 名称(name) 进行自动注入,可在 属性变量、属性Set方法 进行使用

Spring AOP

Spring的AOP实现底层就是对上面动态代理的代码进行封装,封装后我们只需要对关注部分进行代码编写,并通过配置的方式完成指定目标的方法增强

Spring 通知 指定目标类方法 的连接点位置,分为以下5种通知类型

通知类型

  1. 前置通知 在方法执行前的通知,可以应用于权限管理等功能
  2. 后置通知 在方法执行后的通知,可以应用于关闭流、上传文件、删除临时文件等功能
  3. 环绕通知 在方法执行 前、后 都通知,可以应用于日志、事务管理等功能
  4. 异常通知 在方法抛出异常时的通知,可以应用于处理异常记录日志等功能
  5. 最终通知 方法执行完毕后最后的通知

AOP相关术语:

  • Target(目标对象) 要被增强的对象,一般业务逻辑类对象
  • Proxy(代理) 一个类被AOP 织入增强后,产生一个结果代理类
  • Aspect(切面) 切面点 增强功能,就是一些代码完成某些功能,非业务功能。是切入点和通知的结合
  • Joinpoint(连接点) 连接点 指 那些被拦截的点。在Spring中,这些点指定与 核心业务的方法(Spring只支持方法类型的连接点)
  • Pointcut(切入点) 切入点 指 声明一个或多个连接点的集合。通过切入点指定一组方法(非final方法)
  • Advice(通知/增强) 通知 指 拦截到 连接点 后要做的 通知。通知定义了增强代码切入到目标代码的时间点,是目标方法执行之前执行,还是之后执行等。通知类型不同,切入时间不同
  • Weaving(织入) 织入 指 增强应用到目标对象来创建新的代理对象的过程。 spring 采用动态代理织入,而 AspectJ 采用编译期织入和类装载期织入

AspectJ 实现AOP

AspectJ 基于 Java语言的AOP框架,它扩展了 Java语言,使得AOP功能使用更便捷

切入点表达式

AspectJ 定义 专门的表达式 指定 切入点

表达式原型:

execution([modifiers-pattern] ret-type-pattern declaring-type-pattern name-pattern(param-pattern) [throws-pattern])

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

参数说明 (中括号表示可选类型)

modifiers-pattern :访问权限类型 ret-type-pattern :返回类型 declaring-type-pattern :包名类名 name-pattern(param-pattern) :方法名(参数的 类型 和 个数) throws-pattern :抛出异常类型

PS:表达式各个部分可以用空格隔开,可用以下符号:

符号范围说明
*所有0 ~ n 个任意字符
..方法参数、包路径指定任意个参数;当前包的子路径
+类名、接口当前类路径的子类;当前接口及实现的类

切入点表达式实例

execution(* com.service.*.*(..)) 定义 com.service 包路径里的 任意类、任意方法、方法任意返回类型

execution(* com.service..*.*(..)) 定义 com.service 包路径里的 任意子包、任意类、任意方法、方法任意返回类型

execution(* com.service.IUserService+.*(..)) 当路径 com.service.IUserService 的文件类型为以下条件: 若为接口,则为接口中 任意方法及其所有实现类中的任意方法 若为类,则为该类及其子类中的任意方法

AOP声明方式

  • 基于 XML 声明
  • 基于 Annotation 声明

XML实现AOP

基于 XML文件配置 切面切入点 等,Spring 也支持 AOP 的应用,不过应用前提需要 aop命名空间,aop命名空间提供了 <aop:config>标签 主要用于配置AOP应用

实现说明:

  • XML配置AOP 必须在 <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"
           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
           http://www.springframework.org/schema/aop/spring-aop.xsd
           ....
    ">
    
    
  • AOP相关的信息,都在 <aop:config>标签中配置,且他们的子标签必须按照顺序进行配置 **顺序:**pointcut => advisor => aspect

实现步骤:

  1. 配置环境 jar包,spring框架等
  2. 完成业务类以及含有接口
  3. 配置 切入面类(代理类)
  4. 编写配置文件 挂载配置
  5. 测试

依赖、插件

<?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.sans</groupId>
    <artifactId>Spring-AOP</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>15</maven.compiler.source>
        <maven.compiler.target>15</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>

        <!-- spring依赖 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.13.RELEASE</version>
        </dependency>

        <!-- CGLIB的动态代理 -->
        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>3.2.2</version>
        </dependency>

        <!--aop切面依赖-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>5.2.13.RELEASE</version>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <!--编译插件-->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.0</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

业务接口 UserService

public interface UserService {
    int add(User user);
    int del(int id);
    int update(User user);
    List<User> findAll();
    List<User> findById(int id);
}

业务实现类 UserServiceImpl

用于模拟业务需求的场景进行编写的代理应用,本次限于测试学习!

public class UserServiceimpl implements UserService {
    @Override
    public int add(User user) {
        System.out.println("add() 业务方法执行");
        return 1;
    }

    @Override
    public int del(int id) {
        System.out.println("del() 业务方法执行");
        return 1;
    }

    @Override
    public int update(User user) {
        System.out.println("update() 业务方法执行");
        // 制造异常
        int i = 1 / 0;
        return 0;
    }

    @Override
    public List<User> findAll() {
        System.out.println("findAll() 业务方法执行");
        return null;
    }

    @Override
    public List<User> findById(int id) {
        System.out.println("findById() 业务方法执行");
        return null;
    }
}

代理类 MyAspect

public class MyAspect {

    // 前置方法
    public void before(JoinPoint jp) {
        String methodName = jp.getSignature().getName();
        Object[] args = jp.getArgs();
        Object target = jp.getTarget();
        System.out.println("==>前置方法" +
                     "\n\t方法名:" + methodName +
                     "\n\t参数:" + Arrays.toString(args) +
                     "\n\t目标:" + target
        );
    }

    // 后置方法
    public void afterReturning(JoinPoint jp , Object returnValue) {
        String methodName = jp.getSignature().getName();
        Object[] args = jp.getArgs();
        Object target = jp.getTarget();
        System.out.println("==>后置方法" +
                    "\n\t方法名:" + methodName +
                    "\n\t参数:" + Arrays.toString(args) +
                    "\n\t目标:" + target +
                    "\n\t返回值:" + returnValue
        );
    }


    /* 前后环绕方法
    *   参数:强化环绕封装对象
    *   返回:业务方法的返回值
    *  */
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        // 前置环绕
        String methodName = pjp.getSignature().getName();
        Object[] args = pjp.getArgs();
        Object target = pjp.getTarget();
        System.out.println("==>前置环绕方法" +
                        "\n\t方法名:" + methodName +
                        "\n\t参数:" + Arrays.toString(args) +
                        "\n\t目标:" + target
        );

        // 调用被 拦截的代理方法
        Object returnValue = pjp.proceed();

        // 后置环绕
        String methodName2 = pjp.getSignature().getName();
        Object[] args2 = pjp.getArgs();
        Object target2 = pjp.getTarget();
        System.out.println("==>后置环绕方法" +
                        "\n\t方法名:" + methodName2 +
                        "\n\t参数:" + Arrays.toString(args2) +
                        "\n\t目标:" + target2+
                        "\n\t返回值:" + returnValue
        );

        return returnValue;
    }

    // 异常增强
    public void afterThrowing(JoinPoint jp,RuntimeException e) {
        String methodName = jp.getSignature().getName();
        Object[] args = jp.getArgs();
        Object target = jp.getTarget();
        System.out.println("==>异常增强" +
                        "\n\t方法名:" + methodName +
                        "\n\t参数:" + Arrays.toString(args) +
                        "\n\t目标:" + target
        );
        System.out.println("异常增强的消息:" + e.getMessage());
    }

}

配置文件 aop.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: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
       http://www.springframework.org/schema/aop/spring-aop.xsd
">
    <!-- 托管 业务Bean对象 -->
    <bean id="userService" class="com.sans.service.impl.UserServiceimpl"/>
    <!-- 托管 切面对象 -->
    <bean id="myAspect" class="com.sans.proxy.MyAspect"/>

    <!-- xml代理切入 -->
    <aop:config>
        <!-- 解释:任意权限 指定com.sans.service包下的所有类中所有方法以及方法中的所有参数-->
        <aop:pointcut id="pointcut" expression="execution(* com.sans.service.*.*(..))"/>
        <aop:aspect ref="myAspect">
            <!-- 前置 -->
            <aop:before method="before" pointcut-ref="pointcut"/>
            <!-- 后置 -->
            <aop:after-returning method="afterReturning" pointcut-ref="pointcut" returning="returnValue"/>
            <!-- 异常 -->
            <aop:after-throwing method="afterThrowing" pointcut-ref="pointcut" throwing="e" />
            <!-- 环绕 -->
            <aop:around method="around" pointcut-ref="pointcut" 
        </aop:aspect>
    </aop:config>

</beans>

测试

@Test
public void xmlTest() {
    ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("aop.xml");
    UserService userService = (UserService) applicationContext.getBean("userService");

    userService.add(new User("张三",23));
//      userService.del(1);
//      userService.update(new User("张三",23));
//      userService.findAll();
//      userService.findById(23);
    /* 剩下自行测试
        ==>前置方法
        	方法名:add
        	参数:[User{id='null', name='张三', age=23}]
        	目标:com.sans.service.impl.UserServiceimpl@7905a0b8
        ==>前置环绕方法
        	方法名:add
        	参数:[User{id='null', name='张三', age=23}]
        	目标:com.sans.service.impl.UserServiceimpl@7905a0b8
        add() 业务方法执行
        ==>后置环绕方法
        	方法名:add
        	参数:[User{id='null', name='张三', age=23}]
        	目标:com.sans.service.impl.UserServiceimpl@7905a0b8
        	返回值:1
        ==>后置方法
        	方法名:add
        	参数:[User{id='null', name='张三', age=23}]
        	目标:com.sans.service.impl.UserServiceimpl@7905a0b8
        	返回值:1
      */
}

Annotation实现AOP(待解决)

对切面类使用指定 注解 定义 切面、切入点 等

注解类型

注解名称范围说明
@Aspect定义 切面
@Pointcut定义 切入点表达式 (execution()控制切入点
@Before方法定义 前置 通知
@AfterReturning方法定义 后置 通知
@Around方法定义 环绕 通知
@AfterThrowing方法定义 异常 通知
@After方法定义 最终 通知(异常也会通知)

依赖配置业务类业务接口 与上述一致

切面类

package com.newAOP.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

/**
 * 切面类
 * @author Sans
 */
@Component  //创建权限交给 spring容器 进行创建
@Aspect     //aspectj 框架注解 标识该类为切面类
public class MyAspect {
    /**
     * 切点表达式为:
     *      方法 任意返回类型
     *      指定路径 com.newAOP包
     *      指定包下的所有 子包、子类、类、接口、方法、方法参数
     */
    @Pointcut("execution(* com.newAOP..*.*(..))")
    private void pointCut_all(){ }
    
    /**
     * 前置通知
     * @param jp
     */
    @Before("pointCut_all()")
    public void before(JoinPoint jp){
        System.out.println("======================BeforeOpen");
        System.out.println("前置通知");
        System.out.println("拦截信息:");
        System.out.println("\t方法名称:"+jp.getSignature().getName());
        Object[] args = jp.getArgs();
        if (args.length != 0){
            System.out.println("\t参数格式:"+args.length);
            System.out.println("\t参数列表:");
            for (Object arg : args) {
                System.out.println("\t\t"+arg);
            }
        }
        System.out.println("======================BeforeEnd");
    }
    
    /**
     * 后置通知
     * @param result
     */
    @AfterReturning (value="pointCut_all()",returning = "result")
    public void afterReturn(Object result){
        System.out.println("======================AfterReturnOpen");
        System.out.println("后置通知");
        System.out.println("拦截信息:");
        System.out.println("\t方法返回值:"+result);
        System.out.println("======================AfterReturnEnd");
    }
    
    /**
     * 环绕通知
     * @param pjp
     * @return 方法返回类型
     * @throws Throwable 当前方法运行抛出的异常
     */
    @Around("pointCut_all()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable{
        System.out.println("======================AroundOpen");
        System.out.println("环绕通知");
        Object obj = pjp.proceed();
        System.out.println("方法 返回值为:"+obj);
        System.out.println("======================AroundEnd");
        return obj;
    }
    
    /**
     * 异常通知
     * @param jp 连接点状态
     * @param ex 异常返回
     */
    @AfterThrowing(value = "pointCut_all()",throwing = "ex")
    public void exception(JoinPoint jp,Throwable ex){
        System.out.println("======================ExceptionOpen");
        System.out.println("异常通知");
        //返回连接点的签名(异常)
        System.out.println("异常原因:"+jp.getSignature());
        //返回 throwable 的详细消息字符串
        System.out.println("异常类型:"+ex.getMessage());
        System.out.println("======================ExceptionEnd");
    }
    
    /**
     * 最终通知(无论出现异常都会执行的通知)
     */
    @After("pointCut_all()")
    public void myFinally(JoinPoint jp){
        System.out.println("======================FinallyOpen");
        System.out.println("最终通知");
        System.out.println(jp.getSignature().getName()+" 方法结束!");
        System.out.println("======================FinallyEnd");
    }
    
}

配置文件 (扫描包,切面类注解应用)

<?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"
       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/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd">
	//扫描包。个人配置包路径 为 com.newAOP
    <context:component-scan base-package="com.newAOP" />
	//允许 Spring容器 权限应用
    <aop:aspectj-autoproxy expose-proxy="true"/>
</beans>

测试

@Test
public void test(){
    ApplicationContext ac = new ClassPathXmlApplicationContext("MySpring.xml");
    IService teamService = (IService) ac.getBean("teamService");
    teamService.add(001,"张三");
    System.out.println("\n");
    teamService.update(6, 5);
}

/*运行结果

======================AroundOpen
环绕通知
======================BeforeOpen
前置通知
拦截信息:
	方法名称:add
	参数格式:2
	参数列表:
		1
		张三
======================BeforeEnd
MyAspect---add---
======================AfterReturnOpen
后置通知
拦截信息:
	方法返回值:null
======================AfterReturnEnd
======================FinallyOpen
最终通知
add 方法结束!
======================FinallyEnd
方法 返回值为:null
======================AroundEnd


======================AroundOpen
环绕通知
======================BeforeOpen
前置通知
拦截信息:
	方法名称:update
	参数格式:2
	参数列表:
		6
		5
======================BeforeEnd
MyAspect---update---
======================AfterReturnOpen
后置通知
拦截信息:
	方法返回值:true
======================AfterReturnEnd
======================FinallyOpen
最终通知
update 方法结束!
======================FinallyEnd
方法 返回值为:true
======================AroundEnd

*/

XML与Annotation 声明区别

xml在外部文件 .xml进行配置;Annotation在类文件中进行配置 无需外部辅助

xml效率一般(需要解析);Annotation效率高

xml需要解析工具进行完成;Annotation无需解析,利用Java反射进行完成

xml易于观察对象关系(业务量较多时);Annotation 不易观察

问题

  • 代理应用 用于接口的实现过程,如果实现类并未有接口则无法实现

Spring JDBC

Spring 针对数据库开发提供了 JdbcTemplate 类,它封装了 JDBC,支持对数据库的所有操作

JDBC以往的说明:Java学习记录 JDBC篇

jar包:

  • spring-jdbc
  • spring-tx
  • spring-core

应用步骤:

  1. 引入依赖
  2. xml数据源配置
  3. dao层方法继承JdbcTemplate注入
  4. 测试

方法

返回方法说明
intupdate(String sql)用于执行新增、修改、删除等语句
intupdate(String sql,Object... args)用于执行新增、修改、删除等语句 args 表示需要传入的参数
voidexecute(String sql)可以执行任意 SQL,一般用于执行 DDL 语句 action 表示执行完 SQL 语句后,要调用的函数
Tquery(String sql, ResultSetExtractor rse)用于执行查询语句 以 ResultSetExtractor 作为参数的 query 方法返回值为 Object
Listquery(String sql, RowMapper rse)使用查询结果需要对其进行强制转型 以 RowMapper 作为参数的 query 方法返回值为 List
Map<String, Object>queryForMap(String sql)SQL查询多个聚合函数结果值,查询出的结果值形式:key-value
.........

JDBC应用

  1. 导入包 spring-jdbc-x.x.x.jarspring-tx-x.x.x.jar 以下为个人用Maven配置

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>5.2.13.RELEASE</version>
    </dependency>
    
  2. Spring-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">
    
    <!--    XML 配置数据源-->
        <bean id="dateSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
            <!--驱动加载-->
            <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
            <!--连接数据库的url  . 本次连接 test库-->
         	<!--指定IP地址 、 库(个人应用的 test库)-->   
            <property name="url" value="jdbc:mysql://192.168.74.131/test"/>
            <property name="username" value="root"/>
            <property name="password" value="root"/>
        </bean>
    
    <!--    配置jdbc模板-->
        <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
            <!--必须使用数据源-->
            <property name="dataSource" ref="dateSource"/>
        </bean>
    
    <!--    配置注入类使用-->
        <bean id="xxx" class="xxx">
            <property name="jdbcTemplate" ref="jdbcTemplate"/>
        </bean>
    <!--	例如:-->
    <!--    <bean id="studentDao" class="com.StudentDao">-->
    <!--        <property name="jdbcTemplate" ref="jdbcTemplate"/>-->
    <!--    </bean>-->
    	...
    </beans>
    

    配置注入类需要自己指定类进行配置

  3. 创建实体类 Student

    public class Student {
        int id ;
        String name;
        int age;
        
        // 省略多余的 get和set方法
        
        @Override
        public String toString() {
            return "Student{" +
                    "id=" + id +
                    ", name='" + name + '\'' +
                    ", age=" + age +
                    '}';
        }
    }
    
  4. 数据库 test库引入库

    idnameage
    DROP TABLE IF EXISTS `student`;
    CREATE TABLE `student`  (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
      `age` int(11) NULL DEFAULT 16,
      PRIMARY KEY (`id`) USING BTREE
    ) ENGINE = InnoDB AUTO_INCREMENT = 16 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;
    
  5. 指定类注入使用 JdbcTemplate类 (应用指定实例 StudentDao;实现一个操作业务即可)

    public class StudentDao extends JdbcDaoSupport {
        
        // 方法封装
        public Student turnEncapsulation(ResultSet resultSet) throws SQLException {
            Student student = new Student();
            student.setId(resultSet.getInt("id"));
            student.setName(resultSet.getString("name"));
            student.setAge(resultSet.getInt("age"));
            return student;
        }
        
        /**
         * 添加数据
         * @param student 学生类封装
         * @return 更变条数
         */
        public int insert(Student student) {
            if (student == null) {
                return 0;
            } 
            String sql = "INSERT INTO student(name,age) VALUE(?,?)";
            return this.getJdbcTemplate().update(sql,student.getName(),student.getAge());
        }
        
        /**
         * 删除数据
         * @param id 删除指定id
         * @return 更变条数
         */
        public int delete(int id) {
            String sql = "DELETE FROM student WHERE id = ?";
            return this.getJdbcTemplate().update(sql,id);
        }
        
        /**
         * 更变数据
         * @param id 指定学生id
         * @param student 更变指定 学生类
         * @return 更变条数
         */
        public int update(int id , Student student) {
            String sql = "UPDATE student set name=?,age=? WHERE id=?";
            return this.getJdbcTemplate().update(sql,student.getName(),student.getAge(),id);
        }
        
        /**
         * 查询所有条数
         * @return 学生队列
         */
        public List<Student> queryAll(){
            String sql = "SELECT * FROM student";
            return this.getJdbcTemplate().query(sql , new RowMapper<Student>() {
                @Override
                public Student mapRow(ResultSet resultSet , int i) throws SQLException {
                    System.out.println("\ti : " + i);
                    return turnEncapsulation(resultSet);
                }
            });
        }
        
        /**
         * 查询指定学生
         * @param id 指定学生id
         * @return 学生队列
         */
        public List<Student> queryFindById(int id) {
            String sql = "SELECT * FROM student WHERE id = ?";
            return this.getJdbcTemplate().query(sql,new Object[]{id}, new RowMapper<Student>() {
                @Override
                public Student mapRow(ResultSet resultSet , int i) throws SQLException {
                    System.out.println("\ti : " + i);
                    return turnEncapsulation(resultSet);
                }
            });
        }
        
        /**
         * 查询指定学生名称
         * @param name 名称
         * @return 学生队列
         */
        public List<Student> queryFindByName(String name) {
            String sql = "SELECT * FROM student WHERE name = ?";
            //匿名 new RowMapper<Student>() 替换为 lambda
            return this.getJdbcTemplate().query(sql,new Object[]{name}, (resultSet , i) -> {
                System.out.println("\ti : " + i);
                return turnEncapsulation(resultSet);
            });
        }
        
        /**
         * 聚合函数应用
         */
        /**
         * 获取学生总数
         * @return 学生总数
         */
        public int tableSize(){
            String sql = "SELECT count(id) FROM student";
            return this.getJdbcTemplate().queryForObject(sql,Integer.class);
        }
        
        /**
         * 学生首尾id
         * @return MAP形式返回id首尾
         */
        public Map<String , Object> tableMaxMin(){
            String sql = "SELECT max(id),min(id) FROM student";
            return this.getJdbcTemplate().queryForMap(sql);
        }
        
    }
    
  6. 测试类 (测试业务)

    public class connectionMySQL {
    
        ApplicationContext ac = new ClassPathXmlApplicationContext("springJDBC.xml");
        StudentDao dao = (StudentDao) ac.getBean("studentDao");
        
        /**
         * xml应用
         */
        @Test
        public void test01_Insert(){
            int insert = dao.insert(new Student("老哥" , 23));
            System.out.println("insert : " + insert);
        }
        @Test
        public void test02_Delete() {
            int delete = dao.delete(19);
            System.out.println("delete : " + delete);
            
        }
        @Test
        public void test03_Update() {
            int update = dao.update(16,new Student("黑马",32));
            System.out.println("update : " + update);
        }
        @Test
        public void test04_Query() {
            List<Student> student = dao.queryFindById(17);
            System.out.println("student : " + student);
        }
        @Test
        public void test04_Querys() {
            List<Student> students = dao.queryAll();
            // List<Student> students = dao.queryFindByName("李四");
            for (Student student : students) {
                System.out.println(student.toString());
            }
        }
        @Test
        public void test05_group(){
            int i = dao.tableSize();
            System.out.println("i : " + i);
            System.out.println("==============");
            Map<String, Object> stringObjectMap = dao.tableMaxMin();
            System.out.println("stringObjectMap : " + stringObjectMap);
        }
        
    }