博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Spring4.x 基础
阅读量:5897 次
发布时间:2019-06-19

本文共 50130 字,大约阅读时间需要 167 分钟。

196558-20180816091527177-1206906856.png

把以下 jar 包加入到工程的 classpath 下:

搭建Spring开发环境

196558-20180816091316685-458275463.png

Spring 的配置文件: 一个典型的 Spring 项目需要创建一个或多个 Bean 配置文件, 这些配置文件用于在 Spring IOC 容器里配置 Bean. Bean 的配置文件可以放在 classpath 下, 也可以放在其它目录下.

(1).Spring_HelloWorld

Helloworld.class

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

输出

public static void main(String[] args) {//        Helloworld helloworld = new Helloworld();//        helloworld.setName("tangsan");//        helloworld.hello();        //1.创建Spring的IOC容器对象        ApplicationContext context =                new ClassPathXmlApplicationContext("applicationContext.xml");        //2.从IOC获取 Bean 实例        Helloworld hello = (Helloworld)context.getBean("helloWorld");         hello.hello();    }

(2).IOC与DI

  • IOC(Inversion of Control):其思想是反转资源获取的方向. 传统的资源查找方式要求组件向容器发起请求查找资源. 作为回应, 容器适时的返回资源. 而应用了 IOC 之后, 则是容器主动地将资源推送给它所管理的组件, 组件所要做的仅是选择一种合适的方式来接受资源. 这种行为也被称为查找的被动形式
  • DI(Dependency Injection) — IOC 的另一种表述方式:即组件以一些预先定义好的方式(例如: setter 方法)接受来自如容器的资源注入. 相对于 IOC 而言,这种表述更直接

(3).Spring 中的 Bean 配置

配置形式:

  • 基于 XML 文件的方式;
  • 基于注解的方式

配置方式:

  • 通过全类名(反射)
  • 通过工厂方法(静态工厂方法 & 实例工厂方法)
  • FactoryBean

IOC 容器

  • BeanFactory
  • ApplicationContext

依赖注入的方式:

  • 属性注入;
  • 构造器注入;

(3.1).在 Spring 的 IOC 容器里配置 Bean

(3.2).Spring容器

  • 在 Spring IOC 容器读取 Bean 配置创建 Bean 实例之前, 必须对它进行实例化. 只有在容器实例化后, 才可以从 IOC 容器里获取 Bean 实例并使用.
  • Spring 提供了两种类型的 IOC 容器实现.
    • BeanFactory: IOC 容器的基本实现.
    • ApplicationContext: 提供了更多的高级特性. 是 BeanFactory 的子接口.

区别:

  • BeanFactory 是 Spring 框架的基础设施,面向 Spring 本身;
  • ApplicationContext 面向使用 Spring 框架的开发者,几乎所有的应用场合都直接使用 ApplicationContext 而非底层的 BeanFactory。
  • 无论使用何种方式, 配置文件时相同的。

(3.2.1).ApplicationContext

  • ApplicationContext 的主要实现类:
    • ClassPathXmlApplicationContext:从 类路径下加载配置文件
    • FileSystemXmlApplicationContext: 从文件系统中加载配置文件
  • ConfigurableApplicationContext 扩展于 ApplicationContext,新增加两个主要方法:refresh() 和 close(), 让 ApplicationContext 具有启动、刷新和关闭上下文的能力
  • ApplicationContext 在初始化上下文时就实例化所有单例的 Bean。
  • WebApplicationContext 是专门为 WEB 应用而准备的,它允许从相对于 WEB 根目录的路径中完成初始化工作
    196558-20180814100014082-1276509326.png

(3.2.2).从IOC获取Bean

调用 ApplicationContext 的 getBean() 方法

196558-20180814102203685-657144947.png

(3.3).依赖注入的方式

  • 属性注入
  • 构造器注入
  • 工厂方法注入(很少使用,不推荐)

(3.3.1).属性注入

  • 属性注入即通过 setter 方法注入Bean 的属性值或依赖的对象
  • 属性注入使用 <property> 元素, 使用 name 属性指定 Bean 的属性名称,value 属性或 <value> 子节点指定属性值
  • 属性注入是实际应用中最常用的注入方式

(3.3.2).构造器注入

  • 通过构造方法注入Bean 的属性值或依赖的对象,它保证了 Bean 实例在实例化后就可以使用。
  • 构造器注入在 <constructor-arg> 元素里声明属性, <constructor-arg> 中没有 name 属性

(3.3.3).属性值细节

  • 字面值:可用字符串表示的值,可以通过 元素标签或 value 属性进行注入。
  • 基本数据类型及其封装类、String 等类型都可以采取字面值注入的方式
  • 若字面值中包含特殊字符,可以使用 <![CDATA[]]> 把字面值包裹起来。
^]]>

结果:

Car{brand='BMW', corp='
^', price=0.0, maxSpeed=240}

(3.4).Bean之间引用关系

  • 组成应用程序的 Bean 经常需要相互协作以完成应用程序的功能. 要使 Bean 能够相互访问, 就必须在 Bean 配置文件中指定对 Bean 的引用
  • 在 Bean 的配置文件中, 可以通过 <ref> 元素或 ref 属性为 Bean 的属性或构造器参数指定对 Bean 的引用.
  • 也可以在属性或构造器里包含 Bean 的声明, 这样的 Bean 称为内部 Bean

(3.4.1).外部 Bean

创建 Person 类

package Models;public class Person {    private String name;    private int age;    private Car car;    public Person() {    }    /**     * 如果不创建3个构造函数,会报错。Could not resolve matching constructor     * @param name     * @param age     * @param car     */    public Person(String name, int age, Car car) {        this.name = name;        this.age = age;        this.car = car;    }    @Override    public String toString() {        return "Person{" +                "name='" + name + '\'' +                ", age=" + age +                ", car=" + car +                '}';    }}

XML

注意 ref

执行

ApplicationContext context =                new ClassPathXmlApplicationContext("applicationContext.xml");Person person = (Person)context.getBean("Person");        System.out.println(person);//  Person{name='是男是女', age=10, car=Car{brand='BMW', corp='
^', price=0.0, maxSpeed=240}}

(3.4.2).内部 Bean

  • 当 Bean 实例仅仅给一个特定的属性使用时, 可以将其声明为内部 Bean. 内部 Bean 声明直接包含在 <property><constructor-arg> 元素里, 不需要设置任何 id 或 name 属性
  • 内部 Bean 不能使用在任何其他地方
^]]>

(3.4.3).注入参数详解:null 空值和级联属性

  • 可以使用专用的 <null/>元素标签为 Bean 的字符串或其它对象类型的属性注入 null 值 ,或者<null></null>
  • 和 Struts、Hiberante 等框架一样,Spring 支持级联属性的配置。

null空值

级联属性

(3.4.4).集合属性

1).List、数组、Set

  • 在 Spring中可以通过一组内置的 xml 标签(例如: <list>, <set><map>) 来配置集合属性.
  • 配置 java.util.List 类型的属性, 需要指定 <list> 标签, 在标签里包含一些元素. 这些标签可以通过 <value> 指定简单的常量值, 通过 <ref> 指定对其他 Bean 的引用. 通过<bean> 指定内置 Bean 定义. 通过 <null/> 指定空元素. 甚至可以内嵌其他集合.
  • 数组的定义和 List 一样, 都使用 <list>
  • 配置 java.util.Set 需要使用 <set> 标签, 定义元素的方法与 List 一样.

2).Map

  • Java.util.Map 通过 <map> 标签定义, <map> 标签里可以使用多个 <entry> 作为子标签. 每个条目包含一个键和一个值.
  • 必须在 <key> 标签里定义键
  • 因为键和值的类型没有限制, 所以可以自由地为它们指定 <value>, <ref>, <bean><null> 元素.
  • 可以将 Map 的键和值作为 <entry> 的属性定义: 简单常量使用 key 和 value 来定义; Bean 引用通过 key-ref 和 value-ref 属性定义
  • 使用 <props> 定义 java.util.Properties, 该标签使用多个 <prop> 作为子标签. 每个 <prop> 标签必须定义 key 属性.
