Spring

IoC

一 Spring概述

1.什么是Spring?

  • Spring是为了解决企业应用开发的复杂性而创建的,优秀的轻量级企业应用解决方案,开放源代码,主要特点是采用分层结构,允许单独使用某一模块,也可以将多个模块组合使用。
  • 企业应用:为了满足企业运行需要开发的软件系统,现在的企业应用大多运行在开放性平台(存在信息交互的可能性),不再是孤立的系统,而是在相互联系中构成了一个系统群。
  • 轻量级:轻量级框架如Struts、Spring,侧重于降低开发的复杂度,体积小,消耗小,相应地处理能力有所减弱。
  • JavaBean:使用java语言编写的可重用组件,主要用作数据的载体。
  • EJB:Enterprise  JavaBean,服务器端组件模型,定义了一个用于开发基于组件的企业应用的规范,本身复杂而繁琐。

2.Spring的技术基础

  • IoC:Inversion of  Control,控制翻转。
  • AOP:Aspect  Oriented  Program,面向切面编程。

3.Spring的构成

  • Spring core:Spring框架的核心,提供了Spring框架的基础功能,实现了IoC,包含重要的类BeanFactory。BeanFactory是IoC的容器,负责bean的实例化、初始化、使用与销毁。
  • Spring context:继承了BeanFactory,添加了许多功能,如国际化、数据校验等。
  • Spring AOP:继承AOP的所用功能,通过事务可以将Spring管理的任意对象AOP化。
  • Spring DAO:提供了JDBC的抽象层,简化了数据库厂商的异常错误,大幅度减少了代码,并且支持声明式事务与编程式事务。
  • Spring ORM:兼容所有流行的ORM框架,完美地整合了Hibernate\Mybatis。
  • Spring Web:提供了Servlet监听器上下文与Web上下文,集成了现有的Web框架,如JSP、Struts。
  • Spring MVC:建立在Spring的核心功能之上,具有Spring框架的所有特性。

二 BeanFactory

1.BeanFactory

一个容器,通过读取bean的定义文件获取所有的bean对象,实现对bean的创建、使用与管理。

2.ApplicationContext

继承了BeanFactory,添加了许多功能,如国际化、数据验证等,有三个比较重要的实现类:

  • ClassPathXmlApplicationContext:从类路径下加载配置文件,一般常用此类加载applicationContext.xml。
  • FileSystemXmlApplicationContext:从文件路径加载文件,适用性范围广。
  • WebApplicationContext:Spring与Web整合后在bean在Web中的容器。

三 控制反转

1.什么是控制反转?

一种思想,依赖注入是该思想的一种实现,将创建实例的任务交给IoC容器,需要实例时向容器发送请求即可,这样降低了引用类A对被引用类B的依赖,即B发生改变,A需要修改很少一部分,甚至不需要修改。

2.降低耦合度的实现过程

在传统开发中,A类调用B类,就必须在内部创建B类,B b= new B(),当B类被删除,就需要修改A类的代码,这样A类就受到B类变化的拖累。如果A类调用的是B类的接口,并且对象由外部创建,当外部实现类发生改变时,只要提供一个满足要求的实现类,A依然可以正常运行,不需要修改A中的代码,B的变化没有波及A。

3.依赖注入

⑴setter注入

底层调用setter方法实现注入:

<bean id="引用变量"class="全限定性类名">
      <property name="属性名">
             <value>属性值</value>
      </property>
</bean>

⑵构造器注入

底层调用构造方法实现注入:

1 <bean id="引用变量"class="全限定性类名">
2       <constructor-arg>
3           <value>参数值</value>
4       </constructor-arg>
5 </bean>

配置文件中标签的赋值顺序必须与构造方法形参定义顺序一致,否则抛出出错。为了避免顺序不一致导致的错误,在配置文件中为每一个赋值标签设定一个index值,对应其要赋值的形参在构造器中的顺序:

1 <constructor-arg index="0">
2     <value>参数值</value>
3 <constructor-arg>、

⑶集合注入

