Spring笔记

1. Spring IOC

1.1 Spring核心包

Spring核心包

XML编写提示:配置Schema,修改Key type 为Schema location

1.2. SpringIOC入门

  1. IOC: Inversion of Control(控制反转)。将对象的创建权反转给(交给)Spring。

    <bean id="bean1" class="com.joey.spring.Bean1"></bean>
  2. DI:依赖注入,前提必须有IOC的环境,Spring管理这个类的时候将类的依赖的属性注入(设置)进来。

IOC

1.3. Spring工厂类结构图

工厂类结构图

  1. ApplicationContext继承BeanFactory

  2. BeanFactory

    老版本的工厂类,在调用getBean的时候才会产生类的实例

  3. ApplicationContext

    新版本的工厂类,加载配置文件的时候,将Spring管理的类都实例化

    两个实现类:

    • ClassPathXmlApplicationContext:加载类路径下的配置文件
    • FileSystemXmlApplicationContext:加载文件系统下的配置文件

1.4. Spring Bean配置

  1. 标签的id和name配置

    id:使用了约束中的唯一约束,里面不能出现特殊字符。

    name:没有使用约束中的唯一约束(理论上可以出现重复的,但是实际开发不能出现),里面可以出现特殊字符。

  2. Bean的生命周期配置

    init-method:Bean被初始化的时候执行的方法

    destory-method:Bean被销毁的时候执行的方法(Bean是单例创建,工厂关闭)

  3. Bean的作用范围配置

    scope:Bean的作用范围

    • singleton:默认单例模式创建对象
    • prototype:多例模式
    • request:应用在web项目中,Spring创建这个类以后存到request范围中
    • session:应用在web项目中,Spring创建这个类以后存到session范围中
    • globalsession:应用在web项目中,必须在porlet环境下使用,但是如果没有这种环境,相对于session

1.5. Spring的Bean管理(XML方式)

  1. Spring的Bean的实例化方式

    1. 无参构造方法

      • 编写类构造方法

        public class Bean1{
        public Bean1(){
        super();
        System.out.pringt("Bean1的无参构造方法执行。。。");
        }
        }
      • 编写XML配置文件

        <bean id="bean1" class="com.joey.spring.Bean1"></bean>
    2. 静态工厂实例化

      • 编写静态工厂

        public class Bean2Factory{
        public static Bean2 createBean2(){
        System.out.pringt("Bean2的静态工厂方法执行。。。");
        return new Bean2();
        }
        }
      • 编写XML配置文件

        <bean id="bean2" class="com.joey.spring.Bean2Factory" factory-method="createBean2"></bean>
    3. 实例工厂实例化

      • 编写实例工厂

        public class Bean3Factory{
        public static Bean3 createBean3(){
        System.out.pringt("Bean3的实例工厂方法执行。。。");
        return new Bean3();
        }
        }
      • 配置XML文件

        <bean id="bean3Factory" class="com.joey.spring.Bean3Factory" ></bean>
        <bean id="bean3" factory-bean="bean3Factory" factory-method="createBean3"></bean>
  2. Spring的属性注入(DI)

    1. 构造方法的方式属性注入

      <!-- 配置一个可以执行批量的sqlSession -->
      <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
      <constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory"></constructor-arg>
      <constructor-arg name="executorType" value="BATCH"></constructor-arg>
      </bean>
    2. Set方法的方式属性注入

      <!--================== 配置和MyBatis的整合=============== -->
      <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
      <!-- 指定mybatis全局配置文件的位置 -->
      <property name="configLocation" value="classpath:mybatis-config.xml"></property>
      <property name="dataSource" ref="pooledDataSource"></property>
      <!-- 指定mybatis,mapper文件的位置 -->
      <property name="mapperLocations" value="classpath:mapper/*.xml"></property>
      </bean>
    3. P名称空间属性注入

      1. 引入P名称空间

        <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:p="http://www.springframework.org/schema/p"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:context="http://www.springframework.org/schema/context"
        xmlns:aop="http://www.springframework.org/schema/aop"
        xmlns:tx="http://www.springframework.org/schema/tx"
        xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
      2. P名称空间的属性注入

        <bean id="car" class="com.joey.spring.Car" p:name="" p:price=""></bean>
        <bean id="employee" class="com.joey.spring.Employee" p:name="" p:car-ref="car"></bean>
    4. SpEL的属性注入(Spring3.0以后)

      spring表达式语言简称SPEL:是一个支持运行时查询和操作对象图的强大的表达式语言。语法类似于EL,SpEL 使用 #{…} 作为定界符 , 所有在大括号中的字符都将被认为是 SpEL , SpEL 为 bean 的属性进行动态赋值提供了便利。

      通过 SpEL 可以实现:
      • 通过 bean 的 id 对 bean 进行引用。
      • 调用方式以及引用对象中的属性。
      • 计算表达式的值
      • 正则表达式的匹配
      <bean id="carInfo" class="com.joey.spring.CarInfo"></bean>
      <bean id="car" class="com.joey.spring.car">
      <property name="name" value="#{carInfo.name}"></property>
      <property name="drive" value="#{carInfo.drive()}"></property>
      </bean>
  3. Spring集合类型属性注入

    <!-- Spring的集合属性的注入============================ -->
    <!-- 注入数组类型 -->
    <bean id="collectionBean" class="com.joey.spring.CollectionBean">
    <!-- 数组类型 -->
    <property name="arrs">
    <list>
    <value>1</value>
    <value>2</value>
    <value>3</value>
    </list>
    </property>
    <!-- 注入list集合 -->
    <property name="list">
    <list>
    <value>5</value>
    <value>6</value>
    <value>7</value>
    </list>
    </property>
    <!-- 注入set集合 -->
    <property name="set">
    <set>
    <value>aaa</value>
    <value>bbb</value>
    <value>ccc</value>
    </set>
    </property>
    <!-- 注入Map集合 -->
    <property name="map">
    <map>
    <entry key="aaa" value="111"/>
    <entry key="bbb" value="222"/>
    <entry key="ccc" value="333"/>
    </map>
    </property>
    </bean>
  4. 分模块开发配置

  5. 加载配置文件的时候加载多个

    ApplicationCOntext applicationCOntext = new ClassPathXmlApplicationContext("applicationConetxt.xml","applicationConetxt.xml2");
  6. 在一个配置文件中引入多个配置文件

  