PersonMap person = (PersonMap)context.getBean("personmap");        System.out.println(person.toString());        //Person{name='周星星', age=50, car={一号车=Car{brand='BMW', corp='
^--', price=0.0, maxSpeed=218}, // 二号车=Car{brand='Audi', corp='Shanghia', price=0.0, maxSpeed=300000}}}

3).Properties

创建一个 DataSource 类

public class DataSource {    private Properties properties;    public Properties getProperties() {        return properties;    }    public void setProperties(Properties properties) {        this.properties = properties;    }}

配置Bean

root
123
jdbc:mysql:///test
com.mysql.jdbc.Driver

调用

DataSource source = context.getBean(DataSource.class);        System.out.println(source.getProperties());        //{jdbcurl=jdbc:mysql:///test, driverClass=com.mysql.jdbc.Driver, user=root, pass=123}

4).使用 util scheme 定义集合

可以使用 util schema 里的集合标签定义独立的集合 Bean. 需要注意的是, 必须在 根元素里添加 util schema 定义

(3.4.5).P 命名空间

为了简化 XML 文件的配置,越来越多的 XML 文件采用属性而非子元素配置信息。

Spring 从 2.5 版本开始引入了一个新的 p 命名空间,可以通过 <bean> 元素属性的方式配置 Bean 的属性。
使用 p 命名空间后,基于 XML 的配置方式将进一步简化

(3.5).XML 配置里的 Bean 自动装配

  • Spring IOC 容器可以自动装配 Bean. 需要做的仅仅是<bean>autowire 属性里指定自动装配的模式
  • byType(根据类型自动装配): 若 IOC 容器中有多个与目标 Bean 类型一致的 Bean. 在这种情况下, Spring 将无法判定哪个 Bean 最合适该属性, 所以不能执行自动装配.
  • byName(根据名称自动装配): 必须将目标 Bean 的名称和属性名设置的完全相同.
  • constructor(通过构造器自动装配): 当 Bean 中存在多个构造器时, 此种自动装配方式将会很复杂. 不推荐使用

缺点:

  • 在 Bean 配置文件里设置 autowire 属性进行自动装配将会装配 Bean 的所有属性. 然而, 若只希望装配个别属性时, autowire 属性就不够灵活了.
  • autowire 属性要么根据类型自动装配, 要么根据名称自动装配, 不能两者兼而有之.

一般情况下,在实际的项目中很少使用自动装配功能,因为和自动装配功能所带来的好处比起来,明确清晰的配置文档更有说服力一些

3.6.bean 之间的关系:继承;依赖

1).继承

  • Spring 允许继承 bean 的配置, 被继承的 bean 称为父 bean. 继承这个父 Bean 的 Bean 称为子 Bean
  • 子 Bean 从父 Bean 中继承配置, 包括 Bean 的属性配置
  • 子 Bean 也可以覆盖从父 Bean 继承过来的配置
  • 父 Bean 可以作为配置模板, 也可以作为 Bean 实例. 若只想把父 Bean 作为模板, 可以设置 的abstract 属性为 true, 这样 Spring 将不会实例化这个 Bean
  • 并不是 <bean> 元素里的所有属性都会被继承. 比如: autowire, abstract 等.
  • 也可以忽略父 Bean 的 class 属性, 让子 Bean 指定自己的类, 而共享相同的属性配置. 但此时 abstract 必须设为 true

parent 继承

结果

Address address  =(Address) context.getBean("address");        System.out.println(address);        address  =(Address) context.getBean("address2");        System.out.println(address);        /*        Address{city='重庆', street='小龙坎'}        Address{city='重庆1', street='小龙坎'}         */

abstract 抽象,作为模板

2).依赖

作用:depends-on用来指定Bean初始化及销毁时的顺序。

  • Spring 允许用户通过 depends-on 属性设定 Bean 前置依赖的Bean,前置依赖的 Bean 会在本 Bean 实例化之前创建好
  • 如果前置依赖于多个 Bean,则可以通过逗号,空格或的方式配置 Bean 的名称

3.7.Bean 的作用域

  • 在 Spring 中, 可以在 <bean> 元素的 scope 属性里设置 Bean 的作用域.
  • 默认情况下, Spring 只为每个在 IOC 容器里声明的 Bean 创建唯一一个实例, 整个 IOC 容器范围内都能共享该实例:所有后续的 getBean() 调用和 Bean 引用都将返回这个唯一的 Bean 实例.该作用域被称为 singleton, 它是所有 Bean 的默认作用域.
类别 说明
singleton 在 SpringIOC 容器中只存在一个实例,Bean以单实例的形式存在
prototype 每次调用 getBean() 返回一个新实例
request 每次HTTP请求,返回一个新Bean,该作用域适用 WebApplicationContext环境
session 同一个HTTP Session共享一个Bean,不同HTTP Session使用不同的Bean。该作用域适用 WebApplicationContext环境

代码

ApplicationContext ctx = new ClassPathXmlApplicationContext("beans-scope.xml");        Car car1= (Car) ctx.getBean("car");        Car car2= (Car) ctx.getBean("car");        System.out.println(car1.equals(car2)); //true

添加 scope="prototype"

返回结果为: System.out.println(car1.equals(car2)); //false

3.8.使用外部属性文件

  • 在配置文件里配置 Bean 时, 有时需要在 Bean 的配置里混入系统部署的细节信息(例如: 文件路径, 数据源配置信息等). 而这些部署细节实际上需要和 Bean 配置相分离
  • Spring 提供了一个 PropertyPlaceholderConfigurer 的 BeanFactory 后置处理器, 这个处理器允许用户将 Bean 配置的部分内容外移到属性文件中. 可以在 Bean 配置文件里使用形式为 ${var} 的变量, PropertyPlaceholderConfigurer 从属性文件里加载属性, 并使用这些属性来替换变量.
  • Spring 还允许在属性文件中使用 ${propName},以实现属性之间的相互引用。

示例:

导入mysql和jdbc的jar包

java

DataSource dataSource = (DataSource) ctx.getBean("datasource");        System.out.println(dataSource.getConnection());        //1763344271, URL=jdbc:mysql://localhost:3306/day22_jdbc?serverTimezone = GMT, UserName=root@localhost, MySQL Connector/J

注册PropertyPlaceholderConfigurer

196558-20180815163111677-1042581481.png

db.properties

drivername=com.mysql.cj.jdbc.Driverurl = jdbc:mysql://localhost:3306/day22_jdbc?serverTimezone = GMTuser = rootpass = 123

bean设置

3.9.SpEL

  • Spring 表达式语言(简称SpEL):是一个支持运行时查询和操作对象图的强大的表达式语言。
  • 语法类似于 EL:SpEL 使用 #{…} 作为定界符,所有在大框号中的字符都将被认为是 SpEL
  • SpEL 为 bean 的属性进行动态赋值提供了便利
  • 通过 SpEL 可以实现:
    • 通过 bean 的 id 对 bean 进行引用
    • 调用方法以及引用对象中的属性
    • 计算表达式的值
    • 正则表达式的匹配
      196558-20180815173552577-1846954395.png
      196558-20180815173604910-1931940328.png
      196558-20180815173613934-1304996796.png
      196558-20180815173639812-685310032.png
      196558-20180815173647250-2818071.png

address.class

package com.cnblogs.tangge.spel;public class Address {        private String  city;        private String street;    public String getCity() {        return city;    }    public void setCity(String city) {        this.city = city;    }    public String getStreet() {        return street;    }    public void setStreet(String street) {        this.street = street;    }    @Override    public String toString() {        return "Address{" +                "city='" + city + '\'' +                ", street='" + street + '\'' +                '}';    }}

