Spring学习笔记

1、初始Spring

2、Spring入门

  2.1 什么是IOC

  2.2 IOC与DI(依赖注入)

  2.3 Spring工厂类

  2.4 Spring的Bean的作用范围

3、Spring的Bean管理(注解方式)

  3.1 注解方式简单使用

  3.2 IOC注解详解

4、AOP概述 

  4.1 AOP介绍

  4.2 AOP底层原理简单了解

  4.3 AOP相关术语

  4.4 AOP开发入门

  4.5 基于AspectJ注解的AOP开发

5、JDBC模板

6、Spring的事务管理

  6.1 Spring事务管理的相关API

  6.2 Spring的事务传播行为

  6.3 Spring的编程式事务管理

  6.4 Spring的声明式事务管理(基于XML方式)

  6.5 Spring的声明式事务管理(基于注解的方式)

 

结束

 

1、初识Spring

 

2、Spring入门

2.1 什么是IOC

Inversion Of Control:控制反转,也就是将对象的创建权反转交给Spring

IOC底层原理

  这里以dao层举例,最开始的方式就是写一个UserDao接口,然后写一个UserDaoImpl和UserDaoPPImpl进行实现,这时需要UserDaoIMpl对象,创建对象的时候就是    UserDao userDao = new UserDaoImpl();   这样假设后面需求发生变化,需要的是UserDaoPPImpl的对象,我们就需要修改源码为   UserDao userDao = new UserDaoPPImpl();   如果有一百的地方这样写了,就需要修改一百次,这是因为接口与实现类之间有耦合。

  这里就演变出了工厂模式,我们将获取对象的功能单独抽出来做成一个工具类,有以下代码

class Beanfactory {
  public static UserDao getUserDao() {
    return new UserDaoImpl();
      }
}        

这样就可以在需要改成获取UserDaoPPImpl() 时,只修改一处代码。但这样虽然解除了接口与实现类之间的耦合,但接口与工厂之间有了耦合。

  于是,Spring中使用了 工厂 + 反射 + 配置文件 的方式降低耦合,具体就是创建applicationContext.xml对一些Bean进行注册,然后在工厂方法中,解析配置文件,获取想要创建实例的类对象,然后利用反射创建实例,代码如下:

applicationContext.xml文件

<bean name="userDAO" class="com.itheima.spring.demo1.UserDAOImpl" ></bean>

工厂

class BeanFactory{

  public static Object getBean(String name) {

    // 解析XML文件,根据name找到指定类

    ...

    // 反射

    Class clazz = Class.forName(name);

    return clazz.newInstance();

  }

}

 

2.2 IOC与DI(依赖注入)

DI(Dependency Injection):依赖注入,前提是必须有IOC环境,Spring管理这个类的时候将类依赖的属性注入进来。

  其实不管是控制反转还是依赖注入,他们都可以这样理解:当某个Java实例(调用者)需要另一个Java实例(被调用者)时,在传统的程序设计过程中,通常有调用者来创建被调用者的实例。但是在依赖注入/控制反转模式下,创建被调用者的工作不再是有调用者来完成,而是由Spring容器来完成,然后注入调用者。

    依赖注入通常有如下两种:

           1、  设置注入:IoC容器使用属性的setter方法来注入被依赖的实例。

           2、  构造注入:IoC容器使用构造器来注入被依赖的实例。

前提条件:Java类的定义按照JavaBean的规范进行创建         

介绍JavaBean的博客:Java Bean 简介及其应用        参考博客:Spring读书笔记-----Spring核心机制:依赖注入

 

设置注入:set方法进行依赖注入的代码示例

  首先是一个按照JavaBean标准定义的Car类

 1 public class Car {
 2     private String name;
 3     private int price;
 4 
 5     public Car() {
 6     }
 7 
 8     public Car(String name, int price) {
 9         this.name = name;
10         this.price = price;
11     }
12 
13     public String getName() {
14         return name;
15     }
16 
17     public void setName(String name) {
18         this.name = name;
19     }
20 
21     public int getPrice() {
22         return price;
23     }
24 
25     public void setPrice(int price) {
26         this.price = price;
27     }
28 
29     @Override
30     public String toString() {
31         return "Car{" +
32                 "name='" + name + '\'' +
33                 ", price=" + price +
34                 '}';
35     }
36 }

  然后对其在spring-config.xml文件中进行配置,这里就用到了<property>标签,Spring会在调用无参数的构造器、创建默认的Bean实例后,调用相应的setter方法为程序注入属性值。<property…/>定义的属性值将不再有该Bean来主动设置、管理,而是接受Spring的注入。

