Spring AOP的应用配置
一、Spring JdbcTemplate
在spring中为了更加方便的操作JDBC,在JDBC的基础之上定义了一个抽象层,此设计的目的是为不同类型的JDBC操作提供模板方法,每个模板方法都能控制整个过程,并允许覆盖过程中的特定任务,通过这种方式,可以尽可能保留灵活性,将数据库存取的工作量讲到最低。
1、配置并测试数据源
pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.llxazy</groupId> <artifactId>spring_demo</artifactId> <version>1.0-SNAPSHOT</version> <dependencies> <!-- https://mvnrepository.com/artifact/org.springframework/spring-context --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.3.RELEASE</version> </dependency> <!-- https://mvnrepository.com/artifact/com.alibaba/druid --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.21</version> </dependency> <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version> </dependency> <!-- https://mvnrepository.com/artifact/cglib/cglib --> <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.3.0</version> </dependency> <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.5</version> </dependency> <!-- https://mvnrepository.com/artifact/aopalliance/aopalliance --> <dependency> <groupId>aopalliance</groupId> <artifactId>aopalliance</artifactId> <version>1.0</version> </dependency> <!-- https://mvnrepository.com/artifact/org.springframework/spring-aspects --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>5.2.3.RELEASE</version> </dependency> </dependencies> </project>
dbconfig.properties
jdbc.username=root123
password=123456
url=jdbc:mysql://localhost:3306/demo
driverClassName=com.mysql.jdbc.Driver
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" 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 https://www.springframework.org/schema/aop/spring-aop.xsd "> <context:property-placeholder location="classpath:dbconfig.properties"></context:property-placeholder> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="username" value="${jdbc.username}"></property> <property name="password" value="${jdbc.password}"></property> <property name="url" value="${jdbc.url}"></property> <property name="driverClassName" value="${jdbc.driverClassName}"></property> </bean> </beans>
MyTest.java
import com.alibaba.druid.pool.DruidDataSource; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import java.sql.SQLException; public class MyTest { public static void main(String[] args) throws SQLException { ApplicationContext context = new ClassPathXmlApplicationContext("jdbcTemplate.xml"); DruidDataSource dataSource = context.getBean("dataSource", DruidDataSource.class); System.out.println(dataSource); System.out.println(dataSource.getConnection()); } }
1、添加pom依赖
<!-- https://mvnrepository.com/artifact/org.springframework/spring-orm --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-orm</artifactId> <version>5.2.3.RELEASE</version> </dependency>
jdbcTemplate.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" 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 https://www.springframework.org/schema/aop/spring-aop.xsd "> <context:property-placeholder location="classpath:dbconfig.properties"></context:property-placeholder> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="username" value="${jdbc.username}"></property> <property name="password" value="${jdbc.password}"></property> <property name="url" value="${jdbc.url}"></property> <property name="driverClassName" value="${jdbc.driverClassName}"></property> </bean> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <constructor-arg name="dataSource" ref="dataSource"></constructor-arg> </bean> </beans>
import com.alibaba.druid.pool.DruidDataSource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.jdbc.core.JdbcTemplate; import java.sql.SQLException; public class MyTest { public static void main(String[] args) throws SQLException { ApplicationContext context = new ClassPathXmlApplicationContext("jdbcTemplate.xml"); JdbcTemplate jdbcTemplate = context.getBean("jdbcTemplate", JdbcTemplate.class); String sql = "insert into emp(empno,ename) values(?,?)"; int result = jdbcTemplate.update(sql, 1111, "zhangsan"); System.out.println(result); } }
import com.alibaba.druid.pool.DruidDataSource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.jdbc.core.JdbcTemplate; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; public class MyTest { public static void main(String[] args) throws SQLException { ApplicationContext context = new ClassPathXmlApplicationContext("jdbcTemplate.xml"); JdbcTemplate jdbcTemplate = context.getBean("jdbcTemplate", JdbcTemplate.class); String sql = "insert into emp(empno,ename) values(?,?)"; List<Object[]> list = new ArrayList<Object[]>(); list.add(new Object[]{1,"zhangsan1"}); list.add(new Object[]{2,"zhangsan2"}); list.add(new Object[]{3,"zhangsan3"}); int[] result = jdbcTemplate.batchUpdate(sql, list); for (int i : result) { System.out.println(i); } } }
public class MyTest { public static void main(String[] args) throws SQLException { ApplicationContext context = new ClassPathXmlApplicationContext("jdbcTemplate.xml"); JdbcTemplate jdbcTemplate = context.getBean("jdbcTemplate", JdbcTemplate.class); String sql = "select * from emp where empno = ?"; Emp emp = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(Emp.class), 1001); System.out.println(emp); } }
public class MyTest { public static void main(String[] args) throws SQLException { ApplicationContext context = new ClassPathXmlApplicationContext("jdbcTemplate.xml"); JdbcTemplate jdbcTemplate = context.getBean("jdbcTemplate", JdbcTemplate.class); String sql = "select * from emp where sal > ?"; List<Emp> query = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(Emp.class), 1500); for (Emp emp : query) { System.out.println(emp); } } }
public class MyTest { public static void main(String[] args) throws SQLException { ApplicationContext context = new ClassPathXmlApplicationContext("jdbcTemplate.xml"); JdbcTemplate jdbcTemplate = context.getBean("jdbcTemplate", JdbcTemplate.class); String sql = "select max(sal) from emp"; Double aDouble = jdbcTemplate.queryForObject(sql, Double.class); System.out.println(aDouble); } }
<!--在jdbcTemplate.xml中加入如下信息-->
<bean id="namedParameterJdbcTemplate" class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate"> <constructor-arg name="dataSource" ref="dataSource"></constructor-arg> </bean>
public class MyTest {
public static void main(String[] args) throws SQLException {
ApplicationContext context = new ClassPathXmlApplicationContext("jdbcTemplate.xml");
NamedParameterJdbcTemplate jdbcTemplate = context.getBean("namedParameterJdbcTemplate", NamedParameterJdbcTemplate.class);
String sql = "insert into emp(empno,ename) values(:empno,:ename)";
Map<String,Object> map = new HashMap<>();
map.put("empno",2222);
map.put("ename","sili");
int update = jdbcTemplate.update(sql, map);
System.out.println(update);
}
}
二、声明式事务
在事务控制方面,主要有两个分类:
编程式事务:在代码中直接加入处理事务的逻辑,可能需要在代码中显式调用beginTransaction()、commit()、rollback()等事务管理相关的方法
声明式事务:在方法的外部添加注解或者直接在配置文件中定义,将事务管理代码从业务方法中分离出来,以声明的方式来实现事务管理。
spring的AOP恰好可以完成此功能:事务管理代码的固定模式作为一种横切关注点,通过AOP方法模块化,进而实现声明式事务。
1、
Spring从不同的事务管理API中抽象出了一整套事务管理机制,让事务管理代码从特定的事务技术中独立出来。开发人员通过配置的方式进行事务管理,而不必了解其底层是如何实现的。
Spring的核心事务管理抽象是PlatformTransactionManager。它为事务管理封装了一组独立于技术的方法。无论使用Spring的哪种事务管理策略(编程式或声明式),事务管理器都是必须的。
事务管理器可以以普通的bean的形式声明在Spring IOC容器中。下图是spring提供的事务管理器
1)在配置文件中添加事务管理器
<?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 https://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx https://www.springframework.org/schema/tx/spring-tx.xsd "> <context:component-scan base-package="com.llxazy"></context:component-scan> <context:property-placeholder location="classpath:dbconfig.properties"></context:property-placeholder> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="username" value="${jdbc.username}"></property> <property name="password" value="${jdbc.password}"></property> <property name="url" value="${jdbc.url}"></property> <property name="driverClassName" value="${jdbc.driverClassName}"></property> </bean> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <constructor-arg name="dataSource" ref="dataSource"></constructor-arg> </bean> <!--事务控制--> <!--配置事务管理器的bean--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> <!--开启基于注解的事务控制模式,依赖tx名称空间--> <tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven> </beans>
2)在方法上加@Transactional注解
package com.llxazy.service; import com.llxazy.dao.BookDao; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @Service public class BookService { @Autowired BookDao bookDao; /** * 结账:传入哪个用户买了哪本书 * @param username * @param id */ @Transactional public void checkout(String username,int id){ bookDao.updateStock(id); int price = bookDao.getPrice(id); bookDao.updateBalance(username,price); } }
2、事务配置的属性
isolation:设置事务的隔离级别
@Transactional(timeout = 3,isolation = Isolation.READ_COMMITTED)
propagation:事务的传播行为
@Transactional(propagation = Propagation.REQUIRED)
noRollbackFor:那些异常事务可以不回滚
@Transactional(timeout = 3,noRollbackFor = {ArithmeticException.class,NullPointerException.class})
noRollbackForClassName:填写的参数是全类名
@Transactional(timeout = 3,noRollbackForClassName = {"java.lang.ArithmeticException"})
rollbackFor:哪些异常事务需要回滚
@Transactional(timeout = 3,rollbackFor = {FileNotFoundException.class})
rollbackForClassName:填写的参数是全类名
readOnly:设置事务是否为只读事务
@Transactional(timeout = 3,readOnly = true)
如果你一次执行单条查询语句,则没有必要启用事务支持,数据库默认支持SQL执行期间的读一致性;
如果你一次执行多条查询语句,例如统计查询,报表查询,在这种场景下,多条查询SQL必须保证整体的读一致性,
否则,在前条SQL查询之后,后条SQL查询之前,数据被其他用户改变,则该次整体的统计查询将会出现读数据不一致的状态,此时,应该启用事务支持。
对于只读查询,可以指定事务类型为readonly,即只读事务。由于只读事务不存在数据的修改,因此数据库将会为只读事务提供一些优化手段。
timeout:事务超出指定执行时长后自动终止并回滚,单位是秒
@Transactional(timeout = 3)
注意:运行时异常默认回滚,编译时异常默认不回滚
三、基于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/aop https://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx https://www.springframework.org/schema/tx/spring-tx.xsd "> <context:component-scan base-package="com.llxazy"></context:component-scan> <context:property-placeholder location="classpath:dbconfig.properties"></context:property-placeholder> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="username" value="${jdbc.username}"></property> <property name="password" value="${jdbc.password}"></property> <property name="url" value="${jdbc.url}"></property> <property name="driverClassName" value="${jdbc.driverClassName}"></property> </bean> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <constructor-arg name="dataSource" ref="dataSource"></constructor-arg> </bean> <bean id="namedParameterJdbcTemplate" class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate"> <constructor-arg name="dataSource" ref="dataSource"></constructor-arg> </bean> <!--事务控制--> <!--配置事务管理器的bean--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> <!-- 基于xml配置的事务:依赖tx名称空间和aop名称空间 1、spring中提供事务管理器(切面),配置这个事务管理器 2、配置出事务方法 3、告诉spring哪些方法是事务方法(事务切面按照我们的切入点表达式去切入事务方法) --> <bean id="bookService" class="com.llxazy.service.BookService"></bean> <aop:config> <aop:pointcut id="txPoint" expression="execution(* com.llxazy.service.*.*(..))"/> <!--事务建议:advice-ref:指向事务管理器的配置--> <aop:advisor advice-ref="myAdvice" pointcut-ref="txPoint"></aop:advisor> </aop:config> <tx:advice id="myAdvice" transaction-manager="transactionManager"> <!--事务属性--> <tx:attributes> <!--指明哪些方法是事务方法--> <tx:method name="*"/> <tx:method name="checkout" propagation="REQUIRED"/> <tx:method name="get*" read-only="true"></tx:method> </tx:attributes> </tx:advice> </beans>
此只是学习过程中摘抄的笔记,方便后续复习时使用。