car.class

package com.cnblogs.tangge.spel;public class Car {    public Car() {        System.out.println("default constuct...");    }    //品牌    private String brand;    private double price;    //轮胎周长    private double tyrePerimeter;    public void setBrand(String brand) {        this.brand = brand;    }    //#{car.price > 300000 ?'金领':'白领'}的时候,price 这里必须有getter方法。   public double getPrice() {        return price;    }        public void setPrice(double price) {        this.price = price;    }    @Override    public String toString() {        return "Car{" +                "brand='" + brand + '\'' +                ", price=" + price +                ", tyrePerimeter=" + tyrePerimeter +                '}';    }    public void setTyrePerimeter(double tyrePerimeter) {        this.tyrePerimeter = tyrePerimeter;    }}

person.class

package com.cnblogs.tangge.spel;/** * @Description: * @Package: com.cnblogs.tangge.autowire * @ClassName: Person * @Author: tangge * @CreateDate: 2018年08月14 16:39 * @Version: 1.0 **/public class Person {    private String name;    private Car car;    //引用 address bean 的 city 属性    private String city;    //根据car的 price 决定 info:car 的 price >=300000    private String info;    @Override    public String toString() {        return "Person{" +                "name='" + name + '\'' +                ", car=" + car +                ", city='" + city + '\'' +                ", info='" + info + '\'' +                '}';    }    public void setName(String name) {        this.name = name;    }    public void setCar(Car car) {        this.car = car;    }    public void setCity(String city) {        this.city = city;    }    public void setInfo(String info) {        this.info = info;    }}

3.9 Bean 生命周期

3.9.1 init-method 与 destroy-method

  • Spring IOC 容器可以管理 Bean 的生命周期, Spring 允许在 Bean 生命周期的特定点执行定制的任务.
  • Spring IOC 容器对 Bean 的生命周期进行管理的过程:
    1.通过构造器或工厂方法创建 Bean 实例
    2.为 Bean 的属性设置值和对其他 Bean 的引用
    3.调用 Bean 的初始化方法
    4.Bean 可以使用了
    5.当容器关闭时, 调用 Bean 的销毁方法
  • 在 Bean 的声明里设置 init-method 和 destroy-method 属性, 为 Bean 指定初始化和销毁方法.
package com.cnblogs.tangge.cycle;public class Car {private String brand;    public Car() {        System.out.println("Car constructor...");    }    public void setBrand(String brand) {        System.out.println("setBrand..");        this.brand = brand;    }    public void  init(){        System.out.println("init....");    }    public void  destroy(){        System.out.println("destroy....");    }    @Override    public String toString() {        return "Car{" +                "brand='" + brand + '\'' +                '}';    }}

Bean配置

测试

public static void main(String[] args) {        //ApplicationContext 扩展类,支持close()        ConfigurableApplicationContext ctx = new ClassPathXmlApplicationContext("bean-cycle.xml");        Car car = (Car) ctx.getBean("car");        System.out.println(car);        ctx.close();        /*        Car constructor...  //构造器        setBrand..          //setter        init....            //init()        Car{brand='audi'}   //toString()        destroy....         //destroy()         */    }

3.9.2 后置处理器

BeanPostProcessor

  • Bean 后置处理器允许在调用初始化方法前后对 Bean 进行额外的处理.
  • Bean 后置处理器对 IOC 容器里的所有 Bean 实例逐一处理, 而非单一实例. 其典型应用是: 检查 Bean 属性的正确性或根据特定的标准更改 Bean 的属性.
  • 对Bean 后置处理器而言, 需要实现 BeanPostProcessor 接口. 在初始化方法被调用前后, Spring 将把每个 Bean 实例分别传递给上述接口的以下两个方法:
    • Object postProcessBeforeInitialization(Object bean, String beanName)
    • Object postProcessAfterInitialization(Object bean, String beanName)
  • Spring IOC 容器对 Bean 的生命周期进行管理的过程:
    • 通过构造器或工厂方法创建 Bean 实例
    • 为 Bean 的属性设置值和对其他 Bean 的引用
    • 将 Bean 实例传递给 Bean 后置处理器的 postProcessBeforeInitialization 方法
    • 调用 Bean 的初始化方法
    • 将 Bean 实例传递给 Bean 后置处理器的 postProcessAfterInitialization方法
    • Bean 可以使用了
    • 当容器关闭时, 调用 Bean 的销毁方法

新建一个MyBeanPostprocessor.class

package com.cnblogs.tangge.cycle;import org.springframework.beans.BeansException;import org.springframework.beans.factory.config.BeanPostProcessor;public class MyBeanPostprocessor implements BeanPostProcessor {    @Override    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {        System.out.printf("postProcessBeforeInitialization:%s,%s %n",bean,beanName);        if ("car".equals(beanName)){        Car car = new Car();            car.setBrand("BMW");            return car;        }        return bean;    }    @Override    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {        System.out.printf("postProcessAfterInitialization:%s,%s %n",bean,beanName);        return bean;    }}

配置bean

运行

public static void main(String[] args) {        //ApplicationContext 扩展类,支持close()        ConfigurableApplicationContext ctx = new ClassPathXmlApplicationContext("bean-cycle.xml");        Car car = (Car) ctx.getBean("car");        System.out.println(car);        ctx.close();        /**         Car constructor...                                     //1.构造器         setBrand..                                             //2.setter         postProcessBeforeInitialization:Car{brand='audi'},car  //3.postProcessBeforeInitialization         Car constructor...                                      //postProcessBeforeInitialization 先new个car 在stter         setBrand..         init....                                                //4.init         postProcessAfterInitialization:Car{brand='BMW'},car    //5.postProcessAfterInitialization         Car{brand='BMW'}                                        //toString()         destroy....                                             //6.destroy()         */    }

4.其他配置方式(除了反射)

4.1 工厂方法

4.1.1.静态方法 factory-method

调用静态工厂方法创建 Bean是将对象创建的过程封装到静态方法中. 当客户端需要对象时, 只需要简单地调用静态方法, 而不同关心创建对象的细节.

要声明通过静态方法创建的 Bean, 需要在 Bean 的 class 属性里指定拥有该工厂的方法的类, 同时在 factory-method 属性里指定工厂方法的名称. 最后, 使用 <constrctor-arg> 元素为该方法传递方法参数.

package com.cnblogs.tangge.factory;import java.util.HashMap;import java.util.Map;public class staticCarFactory {    private static Map
cars = new HashMap<>(); static { cars.put("audi",new Car("audi",300000)); cars.put("ford",new Car("ford",230000)); } //静态方法 public static Car getCar(String name){ return cars.get(name) ; }}

Bean

constructor-arg:如果工厂方法需要传入参数,则使用constructor-arg来配置参数

这里测试的结果为:Car{brand='ford', price=230000.0}

4.1.2.实例方法 factory-bean

要声明通过实例工厂方法创建的 Bean

  • 在 bean 的 factory-bean 属性里指定拥有该工厂方法的 Bean
  • factory-method 属性里指定该工厂方法的名称
  • 使用 construtor-arg 元素为工厂方法传递方法参数
    InstanceCarFactory.class
package com.cnblogs.tangge.factory;import java.util.HashMap;import java.util.Map;/** * @Description:实例工厂的方法 * **/public class InstanceCarFactory {    private static Map
cars = null; public InstanceCarFactory() { cars = new HashMap<>(); cars.put("audi",new Car("audi",300000)); cars.put("ford",new Car("ford",230000)); } public Car getInstanceCar(String name){ return cars.get(name) ; }}

Bean

调用结果:Car{brand='audi', price=300000.0}

4.2 FactoryBean

  • Spring 中有两种类型的 Bean, 一种是普通Bean, 另一种是工厂Bean, 即FactoryBean.
  • 工厂 Bean 跟普通Bean不同, 其返回的对象不是指定类的一个实例, 其返回的是该工厂 Bean 的 getObject 方法所返回的对象
    196558-20180816114934769-1286044687.png
    FactoryBean接口定义
package org.springframework.beans.factory;public interface FactoryBean
{ T getObject() throws Exception; Class
getObjectType(); boolean isSingleton();}

FactoryBean 通常是用来创建比较复杂的bean,一般的bean 直接用xml配置即可,但如果一个bean的创建过程中涉及到很多其他的bean 和复杂的逻辑,用xml配置比较困难,这时可以考虑用FactoryBean。

创建我的FactoryBean

package com.cnblogs.tangge.factoryBean;import org.springframework.beans.factory.FactoryBean;public class MyFactoryBean implements FactoryBean
{ private String brand; public String getBrand() { return brand; } public void setBrand(String brand) { this.brand = brand; } /** * * @return 返回bean的对象 * @throws Exception */ @Override public Car getObject() throws Exception { return new Car(brand,500000); } /** * * @return bean的类型 */ @Override public Class
getObjectType() { return Car.class; } /** * * @return 是否为单例 */ @Override public boolean isSingleton() { return false; }}

Bean

测试结果:Car{brand='BMW', price=500000.0}

5.其他配置形式(除了基于XML)

5.1.注解

  • 在classPath中扫描组件,侦测和实例化具有特定注解的组件
  • 特定组件包括:
    • @Component: 基本注解, 标识了一个受 Spring 管理的组件
    • @Respository: 标识持久层组件
    • @Service: 标识服务层(业务层)组件
    • @Controller: 标识表现层组件
      Spring 有默认的命名策略: 使用非限定类名, 第一个字母小写. 也可以在注解中通过 value 属性值标识组件的名称
  • 当在组件类上使用了特定的注解之后, 还需要在 Spring 的配置文件中声明 <context:component-scan>
    • base-package 属性指定一个需要扫描的基类包,Spring 容器将会扫描这个基类包里及其子包中的所有类.
    • 当需要扫描多个包时, 可以使用逗号分隔.
    • 如果仅希望扫描特定的类而非基包下的所有类,可使用 resource-pattern 属性过滤特定的类,示例:
      196558-20180816145113351-228555805.png
    • <context:include-filter> 子节点表示要包含的目标类
    • <context:exclude-filter> 子节点表示要排除在外的目标类
    • context:component-scan 下可以拥有若干个 context:include-filter 和 context:exclude-filter 子节点

context:include-filter 和 context:exclude-filter 子节点支持多种类型的过滤表达式:

Filter Type 示例 描述
annotation org.example.SomeAnnotation 符合SomeAnnoation的target class
assignable org.example.SomeClass 指定class或interface的全名
aspectj org.example..*Service+ AspectJ語法
regex org.example.Default.* Regelar Expression
custom org.example.MyTypeFilter Spring3新增自訂Type,實作org.springframework.core.type.TypeFilter

196558-20180816152208125-1895083330.png

TestObject.class

package com.cnblogs.tangge.annoncation;import org.springframework.stereotype.Component;@Componentpublic class TestObject {}

UserController.class

package com.cnblogs.tangge.annoncation.Controller;import org.springframework.stereotype.Controller;@Controllerpublic class UserController {        public void execute(){            System.out.println("UserController execute...");        }}

UserService.class

package com.cnblogs.tangge.annoncation.Service;import org.springframework.stereotype.Service;@Servicepublic class UserService {    public void  add(){        System.out.println("UserService add...");    }}

接口 UserRepository.class

package com.cnblogs.tangge.annoncation.Repository;public interface UserRepository {    void save();}

UserRepositoryImp.class

package com.cnblogs.tangge.annoncation.Repository;import org.springframework.stereotype.Repository;@Repository("userRepository")public class UserRepositoryImp implements UserRepository {    @Override    public void save() {        System.out.println("UserRepository save..");    }}

bean-annocation.xml

测试

public static void main(String[] args) {        ApplicationContext ctx = new ClassPathXmlApplicationContext("bean-annocation.xml");        TestObject test = (TestObject) ctx.getBean("testObject");        System.out.println(test);        UserController controller = (UserController) ctx.getBean("userController");        System.out.println(controller);        UserService service = (UserService) ctx.getBean("userService");        System.out.println(service);        UserRepository repository = (UserRepository) ctx.getBean("userRepository");        System.out.println(repository);    }        /*    com.cnblogs.tangge.annoncation.TestObject@1990a65e    com.cnblogs.tangge.annoncation.Controller.UserController@64485a47    com.cnblogs.tangge.annoncation.Service.UserService@25bbf683    com.cnblogs.tangge.annoncation.Repository.UserRepositoryImp@6ec8211c     */

5.1.1 resource-pattern

如果仅希望扫描特定的类而非基包下的所有类,可使用 resource-pattern 属性过滤特定的类

报错:No bean named 'testObject' available

只能扫描 UserRepository

//        TestObject test = (TestObject) ctx.getBean("testObject");//        System.out.println(test);//        UserController controller = (UserController) ctx.getBean("userController");//        System.out.println(controller);//        UserService service = (UserService) ctx.getBean("userService");//        System.out.println(service);        UserRepository repository = (UserRepository) ctx.getBean("userRepository");        System.out.println(repository);

5.1.2 <context:exclude-filter>

执行

public static void main(String[] args) {        ApplicationContext ctx = new ClassPathXmlApplicationContext("bean-annocation.xml");        TestObject test = (TestObject) ctx.getBean("testObject");        System.out.println(test);        UserController controller = (UserController) ctx.getBean("userController");        System.out.println(controller);        UserService service = (UserService) ctx.getBean("userService");        System.out.println(service);        UserRepository repository = (UserRepository) ctx.getBean("userRepository");        System.out.println(repository);    }    /*    com.cnblogs.tangge.annoncation.TestObject@1990a65e    com.cnblogs.tangge.annoncation.Controller.UserController@64485a47    com.cnblogs.tangge.annoncation.Service.UserService@25bbf683    Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'userRepository' available    'userRepository' 不可用     */

5.1.3 <context:include-filter>

这里执行,直接失败,因为只能实例 userRepository,其他都失败。

5.1.4 type="assignable"

指定class或interface的全名

和上面效果一样,只能实例化 userRepository。

5.1.5 组件装配

<context:component-scan> 元素还会自动注册 AutowiredAnnotationBeanPostProcessor 实例, 该实例可以自动装配具有 @Autowired@Resource@Inject注解的属性.

@Autowired

  • 构造器, 普通字段(即使是非 public), 一切具有参数的方法都可以应用@Authwired 注解
    Bean设置

UserController.class

调用 userService.add();

package com.cnblogs.tangge.annoncation.Controller;import com.cnblogs.tangge.annoncation.Service.UserService;import org.springframework.stereotype.Controller;@Controllerpublic class UserController {    private UserService userService;        public void execute(){            System.out.println("UserController execute...");            userService.add();        }}

UserService.class

调用 userRepository.save();

package com.cnblogs.tangge.annoncation.Service;import com.cnblogs.tangge.annoncation.Repository.UserRepository;import org.springframework.stereotype.Service;@Servicepublic class UserService {    private UserRepository userRepository;    public void  add(){        System.out.println("UserService add...");        userRepository.save();    }}

调用

public static void main(String[] args) {        ApplicationContext ctx = new ClassPathXmlApplicationContext("bean-annocation.xml");        UserController controller = (UserController) ctx.getBean("userController");        controller.execute();    }

执行结果:

UserController execute...    at com.cnblogs.tangge.annoncation.Controller.UserController.execute(UserController.java:13)    at com.cnblogs.tangge.annoncation.annocationMain.main(annocationMain.java:29)

我们UserController在添加@Autowired

@Controllerpublic class UserController {    @Autowired    private UserService userService;        public void execute(){            System.out.println("UserController execute...");            userService.add();        }}

也在UserService添加@Autowired

@Servicepublic class UserService {    @Autowired    private UserRepository userRepository;    public void  add(){        System.out.println("UserService add...");        userRepository.save();    }}

执行结果:

UserController execute...
UserService add...
UserRepository save..

  • 默认情况下, 所有使用 @Authwired 注解的属性都需要被设置. 当 Spring 找不到匹配的 Bean 装配属性时, 会抛出异常, 若某一属性允许不被设置, 可以设置 @Authwired 注解的 required 属性为 false
    现在修改UserRepositoryImp
package com.cnblogs.tangge.annoncation.Repository;import com.cnblogs.tangge.annoncation.TestObject;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Repository;@Repository("userRepository")public class UserRepositoryImp implements UserRepository {    @Autowired    TestObject testObject;    @Override    public void save() {        System.out.println("UserRepository save..");        System.out.println(testObject);    }}

然后把TestObject的@Component 去掉

package com.cnblogs.tangge.annoncation;public class TestObject {}

报错:

DependencyException: Error creating bean with name 'userRepository': Unsatisfied dependency expressed through field 'testObject';

修改 @Autowired(required = false) 不检查 TestObject

@Repository("userRepository")public class UserRepositoryImp implements UserRepository {    @Autowired(required = false)    TestObject testObject;    @Override    public void save() {        System.out.println("UserRepository save..");        System.out.println(testObject);    }}

执行结果:

UserController execute...
UserService add...
UserRepository save..
null

  • 默认情况下, 当 IOC 容器里存在多个类型兼容的 Bean 时, 通过类型的自动装配将无法工作. 此时可以在 @Qualifier 注解里提供 Bean 的名称. Spring 允许对方法的入参标注 @Qualifiter 已指定注入 Bean 的名称

UserRepositoryImp修改,去掉命名@Repository,使用默认

/@Repository("userRepository")@Repositorypublic class UserRepositoryImp implements UserRepository {    @Autowired(required = false)    TestObject testObject;    @Override    public void save() {        System.out.println("UserRepository save..");        System.out.println(testObject);    }}

创建第2个Repository,UserjdbcRrpository.class

package com.cnblogs.tangge.annoncation.Repository;import org.springframework.stereotype.Repository;@Repositorypublic class UserjdbcRrpository implements UserRepository {    @Override    public void save() {        System.out.println("UserjdbcRrpository save...");    }}

执行结果:

报错:expected single matching bean but found 2: userjdbcRrpository,userRepositoryImp

下面,修改UserService,添加 @Qualifier,指定注入的Bean名称

@Servicepublic class UserService {    @Autowired    @Qualifier("userjdbcRrpository")    private UserRepository userRepository;    public void  add(){        System.out.println("UserService add...");        userRepository.save();    }}

执行结果:

UserController execute...
UserService add...
UserjdbcRrpository save...

实际上,@Qualifier("userjdbcRrpository")还可以写到形参前面,

@Servicepublic class UserService {    private UserRepository userRepository;        @Autowired    public void setUserRepository(@Qualifier("userjdbcRrpository") UserRepository userRepository) {        this.userRepository = userRepository;    }    public void  add(){        System.out.println("UserService add...");        userRepository.save();    }}
  • @Authwired 注解也可以应用在数组类型的属性上, 此时 Spring 将会把所有匹配的 Bean 进行自动装配.
  • @Authwired 注解也可以应用在集合属性上, 此时 Spring 读取该集合的类型信息, 然后自动装配所有与之兼容的 Bean.
  • @Authwired 注解用在 java.util.Map 上时, 若该 Map 的键值为 String, 那么 Spring 将自动装配与之 Map 值类型兼容的 Bean, 此时 Bean 的名称作为键值

6.Spring4.x:泛型依赖注入

Spring 4.x 中可以为子类注入子类对应的泛型类型的成员变量的引用

196558-20180817120542673-1556470662.png

7.Spring AOP

  • AOP(Aspect-Oriented Programming, 面向切面编程): 是一种新的方法论, 是对传统 OOP(Object-Oriented - Programming, 面向对象编程) 的补充.
  • AOP 的主要编程对象是切面(aspect), 而切面模块化横切关注点.
  • 在应用 AOP 编程时, 仍然需要定义公共功能, 但可以明确的定义这个功能在哪里, 以什么方式应用, 并且不必修改受影响的类. 这样一来横切关注点就被模块化到特殊的对象(切面)里.
  • AOP 的好处:
    • 每个事物逻辑位于一个位置, 代码不分散, 便于维护和升级
    • 业务模块更简洁, 只包含核心业务代码.
      196558-20180817150923073-231420169.png
  • 切面(Aspect): 横切关注点(跨越应用程序多个模块的功能)被模块化的特殊对象
  • 通知(Advice): 切面必须要完成的工作
  • 目标(Target): 被通知的对象
  • 代理(Proxy): 向目标对象应用通知之后创建的对象
  • 连接点(Joinpoint):程序执行的某个特定位置:如类某个方法调用前、调用后、方法抛出异常后等。连接点由两个信息确定:方法表示的程序执行点;相对点表示的方位。例如 ArithmethicCalculator#add() 方法执行前的连接点,执行点为 ArithmethicCalculator#add(); 方位为该方法执行前的位置
  • 切点(pointcut):每个类都拥有多个连接点:例如 ArithmethicCalculator 的所有方法实际上都是连接点,即连接点是程序类中客观存在的事务。AOP 通过切点定位到特定的连接点。类比:连接点相当于数据库中的记录,切点相当于查询条件。切点和连接点不是一对一的关系,一个切点匹配多个连接点,切点通过 org.springframework.aop.Pointcut 接口进行描述,它使用类和方法作为连接点的查询条件。

7.1 AspectJ 注解

AspectJ:Java 社区里最完整最流行的 AOP 框架.

在 Spring2.0 以上版本中, 可以使用基于 AspectJ 注解或基于 XML 配置的 AOP

1、aspectjweaver.jar 下载地址:

2、下载
spring-aop-4.3.18.RELEASE.jar
spring-aspects-4.3.18.RELEASE.jar

  • 要在 Spring 应用中使用 AspectJ 注解, 必须在 classpath 下包含 AspectJ 类库: aopalliance.jar、aspectj.weaver.jar 和 spring-aspects.jar
  • 将 aop Schema 添加到 <beans> 根元素中.
  • 要在 Spring IOC 容器中启用 AspectJ 注解支持, 只要在 Bean 配置文件中定义一个空的 XML 元素 <aop:aspectj-autoproxy>
  • 当 Spring IOC 容器侦测到 Bean 配置文件中的 <aop:aspectj-autoproxy> 元素时, 会自动为与 AspectJ 切面匹配的 Bean 创建代理.
  • 用 AspectJ 注解声明切面

  • 要在 Spring 中声明 AspectJ 切面, 只需要在 IOC 容器中将切面声明为 Bean 实例. 当在 Spring IOC 容器中初始化 AspectJ 切面之后, Spring IOC 容器就会为那些与 AspectJ 切面相匹配的 Bean 创建代理.
  • 在 AspectJ 注解中, 切面只是一个带有 @Aspect 注解的 Java 类.
  • 通知是标注有某种注解的简单的 Java 方法.
  • AspectJ 支持 5 种类型的通知注解:
    • @Before: 前置通知, 在方法执行之前执行
    • @After: 后置通知, 在方法执行之后执行
    • @AfterRunning: 返回通知, 在方法返回结果之后执行
    • @AfterThrowing: 异常通知, 在方法抛出异常之后
    • @Around: 环绕通知, 围绕着方法执行

7.1.1.前置通知

  1. <aop:aspectj-autoproxy/>
  2. 横向关注点抽象到切面类中
    • 加入@Component
    • @Aspect 注解
  3. 各种通知

bean

接口ArithmeticCalculator

package com.cnblogs.tangge.spring.aop.impl;public interface ArithmeticCalculator {        int add(int i, int j);        int sub(int i, int j);        int mul(int i, int j);        int div(int i, int j);}

实现类ArithmeticCalculatorImpl

package com.cnblogs.tangge.spring.aop.impl;import org.springframework.stereotype.Component;@Componentpublic class ArithmeticCalculatorImpl implements ArithmeticCalculator {    @Override    public int add(int i, int j) {        int result = i+j;        return result;    }    @Override    public int sub(int i, int j) {        int result = i-j;        return result;    }    @Override    public int mul(int i, int j) {        int result = i*j;        return result;    }    @Override    public int div(int i, int j) {        int result = i/j;        return result;    }}

创建一个AOP

package com.cnblogs.tangge.spring.aop.impl;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Before;import org.springframework.stereotype.Component;//把这个类声明为一个切面:需要把该类放入IOC容器中。再声明为一个切面@Aspect@Componentpublic class LogAspect {    //声明一个前置通知:在目标方法开始之前    @Before("execution(public int com.cnblogs.tangge.spring.aop.impl.ArithmeticCalculator.add(int,int))")    public void beforeMethod() {        System.out.println("the method add begins");    }    public void afterMethod() {        System.out.println("sub result:");    }}

测试

public class demo {    public static void main(String[] args) {        //1.创建Spring 的IOC容器        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");        //2.从IOC 容器获取 bean        ArithmeticCalculator arithmeticCalculator = ctx.getBean(ArithmeticCalculator.class);        //3.使用bean        int result = arithmeticCalculator.add(1,3);        System.out.println("result:"+result);        /*         the mothod add begins         result:4         */    }}

7.1.2 后置通知,返回通知,异常通知

package com.cnblogs.tangge.spring.aop.impl;import java.util.Arrays;import java.util.List;import org.aspectj.lang.JoinPoint;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.annotation.After;import org.aspectj.lang.annotation.AfterReturning;import org.aspectj.lang.annotation.AfterThrowing;import org.aspectj.lang.annotation.Around;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Before;import org.springframework.stereotype.Component;//把这个类声明为一个切面:需要把该类放入IOC容器中。再声明为一个切面@Aspect@Componentpublic class LogAspect {    //声明一个前置通知:在目标方法开始之前    @Before("execution(public int com.cnblogs.tangge.spring.aop.impl.ArithmeticCalculator.*(int,int))")    public void beforeMethod(JoinPoint joinPoint) {        String methodName = joinPoint.getSignature().getName();        //Arrays.asList(T... a) 返回由指定数组支持的固定大小的列表。        List args  = Arrays.asList(joinPoint.getArgs());        System.out.println("前置通知@Before:the method "+methodName+"  begins"+args);    }    /**     * 方法执行后,执行的代码     * @param joinPoint     */    @After("execution(public int com.cnblogs.tangge.spring.aop.impl.ArithmeticCalculator.*(..))")    public void afterMethod(JoinPoint joinPoint) {        String methodName = joinPoint.getSignature().getName();        System.out.println("后置通知@After:the method "+methodName+"  end..");    }    /**     * 返回通知, 在方法返回结果之后执行     * @param joinPoint     * @param result1 可以访问方法返回值     */    @AfterReturning(value = "execution(public int com.cnblogs.tangge.spring.aop.impl.ArithmeticCalculator.*(..))",returning = "result1")    public void afterReturningMethod(JoinPoint joinPoint,Object result1) {        String methodName = joinPoint.getSignature().getName();        System.out.println("返回通知@AfterReturning:the method "+methodName+"  AfterReturning..The result with:"+result1);    }    /**     * 异常通知,可以访问异常对象,且可以指定出现特定异常时在执行通知代码     * @param joinPoint     * @param exception     */    @AfterThrowing(value = "execution(public int com.cnblogs.tangge.spring.aop.impl.ArithmeticCalculator.*(..))",            throwing = "exception")    public void afterThrowingMethod(JoinPoint joinPoint,Exception exception) {        String methodName = joinPoint.getSignature().getName();        System.out.println("异常通知@AfterReturning:the method "+methodName+"  throwing exception:"+exception);    }}

执行结果:

前置通知@Before:the method add begins[1, 3]

后置通知@After:the method add end..
返回通知@AfterReturning:the method add AfterReturning..The result with:4
result:4
前置通知@Before:the method div begins[10, 2]
后置通知@After:the method div end..
返回通知@AfterReturning:the method div AfterReturning..The result with:5
result:5

7.1.3 环绕通知

/**     * 环绕通知     * @param joinPoint     */    @Around(value = "execution(public int com.cnblogs.tangge.spring.aop.impl.ArithmeticCalculator.*(..))")    public Object AroundMethod(ProceedingJoinPoint joinPoint) {        String methodName = joinPoint.getSignature().getName();        System.out.println("-----进入环绕通知-----");        //执行目标方法        Object result = null;        try {            System.out.println("前置通知@Before:the method "+methodName+"  begins"+ Arrays.asList(joinPoint.getArgs()));            //执行目标方法            result = joinPoint.proceed();            System.out.println("返回通知@AfterReturning:the method "+methodName+"  AfterReturning..The result with:"+result);        } catch (Throwable throwable) {            System.out.println("异常通知@AfterReturning:the method "+methodName+"  throwing exception:"+throwable.toString());            throwable.printStackTrace();        }        System.out.println("后置通知@After:the method "+methodName+"  end..");        return result;    }

执行结果:

-----进入环绕通知-----

前置通知@Before:the method add begins[1, 3]
返回通知@AfterReturning:the method add AfterReturning..The result with:4
后置通知@After:the method add end..
result:4
-----进入环绕通知-----
前置通知@Before:the method div begins[10, 2]
返回通知@AfterReturning:the method div AfterReturning..The result with:5
后置通知@After:the method div end..
result:5

7.1.3 优先级 @Order

切面的优先级可以通过实现 Ordered 接口或利用 @Order 注解指定.

实现 Ordered 接口, getOrder() 方法的返回值越小, 优先级越高.
若使用 @Order 注解, 序号出现在注解中

196558-20180820111629664-1896591591.png

7.1.4 重用切入点 @Pointcut

/**         * @Pointcut 声明切入点表达式         * 其他通知直接使用方法名引用当前的切入点      */    @Pointcut("execution(public int com.cnblogs.tangge.spring.aop.impl.ArithmeticCalculator.*(..))")        public void declarexecution() {}

其他方法通过 @Pointcut 注解将一个切入点声明成简单的方法. 切入点的方法体通常是空的, 因为将切入点定义与应用程序逻辑混在一起是不合理的.

/**     * 环绕通知     * @param joinPoint     */    @Around(value = "declarexecution()")    public Object AroundMethod(ProceedingJoinPoint joinPoint) {    ...    }

7.2 在 XML 配置的 AOP

  • 除了使用 AspectJ 注解声明切面, Spring 也支持在 Bean 配置文件中声明切面. 这种声明是通过 aop schema 中的 XML 元素完成的.
  • 正常情况下, 基于注解的声明要优先于基于 XML 的声明. 通过 AspectJ 注解, 切面可以与 AspectJ 兼容, 而基于 XML 的配置则是 Spring 专有的. 由于 AspectJ 得到越来越多的 AOP 框架支持, 所以以注解风格编写的切面将会有更多重用的机会.

196558-20180820121621924-878025464.png

8 JdbcTemplate

  • 每次使用都创建一个 JdbcTemplate 的新实例, 这种做法效率很低下.
  • JdbcTemplate 类被设计成为线程安全的, 所以可以再 IOC 容器中声明它的单个实例, 并将这个实例注入到所有的 DAO 实例中.
  • JdbcTemplate 也利用了 Java 1.5 的特定(自动装箱, 泛型, 可变长度等)来简化开发
  • Spring JDBC 框架还提供了一个 JdbcDaoSupport 类来简化 DAO 实现. 该类声明了 jdbcTemplate 属性, 它可以从 IOC 容器中注入, 或者自动从数据源中创建.

db.properties

drivername=com.mysql.cj.jdbc.Driverurl = jdbc:mysql://localhost:4040/day22_jdbc?serverTimezone = GMTuser = rootpass = 123

Bean配置 applicationContext.xml

下面进行测试

package com.cnblogs.tangge.jdbc;import java.sql.SQLException;import java.util.ArrayList;import java.util.List;import javax.sql.DataSource;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;import org.springframework.jdbc.core.BeanPropertyRowMapper;import org.springframework.jdbc.core.JdbcTemplate;import org.springframework.jdbc.core.RowMapper;public class JDBCTest {    ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");    JdbcTemplate jdbcTemplate = (JdbcTemplate) ctx.getBean("jdbcTemplate");    public static void main(String[] args) {        JDBCTest test = new JDBCTest();        try {            test.UpdateTemplate();        } catch (Exception e) {            e.printStackTrace();        }    }    /**     * 更新:insert,update,delete     */    public void UpdateTemplate() {        String sql = "UPDATE `day22_jdbc`.`sort` SET  `sname`=? WHERE (`sid`=?);\n ";        jdbcTemplate.update(sql, "什么球", 5);    }    /**     * 批量更新:insert,update,delete     */    public void InsertTemplate() {        String sql = "insert into sort(`sname`, `sprice`, `sdesc`) VALUES (?,?,?) ";        List
batchList = new ArrayList<>(); batchList.add(new Object[]{"棒球", 36.22, "体育用品"}); batchList.add(new Object[]{"冰箱", 1466.99, "加点用品"}); jdbcTemplate.batchUpdate(sql, batchList); } /** * 读取一条数据,得到对应的对象 * 注意不是调用 queryForObject(String sql, Class
requiredType, Object... args) * 需要: queryForObject(String sql, RowMapper
rowMapper, Object... args) * 1.RowMapper
指定如何去映射行结果集的行,常用实现类 BeanPropertyRowMapper * 2.不支持级联属性 JdbcTemplate 是一个小工具,不是 ORM 框架 */ public void testQueryForObject() { String sql = "select * from employee where id = ?"; //Employee employee =jdbcTemplate.queryForObject(sql,Employee.class,1); RowMapper
rowMapper = new BeanPropertyRowMapper<>(Employee.class); Employee employee = jdbcTemplate.queryForObject(sql, rowMapper, 1); System.out.println(employee); //结果:Employee{id=1, lastName='sansan', firstName='tang', email='fenm@qq.com'} } /** * 读取一条数据,得到对应的对象 * 配置Dao,调用Dao封装方法 */ public void testQueryForObjectByDao() { //EmployeeDao dao = new EmployeeDao(); //这里需要bean配置,直接new是傻了,EmployeeDao类上 @Repository自动实例 EmployeeDao dao = ctx.getBean(EmployeeDao.class); Employee employee = dao.getEmployee(1); System.out.println(employee); //结果:Employee{id=1, lastName='sansan', firstName='tang', email='fenm@qq.com'} } /** * 查询:实体类集合 * 不是调用的 queryForList */ public void TestQueryForList() { String sql = "select * from employee"; RowMapper
rowMapper = new BeanPropertyRowMapper<>(Employee.class); List
employee = jdbcTemplate.query(sql, rowMapper); System.out.println(employee); //[Employee{id=1, lastName='sansan', firstName='tang', email='fenm@qq.com'}, // Employee{id=2, lastName='li', firstName='xiao', email='denm@qq.com'}] } /** * 查询:单个的值 */ public void testQueryForOne() { String sql = "select count(*) from employee"; Long count = jdbcTemplate.queryForObject(sql, Long.class); System.out.println(count); //2 } public void TestConnection() throws SQLException { DataSource dataSource = (DataSource) ctx.getBean("datasource"); System.out.println(dataSource.getConnection()); }}

Dao封装方法

@Repositorypublic class EmployeeDao {    @Autowired    private  JdbcTemplate jdbcTemplate;    public  Employee getEmployee(int id) {        String sql = "select * from employee where id = ?";        RowMapper
rowMapper = new BeanPropertyRowMapper<>(Employee.class); Employee employee =jdbcTemplate.queryForObject(sql,rowMapper,id); return employee; }}

调用

/**     * 读取一条数据,得到对应的对象     * 配置Dao,调用Dao     */    public void testQueryForObjectByDao() {        //EmployeeDao dao = new EmployeeDao();        //这里需要bean配置,直接new是傻了,EmployeeDao类上 @Repository自动实例        EmployeeDao dao = ctx.getBean(EmployeeDao.class);        Employee employee = dao.getEmployee(1);        System.out.println(employee);        //结果:Employee{id=1, lastName='sansan', firstName='tang', email='fenm@qq.com'}    }

8.1 在JDBC使用具名参数 NamedParameterJdbcTemplate

  • 在 SQL 语句中使用具名参数时, 可以在一个 Map 中提供参数值, 参数名为键
  • 也可以使用 SqlParameterSource 参数
  • 批量更新时可以提供 Map 或 SqlParameterSource 的数组

bean

实现

NamedParameterJdbcTemplate namedParameterJdbcTemplate = ctx.getBean(NamedParameterJdbcTemplate.class); /**     * update(String sql, Map
paramMap) * Map
paramMap 可以为参数起名字。 * 1. 优点:若多个参数,不用对应位置,直接对应参数名 * 2. 缺点:麻烦。 */ public void testNamedParameterJdbcTemplate() { String sql = "insert into employee(`last_name`, `first_name`, `email`) VALUES (:ln,:fn,:email) "; Map
paramMap = new HashMap<>(); paramMap.put("ln","ww"); paramMap.put("fn","cc"); paramMap.put("email","wccw@qq.com"); namedParameterJdbcTemplate.update(sql,paramMap); } /** * 具名参数可以使用 update(String sql, SqlParameterSource paramSource) * 1.SQL 语句中参数名和类属性一致 * 2.使用 SqlParameterSource 的 BeanPropertySqlParameterSource 实现类作为参数, * (:字段)具有setter与getter方法。 */ public void testNamedParameterJdbcTemplateWithParamSource() { String sql = "insert into employee(`last_name`, `first_name`, `email`) VALUES (:lastName,:firstName,:email) "; Employee employee = new Employee(); employee.setLastName("XYZ"); employee.setFirstName("XYZ"); employee.setEmail("XYZ@qq.com"); SqlParameterSource source = new BeanPropertySqlParameterSource(employee); namedParameterJdbcTemplate.update(sql,source); }

9 事务

事务的四个关键属性(ACID)

  • 原子性(atomicity): 事务是一个原子操作, 由一系列动作组成. 事务的原子性确保动作要么全部完成要么完全不起作用.
  • 一致性(consistency): 一旦所有事务动作完成, 事务就被提交. 数据和资源就处于一种满足业务规则的一致性状态中.
  • 隔离性(isolation): 可能有许多事务会同时处理相同的数据, 因此每个事物都应该与其他事务隔离开来, 防止数据损坏.
  • 持久性(durability): 一旦事务完成, 无论发生什么系统错误, 它的结果都不应该受到影响. 通常情况下, 事务的结果被写到持久化存储器中

196558-20180821110613027-725345551.png

196558-20180821110553968-1952603682.png

Repository层

public interface BookShopDao {    //根据书号找单价    public int findBookPriceByIsbn(String isbn);    //更新书的库存(书号对应的库存-1)    public boolean updateBookStock(String isbn);    //更新用户账户余额:username的 blance -price    public boolean updateUserAccount(String username,int price);}

实现类 BookShopDao

@Repositorypublic class BookShopDaoImpl implements BookShopDao {    @Autowired    private JdbcTemplate jdbcTemplate;    @Override    public int findBookPriceByIsbn(String isbn) {        String sql = "select price from book where isbn = ?";        return jdbcTemplate.queryForObject(sql, Integer.class, isbn);    }   @Override    public boolean updateBookStock(String isbn) {        //检查库存是否不够,若不够则抛出异常        String sql2 = "select stock from book_stock where isbn = ?";        int stock = jdbcTemplate.queryForObject(sql2, Integer.class, isbn);        if (stock == 0) {            throw new BookShopException("库存不足");        } else {            String sql = "update book_stock set stock = stock-1 where isbn = ?";            return (jdbcTemplate.update(sql, isbn)) > 0;        }    }    @Override    public boolean updateUserAccount(String username, int price) {        //验证余额不足        String sql2 = "select balance from account where username = ?";        int balance = jdbcTemplate.queryForObject(sql2, Integer.class, username);        if (balance < price) {            throw new AccountException("余额不足");        } else {            String sql = "update account set balance = balance-? where username = ?";            return (jdbcTemplate.update(sql, price, username)) > 0;        }    }}

service层

public interface BookShopService {    //某人买一本书    boolean purchase(String isbn, String username);}

service实现

@Servicepublic class BookShopServiceImpl implements BookShopService {    @Autowired    private BookShopDao bookShopDao;    @Override    public boolean purchase(String isbn, String username) {        //1.获取书单价        int price = bookShopDao.findBookPriceByIsbn(isbn);        //2.更新书的库存        bookShopDao.updateBookStock(isbn);        //3.更新用户余额        return bookShopDao.updateUserAccount(username, price);    }}

demo实现(没有事务的情况下,出现只扣库存的操作)

public class TransactionDemo {    static  ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");    static BookShopService bookShopService  = ctx.getBean(BookShopService.class);    public static void main(String[] args) {        testpurchase();    }    public static void testpurchase(){        System.out.println(bookShopService.purchase("0001","Tom"));    }    //余额不足}

为了方便查看是哪个方法的错误,实现了2个自定义异常,重写RuntimeException方法

public class AccountException extends RuntimeException{...}public class BookShopException extends RuntimeException{...}

9.1 声明式事务 @Transactional

  • 编程式事务管理: 将事务管理代码嵌入到业务方法中来控制事务的提交和回滚. 在编程式管理事务时, 必须在每个事务操作中包含额外的事务管理代码.
  • 声明式事务管理: 大多数情况下比编程式事务管理更好用. 它将事务管理代码从业务方法中分离出来, 以声明的方式来实现事务管理. 事务管理作为一种横切关注点, 可以通过 AOP 方法模块化. Spring 通过 Spring AOP 框架支持声明式事务管理.

bean

在实现类添加@Transactional

@Servicepublic class BookShopServiceImpl implements BookShopService {    @Autowired    private BookShopDao bookShopDao;    //添加事务注解    @Transactional    @Override    public boolean purchase(String isbn, String username) {        //1.获取书单价        int price = bookShopDao.findBookPriceByIsbn(isbn);        //2.更新书的库存        bookShopDao.updateBookStock(isbn);        //3.更新用户余额        return bookShopDao.updateUserAccount(username, price);    }}

疑难:Spring @transaction不起作用,Spring事物注意事项

  1. 在需要事务管理的地方加@Transactional 注解。@Transactional 注解可以被应用于接口定义和接口方法、类定义和类的 public 方法上 。

  2. @Transactional 注解只能应用到 public 可见度的方法上 。 如果你在 protected、private 或者 package-visible 的方法上使用 @Transactional 注解,它也不会报错, 但是这个被注解的方法将不会展示已配置的事务设置。

  3. 注意仅仅 @Transactional 注解的出现不足于开启事务行为,它仅仅 是一种元数据。必须在配置文件中使用配置元素,才真正开启了事务行为。

重点事项:

Spring事物是基于类和接口的(通俗理解即:在调用的时候不能再同一个类里面被调用,必须调用外面的类去做事物操作)

Spring的事物必须是可见的(即:定义的方法必须是public的)

9.1.1 事务传播行为 REQUIRES_NEW

196558-20180821140806080-1785252282.png

定义消费接口

public interface CashierService {    public boolean checkout(String username,List
isbns);}

实现

@Servicepublic class CashierServiceImp implements CashierService {    @Autowired    private BookShopService bookShopService;    @Transactional    @Override    public boolean checkout(String username, List
isbns) { try { for (String isbn : isbns) { bookShopService.purchase(isbn,username); } return true; } catch (Exception e) { e.printStackTrace(); return false; } }}

测试

static CashierService cashierService = ctx.getBean(CashierService.class); public static void main(String[] args) {        tesCashier();    } public static void tesCashier() {        List
list = Arrays.asList("0001", "0002"); System.out.println(cashierService.checkout("Tom",list)); }

196558-20180821151351780-1198637406.png

默认传播模式 propagation = Propagation.REQUIRES
如果同时买0001与0002的价格不够,就不能购买成功。
196558-20180821151931480-1430706348.png

196558-20180821151519975-1557897230.png

现在该为 REQUIRES_NEW 模式:
只要能购买成功0001商品,就能成功,后面不能购买时,报错。
相当于purchase分为一个小的事务。
196558-20180821152121950-397700223.png

9.1.2 属性(隔离级别&回滚&只读&过期)

并发导致的问题

  • 当同一个应用程序或者不同应用程序中的多个事务在同一个数据集上并发执行时, 可能会出现许多意外的问题
  • 并发事务所导致的问题可以分为下面三种类型:
    • 脏读: 对于两个事物 T1, T2, T1 读取了已经被 T2 更新但 还没有被提交的字段. 之后, 若 T2 回滚, T1读取的内容就是临时且无效的.
    • 不可重复读:对于两个事物 T1, T2, T1 读取了一个字段, 然后 T2 更新了该字段. 之后, T1再次读取同一个字段, 值就不同了.
    • 幻读:对于两个事物 T1, T2, T1 从一个表中读取了一个字段, 然后 T2 在该表中插入了一些新的行. 之后, 如果 T1 再次读取同一个表, 就会多出几行.

隔离级别

196558-20180821154958123-949179685.png

//添加事务注解    //isolation  隔离级别,常取值 READ_COMMITTED    //noRollbackFor = {AccountException.class} 对哪个异常不回滚 ,声明为 Class[] 类型的    //rollbackFor 遇见必须回滚 ,声明为 Class[] 类型的    //readOnly = true 只读事务属性: 表示这个事务只读取数据但不更新数据, 这样可以帮助数据库引擎优化事务.    //timeout(s)  指定强制回滚之前事务占用时间(秒)    @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT            , noRollbackFor = {AccountException.class}, readOnly = true, timeout = 3    )

9.2 XML配置事务

转载地址:http://llxsx.baihongyu.com/

你可能感兴趣的文章
图片对齐问题
查看>>
ibatis学习
查看>>
Top 6 Programming Languages for Mobile App Development
查看>>
1023. 组个最小数 (20)
查看>>
学习刘伟择优excel视频
查看>>
关于c#邮件发送的简单例子
查看>>
H5地理定位以及移动距离计算
查看>>
MySQL操作失误,如何恢复数据
查看>>
简单调整配置深度Linux-15迅速开始学习工作
查看>>
[USACO 2010 Open Silver 3.Time Travel]——链表
查看>>
[Codeforces261D]Maxim and Increasing Subsequence——树状数组+DP
查看>>
自己瞎写的小项目随笔
查看>>
【语法】NSString
查看>>
最短路径(弗洛伊德算法)- 数据结构和算法65
查看>>
Java 环境设置
查看>>
在Windows上使用libcurl发起HTTP请求
查看>>
Django中多对多关系的orm表设计
查看>>
【原】硬解析和物理读取vs软解析和逻辑读取
查看>>
还需要注册的是我们还有一个是“交差集” cross join, 这种Join没有办法用文式图表示,因为其就是把表A和表B的数据进行一个N*M的组合,即笛卡尔积。表达式如下:...
查看>>
大话微服务
查看>>