<bean id="car" class="com.xidian.demo.Car">
  <property name="name" value="奥迪"></property>
  <property name="price" value="400000"></property> </bean>

  测试输出结果

ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
Car car = context.getBean("car", Car.class);
System.out.println(car);

设置注入代码示例2:属性有自定义的对象类型

   首先我们创建轮胎类,然后在前面提到的Car类中添加一个轮胎对象的属性,并重写toString()方法

public class Tire {
    private String name;
    private int price;

    public Tire() {
    }

    public Tire(String name, int price) {
        this.name = name;
        this.price = price;
    }

    public String getName() {
        return name;
    }

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

    public int getPrice() {
        return price;
    }

    public void setPrice(int price) {
        this.price = price;
    }

    @Override
    public String toString() {
        return "Tire{" +
                "name='" + name + '\'' +
                ", price=" + price +
                '}';
    }
}
   private Tire tires;

    public Tire getTires() {
        return tires;
    }

    public void setTires(Tire tires) {
        this.tires = tires;
    }

  对其在spring-config.xml文件中进行配置,这里就用了<ref>这个标签

    <bean id="tire" class="com.panlei.demo.Tire">
        <property name="name" value="朝阳"></property>
        <property name="price" value="3000"></property>
    </bean>

    <bean id="car" class="com.panlei.demo.Car">
        <property name="name" value="奥迪"></property>
        <property name="price" value="400000"></property>
        <property name="tires" ref="tire"></property>
    </bean>

  最终测试结果 

    

 构造注入

 Car中的构造方法

public Car(String name, int price, Tire tires) {
        this.name = name;
        this.price = price;
        this.tires = tires;
    }

spring-config.xml中的配置,这里就可以不用无参构造方法了,直接使用有参构造

<bean id="car" class="com.panlei.demo.Car">
  <constructor-arg name="name" value="奔驰"></constructor-arg>
  
<constructor-arg name="price" value="400000"></constructor-arg>
  <constructor-arg name="tires" ref="tire"></constructor-arg> </bean>

测试结果:

 

2.3 Spring工厂类

BeanFactory            :老版本的工厂类

    BeanFactory:调用getBean的时候,才会生成类的实例。

ApplicationContext  :新版本的工厂类

    ApplicationContext:加载配置文件的时候,就会将Spring管理的类都实例化。

  ApplicationContext有两个实现类

    ClassPathXmlApplicationContext  :加载类路径下的配置文件

    FileSystemXmlApplicationContext   :加载文件系统下的配置文件

 

2.4 Spring的Bean的作用范围

scope             :Bean的作用范围

singleton          :默认的,Spring会采用单例模式创建这个对象。

prototype         :多例模式。(Struts2和Spring整合一定会用到)

request              :应用在web项目中,Spring创建这个类以后,将这个类存入到request范围中。

session              :应用在web项目中,Spring创建这个类以后,将这个类存入到session范围中。

globalsession    :应用在web项目中,必须在porlet环境下使用。但是如果没有这种环境,相对于session。

 

<bean id="car" class="com.test.spring.demo2" scope="prototype" init-method="setup" destroy-method="destroy"/>

 

3、Spring的Bean管理(注解方式)

前面介绍的Spring的依赖注入是Spring的Bean管理的一种方式,是基于XML的方式,还有另外一种方式,就是注解方式

3.1 注解方式简单使用

1、在spring-config.xml中引入相关约束

<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、编写测试类,仍以上面的Car类作为示例

3、在xml中配置注解的组件扫描

<context:component-scan base-package="com.panlei.demo"/>

4、在相关类上添加注解。使用注解,可以没有set方法,需要将注解添加到属性处,有set方法就添加到set方法上

package com.panlei.demo;

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

@Component("car")     // 相当于<bean id="car" class="com.panlei.demo.Car"/>
public class Car  {
    private String name;
    private int price;

    public Car() {
    }

    public Car(String name, int price) {
        this.name = name;
        this.price = price;
    }

    public String getName() {
        return name;
    }

    @Value("奥拓")
    public void setName(String name) {
        this.name = name;
    }

    public int getPrice() {
        return price;
    }

