一、HelloWorld程序
- 导入四个核心包(core、beans、expression、context)和一个logging的包;
- 写一个类并在 xml 中配置相应的bean(两个重要属性 id 和 class,注意spring名称空间的引入);
- 通过spring的上下文对象ClassPathXmlApplicationContext加载配置文件,用getBean方法从容器中取出相应的 bean;
二、IOC(控制反转) 和 DI(依赖注入)
1、IOC 和 DI 简单理解
(1)IOC(Inversion of Control,控制反转)
- IOC 就是由spring来负责控制对象的创建销毁和对象间的依赖关系;
- 所有对象的创建销毁都由 spring 来控制,也就是说由spring来控制对象的生存周期;
- 由spring容器帮我们查找及注入依赖对象,对象只是被动的接受依赖对象,依赖对象的获取被反转了;
(2)DI(依赖注入)
- 接管对象的创建工作,并将对象的引用注入到需要该对象的组件;
- 依赖注入的形式有:setter、构造、Autowired注解注入(基于字段);
- IOC的一个重点是在系统运行中,动态的向某个对象提供它所依赖的对象,这一点是通过DI来实现的;
2、bean的配置
(1)基本配置
- id 是唯一标识,通过他,我们可以在容器中找到这个 bean,class 配全限定名,以便spring通过反射来创建bean,所以这个类必须要有空参构造器;
- bean 的创建有两种方法:beanfactory 和 Applicationcontext(一般用这个);
- 依赖注入的方式:有属性注入和构造器注入,属性注入有name 和 value,构造器注入只有value,可以通过index 和 参数的类型(type)来区分参数的对应;
- 引入外部的配置文件:<context:property-placeholder location="classpath:db.properties" />,在当前的配置文件中用 ${ } 的方法来取用相应的值;
(2)bean属性配置(property)
- property标签两个属性 name 和 value,引用类型 value 要换成 ref;
- spring 支持级联属性,使用要注意,首先要初始化bean(属性和构造器都可以),其次就是相应的类属性一定要提供外部访问的接口(getter);
- 属性是集合类型的list(list--ref);set(set--ref);map(map--entry--key--value);properties(props--prop);
- 使用 p 命名空间(先要在 beans 中引入名称空间)可以直接在bean 标签里直接配置属性 P:name = value
- 内部 bean(property-bean):不能为外部所引用;
- 独立集合 bean:方便在其他地方引用,使用前引入 util 命名空间(<util:list>);
(3)属性自动装配(autowire)
- bean 标签中有一个 autowire 属性,其值可以是 byName(属性名和 bean 的 id 一致方可装配成功,否则为 null) 或者 byType(类型相同即可保证装配成功,必须保证单例);
- 注解方式:@autowire(按类型);@autowire加@qualifier可以按名称装配;@Resource原则是先按名称,不行再按类型;
- 注解开发要导入 AOP 的 jar,引入context 并开启属性注解:<context:annotation-config/>;
- 使用注解可以不用写 get 和 set 方法,不用配置在 xml 依赖关系;
(4)bean 之间的关系
- 抽象 bean :abstract=“true” ,不可以实例化,不能被依赖;
- 继承:parent属性指定,继承抽象类就可以间接实例化了;
- 依赖:depends-on来指定,设置了这个属性依赖的类就必须存在不然会报错;
(5)bean 的作用域
- scope="singleton":单例,只创建一个实例,默认就是单例的;
- scope="prototype":每次 get 都会创建一个新的实例;
(6)bean 的生命周期
- bean 标签中可以用 init/destroy-method 指定 bean 创建和销毁时要调用的方法;
- 通过实现 BeanPostProcessor 的 before 和 after 方法,可以实现前置和后置处理;
(7)spEL(spring表达式语言):有点类似于 jsp 中的 el 表达式,具体的作用如下:
3、工厂方法配置 bean
(1)静态工厂
(2)实例工厂
(3)通过 factoryBean 来配置 :实现 FactoryBean 接口,重写 getObject 方法
4、bean 的自动装配
(1)四个注解
- @component(通用)、@repository(dao)、@service、@controller;
- 作用就是当作标识,让 spring 能够找到这些bean,并且实例化;
(2)组件扫描
- <ontext:component-scan>:属性 package 和 useDefaultfilter
(3)扫描过滤
- 用resource-pattern 属性过滤:
- include-filter 和 exclude-filter 标签来实现过滤:注意在 include 的时候一定要指定 udf 为 false;除了可以用注解过滤还可以用类指定过滤的策略;
(4)依赖注入
- 说明:同类型的 bean 有很多个,可以通过@service("service1")和@qualifier("service2")指定名字来区分;
- 说明:@AutoWired注解可以用在属性、方法和构造器上;
(5)泛型依赖注入
- 实际上就是带泛型的父类之间有依赖的关系,子类继承之后会自动拥有这个关系,并且注入的是确定泛型的子类;
- 具体的例子参考:https://blog.csdn.net/u010837612/article/details/45582043;
- 注意:这个特性是 spring4.x 才有的;
三、AOP(面向切面编程)
1、概述
(1)原理:
- AOP采用一种称为“横切”的技术,将涉及多业务流程的通用功能抽取并单独封装,形成独立的切面,在合适的时机将这些切面横向切入到业务流程指定的位置;
- aop底层就是用动态代理来实现的,基于aspectJ框架实现的;
(2)几个概念
- 切面(Aspect):切入业务流程的一个独立模块;
- 通知(Advice):是切面的具体实现方法,可分为前置通知(Before)、后置通知(AfterReturning)、异常通知(AfterThrowing)、最终通知(After)和环绕通知(Around)五种;
- 连接点(joinpoint):业务流程中需要插入切面的具体位置(要增强的方法);
- 切点(cutpoint):用于定义通知应该切入到哪些连接点上,可以理解为连接点的集合;
- 目标对象(target):被一个或者多个切面所通知的对象;
- 代理对象(proxy):将通知应用到目标对象之后被动态创建的对象,代理对象可以理解为目标对象的业务逻辑功能加上被切入的切面所形成的对象;
- 切入(weaving):将切面应用到目标对象从而创建一个新的代理对象的过程;
2、通知
(1)前置通知@before()
- 导包4个并在配置中加入aop 的命名空间;
- 把横切关注点的逻辑提取到切面类中,该类通过注解@Component注入到容器,通过@Aspect标记为切面,在具体的方法上用@before(具体的类和方法)声明切点;before括号中的内容可以用 * 作为通配符;
- 配置文件中开启aop的注解支持;
- 进行测试;
(2)后置通知@after()
- 和前置不同的就是before 变成了 after;
- 实际上后置通知的执行位置应该是方法执行后返回结果前;所以后置通知中无法获取到返回的结果;
- 后置通知无论方法是否发生异常都会执行;
- 所有的通知都可以在参数中加一个joinpoint 来获取连接点的信息;
(3)返回通知@AfterReturning(value="",returning="")
- 没有发生异常的情况下可以获取到方法的返回值;
(4)异常通知@AfterThrowing(value="",throwing="")
- 发生异常的情况下会执行异常通知;
- 可以针对特点的异常来触发通知;在异常通知里可以获取到异常的相关信息;
(5)环绕通知@Around()
- 本身就类似于一个代理对象了,可以实现所有的前置后置之类的通知;
- 其中重要的参数是 ProceedingJoinPoint,这个参数可以控制目标对象方法的执行;
- 环绕通知必须有返回值,返回值就是目标方法的返回值;
- 环绕通知会吞噬异常,所以不能与异常通知一起使用,用了也白用;
(6)其他说明
- 切面优先级:在类的上方用 @Order(1)来指定,数字越小优先级越高;
- 切点重用:在类中用 @Pointcut 来指定一个空的方法为切点,后面直接在通知的注解里写方法的名称即可引用;
- 通知执行顺序:around(before)--before--executeMethod--around(after)--after--afterReturning(afterThrowing)
- 切点表达式:下面的表达式的含义是匹配 com.stan.test 这个包及其所有子包任意类的任意方法(任意参数和返回值);
@Pointcut("execution(* com.stan.test..*.*(..))")
public void pointCut(){}
@Before("pointCut()")
public void doBefore(JoinPoint joinPoint){
System.out.println("AOP Before Advice...");
}
3、在配置文件中配置 AOP
(1)配置目标对象的bean;
(2)配置切面的 bean;
(3)配置 Aop:
<bean id="userService" class="com.stan.test.UserService"></bean>
<bean id="operator" class="com.stan.test.Operator"></bean>
<aop:config>
<aop:pointcut expression="execution(* com.stan.test..*.*(..))" id="pointcut1"/>
<aop:aspect ref="operator">
<aop:before method="doBefore" pointcut-ref="pointcut1"/>
<aop:after-returning method="afterReturn" pointcut-ref="pointcut1" returning="returnVal"/>
</aop:aspect>
</aop:config>
4、jdbctemplate 和 jdbcdaosurpport 使用
(1)jdbctemplate:完成增删改查的操作
- 在spring 的配置文件中配置数据源和 jdbctemplate;
- dataSource 注入 jdbcTemplate,jdbcTemplate 注入 dao;
- 数据库的增删改操作都是使用 update(sql,field,parameter) 方法;
- 字段的查询和对象的查询均使用 queryForObject,对象的查询要定义一个 RowMapper 来指定结果集的映射规则(对象的属性和数据表的字段映射);
- query 方法可以得到对象集合,同样要使用 RowMapper 来指定映射规则;
(2)jdbcdaosurpport:
- 不推荐使用,使用的时候要注入datasource 或者 jdbctemplate
- 本质上还是通过jdbctemplate 来操作数据库
(3)NamedparameterJdbctemplate
- 这个 template 的特点是在sql使用占位符的时候指定一个名字,而不是使用?,指定名字的格式为(:name);
- 其中的指定的名称要和 bean 的属性一致才能正确设置参数;
- 具体的使用示例参考链接:https://www.cnblogs.com/sharpest/p/5622884.html
四、spring 事务
1、事务概述
⑴ 原子性(Atomicity)
- 事务包含的所有操作要么全部成功,要么全部失败回滚;
⑵ 一致性(Consistency)
- 事务必须使数据库从一个一致性状态变换到另一个一致性状态,也就是说一个事务执行之前和执行之后都必须处于一致性状态;
⑶ 隔离性(Isolation)
- 多个并发事务之间要相互隔离(事务的隔离级别参考数据库部分);
⑷ 持久性(Durability)
- 一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的,即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作;
2、spring 的声明式事务
(1)使用步骤:
- 配置一个 transactionManager,注入 dataSource;
- 配置事务的注解驱动;
- 在需要加入事务管理的 service 方法上加上@Transactional 的注解;
<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 使得事务注解生效 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
(2)xml 方式配置事务
- 正常配置 bean;
- 配置 transactionManager;
- 配置事务属性 tx:advice;
- 在 aop:config 配置切点 ,并把切点和事务属性关联;
<tx:advice id="advice1" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="purchase" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="pointcut1" expression="execution(* com.stan.transaction..*(..))"/>
<aop:advisor advice-ref="advice1" pointcut-ref="pointcut1"/>
</aop:config>
(3)应用示例:https://www.cnblogs.com/caoyc/p/5632198.html
3、事务的属性(在 tx:method 标签中配置相关属性)
(1)传播行为
- 指的就是一个事务方法对另一个事务方法的影响(二者有调用的关系);
- 默认的传播行为是 required ,在事务注解里加上参数propagation = propagation.required,required 就是被调用方法沿用调用者的事务;
- 另一个属性的值是 require new 就是被调用方法用自己的事务,方法执行时调用者的事务挂起;
(2)隔离级别 | 回滚 | 只读 | 过期
- 隔离级别:在事务注解中用 isolation 配置;
- 回滚:norollBackfor 一般不用配,默认即可;
- 只读:readOnly 只是查询的话可以配置为true,有利于数据库引擎的优化;
- 过期:timeout 可以避免一个事务长时间占用数据库资源;
五、SSH整合
1、spring 和 hibernate 整合
(1)整合目的:就是让spring 管理 hibernate 的sessionFactory ,并且实现声明式事务管理;
(2)整合步骤
1、加入hibernate
- 加入 jar;
- 配置 hibernate.cfg.xml 和 *.hbm.xml 文件;
- hibernate 的配置文件里只用配置数据库方言、缓存啥的,甚至是可以不要cfg 配置文件;
2、加入spring
- 加入 jar;
- 编写配置文件:dataSource-->sessionFactory(dataSource、configLocation、mappingLocations、hibernateProperties)-->事务配置;
- 事务配置还是老样:配置 transactionManager----tx:advice---aop:config
(3)spring 管理 hibernate 事务流程(就是 AOP 的思想)
- 方法开始之前:获取 session 和当前线程绑定,这样就可以在 dao 中用sessionFactory的getCurrentSession()方法来获取,并开启事务;
- 方法正常结束:提交事务,解除session 和当前线程的绑定,关闭 session;
- 方法发生异常:回滚,解除绑定,关闭session;
2、spring 和 struts 整合
(1)web 环境下使用Spring
- 加入两个 jar,一个 web 和一个 webmvc;
- spring 配置文件无特殊配置;
- web xml 文件中需要加入两个配置,一个spring配置文件位置的全局变量和一个监听器,监听器的作用就是在servletContext创建的时候,创建一个 ioc 容器,并把 ioc 容器放到servletContext 中;
(2)spring 整合 struts 的目标是让 spring 来管理 struts 的Action
(3)具体整合步骤:
- 正常加入 struts;
- 在 spring 的 ioc 容器中配置 Action,配置 action 需将 scope 属性设置为 prototype;
- 配置 struts 的配置文件:class属性的值不能再是全路径名了,而是 spring 中的bean id,具体的原理就是先根据bean 的id 到 ioc 容器中去找action,找不到再自己创建一个;
- 加入 struts-spring-plugin 的jar 包,这个 jar 包中有一个对象工厂;