数组与List集合: 

1 <list>
2      <value>value01</value>
4      <value>value02</value>
5 </list>

数组还有一种更简单的赋值方式,值写在一块,用逗号隔开:

1 <property name=""value="value01,value02"/>

Set集合:

1 <set>
2     <value>value01</value>
3     <value>value02</value>
4 </set>

Map集合:

 <map>
2     <entry key=""value=""/>
3     <entry key=""value=""/>
4 </map>

Properties对象:  

1  <props>
3       <prop key="xxx"></prop>
5       <prop key="xxx"></prop>
7 </props>

⑷引用bean   

<property name="">
      <ref local="id"/>
</property>

⑸自动装配

byName:通过名称装配,将IoC容器中与属性名同名的bean注入。

<bean autowire="byName"id=""class=""/>
  • 按名称装配的不足:如果IoC容器中存在多个同名而类型不同的bean,注入后就会发生错误。

byType:将与属性数据相同的bean注入。

<bean autowire="byType"id=""class=""/>
  • 按类型转配的不足:如果IoC容器中存在多个类型相同的bean,IoC容器会因为无法识别需要的bean而报错。

⑹内部bean

如果希望内部bean只能通过外部bean访问,那么将内部bean的定义放在外部bean内部:

<bean id="xxx"class="OuterClass">
     <property name="innerClassAttrName">
          <bean class="InnerClass">//因为不支持外部对象引用,所以不需要定义id
                <property name="">xxxxx</property>
          </bean>
     </property>
</bean>

四 同类抽象bean与异类抽象bean

1.背景

在配置文件中,如果多个bean拥有共同的属性,分别为每一个bean配置,数据冗余,为了消除冗余,将这些共同的属性提取出来,定义在一个抽象bean中,其他bean通过引用该抽象bean为属性赋值。

2.同类抽象bean

为同类的bean赋值:

<bean id="abstractBean"class="ClassA"abstract="true">
      <property  name=""value=""/>
     ........为共同属性赋值..........
</bean>
<bean id="xxx"parent="abstractBean">
      <property name=""value=""/>
     ........为特有属性赋值..........
</bean>
  • abstract="true":将该bean定义为抽象bean,防止通过getBean直接访问该bean。

3.异类抽象bean

为不同类型的bean赋值:

<bean id="abstractBean"abstract="true">
      <property  name=""value=""/>
      ........为共同属性赋值..........
</bean>
<bean id="xxx"class=""parent="abstractBean">
     <property  name=""value=""/>
      ........为特有属性赋值..........
</bean> 

五 bean作用域

1.生命周期的设定

<bean  id=""class=""/>//采用默认值singleton
<bean  id=""class=""singleton="true/false"/>
<bean  id=""class=""scope="singleton/prototype"/>

2.singleton与prototype

  • singleton:bean采用单例模式,生命周期与IoC容器相同,由IoC容器管理,每次请求返回的是同一bean对象,。
  • prototype:采用多例模式,IoC容器将对象交给使用者,由使用者管理,每一次请求获取的都是不同对象,。

六 SpEL

1.SpEL是什么?

Spring  Expression Language,用于在配置文件中动态地为属性赋值,该值可以来源于容器中其他bean的属性,调用bean方法的返回值,也可是调用容器以外其他类静态方法的返回值。

2.基本语法

#{expression}
  • 调用容器中其他bean的属性:#{beanName.attrName}
  • 调用容器中bean的方法:#{beanId.method(arg)}
  • 调用容器以外其他类的静态方法:#{T(全限定性类名).method(arg)}

七 bean的生命周期

1.11个阶段

bean的生命周期可分为11个阶段,即总共有11时机可以用来改变bean,除自定义的BeanPostProcessor适用于所有bean而单独创建外,其他的都通过bean实现相应的接口实现,如BeanNameAware\BeanFactoryAware\InitializingBean\DisposableBean等。

2.相关的接口或者类