    @Value("10000")
       public void setPrice(int price) {
        this.price = price;
    }

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

5、测试输出

ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
Car car = context.getBean("car", Car.class);
System.out.println(car);

3.2 IOC注解详解

@Component:组件,作用在类上

Spring提供了@Component的三种衍生注解,为了更好的区分不同的层,让标注类本身的用途清晰:

  @Controller:WEB层

  @Service:业务层

  @Repository:持久层

 

属性注入的注解:

@Value:用于注入普通类型

@Autowired:自动装配,会按照注解的类型进行装配

若想要@Autowired按照名称进行注入,需结合   @Qualifier   强制使用名称注入

@Resource:相当于@Autowired与@Qualifier一起使用

 

Bean作用范围的注解:

@Scope:

  singleton:单例

  prototype:多例

Bean生命周期的配置:

@PostConstruct:相当于 init-method

@PreDestroy: 相当于destroy-method

 

XML方式与注解方式的比较:

  XML:结构清晰

  注解:开发方便,便于思考的连续性

实际开发中还有一种XML与注解整合的开发方式:也就是Bean在XML中进行配置,但是属性使用注解进行注入,需要在xml文件中声明,就不需要声明组件扫描

 

<context:annotation-config></context:annotation-config>

 

 

4、AOP概述

4.1 AOP介绍

AOP(Aspect Oriented Programming,面向切面的编程)。利用AOP对业务逻辑各个部分进行隔离,降低耦合度

 

4.2 AOP底层原理简单了解

利用了代理机制:Spring 的 AOP 的底层用到两种代理机制

JDK 的动态代理 :针对实现了接口的类产生代理。

Cglib 的动态代理 :针对没有实现接口的类产生代理,应用的是底层字节码增强技术 应用的是底层字节码增强技术,生成当前类的子类对象 .

 

4.3 AOP相关术语及知识

Joinpoint(连接点):所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点.

Pointcut(切入点):所谓切入点是指我们要对哪些Joinpoint进行拦截的定义.

Advice(通知/增强):所谓通知是指拦截到Joinpoint之后所要做的事情就是通知.通知分为前置通知,后置通知,异常通知,最终通知,环绕通知(切面要完成的功能)

Introduction(引介):引介是一种特殊的通知在不修改类代码的前提下, Introduction可以在运行期为类动态地添加一些方法或Field.

Target(目标对象):代理的目标对象

Weaving(织入):是指把增强应用到目标对象来创建新的代理对象的过程. spring采用动态代理织入,而AspectJ采用编译期织入和类装在期织入

Proxy(代理):一个类被AOP织入增强后,就产生一个结果代理类

Aspect(切面): 是切入点和通知(引介)的结合 

4.4 AOP开发入门(基于AspectJ的XML方式)

SpringAOP与AspectJ的关系

  AOP面向切面编程是一种编程思想,是对OOP面向对象编程的一种补充。对于AOP这种编程思想,很多框架都进行了实现。Spring就是其中之一,可以完成面向切面编程。

  而AspectJ也实现了AOP的功能,且其实现方式更为简捷,使用更为方便,而且还支持注解式开发。所以,Spring又将AspectJ的对于AOP的实现也引入到了自己的框架中。

使用时需要引入相关的jar包

  引入AOP联盟的jar包,这是AOP的规范

  引入AspectJ的jar包,这是对上面AOP规范的实现

  引入整合Spring和AspectJ的jar包

  引入Spring的AOP jar包,因为上面Spring和AspectJ的整合jar包用到了Spring的AOP jar包  

  

 

1、Spring配置文件中关于AOP的配置

2、编写接口及其实现类,并在配置文件中进行<bean>配置

 

 

3、编写自己的切面类,并在配置文件中进行<bean>配置

 

 

4、针对具体类中的方法进行具体的AOP的切入点的配置

 

 

 通知类型

前置通知:在目标方法执行之前执行

后置通知:在目标方法执行之后执行,可以获取被代理方法的返回值,注意参数名字的一致性

切面类中的方法可以有返回值,其返回值是如何处理的?待解决

环绕通知:注意需要调用被代理的方法

异常抛出通知:可以接收异常信息

最终通知

切入点表达式

execution( 表达式 )

 表达式 : [方法访问修饰符 ] 方法返回值 包名 .类名 .方法名 (方法的参数)

   public * com.panlei.spring.dao.*.*(..)

   * com.panlei.spring.dao.*.*(..)        // 包下面的所有类所有方法

   * com.panlei.spring.dao.UserDao+.save(..)   //  +  代表当前类及其子类的save方法