<import resource="applicationConetxt2.xml"/>

1.6. Spring的Bean管理(注解开发)

  1. 引入context约束

  2. 开启Spring组件扫描

    <context:component-scan base-package="com.joey.spring"></context:component-scan>
  3. 类上添加注解

    @Compoent("userDao") //相当于<bean id="userDao" class="com.joey.spring.UserDaoImpl"></bean>
    public class UserDaoImpl implements UserDao{

    }
  4. 注解方式设置属性值

    使用注解方式,可以没有set方法的

    • 属性如果有set方法,需要将属性注入的注解添加到set方法

    • 如果没有set方法,需要将属性注入的注解添加属性上

  5. IOC 注解

    1. @Component:组件,修饰一个类,将这个类交给Spring管理
    2. 三个衍生注解,修饰类
      • @Controller:Web层
      • @ServIce:service层
      • @Respository:dao层
    3. 属性注入的注解:
      • 普通属性:@Value
      • 按照类型对象注入:@Autoware
      • 按照名称对象注入:@Resource
    4. Bean的其他注解:
      • 生命周期相关注解
        • PostConstruct:初始化方法
        • PreDestory:销毁方法
      • Bean作用范围的注解
        • @Scope
          • Singleton
          • prototype
          • request
          • session
          • globalsession
  6. IOC的XML和注解开发比较

    XML :可以适用任何场景,结构清晰,维护方便

    注解:有些地方用不了,类不是自己提供,开发方便

    XML和注解整合开发:XML管理Bean,注解完成属性注入

    没有使用扫描类上的注解,需要开启属性注入的注解

    <context:annotation-config/>
    基于XML配置 基于注解配置
    Bean定义 @Component,衍生类:@Repository、@Service、@Controller
    Bean名称 通过id或name指定 @Component(“person”)
    Bean注入 或者通过p命名空间 @Autowired按照类型注入,@Qualifier按照名称注入
    生命过程,Bean作用范围 init-method,destory-method,范围scope属性 @PostConstructa初始化、@PreDestory销毁、@Scope设置作用范围
    适合场景 Bean来自第三方,使用其它 Bean的实现类由用户自己开发

2. Spring AOP

2.1 Spring 底层实现原理