⑴在bean内部自定义控制生命周期的方法

  • 定义在setter方法执行完毕后调用的方法,该方法必须无参:
  • <bean  init-method="方法名">
  • 定义在IoC容器关闭以后调用的方法,必须无参:
  • <bean destory-method="方法名"/>

⑵BeanPostProcessor

一个接口,其实现类只有在配置文件中配置之后才可以使用,由底层调用,配置时无需设定id。容器中所有的bean在初始化阶段都会调用其中的两个方法:

  • postProcessBeforeInitialization:在bean初始化完毕之前由容器调用;
  • postProcessAfterInitialization:在bean初始化完毕之后由容器调用。

八 配置文件的分散编写

1.平等关系

两个配置文件各自单独存在,不存在包含关系。在java代码中读取平等关系的配置文件需要使用指定类的可变参数的形式:

new  ClassPathXmlApplicationContext(String...)

2.包含关系

一个配置文件在内容上包含另一个配置文件:

<import resources="classpath:xxxxx.xml"/>
  • 配置文件路径前一定要加classpath,为底层指定加载方式,因为配置文件的加载方法有多种:以ClassPath开头的类从类路径加载,以FileSystem开头的类从文件路径加载。在java中加载时使用ClassPath开头的类实际上已经设定了文件的加载方法,程序员只负责加载外层文件,内层文件的加载是由底层负责的,如果不指明加载方式,底层无法加载。底层执行过程大致是:提取classpath,判断,根据判断结果选择加载方式,如果classpath不存在,底层就无法进行判断,无法运行,就会抛出错误。

九 注解式开发

1.注册扫描器

在配置文件中注册注册扫描器:

<context:component-scan base-package="xxx"/>

Spring不同于Hibernate与Struts,没有默认的配置文件,被动加载,必须显式指明配置文件,因此当采用注解开始时也必须显式指明组件位置。

  • 如果包名为xxx,表示扫描该包及其子包。
  • 如果包名为xxx.*,表示只扫描子包。

2.常用注释:

  • @Component(value="相当于配置文件中的id"):表明该类是一个组件,扫描时容器会创建该类的实例,这是bean注解的第一步,先让容器把该类当做需要创建bean的类。作用相同的注解有@Repository(注解在Dao接口的实现类上)\@Service(注解在Service的实现类上)\@Controller(注解在Controller类上)。
  • @Scope(value="singleton/prototype"):设置作用域。
  • @Value(value=""):为一般属性赋值。
  • @Resource(name=""):为域属性赋值。域属性就是自定义类的对象。@Autowired::Spring提供的依照类型的域属性注入注解。
  • @PostConstruct:在bean内部设定在初始化完成之后调用的方法。初始化完成的标识是InitializingBean实现类的方法afterPropertiesSet调用完毕。
  • @PreDestroy:在bean内部设定bean销毁前调用的方法。

十 SpringJUnit4

SpringJUnit4提供了对bean的简化测试方案,进行简单设置之后,测试时就不需要再创建容器,可以直接使用bean:

  1. 在类上加@RunWith(SpringJUnit4ClassRunner.class):表示采用SpringJUnit4进行测试。
  2. 在类上加@ContextConfiguration(locations="classpath:xxxx.xml"):指明配置文件。
  3. 在域属性加@Autowired:按照类型注入,也可以采用其他方式注入

 


  AOP

一概述

1.产生背景

在AOP产生以前,OOP是编程的基本原则,所有对象执行需要的代码都必须写在类中,如果多个类拥有一部分相同的代码,那么这些代码就必须在每个类中都编写,不仅代码大量重复,而且不便于维护,这时就产生了将那些重复出现、与业务无关的代码从业务中分离出来的思想,就是AOP。AOP将重复的、与业务无关的代码从业务中分离处理,业务执行时切入业务中,形式上与业务分离,执行时与业务结合,不仅便于维护,而且降低了业务逻辑部分与非业务逻辑部分的耦合度。

2.原理

Aspect Oriented Program,面向切面编程,建立在动态代理机制之上。