   * com.panlei.spring.dao..*.*(..)         // 这个包以及其子包下面的所有类所有方法

4.5 基于AspectJ的注解的AOP开发

继续使用上面的项目,我们还是编写好目标类以及切面类,并在spring的配置文件中进行相应的bean配置,然后根IOC注解开发类似,我们需要在spring配置文件中配置一开启注解AOP开发的语句

然后就可以在切面类上进行注解开发,使用的语法仍然是切入点表达式,如下图为前置通知的注解

同样,也有后置通知,也可以接收被代理类的返回值

环绕通知

 

异常抛出通知

 

最终通知

另外,针对每一个方法配置其切入点太麻烦,我们可以单独配置切入点,然后直接引用方法名即可

 

5、JDBC模板

以C3P0作为数据库连接池

1、引入jar包,在Spring配置文件中配置C3P0数据库连接池和JDBC模板,其中数据库的配置使用单独的配置文件,然后在Spring配置文件中引入该配置文件

2、简单使用其进行数据库操作

查询测试

 

6、Spring的事务管理

6.1 Spring事务管理的相关API

PlatformTransactionManager:平台事务管理器,是一个接口类,Spring用于管理事务的真正对象,有两个实现类

  DataSourceTransactionManager       :底层使用JDBC管理事务  

  HibernateTransactionManager          :底层使用Hibernate管理事务

TransactionDefinition       :事务定义信息,用于定义事务的相关的信息,隔离级别、超时信息、传播行为、是否只读等

TransactionStatus:事务的状态,用于记录在事务管理过程中,事务的状态的对象

它们之间的关系

  Spring进行事务管理的时候,首先平台事务管理器根据事务定义信息进行事务的管理,在事务管理过程中,产生各种状态,将这些状态的信息记录到事务状态的对象中。

6.2 Spring的事务传播行为

Spring中提供了七种事务的传播行为

6.3 Spring的编程式事务管理

我们以一个事务讲解常用的银行转账的案例作为例子

1、首先先搭建好一个转账的环境,也就是定义好Service层与Dao层,并在配置文件中配置bean以及数据库连接池

这里注意一点,由于我们需要JdbcTemplate进行数据相关的增删改查的操作,但如果给每一个Dao中都定义一个JdbcTemplate的话,其实获取的都是同一个对象,Spring考虑到了这种需求,就将其写为JdbcDaoSupport这个类,我们只需要继承这个类,这个类中就有一个JdbcTemplate对象,调用get方法获取即可,在配置Dao的时候要设置一个dataSource参数,用来初始化JdbcTemplate对象。这样就不需要在配置文件中配置JdbcTemplate了。

2、配置平台事务管理器以及事务管理的模板类,并在Service中注入事务管理的模板

3、在Service层编写事务管理的代码

4、运行测试

当我们故意在转账操作之间加入异常时,会发现不会出现一边已经扣钱,另一边还没有加钱的情况。

6.4 Spring的声明式事务管理(基于XML方式)

声明式事务管理是底层基于AOP的,也就是需要引入aop的jar包以及在配置文件中配置一些aop

1、仍然使用上一节的例子,配置好Service与Dao、数据库连接池以及平台事务管理器

2、配置事务的增强:因为事务的管理是一个固定的模式,也就是切面的增强是固定的,所以切面类不需要自己写,只需要进行配置即可。配置的信息主要是事务的配置,<tx:advice>中主要事务的一些配置,主要的配置信息在<tx:method>中,name表示的是应用事务的方法,里面可以使用通配符,比如save*表示save开头的方法,还可以定义传播行为等信息。然后就是aop的配置,用<aop:advisor>将一个<tx:advice>与切入点联合起来。

 

3、运行测试

当我们故意在转账操作之间加入异常时,会发现不会出现一边已经扣钱,另一边还没有加钱的情况。

6.5 Spring的声明式事务管理(基于注解的方式)

1、仍然使用上一节的例子,配置好Service与Dao、数据库连接池以及平台事务管理器

2、开启注解事务

3、在具体的service代码中添加事务注解,本例中指的是AccountServiceImpl。在注解中可以添加事务的各种定义信息

4、运行测试

当我们故意在转账操作之间加入异常时,会发现不会出现一边已经扣钱,另一边还没有加钱的情况。

 

posted @ 2019-02-22 19:56  潘_磊  阅读(347)  评论(0编辑  收藏  举报