动态代理

  • JDK动态代理(默认,先修改java文件然后再编译成class文件)

    JDK动态代理只能够对接口进行代理,不能对普通的类进行代理(因为所有生成的代理类的父类为Proxy,Java类继承机制不允许多重继承)

    JDK动态代理主要涉及java.lang.reflect包下的两个类:Proxy类和InvocationHandler接口。
    JDK动态代理实现的三个要点:

    1. 通过java.lang.reflect.Proxy类来动态生成代理类
    2. 代理类要实现InvocationHandler接口
    3. JDK动态代理只能基于接口进行动态代理的
  • Cglib动态代理(运行更快,在内存直接生成子类class文件继承父类重写所有父类方法(不能声明成final))

    Cglib采用非常底层的字节码技术,可以为一个类创建子类,并在子类中采用方法拦截的技术拦截所有的父类方法的调用,并顺势织入横切逻辑。

  • 区别

    JDK动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。

    而Cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。

    1. 如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP

    2. 如果目标对象实现了接口,可以强制使用CGLIB实现AOP’

      如何强制使用Cglib:

      1. 在spring配置文件中加入<aop:aspectj-autoproxy proxy-target-class=“true”/>
      2. EnableAspectJAutoProxy注解参数proxyTargetClass=true使用Cglib
    3. 如果目标对象没有实现了接口,必须采用Cglib库,spring会自动在JDK动态代理和Cglib之间转换

    4. JDK动态代理和Cglib字节码生成的区别?
      (1)JDK动态代理只能对实现了接口的类生成代理,而不能针对类
      (2)Cglib是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法。 因为是继承,所以该类或方法最好不要声明成final

  • 性能问题

    由于Cglib代理是利用ASM字节码生成框架在内存中生成一个需要被代理类的子类完成代理,而JDK动态代理是利用反射原理完成动态代理,所以Cglib创建的动态代理对象性能比JDK动态代理动态创建出来的代理对象新能要好的多,但是对象创建的速度比JDK动态代理要慢,所以,当Spring使用的是单例情况下可以选用Cglib代理,反之使用JDK动态代理更加合适。同时还有一个问题,被final修饰的类只能使用JDK动态代理,因为被final修饰的类不能被继承,而Cglib则是利用的继承原理实现代理的。

2.2 Spring AOP相关术语

  • Joinpoint(连接点) :

    ​ 所谓连接点是指那些被拦截到的点。在Spring中,这些点指的是方法,因为Spring只支持方法类型的连接点。

  • Pointcut(切入点) :

    ​ 所谓的切入点是指我们要对哪些Joinpoint进行拦截的定义。

  • Advice(通知/增强) :

    ​ 所谓通知是指拦截到Joinpoint之后所要做的事情就是通知。

    ​ 通知的类型:前置类型,后置通知,异常通知,环绕通知。

  • Introduction(引介) :

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

  • Target(目标对象) :

    ​ 代理的目标对象。

  • Weaving(织入) :

    ​ 是指把增强应用到目标对象来创建新的代理对象的过程。

    ​ Spring采用动态代理织入,而AspectJ采用编译期织入和类装载期织入。

  • Proxy(代理) :

    ​ 一个类被AOP织入增强,就产生一个结果代理类。

  • Aspect(切面) :

    ​ 是切入点和通知(引介)的结合。

AOP相关术语

2.3 SpringAOP 的入门开发

  1. 引入jar包

  2. 编写目标类并配置

  3. 编写切面类并配置

  4. 进行aop配置

    <aop:config>
    <aop:pointcut expression="execution(表达式" id="pc1"/>
    <aop:aspect >
    <aop:before method="" pointcut-ref="pc1"/>
    </aop:aspect>
    </aop:config>
  5. 通知类型

    • 前置通知
    • 后置通知
    • 环绕通知
    • 异常抛出通知
    • 最终通知
  6. XML配置

  7. 基于execution的函数完成

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

2.4 基于AspectJ的XML配置

<aop:aspect >
<!--前置通知 : 获得切入点信息-->
<aop:before method="" pointcut-ref="pc1"/>
<!--后置通知 : 获得方法的返回值-->
<aop:after-returning method="" pointcut-ref="pc1"/>
<!--环绕通知 : 阻止目标方法的执行-->
<aop:around method="" pointcut-ref="pc1"/>
<!--异常抛出通知-->
<aop:after-throwing method="" pointcut-ref="pc1"/>
<!--最终通知-->
<aop:after method="" pointcut-ref="pc1"/>
</aop:aspect>

2.5 基于AspectJ的注解配置

  1. 在配置文件中开启AOP注解开发

    <aop:aspectj-autoproxy/>
  2. 切面类上使用注解

    @Aspect
    public class MyAspectj{
    @Before(value="execution(* com.joey.spring.save(..))")
    public void before(){
    System.out.println("前置通知");
    }
    @AfterReturning(value="execution(* com.joey.spring.save(..))")
    public void afterReturning(){
    System.out.println("后置通知");
    }
    @Around(value="execution(* com.joey.spring.save(..))")
    public void around(){
    System.out.println("环绕通知");
    }
    @AfterThrowing(value="execution(* com.joey.spring.save(..))")
    public void afterThrowing(){
    System.out.println("异常抛出通知");
    }
    @After(value="execution(* com.joey.spring.save(..))")
    public void after(){
    System.out.println("最终通知");
    }
    }