3.重要的概念

  • 切面:对象执行过程中切入流程的、与业务逻辑无关的代码。在程序中,切面是一个集中了通知的类。
  • 切入点:切面切入流程的点,Spring只支持方法类型的切入点,即切面只能在对象方法执行前后切入,不能切入方法内部。
  • 通知:切入点被横切时,所采取的业务逻辑,通知是切面中的方法。
  • 目标对象:切面切入的对象。
  • 织入:将切面功能应用于目标对象的过程。
  • 引用:动态地向目标对象添加属性与方法的过程。

二 Spring自身对AOP的实现

1.原理

Spring自身提供了对AOP思想的实现,主要借助内置接口的实现类与配置文件完成。

2.Advice

通知,切入点被横切时采取的处理逻辑,即在切入点前后执行的方法。在Spring中,Advice是一个层级比较高的接口,具体使用的通知都间接实现了该接口。缺点是,一旦定义就对所有方法都起作用。

  • 前置通知:在切点执行前执行,实现MethodBeforeAdvice实现。
  • 后置通知:在切点成功执行后执行,排在环绕通知之后,方法无返回值,不可以修改返回值,实现AfterReturningAdvice。
  • 环绕通知:在切点执行前与执行成功后执行,可以修改返回值,实现MethodInterceptor。
  • 异常通知:在切点抛出异常时,执行,实现ThrowsAdvice。

3.Advisor

顾问,Advice的装饰者,可以对方法进行筛选,使通知只对选定的方法起作用。常用到的两个实现类:

  • NameMatchMethodPointcutAdvisor:列举选定的方法,使通知只对选定的方法有效。
  • RegexMethodPointccutAdvisor:通过正则表达式匹配方法。

⑴NameMatchMethodPointcutAdvisor

<bean id=""class="Advisor的两个实现类其中一个">
       <property name="advice"ref="通知名"/>//装饰的通知
       <property name="mappedNames"value="方法名"/>//即切点
</bean>

⑵RegexpMethodPointcutAdvisor

<property name="advice"ref="通知名" />
<property name="pattern" value="正则表达式,匹配对象时接口的方法" />

4.ProxyFactoryBean

⑴代理通过ProxyFactoryBean类创建,底层执行Proxy.newProxyInstance(),对比底层实现为该类属性赋值,动态地执行:

<bean id=""class="xxxxxx.ProxyFactoryBean">
         <property name="targetName"name=""/>
         <property name="interceptorNames"value="通知名或顾问名"/>
</bean>

5.基于顾问的自动代理生成器

当有多个目标对象时,使用ProxyFactoryBean创建代理对象时,需要为每一个代理对象编写创建过程,而使用DefaultAdvisorProxyCreator,可以自动为目标对象创建代理。因为DefaultAutoProxyCreator底层实现了BeanPostProcessor,由底层自动调用。

<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/>

6.基于beanName的自动代理生成器

⑴该代理生成器弥补了基于顾问的代理生成器无法选择通知、无法选择目标对象的不足。

<bean id="proxy"class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
         <property name="beanNames" value="target" />//选择要代理的对象
         <property name="interceptorNames" value="methodInterceptor" />//选择切面
</bean>

7.获取代理

无论是基于顾问的,还是基于beanName的自动代理生成器,都可以同时代理多个目标对象,为了区分被代理的目标对象只能通过目标对象来获取代理对象,这样才能保证一个代理对象指向一个目标对象。

三 AspectJ

<aop:config proxy-target-class="true/false">
      <aop:aspect ref="切面所在类的id">
             ---将切点定义在切面内,对其他切面不可见。也可以将切点定义在切面外,对所有切面可见---
                 <aop:pointcut ref="切点所在类的id" expression="execution(切入点表达式)"/>
                 <aop:before method="方法名"pointcut-ref="切点id"/>
                 <aop:after method="方法名"pointcut-ref="切点id"/>
      </aop:aspect>
</aop:config>

1.切入点表达式

用于限制切入点的表达式,与表达式匹配的方法都可以用作切入点,语法格式

