Spring 是分层的 Java SE/EE 应用 full-stack 轻量级开源框架,以 IoC(Inverse Of Control: 反转控制)和 AOP(Aspect Oriented Programming:面向切面编程)为内核,提供了展示层 Spring MVC 和持久层java
Spring JDBC 以及业务层事务管理等众多的企业级应用技术,还能整合开源世界众多著名的第三方框架和类库,逐渐成为使用最多的 Java EE 企业应用开源框架。web
在软件设计中,若是项目的耦合程度高,那么维护项目所须要的崇本也高。设计一个软件,其耦合度和内聚度一般做为衡量模块独立的标准。而划分模块的一个准则就是高内聚低耦合。spring
耦合是不可能被彻底消除的,只能下降一部分的耦合能用数据耦合(模块间经过参数来传递数据,最低的耦合关系)就不用控制耦合(传递控制信号来执行操做),能用公共耦合(多个模块共拥有给全局数据项)就不用内容耦合(一个模块直接操做另外一个模块的数据,或不经过正常入口而转入另外一个模块,最高程度的耦合)。sql
因此使用配置文件配置key value的全限定类名字符串,就能够动态配置不一样的驱动。编程
而且jdbc的这种方式再也不依赖具体的驱动类。session
因此咱们可使用工厂模式,经过配置文件来配置建立对象的条件,来使用一个读取配置文件,并能建立和获取三层对象的工厂来解耦。框架
比起直接建立对象的主动方式,用工厂来建立对象的方式是被动的。而这种被动接收的方式来获取对象,就是控制反转IOC的思想(下降程序的耦合),他是spring框架的核心之一。dom
下面咱们用一个银行转帐的小demo用3种方式来进行配置(事务控制用了2种方式)
项目结构:
config是配置目录:函数
由SpringConfiguration做为总配置类
JdbcConfig类和TransactionConfig类是配置数据库链接的类和事务控制的类
dao是持久层
domain存放的是实体类
service是服务层
resources
jdbcConfig.properties中存放链接数据库的链接数据
test
AccountServiceTest测试类
由运行顺序讲解代码
经过测试类
由于junit的原理,因此他是没法识别咱们是否使用了Spring框架的,因此必需要用spring的@Runwith来替换Junit原有的运行器
而后使用@ContextConfiguration的classes属性指定 spring 配置文件的位置
@ContextConfiguration 注解:
locations 属性:用于指定配置文件的位置。若是是类路径下,须要用 classpath:代表
classes 属性:用于指定注解的类。当不使用 xml 配置时,须要用此属性指定注解类的位置。
而后咱们经过注解进入配置文件类
扫描全部包就能够识别出全部的注解配置
导入了2个配置类
开启对AOP注解的支持
扫描包的话给出几个配置的典型例子
持久层的实现类:
@Repository持久层注解,
与@Component功能一致
做用:
把资源让 spring 来管理。至关于在 xml 中配置一个 bean。
属性:
value:指定 bean 的 id。若是不指定 value 属性,默认 bean 的 id 是当前类的类名。首字母小写。
@Controller @Service @Repository
他们三个注解都是针对第一个的衍生注解,他们的属性都是如出一辙的。
他们只不过是提供了更加明确的语义化。
@Controller:通常用于表现层的注解。
@Service:通常用于业务层的注解。
@Repository:通常用于持久层的注解。
细节:若是注解中有且只有一个属性要赋值时,且名称是 value,value 在赋值是能够不写。
@Autowired
做用:
自动按照类型注入。当使用注解注入属性时,set 方法能够省略。它只能注入其余 bean 类型。当有多个
类型匹配时,使用要注入的对象变量名称做为 bean 的 id,在 spring 容器查找,找到了也能够注入成功。找不到
就报错。
服务层的实现类:
@Service:业务层的注解
@Autowired自动注入
@Transactional
该注解的属性和 xml 中的属性含义一致。该注解能够出如今接口上,类上和方法上。
出现接口上,表示该接口的全部实现类都有事务支持。
出如今类上,表示类中全部方法有事务支持
出如今方法上,表示方法有事务支持。
以上三个位置的优先级:方法>类>接口
进入导入的第一个配置类
@Value注入基本类型和String类型的数据
@Bean标签,
做用:
该注解只能写在方法上,代表使用此方法建立一个对象,而且放入 spring 容器。
属性:
name:给当前@Bean 注解方法建立的对象指定一个名称(即 bean 的 id)。
而后看另外一个子配置类
仍然是提供了一个获取txManager对象的方法
而后是读取jdbc属性的配置,参数为类路径下的配置文件位置
最后@EnableTransactionManagement的配置是开启AOP的支持,配置了就开启,不配置就关闭
而后就实现了彻底基于注解的配置
能够和上面进行对照更便于理解、使用
* 他们的做用就和在xml配置文件中编写一个<bean>标签实现的功能是同样的
* Component
* 做用:把当前对象存入spring容器中
* 属性:
* value:用于指定bean的id,当咱们不写时,默认值是当前类名且首字母变为小写
* Controller:通常用在表现层
* Service:通常用在业务层
* Repository:通常用在持久层
* 以上三个注解他们的做用和属性与Component是如出一辙的
* 他们三个是spring框架为咱们提供明确的三层使用的注解,使咱们的三层对象更加清晰
*
* 他们的做用就和在xml配置文件中的<bean>标签中写<property>标签的做用是同样的
* Autowired
* private IAccountDao accountDao1=null;
* 访问修饰符 数据类型 变量名称 数据类型
* 注入时主要考虑变量名称与id的匹配
* 做用:
* 自动按照类型注入(接口的名称,而非本身起的id)。只要容器中有惟一的一个bean对象类型和要注入的变量类型匹配就能够注入成功。
* 若是ioc容器中没有任何bean的类型和要注入的变量类型匹配就报错。
* 出现位置:
* 变量、方法
* 细节:
* 在使用注解注入时,set方法不是必须的。
* Qualifier:
* 做用:
* 在按照类中注入的基础上再按照名称注入。他在给类成员注入时不能单独使用(要与Autowired配合)。可是给方法参数注入时能够
* Resource:
* 做用:
* 直接按照bean的id注入。他能够独立使用
* 属性:
* name:用于指定bean的id。
* 以上3中注入都只能注入其余bean类型的数据,而基本类型和String类型没法使用上述注解实现
* 另外集合类型的注入只能经过xml来实现。
*
* value
* 做用:
* 用于注入基本类型和String类型的数据
* 属性:
* value:用于指定数据的值。它可使用spring中SpEL(也就是spring的el表达式)
* SpEL的写法:${表达式},看el表达式出现的位置
*
* 他们的做用和在bean标签中使用scope属性实现的功能是同样的
* Scope
* 做用:
* 用于指定bean的做用范围
* 属性:
* value:指定范围的取值。经常使用取值:singleton prototype,不写默认是单例的
* 他们的做用和在bean标签中使用init-method和destroy-method的做用是同样的
* PostConstruct
* 做用:
* 用于指定初始化方法
* PreDestroy
* 做用:
* 用于指定销毁方法(多例对象的销毁,spring并不负责)
事务控制方面选Z额了spring编程式事务控制
dao是持久层
domain存放的是实体类
service是服务层
resources
bean.xml中存放了基于xml的spring配置信息
test
AccountServiceTest测试类
如下全部重复的部分不作解释:
@ContextConfiguration 注解:使用locations 属性来读取配置文件
locations 属性:用于指定配置文件的位置。若是是类路径下,须要用 classpath:代表
classes 属性:用于指定注解的类。当不使用 xml 配置时,须要用此属性指定注解类的位置。
而后咱们进入配置文件
关于bean标签和依赖注入的细节在下面:
依赖注入使用的是下面依赖注入方法中的set方法
用TransactionTemplate手动管理事务
<!--<bean id="accountService" class="com.lky.service.impl.AccountServiceImpl"></bean>-->
<!--<bean id="instanceFactory" class="com.lky.factory.InstanceFactory"></bean>-->
<!--<bean id="accountService" factory-bean="instanceFactory" factory-method="getAccountService"></bean>-->
<!--<bean id="accountService" class="com.lky.factory.StaticFactory" factory-method="getAccountService"></bean>—>
<!--<bean id="accountService" class="com.lky.service.impl.AccountServiceImpl" scope="prototype"></bean>—>
<!--
依赖注入:
Dependency Injection
IOC的做用:
下降程序间的耦合(依赖关系)
依赖关系的管理:
都交给spring来维护
当前类须要用到其余类的对象,由spring为咱们提供,咱们只须要在配置文件中说明
依赖关系的维护:
称之为依赖注入
依赖注入:
能注入的数据有3类:
基本类型和String
其余bean类型(在配置文件中或注解配置过的bean)
复杂类型/集合类型
注入的方式:
1。使用构造函数提供
2。使用set方法
3。使用注解提供
—>
使用的标签:constructor-arg
出现的位置:bean标签内部
标签中的属性
type:用于指定要注入的数据的类型,该数据类型也是构造函数中某个或某些参数的类型(没法区分多个,因此不能独立实现)
index:用于指定要注入的数据给构造函数中指定索引位置的参数赋值,从0开始(能够独立实现,但必须清楚类型)
name:用于指定给构造函数中指定名称的参数赋值(经常使用)
=============以上3个用于指定给构造函数中的参数赋值=================================
value:用于提供基本类型和String类型的数据
ref:用于指定其余的bean类型的数据。指的是spring的IOC核心容器中出现过的bean对象
优点:
在获取bean对象时,注入数据时必须的操做,不然对象没法建立成功。
缺点:
改变了bean对象的实例化方式,使建立对象时,若是不使用这些数据,也必须提供。
-->
<bean id="accountService" class="com.lky.service.impl.AccountServiceImpl">
<constructor-arg name="name" value="柯"></constructor-arg>
<constructor-arg name="age" value="18"></constructor-arg>
<constructor-arg name="birthday" ref="time"></constructor-arg>
</bean>
<!--配置一个时间对象-->
<bean id="time" class="java.util.Date"></bean>
使用的标签:property
出现的位置:bean标签内部
标签中的属性:
name:用于指定给构造函数中指定名称的参数赋值(经常使用),从set方法中寻找
=============以上3个用于指定给构造函数中的参数赋值=================================
value:用于提供基本类型和String类型的数据
ref:用于指定其余的bean类型的数据。指的是spring的IOC核心容器中出现过的bean对象
优点:
建立对象时没有明确的限制,能够直接使用默认构造函数
缺点:
若是摸个成员必须有值,则获取对象时,set方法没法保证必定注入(可能不执行set),
-->
<bean id="accountService2" class="com.lky.service.impl.AccountServiceImpl2">
<property name="name" value="柯雨"></property>
<property name="age" value="21"></property>
<property name="birthday" ref="time"></property>
</bean>
用于给list集合注入的标签有:
list array set
用于给Map集合注入的标签有:
map props
结构相同,标签能够互换
-->
<bean id="accountService3" class="com.lky.service.impl.AccountServiceImpl3">
<property name="myStrs">
<array>
<value>AAA</value>
<value>BBB</value>
<value>CCC</value>
</array>
</property>
<property name="myList">
<list>
<value>AAA</value>
<value>BBB</value>
<value>CCC</value>
</list>
</property>
<property name="mySet">
<set>
<value>AAA</value>
<value>BBB</value>
<value>CCC</value>
</set>
</property>
<property name="myMap">
<map>
<entry key="a" value="AAA"></entry>
<entry key="b">
<value>BBB</value>
</entry>
</map>
</property>
<property name="myProps">
<props>
<prop key="c">CCC</prop>
<prop key="d">DDD</prop>
</props>
</property>
</bean>
由于采用xml文件配置,因此类中都是基本代码。
重点帖一下服务层实现的部分,由于服务层进行着事务控制
spring编程式事务控制,经过使用transactionTemplate中的excute方法
excute在执行时会使用doInTransaction(status)的方法,而且在执行时,这个doInTransaction的部分实现了rollback,commit等事务操做。
由于经过代码来实现事务很是麻烦,增长开发难度,而且会增长业务层重复的代码。因此通常不多使用。
是由于银行转帐其实是进行事务控制,而事务控制是许多业务的必要部分。
持久层connection 对象的 setAutoCommit(true)方法会使单条sql语句的事务获得成功的执行,但若是业务层同时执行多个持久层方法,而且在中间产生了异常,业务层的此次调用在不进行处理的状况下是没法进行总体自动回滚的,他会执行能成功的语句,回滚失败的语句。此时咱们的银行转帐会出现严重的问题。咱们也能够是用try catch finally的方式去处理,可是由于许多业务都涉及了事务控制,这样去处理,重复的代码量极大,是会增长开发成本的。
动态代理,咱们能够经过动态代理,使须要进行事务控制的方法通过代理类,从而实现事务控制。
而动态代理的实现方式有两种,基于接口和基于子类。
而在spring中,经过配置,框架会根据目标类是否实现了接口来决定采用哪一种动态代理的方式。
这这种解决上述问题的spring配置,称之为AOP
Joinpoint(链接点):
所谓链接点是指那些被拦截到的点。在 spring 中,这些点指的是方法,由于 spring 只支持方法类型的链接点。
Pointcut(切入点):
所谓切入点是指咱们要对哪些 Joinpoint 进行拦截的定义。
Advice(通知/加强):
所谓通知是指拦截到 Joinpoint 以后所要作的事情就是通知。
通知的类型:前置通知,后置通知,异常通知,最终通知,环绕通知。
Introduction(引介):
引介是一种特殊的通知在不修改类代码的前提下, Introduction 能够在运行期为类动态地添加一些方法或 Field。
Target(目标对象):
代理的目标对象。
Weaving(织入):
是指把加强应用到目标对象来建立新的代理对象的过程。
spring 采用动态代理织入,而 AspectJ 采用编译期织入和类装载期织入。
Proxy(代理):
一个类被 AOP 织入加强后,就产生一个结果代理类。
Aspect(切面):
是切入点和通知(引介)的结合。
重复的部分再也不赘述
首先是执行的测试类
而后是读取的bean.xml配置文件
这里的spring关于aop的配置约束能够在spring官网的Core包中找到
首先配置事务管理器
而后配置事务的通知(加强)去引用事务管理器,而这个通知,
就是公共重复的代码部分,也是咱们以前想经过动态代理去加强的部分。
对转帐方法设定了必定有事务的事务传播行为
对全部的find开头方法进行了查询方式的事务加强
切入点表明咱们对业务层实现下的全部方法(链接点)进行拦截
而后把切入点与上述的通知创建关系。
即经过使用AOP的方式实现了事务的控制。