3. Spring的JDBC模板使用

  • Spring的JDBC模板

    ORM持久化技术 模板类
    JDBC org.springframework.jdbc.core.JdbcTemplate
    Hibernate3.0 org.springframework.orm.hibernate3.HeibernateTemplate
    IBatis(Mybatis) org.springframework.orm.ibatis.SqlMapClientTemplate
    JPA org.springframework.orm.jpa.JpaTemplate
  • JDBC模板使用(例: c3p0)

    1. 引入jar包

    2. 配置c3p0连接池

    3. 抽取配置到属性文件

    4. 在Spring的配置文件中引入属性文件

      <!--第一种(常用)-->
      <context:property-placeholder location="classpath:dbconfig.properties" />
      <!--第二种-->
      <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
      <property name="location" value="classpath:dbconfig.properties"/>
      </bean>
    5. 引入属性文件的值

      <bean id="pooledDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
      <property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
      <property name="driverClass" value="${jdbc.driverClass}"></property>
      <property name="user" value="${jdbc.user}"></property>
      <property name="password" value="${jdbc.password}"></property>
      </bean>

4. Spring 的事务管理

4. 1 事务特性

  • 原子性 : 事务不可分割
  • 一致性 : 事务执行前后数据完整性保持一致
  • 隔离性 : 一个事务的执行不应该受到其他事务的干扰
  • 持久性 : 一旦事务结束,数据就持久到数据库

4.2 事务隔离级别

  • Read uncommitted :未提交读,任何读问题解决不了。

  • Read committed :已提交读,解决脏读,但是不可重复读和虚读有可能发生。

  • Repeatable read :重复读,解决脏读和不可重复读,但是虚读有可能发生。

  • Serializable :解决所有读问题。

4.3 事务管理的API

  1. PlatformTransactionManager: 平台事务管理器

    • DataSourceTransactionManager: 底层使用JDBC管理事务
    • HibernateTransactionManager : 底层使用Hibernate管理事务
  2. 事务定义信息

    用于定义事务的相关信息

    • 隔离级别
    • 超时信息
    • 传播行为
    • 是否只读
  3. 事务的状态

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

4.4 Spring事务的传播行为(七种事务)

  1. 保证多个操作在同一个事务中
    • PROPAGATION_REQUIRED :默认值,如果A中有事务,使用A中的事务,如果A没有,创建一个新的事务,将操作包含进来
    • PROPAGATION_SUPPORTS :支持事务,如果A中有事务,使用A中的事务。如果A没有事务,不使用事务。
    • PROPAGATION_MANDATORY:如果A中有事务,使用A中的事务。如果A没有事务,抛出异常。
  2. 保证多个操作不在同一个事务中
    • PROPAGATION_REQUIRES_NEW :如果A中有事务,将A的事务挂起(暂停),创建新事务,只包含自身操作。如果A中没有事务,创建一个新事务,包含自身操作。
    • PROPAGATION_NOT_SUPPORTED :如果A中有事务,将A的事务挂起。不使用事务管理。
    • PROPAGATION_NEVER:如果A中有事务,报异常。
  3. 嵌套式事务
    • PROPAGATION_NESTED:嵌套事务,如果A中有事务,按照A的事务执行,执行完成后,设置一个保存点,执行B中的操作,如果没有异常,执行通过,如果有异常,可以选择回滚到最初始位置,也可以回滚到保存点。

4.5 Spring事务管理

  1. 编程式事务(需手动编写代码)

    1. 配置平台事务管理器
    2. 配置事务管理的模板类
    3. 在业务层注入事务管理的模板
    4. 编写事务管理的代码
  2. 声明式事务管理(XML方式)

    1. 配置事务管理器

      <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
      <!--控制住数据源 -->
      <property name="dataSource" ref="pooledDataSource"></property>
      </bean>
    2. 配置增强

      <tx:advice id="txAdvice" transaction-manager="transactionManager">
      <tx:attributes>
      <!-- 所有方法都是事务方法 -->
      <tx:method name="*"/>
      <!--以get开始的所有方法 -->
      <tx:method name="get*" read-only="true"/>
      </tx:attributes>
      </tx:advice>
    3. AOP配置

      <aop:config>
      <!-- 切入点表达式 -->
      <aop:pointcut expression="execution(* com.joey.service..*(..))" id="txPoint"/>
      <!-- 配置事务增强 -->
      <aop:advisor advice-ref="txAdvice" pointcut-ref="txPoint"/>
      </aop:config>
  3. 声明式事务管理(注解方式)

    1. 配置事务管理器

      <!--配置事务增强,事务如何切入  -->
      <tx:advice id="txAdvice" transaction-manager="transactionManager">
      <tx:attributes>
      <!-- 所有方法都是事务方法 -->
      <tx:method name="*"/>
      <!--以get开始的所有方法 -->
      <tx:method name="get*" read-only="true"/>
      </tx:attributes>
      </tx:advice>
    2. 开启注解事务

      <tx:annotation-driven transaction-manager="transationManager"/>
    3. 在业务层添加注解

      @Transactional
      public class UserServiceImple implements UserService{
      }