[访问权限] 返回值类型 [全限定性类名] 方法名(..)
  • ():匹配不带参数的方法。
  • (*):匹配带一个参数的方法。
  • (..):匹配带任意形式参数或不带参数的方法。
  • *:代表若干字符。
  • ..:代表多级目录。
  • +:代表该类及其子类。

2.其他标签

  • <aop:config>:配置AOP的全部信息,包括切面、切点。proxy-target-class指明所用的代理机制,CGLIB,还是基于接口的代理JDK,默认为false,使用基于接口的代理。
  • <aop:aspect>:配置切面,包含切点前执行的方法、切点后执行的方法、适用的切点等。
  • <aop:pointcut>:定义切点,Spring只支持方法类型的切点,expression="execution(* 方法)"指明用作切点的方法。
  • <aop:before>:指明在切点前执行的方法,pointcut-ref属性指明适用的切点。
  • <aop:after>:指明在切点后执行的方法,无论切点是否成功执行都会执行,pointcut-ref属性指明适用的切点。
  • <aop:after-throwing>:在抛出异常后执行。
  • <aop:after-returning returning="返回值result">:在返回值之后执行,返回值名必须与方法形参名相同。
  • <aop:around>:紧贴代理对象方法前后执行,方法必须有返回值,可以修改目标方法的返回值。当使用环绕通知时,目标对象的方法只在环绕方法内部被调用后才可以执行,因为环绕通知底层采用MethodInterceptor,,而拦截器内部必须显式地调用后面的方法,进程才能推进。目标对象的方法可能有返回值,因此环绕通知必须有返回值。

四 DAO

1.什么是DAO?

Data Access Object,数据访问对象,封装了一些对数据库进行持久化操作的方法,将持久化操作与一般的业务逻辑分开,便于维护与测试。

2.数据源

Spring提供的数据源DriverManagerDataSource,,在实际开发中很少用到,常用到第三方数据源C3P0、DBCP。

DriverManagerDataSource:

<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
       <property name="driverClassName">  <value>com.mysql.jdbc.Driver</value> </property>
       <property name="url"> <value>jdbc:mysql://localhost:3366/db_spring</value>  </property>
       <property name="username"> <value>root</value> </property>
       <property name="password"> <value>123</value> </property>
</bean>

C3P0:

<bean id="c3p0"class="com.mchange.v2.c3p0.ComboPooledDataSource">
       <property name="driverClass" value="com.mysql.jdbc.Driver" />
       <property name="jdbcUrl" value="jdbc:mysql://127.0.0.1:3366/db_spring"/>
       <property name="user" value="root" />
       <property name="password" value="123" />
</bean>

DBCP:

<bean id="dbcp"class="org.apache.commons.dbcp2.BasicDataSource">
      <property name="driverClassName" value="com.mysql.jdbc.Driver" />
      <property name="url" value="jdbc:mysql://127.0.0.1:3366/db_spring"/>
      <property name="username" value="root" />
      <property name="password" value="123" />
</bean>

3.DAO类

⑴JdbcDaoSupport

支持以基础的JDBC技术操作数据库的抽象类,开发者需要设置数据源,通过子类获得JdbcTemplate来访问数据库。利用框架提供的类DriverManagerDataSource创建与数据的连接,然后通过该类的实例获得Connection对象,后续操作同JDBC相同。Dao层实现继承JdbcDaoSupport,内部获取getJdbcTemplate进行底层数据库操作。

Dao的实现类通过JdbcDaoSupport.getJdbcTemplate获取模板对象,通过模板对象封装的方法操作数据库:

  • update:在模板中增删改操作统一用updae方法。
  • queryForObject(slq,requiredClass,arg):用于查询一个字段,并且查询结果唯一。requeredClass表示查询字段的类型,以便对查询结果转型。
  • queryForList(slq,requiredClass,arg):用于查询一个字段,结果可能有多个。
  • queryForObject(sql,rowMapper,arg):查询结果是一个对象,其中rowMapper用于将查询字段封装成一个对象,查询结果必须包含对象的全部属性。
  • query(sql,rowMapper,arg):返回对个对象。
  • 数据源注入时自动创建模板,所以不需要在Dao的实现层显式注入JdbcTemplate的,即注入数据源时自动创建模板。
  • 系统自动在方法结束时销毁JdbcTemplate对象,因此每次在方法中使用时都需要重新创建。
  • HibernateDaoSupport:支持在Hibernate中操作数据库的抽象类,开发者需要设置SessionFactory,然后获得Hibernate的实现,HibernatDaoSupport底层实现复杂,效率低,不建议使用。

