[ SSH框架 ] Spring框架学习之二(Bean的管理和AOP思想)
Posted Kevin_Zhang
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[ SSH框架 ] Spring框架学习之二(Bean的管理和AOP思想)相关的知识,希望对你有一定的参考价值。
一、Spring 使用 AspectJ 进行 AOP 的开发:注解的方式
1.1 引入相关的jar包
1.2 引入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: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/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"> <!-- 开启AOP操作 --> <aop:aspectj-autoproxy></aop:aspectj-autoproxy> </beans>
1.3 编写目标类
package com.Kevin.aop; /** * 使用注解方式进行AOP操作 * 被增强类 * @author Kevin * */ public class Book { public void add(){ System.out.println("Book Method add----"); } }
1.4 配置目标类
<!-- 创建对象 -->
<bean id="book" class="com.Kevin.aop.Book"></bean>
1.5 开启aop注解的自动代理
<!-- 开启AOP操作 --> <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
1.6 AspectJ 的 AOP 的注解
● Pointcut:切入点,在类中有很多方法可以被增强,而只有实际增强的方法称为切入点;
● Advice:通知/增强,实际增强的逻辑,被称为通知/增强,比如拓展日志功能,日志功能被称为通知/增强;
前置通知:在方法之前执行
后置通知:在方法之后执行
异常通知:方法出现异常
最终通知:在后置之后执行
环绕通知:在方法之前和之后执行
● Introduction(引介):引介是一种特殊的通知在不修改类代码的前提下, Introduction 可以在运行期为类动态地添加一些方法或 Field;
● Target(目标对象):代理的目标对象;
● Weaving(织入):是指把增强应用到目标对象来创建新的代理对象的过程(spring 采用动态代理织入,而 AspectJ 采用编译期织入和类装在期织入);
● Proxy(代理):一个类被 AOP 织入增强后,就产生一个结果代理类。
● Aspect:切面,将增强应用到具体方法上的过程称为切面(把增强用到切入点过程)
● Joinpoint:连接点,类里面可以被增强的方法,被称为连接点
1.7 编写切面类
package com.Kevin.aop; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.context.annotation.Bean; /** * 使用注解方式进行AOP操作 * 增强类 * @author Kevin * */ @Aspect public class StrBook { //在方法上使用注解完成增强配置 @Before(value="execution(* com.Kevin.aop.Book.*(..))") public void add(){ System.out.println("Before Strength---"); } }
1.8 配置切面
<bean id="strBook" class="com.Kevin.aop.StrBook"></bean>
二、Spring的JDBC模版
2.1 Spring 提供了很多持久层技术的模板类简化编程
2.2 引入开发相关的包
2.3 创建一个测试类
package com.Kevin.jdbc; import org.junit.Test; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.datasource.DriverManagerDataSource; /** * 使用jdbcTemplate模版操作数据库 * @author Kevin * */ public class JdbcTemplateDemo1 { //添加操作 @Test public void add(){ //设置数据库信息 DriverManagerDataSource dataSource=new DriverManagerDataSource(); dataSource.setDriverClassName("com.mysql.jdbc.Driver"); dataSource.setUrl("jdbc:mysql:///springday3"); dataSource.setUsername("root"); dataSource.setPassword("admin"); //创建jdbcTemplate对象,设置数据源 JdbcTemplate jdbcTemplate=new JdbcTemplate(dataSource); //调用jdbcTemplate对象里的方法实现操作 //创建sql语句 String sql="insert into user values(?,?)"; int rows=jdbcTemplate.update(sql,"Kevin","admin"); System.out.println(rows); } }
三、将连接池交给Spring管理
3.1 Spring的c3p0连接池配置
【引入相应的jar包】
【编写c3p0配置文件】
<?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" 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/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"> <!-- 配置c3p0连接池 --> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <!-- 注入属性 --> <property name="driverClass" value="com.mysql.jdbc.Driver"></property> <property name="jdbcUrl" value="jdbc:mysql:///springday3"></property> <property name="user" value="root"></property> <property name="password" value="admin"></property> </bean> <!-- 创建jdbcTemplate对象 --> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <!-- 把dataSource传递到模版对象里 --> <property name="dataSource" ref="dataSource"></property> </bean> </beans>
3.2 将数据库连接信息配置到属性文件中
【定义属性文件】
//设置数据库信息 DriverManagerDataSource dataSource=new DriverManagerDataSource(); dataSource.setDriverClassName("com.mysql.jdbc.Driver"); dataSource.setUrl("jdbc:mysql:///springday3"); dataSource.setUsername("root"); dataSource.setPassword("admin");
四、JDBC模版的CRUD操作
【添加操作】
//添加操作 @Test public void add(){ //设置数据库信息 DriverManagerDataSource dataSource=new DriverManagerDataSource(); dataSource.setDriverClassName("com.mysql.jdbc.Driver"); dataSource.setUrl("jdbc:mysql:///springday3"); dataSource.setUsername("root"); dataSource.setPassword("admin"); //创建jdbcTemplate对象,设置数据源 JdbcTemplate jdbcTemplate=new JdbcTemplate(dataSource); //调用jdbcTemplate对象里的方法实现操作 //创建sql语句 String sql="insert into user values(?,?)"; int rows=jdbcTemplate.update(sql,"Kevin","admin"); System.out.println(rows); }
【修改操作】
//修改操作 @Test public void update(){ //设置数据库信息 DriverManagerDataSource dataSource=new DriverManagerDataSource(); dataSource.setDriverClassName("com.mysql.jdbc.Driver"); dataSource.setUrl("jdbc:mysql:///springday3"); dataSource.setUsername("root"); dataSource.setPassword("admin"); //创建jdbcTemplate对象,设置数据源 JdbcTemplate jdbcTemplate=new JdbcTemplate(dataSource); //调用jdbcTemplate对象里的方法实现操作 //创建sql语句 String sql="update user set password=? where username=?"; int rows=jdbcTemplate.update(sql,"666","Kevin"); System.out.println(rows); }
【删除操作】
//删除操作 @Test public void delete(){ //设置数据库信息 DriverManagerDataSource dataSource=new DriverManagerDataSource(); dataSource.setDriverClassName("com.mysql.jdbc.Driver"); dataSource.setUrl("jdbc:mysql:///springday3"); dataSource.setUsername("root"); dataSource.setPassword("admin"); //创建jdbcTemplate对象,设置数据源 JdbcTemplate jdbcTemplate=new JdbcTemplate(dataSource); //调用jdbcTemplate对象里的方法实现操作 //创建sql语句 String sql="delete from user where username=?"; jdbcTemplate.update(sql,"Kevin"); }
五、事务回顾
5.1 什么是事务
事务逻辑上的一组操作,组成这组操作的各个逻辑单元,要么一起成功,要么一起失败。
5.2 事务特性
● 原子性(Atomicity):操作这些指令时,要么全部执行成功,要么全部不执行。只要其中一个指令执行失败,所有的指令都执行失败,数据进行回滚,回到执行指令前的数据状态。
eg:拿转账来说,假设用户A和用户B两者的钱加起来一共是20000,那么不管A和B之间如何转账,转几次账,事务结束后两个用户的钱相加起来应该还得是20000,这就是事务的一致性。
● 一致性(Consistency):事务的执行使数据从一个状态转换为另一个状态,但是对于整个数据的完整性保持稳定。
● 隔离性(Isolation):隔离性是当多个用户并发访问数据库时,比如操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。
即要达到这么一种效果:对于任意两个并发的事务T1和T2,在事务T1看来,T2要么在T1开始之前就已经结束,要么在T1结束之后才开始,这样每个事务都感觉不到有其他事务在并发地执行。
● 持久性(Durability):当事务正确完成后,它对于数据的改变是永久性的。
5.3 如果不考虑隔离性而引发的问题
● 脏读 :一个事务读到了另一个事务的未提交的数据。
● 不可重复读 :一个事务读到了另一个事务已经提交的 update 的数据导致多次查询结果不一致。
● 虚幻读 :一个事务读到了另一个事务已经提交的 insert 的数据导致多次查询结果不一致。
5.4 解决读问题:设置事务隔离级别
事务的隔离级别有4种,由低到高分别为Read uncommitted 、Read committed 、Repeatable read 、Serializable 。而且,在事务的并发操作中可能会出现脏读,不可重复读,幻读。下面通过事例一一阐述它们的概念与联系。
Read uncommitted(最低级别,任何情况都无法保证。)
读未提交,顾名思义,就是一个事务可以读取另一个未提交事务的数据。
eg:老板要给程序员发工资,程序员的工资是3.6万/月。但是发工资时老板不小心按错了数字,按成3.9万/月,该钱已经打到程序员的户口,但是事务还没有提交,就在这时,程序员去查看自己这个月的工资,发现比往常多了3千元,以为涨工资了非常高兴。但是老板及时发现了不对,马上回滚差点就提交了的事务,将数字改成3.6万再提交。
Analyse:实际程序员这个月的工资还是3.6万,但是程序员看到的是3.9万。他看到的是老板还没提交事务时的数据。这就是脏读。
那怎么解决脏读呢?Read committed!读提交,能解决脏读问题。
Read committed(可避免脏读的发生。)
读提交,顾名思义,就是一个事务要等另一个事务提交后才能读取数据。
eg:程序员拿着信用卡去享受生活(卡里当然是只有3.6万),当他埋单时(程序员事务开启),收费系统事先检测到他的卡里有3.6万,就在这个时候!!程序员的妻子要把钱全部转出充当家用,并提交。当收费系统准备扣款时,再检测卡里的金额,发现已经没钱了(第二次检测金额当然要等待妻子转出金额事务提交完)。程序员就会很郁闷,明明卡里是有钱的…
Analyse:这就是读提交,若有事务对数据进行更新(UPDATE)操作时,读操作事务要等待这个更新操作事务提交后才能读取数据,可以解决脏读问题。但在这个事例中,出现了一个事务范围内两个相同的查询却返回了不同数据,这就是不可重复读。
那怎么解决可能的不可重复读问题?Repeatable read !
Repeatable read(可避免脏读、不可重复读的发生。)
重复读,就是在开始读取数据(事务开启)时,不再允许修改操作
eg:程序员拿着信用卡去享受生活(卡里当然是只有3.6万),当他埋单时(事务开启,不允许其他事务的UPDATE修改操作),收费系统事先检测到他的卡里有3.6万。这个时候他的妻子不能转出金额了。接下来收费系统就可以扣款了。
Analyse:重复读可以解决不可重复读问题。写到这里,应该明白的一点就是,不可重复读对应的是修改,即UPDATE操作。但是可能还会有幻读问题。因为幻读问题对应的是插入INSERT操作,而不是UPDATE操作。
什么时候会出现幻读?
eg:程序员某一天去消费,花了2千元,然后他的妻子去查看他今天的消费记录(全表扫描FTS,妻子事务开启),看到确实是花了2千元,就在这个时候,程序员花了1万买了一部电脑,即新增INSERT了一条消费记录,并提交。当妻子打印程序员的消费记录清单时(妻子事务提交),发现花了1.2万元,似乎出现了幻觉,这就是幻读。
那怎么解决幻读问题?Serializable!
Serializable(可避免脏读、不可重复读、幻读的发生。) 序列化
Serializable 是最高的事务隔离级别,在该级别下,事务串行化顺序执行,可以避免脏读、不可重复读与幻读。但是这种事务隔离级别效率低下,比较耗数据库性能,一般不使用。
Tips:大多数数据库默认的事务隔离级别是Read committed,比如Sql Server , Oracle。
Mysql的默认隔离级别是Repeatable read。
Tips:隔离级别的设置只对当前链接有效。对于使用MySQL命令窗口而言,一个窗口就相当于一个链接,当前窗口设置的隔离级别只对当前窗口中的事务有效;对于JDBC操作数据库来说,一个Connection对象相当于一个链接,而对于Connection对象设置的隔离级别只对该Connection对象有效,与其他链接Connection对象无关。
Tips:设置数据库的隔离级别一定要是在开启事务之前。
有关事务的详情可查阅什么是事务?事务的四个特性以及事务的隔离级别
五、Spring进行事务管理一组API
5.1 PlatformTransactionManager:平台事务管理器
真正管理事务的对象:
● JDBC 或 iBatis 进行持久化数据时使用 org.springframework.jdbc.datasource.DataSourceTransactionManager
● Hibernate 版本进行持久化数据时使用 org.springframework.orm.hibernate3.HibernateTransactionManager
5.2 TransactionDefinition:事务定义信息
事务定义信息:
● 隔离级别
● 传播行为
● 超时信息
● 是否只读
5.3 事务的传播行为
PROPAGION_XXX :事务的传播行为
● 保证同一个事务中
PROPAGATION_REQUIRED:支持当前事务,如果不存在 就新建一个(默认)
PROPAGATION_SUPPORTS :支持当前事务,如果不存在,就不使用事务
PROPAGATION_MANDATORY :支持当前事务,如果不存在,抛出异常
● 保证没有在同一个事务中
PROPAGATION_REQUIRES_NEW:如果有事务存在,挂起当前事务,创建一个新的事务
PROPAGATION_NOT_SUPPORTED: 以非事务方式运行,如果有事务存在,挂起当前事务
PROPAGATION_NEVER :以非事务方式运行,如果有事务存在,抛出异常
PROPAGATION_NESTED:如果当前事务存在,则嵌套事务执行
六、案例:搭建一个转账环境
6.1 创建业务层和DAO类
package com.Kevin.dao; import org.springframework.jdbc.core.JdbcTemplate; import com.mchange.v2.c3p0.ComboPooledDataSource; public class OrdersDao { //注入jdbcTemplate private JdbcTemplate jdbcTemplate; public void setJdbcTemplate(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } //少钱方法 public void lessmoney(){ String sql="update account set salary=salary+? where username=?"; jdbcTemplate.update(sql,"-1000","Ryan"); } //多钱方法 public void moremoney(){ String sql="update account set salary=salary+? where username=?"; jdbcTemplate.update(sql,"1000","Kevin"); } }
package com.Kevin.service; import com.Kevin.dao.OrdersDao; public class OrdersService { private OrdersDao ordersDao; public void setOrdersDao(OrdersDao ordersDao) { this.ordersDao = ordersDao; } //调用dao方法 //业务逻辑层,编写转账业务 public void accountMoney(){ ordersDao.lessmoney(); ordersDao.moremoney(); } }
6.2 配置业务层和DAO
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" 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-4.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.2.xsd "> <!-- 配置c3p0连接池 --> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <!-- 注入属性 --> <property name="driverClass" value="com.mysql.jdbc.Driver"></property> <property name="jdbcUrl" value="jdbc:mysql:///springday3"></property> <property name="user" value="root"></property> <property name="password" value="admin"></property> </bean> <!-- 第一步:配置事务管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> <!-- 第二步:配置事务增强 --> <tx:advice id="txadvice" transaction-manager="transactionManager"> <!-- 设置进行事务操作的方法匹配原则 --> <tx:attributes> <tx:method name="account*" propagation="REQUIRED" /> </tx:attributes> </tx:advice> <!-- 第三步:配置切面 --> <aop:config> <!-- 切入点 --> <aop:pointcut expression="execution(* com.Kevin.service.OrdersService.*(..))" id="pointcut1"/> <!-- 切面 --> <aop:advisor advice-ref="txadvice" pointcut-ref="pointcut1"/> </aop:config> <bean id="ordersService" class="com.Kevin.service.OrdersService"> <property name="ordersDao" ref="ordersDao"></property> </bean> <bean id="ordersDao" class="com.Kevin.dao.OrdersDao"> <property name="jdbcTemplate" ref="jdbcTemplate"></property> </bean> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"></property> </bean> </beans>
6.3 编写测试类
package com.Kevin.service; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class TestService { @Test public void testService(){ ApplicationContext context=new ClassPathXmlApplicationContext("beans1.xml"); int i=1/0; OrdersService service=(OrdersService) context.getBean("ordersService"); service.accountMoney(); } }
七、Spring声明事物的注解方式
7.1 引入jar包
7.2 配置事务管理器并开启事务注解
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" 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-4.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.2.xsd "> <!-- 配置c3p0连接池 --> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <!-- 注入属性 --> <property name="driverClass" value="com.mysql.jdbc.Driver"></property> <property name="jdbcUrl" value="jdbc:mysql:///springday3"></property> <property name="user" value="root"></property> <property name="password" value="admin"></property> </bean> <!-- 第一步:配置事务管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> <!-- 开启事务注解 --> <tx:annotation-driven transaction-manager="transactionManager"/> <bean id="ordersService" class="com.Kevin.service.OrdersService"> <property name="ordersDao" ref="ordersDao"></property> </bean> <bean id="ordersDao" class="com.Kevin.dao.OrdersDao"> <property name="jdbcTemplate" ref="jdbcTemplate"></property> </bean> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"></property> </bean> </beans>
7.3 在使用事务的类上添加一个注解:@Transactional
package com.Kevin.service; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.transaction.annotation.Transactional; @Transactional public class TestService { @Test public void testService(){ ApplicationContext context=new ClassPathXmlApplicationContext("beans1.xml"); OrdersService service=(OrdersService) context.getBean("ordersService"); service.accountMoney(); } }
以上是关于[ SSH框架 ] Spring框架学习之二(Bean的管理和AOP思想)的主要内容,如果未能解决你的问题,请参考以下文章