Spring
Spring
Spring是一个开源框架,spring是2003兴起的一个轻量级Java框架,由Rob Johnson创建,简单spring是一个分层的JavaSE/EEfull-stack(一站式)轻量级开源框架.
JavaEE开发规范规定我们的程序应该要分为三层:WEB层、业务层、持久层。每一层的含的含义如下:
WEB层(页面数据显示、页面跳转调度)-jsp/servlet
业务层(业务处理和功能逻辑、事务控制)-service
持久层(数据存取和封装、和数据库打交道)-dao

Spring的优势
方便解耦,简化开发
通过Spring提供的IoC容器,可以将对象间的依赖关系交由Spring进行控制,避免硬编码所造成的过度程序耦合。用户也不必再为单例模式类、属性文件解析等这些很底层的需求编写代码,可以更专注于上层的应用。
AOP编程的支持
通过Spring的AOP功能,方便进行面向切面的编程,许多不容易用传统OOP实现的功能可以通过AOP轻松应付。
声明式事务的支持
可以将我们从单调烦闷的事务管理代码中解脱出来,通过声明式方式灵活的进行事务的管理,提高开发效率和质量。
方便程序的测试
可以用非容器依赖的编程方式进行几乎所有的测试工作,测试不再是昂贵的操作,而是随手可做的事情。
方便集成各种优秀框架
Spring可以降低各种框架的使用难度,提供了对各种优秀框架(Struts、Hibernate、Hessian、Quartz等)的直接支持。
降低JavaEE API的使用难度
Spring对JavaEE API(如JDBC、JavaMail、远程调用等)进行了薄薄的封装层,使这些API的使用难度大为降低。
IOC
控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。其中最常见的方式叫做依赖注入(Dependency Injection,简称DI),还有一种方式叫“依赖查找”(Dependency Lookup)。通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体将其所依赖的对象的引用传递给它。也可以说,依赖被注入到对象中。
DI(依赖注入)其实就是IOC的另外一种说法,DI是由Martin Fowler 在2004年初的一篇论文中首次提出的。他总结:控制的什么被反转了?就是:获得依赖对象的方式反转了。
IOC,它是Inverse of Control的缩写,中文含义是控制反转,表示将对象的创建权力反转给Spring框架!!
IOC解决的问题:使用IOC可以解决的程序耦合性高的问题!!
Spring中的工厂
在spring中提供了两个工厂接口:
1、ApplicationContext接口
使用该接口可以获取到具体的Bean对象
该接口下有两个具体的实现类
* ClassPathXmlApplicationContext -- 加载类路径下的Spring配置文件
* FileSystemXmlApplicationContext -- 加载本地磁盘下的Spring配置文件
2、BeanFactory接口
BeanFactory是Spring框架早期的创建Bean对象的工厂接口。
BeanFactory和ApplicationContext的区别
* BeanFactory -- BeanFactory采取延迟加载,第一次getBean时才会初始化Bean
* ApplicationContext -- 在加载applicationContext.xml时候就会创建具体的Bean对象的实例
Spring生成bean的三种方式
1. 无参构造方法
默认调用无参构造方法实例化bean。在此之前,都是调用无参构造来实例化的。
2. 静态工厂实例化方式
通过调用工厂类的静态方法来生成bean
3. 实例工厂实例化方式
修改Factory工厂类,创建实例工厂方法
依赖注入
1.什么是依赖注入
IOC和DI的概念:
* IOC -- Inverse of Control,控制反转,将对象的创建权反转给Spring!!
* DI -- Dependency Injection,依赖注入,在Spring框架负责创建Bean对象时,动态的将依赖对象注入到Bean组件中!!
2.构造方法注入
构造方法注入就是利用bean的构造方法完成对bean中属性的赋值。
3.set方法注入
set方法注入就是利用bean中属性的set方法对属性赋值。
通过set方法注入还有其它两种写法,这两种写法都是spring在新的版本中提供的写法:
1、p命名空间的写法,语法:
p:简单属性名=”值”
p:对象属性名-ref=”bean的id”
2、SpEL的写法(spring3.0提供),语法:
注入字符串:#{‘字符串’},
注入数字:#{数字},
注入其它对象:#{对象id}
SpEL还可以注入其它对象的属性或方法的返回值
4.数组或list注入
新建bean类:
public class CollectionBean implements Serializable{ private static final long serialVersionUID = 1L; private List<String> list;//也可以是数组 public void setList(List<String> list) { this.list = list; } @Override public String toString() { return "CollectionBean [list=" + list + "]"; } }
在applicationContext.xml中配置CollectionBean
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- 构造方法注入 --> <bean id="car" class="cn.an.test.Car"> <constructor-arg name="name" value="A6"></constructor-arg> <constructor-arg name="price" value="57.3"></constructor-arg> </bean> <!-- 第二种注入形式:set方法注入 --> <!-- <bean id="people" class="cn.an.test.People"> <property name="name" value="小明"></property> <property name="address" value="上海"></property> <property name="car" ref="car"></property> </bean> --> <!-- <bean id="people" class="cn.an.test.People" p:name="小刚" p:address="北京" p:car-ref="car"></bean> --> <bean id="people" class="cn.an.test.People"> <property name="name" value="#{'小明'}"></property> <property name="address" value="#{'上海'}"></property> <property name="car" value="#{car}"></property> </bean> <!-- SpEL的写法 --> <bean id="carInfo" class="cn.an.test.CarInfo"></bean> <bean id="car2" class="cn.itcast.domain.Car2"> <property name="name" value="#{carInfo.name}"></property> <property name="price" value="#{carInfo.calculatePrice()}"></property> </bean> <!-- 特殊类型的注入 --> <bean id="cb" class="cn.an.test.CollectionBean"> <property name="list"> <list> <value>fjg</value> <value>sdg</value> <value>sdg</value> </list> </property> </bean> </beans>
5.Set集合的注入
public void setSet(Set<String> set) { this.set = set; }
<property name="set">
<set>
<value>asdghf</value>
<value>sdg</value>
<value>asgds</value>
</set>
</property>
提示:spring在注入set的时,给我们注入的是一个LinkedHashSet,所以在输入set集合中的元素时,是按照我们注入的顺序来的,并不是无序的。
6.Map集合的注入
private Map<String,String> map; public void setMap(Map<String, String> map) { this.map = map; }
<property name="map">
<map>
<entry key="id" value="1"></entry>
<entry key="username" value="张三"></entry>
</map>
</property>
说明:
<entry>表示map中的一个键值对;
<entry>的key表示键,value表示值;
7.properties的注入
修改CollectionBean,在其中添加一个Properties类型的属性并提供set方法:
public class CollectionBean implements Serializable{ /** * */ private static final long serialVersionUID = 1L; private List<String> list;//也可以也成数组 private Set<String> set; private Map<String,String> map; private Properties properties; public void setList(List<String> list) { this.list = list; } public void setSet(Set<String> set) { this.set = set; } public void setMap(Map<String, String> map) { this.map = map; } public void setProperties(Properties properties) { this.properties = properties; } @Override public String toString() { return "CollectionBean [list=" + list + ", set=" + set + ", map=" + map + ", properties=" + properties + "]"; } }
在applicationContext.xml中给Properties类型的属性注入值:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- 构造方法注入 --> <bean id="car" class="cn.an.test.Car"> <constructor-arg name="name" value="奥迪A6"></constructor-arg> <constructor-arg name="price" value="57.3"></constructor-arg> </bean> <!-- 第二种注入形式:set方法注入 --> <!-- <bean id="people" class="cn.an.test.People"> <property name="name" value="小明"></property> <property name="address" value="上海"></property> <property name="car" ref="car"></property> </bean> --> <!-- <bean id="people" class="cn.an.test.People" p:name="小刚" p:address="北京" p:car-ref="car"></bean> --> <bean id="people" class="cn.an.test.People"> <property name="name" value="#{'小明'}"></property> <property name="address" value="#{'上海'}"></property> <property name="car" value="#{car}"></property> </bean> <!-- SpEL的写法 --> <bean id="carInfo" class="cn.an.test.CarInfo"></bean> <bean id="car2" class="cn.an.test.Car2"> <property name="name" value="#{carInfo.name}"></property> <property name="price" value="#{carInfo.calculatePrice()}"></property> </bean> <!-- 特殊类型的注入 --> <bean id="cb" class="cn.an.test.CollectionBean"> <property name="list"> <list> <value>sdf</value> <value>sdf</value> <value>sdf</value> </list> </property> <property name="set"> <set> <value>sdf</value> <value>sdf</value> <value>sdf</value> </set> </property> <property name="map"> <map> <entry key="id" value="1"></entry> <entry key="username" value="张三"></entry> </map> </property> <property name="properties"> <props> <prop key="id">2</prop> <prop key="name">小明</prop> </props> </property> </bean> </beans>
说明:
<prop>:表示一个键值对;
<prop>中的key表示键,在<prop></prop>中写的是值;
配置文件的分离:
方式一:在applicationContext.xml中采用import标签导入另一个配置文件
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="userDao" class="cn.an.dao.impl.UserDaoImpl" init-method="init" destroy-method="destroy"></bean> <!-- <bean id="deptDao" class="cn.an.factory.Factory" factory-method="create"></bean> --> <!-- 实例工厂方法来实例化 --> <bean id="factory" class="cn.an.factory.Factory"></bean> <bean id="deptDao" factory-bean="factory" factory-method="create"></bean> <!-- 构造方法注入 --> <bean id="car" class="cn.an.test.Car"> <constructor-arg name="name" value="A6"></constructor-arg> <constructor-arg name="price" value="57.3"></constructor-arg> </bean> <import resource="applicationContext2.xml"/> </beans>
方式二:在实例化ApplicationContext的时候,指定多个配置文件。
@Test public void test4(){ //创建Spring工厂(创建IOC容器) ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml","applicationContext2.xml"); DeptDao deptDao = (DeptDao) ac.getBean("deptDao"); deptDao.save(); }
Spring框架中bean管理的常用注解
Spring中用于管理bean的注解分为四大类:
1、用于创建对象
2、用于给对象中的属性注入值
3、用于改变作用范围
4、用于定义生命周期
1.用于创建对象
用于创建对象的有四个:
@Component
作用:
把资源让spring来管理。相当于在xml中配置一个bean。
属性:
value:指定bean的id。如果不指定value属性,默认bean的id是当前类的类名。首字母小写。
@Controller(表现层)
@Service(业务层)
@Repository(持久层)
2、用于给对象中的属性注入值
用于注入数据的注解有:
1、@Value
作用:
注入基本数据类型和String类型数据的
属性:
value:用于指定值
2、@Autowired
作用:
自动按照类型注入。当使用注解注入属性时,set方法可以省略。它只能注入其他bean类型。当有多个类型匹配时,使用要注入的对象变量名称作为bean的id,在spring容器查找,找到了也可以注入成功。找不到就报错。
3、@Qualifier
作用:
在自动按照类型注入的基础之上,再按照Bean的id注入。它在给字段注入时不能独立使用,必须和@Autowire一起使用;但是给方法参数注入时,可以独立使用。
属性:
value:指定bean的id。
4、@Resource
作用:
直接按照Bean的id注入。它也只能注入其他bean类型。
属性:
name:指定bean的id。
相当于:<property name="" ref="">
<property name="" value="">
3、用于改变作用范围
@Scope
作用:
指定bean的作用范围。
属性:
value:指定范围的值。
取值:singleton(默认) prototype request session globalsession
4、用于定义生命周期
相当于:<bean id="" class="" init-method="" destroy-method="" />
@PostConstruct
加在方法上,指定bean对象创建好之后,调用该方法初始化对象,类似于xml的init-method方法。
@PreDestory
加在方法上,指定bean销毁之前,调用该方法,类似于xml的destory-method方法。
XML和注解的比较
注解的优势:
配置简单,维护方便。(我们找到了类,就相当于找到了配置)
XML的优势:
修改时,不用改源码。不涉及重新编译和部署。

(https://www.cnblogs.com/xdp-gacl/p/4249939.html)
AOP
Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
AOP(Aspect Orient Programming),我们一般称为面向方面(切面)编程,作为面向对象的一种补充,用于处理系统中分布于各个模块的横切关注点,比如事务管理、日志、缓存等等。AOP实现的关键在于AOP框架自动创建的AOP代理,AOP代理主要分为静态代理和动态代理,静态代理的代表为AspectJ;而动态代理则以Spring AOP为代表。
AOP的作用:在不修改源代码的情况下,可以实现功能的增强。
AOP 思想: 基于代理思想,对原来目标对象,创建代理对象,在不修改原对象代码情况下,通过代理对象,调用增强功能的代码,从而对原有业务方法进行增强 !
AOP的应用场景
场景一: 记录日志
场景二: 监控方法运行时间 (监控性能)
场景三: 权限控制
场景四: 缓存优化 (第一次调用查询数据库,将查询结果放入内存对象, 第二次调用, 直接从内存对象返回,不需要查询数据库 )
场景五: 事务管理 (调用方法前开启事务, 调用方法后提交关闭事务 )
AOP的实现原理:
1、JDK动态代理(通过反射跟动态编译来实现的)
JDK动态代理通过反射来接收被代理的类,并且要求被代理的类必须实现一个接口。JDK动态代理的核心是InvocationHandler接口和Proxy类。
2、CGLIB动态代理(通过修改字节码来实现代理)
如果目标类没有实现接口,那么Spring AOP会选择使用CGLIB来动态代理目标类,实际上是生成了目标类的子类来增强。CGLIB(Code Generation Library),是一个代码生成的类库,可以在运行时动态的生成某个类的子类,注意,CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final,那么它是无法使用CGLIB做动态代理的。
(http://www.importnew.com/24305.html)
AOP核心概念
1)连接点(Joinpoint):所谓连接点,是指那些被连接到的点,在spring中,这些点指的是方法,因为spring只支持方法类型的连接点.
2)切点(Pointcut):所谓切入点是指我们要对哪些Joinpoint进行拦截的定义.
3)增强/通知(Advice):所谓通知是指拦截到Joinpoint之后要做的事情就是通知,通知分为前置通知,后置通知,异常通知,最终通知,环绕通知(切面要完成的功能).
4)引介(Introduction):引介是一种特殊的通知,在不修改类代码的前提下,Introduction可以在运行期为类动态地添加一些方法或Field.
5)目标对象(Target):代理的目标对象.
6)织入(Weaving):是指把增强应用到目标对象来创建新的代理对象的过程.spring采用动态代理织入,而AspectJ采用编译期织入和类装在期织入.
7)代理(Proxy):一个类被AOP织入增强后,就产生一个结果代理类.
8)切面(Aspect):是切入点和通知(引介)的结合.
(https://www.cnblogs.com/yangyquin/p/5462488.html)
Aop代码编写流程
1、编写目标类
2、编写切面类以及通知方法
3、把目标类和切面类交给spring管理
4、指定切面类:用哪个类来增强
5、配置切入点表达式:指定哪些方法需要增强
配置通知:指定遇到满足切入点表达式的方式用切面类中的哪个方法增强
前置通知:
应用: 权限控制 (权限不足,抛出异常)、 记录方法调用信息日志
后置通知:
特点:在目标方法运行后,返回值后执行增强代码逻辑。
应用场景:与业务相关的,如ATM取款机取款后,自动下发短信。
环绕通知:
特点:目标执行前后,都进行增强(控制目标方法执行)
应用场景:日志、缓存、权限、性能监控、事务管理
增强代码的方法要求:
接受的参数:ProceedingJoinPoint(可执行的连接点)
返回值:Object返回值
抛出Throwable异常。
异常通知:
作用:目标代码出现异常,通知执行。记录异常日志、通知管理员(短信、邮件)
应用场景:处理异常(一般不可预知),记录日志
最终通知:
作用:不管目标方法是否发生异常,最终通知都会执行(类似于finally代码功能)
应用场景:释放资源 (关闭文件、 关闭数据库连接、 网络连接、 释放内存对象 )
注意,最终通知和后置通知的区别:最终通知,不管异常与否,都执行;而后置通知在异常时不执行。
基于XML的Spring AOP
/** * 切面类 * * */ public class MyAspectXml { /** * 前置通知方法 应用场景: 权限控制 (权限不足,抛出异常)、 记录方法调用信息日志 joinPoint:连接点,指的是被增强的那个方法 */ public void before(JoinPoint joinPoint) { String username = "rose"; if (!"admin".equals(username)) { // 非admin用户,不具备权限,抛出异常 // joinPoint.getTarget().getClass().getName()获取目标类的名字 // joinPoint.getSignature().getName()获取被增强方法的名字 throw new RuntimeException("对不起!您没有对" + joinPoint.getTarget().getClass().getName() + "类中" + joinPoint.getSignature().getName() + "方法的访问权限"); } } /** * 后置通知方法 应用场景: ATM取款机取款后,自动下发短信 参数result:被增强那个方法的返回值 */ public void afterReturning(JoinPoint joinPoint, Object result) { Date date = new Date(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String dateStr = sdf.format(date); System.out.println("尊敬的用户,您于" + dateStr + "取款" + result + "元"); } /** * 环绕通知方法 应用场景:事务处理 * * @param proceedingJoinPoint * 正在执行的连接点 * @return */ public Object around(ProceedingJoinPoint proceedingJoinPoint) { System.out.println("开启事务"); // 获取目标方法的参数 Object[] args = proceedingJoinPoint.getArgs(); Object result = null; try { // 调用目标方法,获取目标方法的返回值 result = proceedingJoinPoint.proceed(args); System.out.println("提交事务"); } catch (Throwable e) { System.out.println("回滚事务"); } // 返回目标方法的返回值 return result; } /** * 异常通知方法 应用场景:处理异常 * * @param ex * 目标方法抛出的异常 */ public void afterThrowing(JoinPoint joinPoint, Throwable ex) { System.out.println("注意了:在" + joinPoint.getTarget().getClass().getName() + "中的" + joinPoint.getSignature().getName() + "方法中发生了异常:" + ex.getMessage()); } /** * 最终通知方法 应用场景:释放资源 (关闭文件、 关闭数据库连接、 网络连接、 释放内存对象 ) * * @param joinPoint * 被增强的那个方法 */ public void after(JoinPoint joinPoint) { System.out.println("开始释放资源,对应的连接点信息为:" + joinPoint.getTarget().getClass().getName() + "的" + joinPoint.getSignature().getName() + "方法"); } }
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <bean id="customerDao" class="cn.an.dao.impl.CustomerDaoImpl"></bean> <bean id="myAspectXml" class="cn.an.aspect.MyAspectXml"></bean> <!-- AOP配置 --> <aop:config> <!-- 配置切入点 --> <aop:pointcut expression="execution(* cn.an.dao.impl.CustomerDaoImpl.save(..))" id="pointcut1" /> <aop:pointcut expression="execution(* cn.an.dao.impl.CustomerDaoImpl.delete(..))" id="pointcut2" /> <aop:pointcut expression="execution(* cn.an.dao.impl.CustomerDaoImpl.update(..))" id="pointcut3" /> <aop:pointcut expression="execution(* cn.an.dao.impl.CustomerDaoImpl.list(..))" id="pointcut4" /> <!-- 配置切面 --> <aop:aspect ref="myAspectXml"> <!-- 前置通知 --> <aop:before method="checkPrivilege" pointcut-ref="pointcut1" /> <!-- 后置通知 --> <aop:after-returning method="afterReturning" pointcut-ref="pointcut2" returning="result" /> <!-- 环绕通知 --> <aop:around method="around" pointcut-ref="pointcut3" /> <!-- 抛出异常通知 --> <aop:after-throwing method="afterThrowing" pointcut-ref="pointcut4" throwing="ex" /> <!-- 最终通知 --> <aop:after method="after" pointcut-ref="pointcut4" /> </aop:aspect> </aop:config> </beans>
基于注解的Spring AOP
/** * 切面类 * */ @Component @Aspect//表示该类是一个切面类 public class MyAspectAnnotation { /*@Before("execution(* cn.an.dao.impl.CustomerDaoImpl.save(..))") public void writeLog(){ System.out.println("记录日志啦....."); }*/ /** * 前置通知方法 应用场景: 权限控制 (权限不足,抛出异常)、 记录方法调用信息日志 joinPoint:连接点,指的是被增强的那个方法 */ @Before("execution(* cn.an.dao.impl.CustomerDaoImpl.save(..))") public void before(JoinPoint joinPoint) { String username = "rose"; if (!"admin".equals(username)) { // 非admin用户,不具备权限,抛出异常 // joinPoint.getTarget().getClass().getName()获取目标类的名字 // joinPoint.getSignature().getName()获取被增强方法的名字 throw new RuntimeException("对不起!您没有对" + joinPoint.getTarget().getClass().getName() + "类中" + joinPoint.getSignature().getName() + "方法的访问权限"); } } /** * 后置通知方法 应用场景: ATM取款机取款后,自动下发短信 参数result:被增强那个方法的返回值 * returning属性指定目标方法返回值的名字 */ @AfterReturning(value="execution(* cn.an.dao.impl.CustomerDaoImpl.delete(..))",returning="result") public void afterReturning(JoinPoint joinPoint, Object result) { Date date = new Date(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String dateStr = sdf.format(date); System.out.println("尊敬的用户,您于" + dateStr + "取款" + result + "元"); } /** * 环绕通知方法 应用场景:事务处理 * * @param proceedingJoinPoint * 正在执行的连接点 * @return */ @Around("execution(* cn.an.dao.impl.CustomerDaoImpl.update(..))") public Object around(ProceedingJoinPoint proceedingJoinPoint) { System.out.println("开启事务"); // 获取目标方法的参数 Object[] args = proceedingJoinPoint.getArgs(); Object result = null; try { // 调用目标方法,获取目标方法的返回值 result = proceedingJoinPoint.proceed(args); System.out.println("提交事务"); } catch (Throwable e) { System.out.println("回滚事务"); } // 返回目标方法的返回值 return result; } /** * 异常通知方法 应用场景:处理异常 * * @param ex * 目标方法抛出的异常 */ @AfterThrowing(value="execution(* cn.an.dao.impl.CustomerDaoImpl.list(..))",throwing="ex") public void afterThrowing(JoinPoint joinPoint, Throwable ex) { System.out.println("注意了:在" + joinPoint.getTarget().getClass().getName() + "中的" + joinPoint.getSignature().getName() + "方法中发生了异常:" + ex.getMessage()); } /** * 后置通知方法 应用场景:释放资源 (关闭文件、 关闭数据库连接、 网络连接、 释放内存对象 ) * * @param joinPoint * 被增强的那个方法 */ @After("execution(* cn.an.dao.impl.CustomerDaoImpl.list(..))") public void after(JoinPoint joinPoint) { System.out.println("开始释放资源,对应的连接点信息为:" + joinPoint.getTarget().getClass().getName() + "的" + joinPoint.getSignature().getName() + "方法"); } }
Spring整合DBUtils实现增删改查
1.引入依赖
<artifactId>spring-context</artifactId>
<artifactId>slf4j-log4j12</artifactId>
<artifactId>junit</artifactId>
<artifactId>mysql-connector-java</artifactId>
<artifactId>commons-dbutils</artifactId>
<artifactId>druid</artifactId>
2.创建业务层接口及实现类
public interface CustomerService { /** * 业务层:查询所有客户 * @return */ public List<Customer> findAllCustomer(); }
3.创建dao层接口及实现类
public interface CustomerDao { public List<Customer> findAll(); }
4.编写spring配置
创建spring的配置文件applicationContext.xml,把Service、Dao、QueryRunner、DataSource配置到Spring中
1、Service中需要注入DAO
2、DAO中需要注入QueryRunner
3、QueryRunner中需要注入DataSource
4、DataSource中需要注入驱动、连接地址、用户名、密码
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="customerService" class="cn.an.service.impl.CustomerServiceImpl"> <property name="customerDao" ref="customerDao"></property> </bean> <bean id="customerDao" class="cn.an.dao.impl.CustomerDaoImpl"> <property name="queryRunner" ref="queryRunner"></property> </bean> <bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner"> <constructor-arg name="ds" ref="dataSource"></constructor-arg> </bean> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"></property> <property name="url" value="jdbc:mysql://localhost:3306/mybatis"></property> <property name="username" value="root"></property> <property name="password" value="123456"></property> </bean> </beans>
注意:还得引入log4j.properties文件
采用注解把spring和DBUtils整合
1.在类上加对应注解修改CustomerServiceImpl类,在该类上加@Service注解。在customerDao上加@Autowired注解,表示给该属性注入值
@Service("customerService")
public class CustomerServiceImpl implements CustomerService {
@Autowired
private CustomerDao customerDao;
@Override
public List<Customer> findAllCustomer() {
List<Customer> list = customerDao.findAllCustomer();
return list;
}
}
2.修改CustomerDaoImpl类,在该类上加@Repository注解。在queryRunner上加@Autowired注解,表示给该属性注入值。
@Repository("customerDao")
public class CustomerDaoImpl implements CustomerDao {
@Autowired
private QueryRunner queryRunner;
@Override
public List<Customer> findAll() {
List<Customer> list = null;
try {
list = queryRunner.query("select * from cst_customer", new BeanListHandler<Customer>(Customer.class));
} catch (SQLException e) {
e.printStackTrace();
}
return list;
}
}
3.修改spring配置,在applicationContext.xml中开启spring注解扫描。
注意:QueryRunner和DataSource这两个bean暂时没有办法用注解来配置,因为属于jar包里的bean.
<?xml version="1.0" encoding="UTF-8"?> <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"> <context:component-scan base-package="cn.an"></context:component-scan> <bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner"> <constructor-arg name="ds" ref="dataSource"></constructor-arg> </bean> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"></property> <property name="url" value="jdbc:mysql://localhost:3306/mybatis"></property> <property name="username" value="root"></property> <property name="password" value="root"></property> </bean> </beans>
spring框架整合JUnit单元测试
1.引入依赖
junit
spring-test
2.具体类上添加注解
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
@Test
@RunWith注解:这个是指定使用的单元测试执行类,这里就指定的是SpringJUnit4ClassRunner.class;
@ContextConfiguration注解:这个指定spring配置文件所在的路径,可以同时指定多个文件;
事务的概念 事务是逻辑上一组操作,组成这组操作各个逻辑单元,要么一起成功,要么一起失败。 事务的特性 原子性 一致性 隔离性 持久性 事物并发引起一些读的问题 脏读 不可重复读 虚读(幻读) 解决读问题 设置事务隔离级别 read uncommitted read committed repeatable read Serializable



事务的隔离级别

事务的传播行为
REQUIRED:如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。一般的选择(默认值)
REQUERS_NEW:新建事务,如果当前在事务中,把当前事务挂起。
SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行(没有事务)
MANDATORY:使用当前的事务,如果当前没有事务,就抛出异常
NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起
NEVER:以非事务方式运行,如果当前存在事务,抛出异常
NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行REQUIRED类似的操作。
超时时间
默认值是-1,没有超时限制。如果有,以秒为单位进行设置。
事务是否只读
建议查询时设置为只读。

Spring框架进行事务的管理,首先使用TransactionDefinition对事务进行定义。通过PlatformTransactionManager根据TransactionDefinition的定义信息进行事务的管理。在事务管理过程中产生一系列的状态:保存到TransactionStatus中。 (https://www.cnblogs.com/mz-cnblogs/articles/13749859.html)
基于xml的声明式事务控制
Spring声明式事务控制的采用的是AOP思想,所以需要引入与AOP相关的依赖。
总共需要引入的依赖有:
1、ioc的依赖;
2、aop的依赖
3、jdbc模板+数据库连接池的依赖
4、spring整合junit单元测试的依赖
完整的pom.xml如下:
spring-context
slf4j-log4j12
junit
spring-test
spring-aspects
<!-- jdbc模板 -->
spring-jdbc
druid
mysql-connector-java
注意:需要新建jdbc.properties文件
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis
jdbc.username=root
jdbc.password=root
applicationContext.xml内容如下:
<?xml version="1.0" encoding="UTF-8"?> <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" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" 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 http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <context:property-placeholder location="classpath:jdbc.properties"/> <bean id="accountService" class="cn.test.service.impl.AccountServiceImpl"> <property name="accountDao" ref="accountDao"></property> </bean> <bean id="accountDao" class="cn.test.dao.impl.AccountDaoImpl"> <property name="dataSource" ref="dataSource"></property> </bean> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="${jdbc.driverClass}"></property> <property name="jdbcUrl" value="${jdbc.url}"></property> <property name="user" value="${jdbc.username}"></property> <property name="password" value="${jdbc.password}"></property> </bean> <!-- 配置事务管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> <!-- 配置事务的属性 id:指定advice的id,后边要用到 transaction-manager:写的是事务管理器的id --> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <!-- find开头的方法加只读事务 ,*表示通配符,匹配任意--> <tx:method name="find*" read-only="true"/> <!-- 其余方法是加可读写的事务 --> <tx:method name="*"/> </tx:attributes> </tx:advice> <!-- 配置事务切面 --> <aop:config> <!-- 配置切入点表达式:告诉框架哪些方法要控制事务 --> <aop:pointcut expression="execution(* cn.test.service.impl.*.*(..))" id="pt"/> <!--将定义好的事务属性应用到上述的切入点 --> <aop:advisor advice-ref="txAdvice" pointcut-ref="pt"/> </aop:config> </beans>
基于注解的声明式事务控制
开启Spring对注解的支持
<?xml version="1.0" encoding="UTF-8"?> <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" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" 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 http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- 开启spring注解扫描 --> <context:component-scan base-package="cn.test"></context:component-scan> <context:property-placeholder location="classpath:jdbc.properties"/> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="${jdbc.driverClass}"></property> <property name="url" value="${jdbc.url}"></property> <property name="username" value="${jdbc.username}"></property> <property name="password" value="${jdbc.password}"></property> </bean> </bean>
在相关类上加注解
注意:因为采用的是注解配置,此处不能继承JdbcDaoSupport,只能在DAO中声明JdbcTemplate属性
@Repository("accountDao")
public class AccountDaoImpl implements AccountDao {
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public void update(Account account) {
this.jdbcTemplate.update("update account set name = ?,money = ? where id = ?",account.getName(),account.getMoney(),account.getId());
}
@Override
public Account findById(Long id) {
return this.jdbcTemplate.queryForObject("select * from account where id = ?", new AccountRowMapper(), id);
}
}
@Service("accountService")
public class AccountServiceImpl implements AccountService{
@Autowired
private AccountDao accountDao;
@Override
public void transfer(Long fromId, Long toId, Double money) {
//查询转出账户
Account fromAccount = accountDao.findById(fromId);
//查询转入账户
Account toAccount = accountDao.findById(toId);
//转出账户减钱
fromAccount.setMoney(fromAccount.getMoney() - money);
//转入账户加钱
toAccount.setMoney(toAccount.getMoney() + money);
//更新转出账户
accountDao.update(fromAccount);
int i = 10 / 0;
//更新转入账户
accountDao.update(toAccount);
}
}
第一步:配置事务管理器
开启Spring对注解事务的支持
<?xml version="1.0" encoding="UTF-8"?> <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" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" 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 http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> <!-- 开启spring注解扫描 --> <context:component-scan base-package="cn.test"></context:component-scan> <!-- 开启事务注解的支持 transaction-manager:写事务管理器的id --> <tx:annotation-driven transaction-manager="transactionManager"/> <context:property-placeholder location="classpath:jdbc.properties"/> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="${jdbc.driverClass}"></property> <property name="url" value="${jdbc.url}"></property> <property name="username" value="${jdbc.username}"></property> <property name="password" value="${jdbc.password}"></property> </bean> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"></property> </bean> <!-- 配置事务管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean>
第三步:在业务层加@Transactional注解
@Service("accountService")
@Transactional//该类中所有的方法都加可读写的事务
public class AccountServiceImpl implements AccountService{
@Autowired
private AccountDao accountDao;
@Override
public void transfer(Long fromId, Long toId, Double money) {
//查询转出账户
Account fromAccount = accountDao.findById(fromId);
//查询转入账户
Account toAccount = accountDao.findById(toId);
//转出账户减钱
fromAccount.setMoney(fromAccount.getMoney() - money);
//转入账户加钱
toAccount.setMoney(toAccount.getMoney() + money);
//更新转出账户
accountDao.update(fromAccount);
int i = 10 / 0;
//更新转入账户
accountDao.update(toAccount);
}
}
第四步:在find开头的方法加只读事务
@Service("accountService")
@Transactional//该类中所有的方法都加可读写的事务
public class AccountServiceImpl implements AccountService{
@Autowired
private AccountDao accountDao;
@Override
public void transfer(Long fromId, Long toId, Double money) {
//查询转出账户
Account fromAccount = accountDao.findById(fromId);
//查询转入账户
Account toAccount = accountDao.findById(toId);
//转出账户减钱
fromAccount.setMoney(fromAccount.getMoney() - money);
//转入账户加钱
toAccount.setMoney(toAccount.getMoney() + money);
//更新转出账户
accountDao.update(fromAccount);
int i = 10 / 0;
//更新转入账户
accountDao.update(toAccount);
}
@Override
@Transactional(readOnly=true)//只读事务
public void findById(Long id) {
Account fromAccount = accountDao.findById(id);
fromAccount.setMoney(10000D);
accountDao.update(fromAccount);
}
}
提示:@Transactional注解也可以加在方法上,如果类上和方法上都有@Transactional,则以方法上的为准。
spring中核心监听器
ContextLoaderListener是Spring中的核心监听器,当web工程启动时,该监听器负责创建Spring的IOC容器,存放在ServletContext中;当需要用IOC容器时,就从ServletContext中获取。
好处:
1、不用我们手动创建IOC容器,由监听器负责创建;
2、可以保证整个工程中只有一个IOC容器;
1.创建web工程
2.引入依赖:
spring-web
3.创建dao,service
4.把相关bean配置到spring中
<?xml version="1.0" encoding="UTF-8"?> <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" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" 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 http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> <context:property-placeholder location="classpath:jdbc.properties"/> <bean id="accountService" class="cn.test.service.impl.AccountServiceImpl"> <property name="accountDao" ref="accountDao"></property> </bean> <bean id="accountDao" class="cn.test.dao.impl.AccountDaoImpl"> <property name="dataSource" ref="dataSource"></property> </bean> <!-- 配置数据源 --> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="${jdbc.driverClass}"></property> <property name="url" value="${jdbc.url}"></property> <property name="username" value="${jdbc.username}"></property> <property name="password" value="${jdbc.password}"></property> </bean> <!-- 配置事务管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!-- 注入数据源 --> <property name="dataSource" ref="dataSource"></property> </bean> <!-- 配置事务的属性 --> <tx:advice transaction-manager="transactionManager" id="txAdvice"> <tx:attributes> <!-- find开头方法只读事务 --> <tx:method name="find*" read-only="true" /> <!-- 其它方法加可读写事务 --> <tx:method name="*"/> </tx:attributes> </tx:advice> <!-- aop配置 --> <aop:config> <!-- 告诉spring对哪些方法加事务 --> <aop:pointcut expression="execution(* cn.test.service.impl.*.*(..))" id="pt"/> <!-- 把上面定义的事务属性应用到切入点 --> <aop:advisor advice-ref="txAdvice" pointcut-ref="pt"/> </aop:config> </beans>
5.在web.xml中配置核心监听器
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">
<display-name>spring4_day04_listener</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
<!-- 指定spring核心配置文件路径 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<!-- 配置监听器,当工程启动时,创建ioc容器,存放在ServletContext中 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>
6.创建servlet
public class AccountServlet extends HttpServlet { private static final long serialVersionUID = 1L; public AccountServlet() { super(); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //从ServletContext中获取唯一的IOC容器 ApplicationContext ac = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext()); //从IOC容器中获取AccountService AccountService accountService = (AccountService) ac.getBean("accountService"); List<Account> accounts = accountService.findAllAccount(); request.setAttribute("accounts", accounts); request.getRequestDispatcher("/accounts.jsp").forward(request, response); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
在web.xml中配置Servlet:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">
<display-name>spring4_day04_listener</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
<!-- 指定spring核心配置文件路径 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<!-- 配置监听器,当工程启动时,创建ioc容器,存放在ServletContext中 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>findAllAccount</servlet-name>
<servlet-class>cn.test.servlet.AccountServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>findAllAccount</servlet-name>
<url-pattern>/findAllAccount</url-pattern>
</servlet-mapping>
</web-app>
7.创建页面

浙公网安备 33010602011771号