4.继承

JdbcDaoSupport/HibernateDaoSupport层级都高于对应的模板类,内部都提供了获取相应模板类的方法,与数库的交互通过模板类来完成。应用Spring提供的DAO模块时,自定义类通常继承XXXDaoSupport类,在自定义方法内部获得响应模板类来具体操作数据库。

五 事务概述

1.原理

Spring事务管理是基于AOP实现的,而Spring的AOP是以方法为单位的,所以Spring的事务属性就是将事务应用到方法上的策略。

2.事务管理器

Spring提供了PlatformTransactionManager接口来管理事务,该类提供了用于事务管理的通知,可以将该类视作切面,两个重要的实现类:

  • DataSourceTransactionManager:适用于JDBC、Mybatis。
  • HibernateTransactionManager:适用于Hibernate。

3.异常

Spring在默认情况下,发生运行时异常回滚,编译时异常提交。程序员可以改变回滚方式。

4.事务属性

事务的四大属性分为传播行为、隔离级别、只读和超时。事务的传播行为用于确定是否将事务应用在方法上以及应用的策略,比如应用当前事务,新建事务。

六 基于Spring AOP的事务管理

1.四个要素

代理工厂、目标对象、切点、切面。

  • 代理工厂:在SpringAOP事务管理中采用TransactionProxyFactoryBean充当代理工厂。
  • 切面:在Spring事务管理中PlatformTransactionManager充当切面。

2.配置文件

<bean id=""class="xxxTransactionProxyFactoryBean">//代理工厂
       <property name="target" ref=""/>//目标对象
       <property name="transactionManager"ref=""/>//切面
         //事务属性,就是事务应用到目标对象方法上的策略
       <property name="transactionAttributes>
             <props>
                   <prop key="目标对象方法">事务四大属性</prop>
             </props>
      </property>
</bean>

3.事务的属性

因为目标对象中有多个方法,并不是所有方法都需要被事务管理,被事务管理的方法所需要的管理方式也不同,因此需要通过事务属性进行个性化定制,如ISOLATION_DEFAULT/PROPAGATION_REQUIRED/-Exception。

4.异常

Spring事务管理默认在发生编译时异常时提交,也可以设置成发生编译时异常回滚,在事务属性中设置:"-Excpetion",以负号开头,加异常,表示当发生该异常时事务回滚。

七 基于AspectJ的事务管理

1.配置文件的编写:

<tx:advice id="advice"tansaction-manager="transactionManger">
         <tx:attributes>
                 <tx:method name="buyStock" isolation="DEFAULT"propagation="REQUIRED"rollback-for="StockProcessException" />
         </tx:attributes>
</tx:advice>
<aop:config>
         <aop:pointcut id="p01"expression="execution(xxxx)"/>
         <aop:advisor advice-ref="advice"pointcut-ref="p01"/>
</aop:config>

八 注解式开发

以上两种事务管理是基于配置文件实现的,缺点是需要为每一个代理对象创建代理工厂,因此提供了基于注解的实现,可以方便地为多个对象创建代理:

在配置文件中添加:

<tx:annotation-driven transaction-manager="transactionManager"/>

在目标对象方法上添加以下注解,设定事务的属性:

@Transactional(isolation =Isolation.DEFAULT, propagation = Propagation.REQUIRED, rollbackFor=StockProcessException.class)
posted @ 2017-05-27 22:14  tonghun  阅读(363)  评论(0编辑  收藏  举报