1、切入点方法的定义
表达式匹配规则举例:
public * addUser(com.pb.entity.User):*表示匹配所有类型的返回值
示例:
public int addUser(User u);
public String addUser(User u);
public void *(com.pb.entity.User):"*"表示匹配所有方法名。
示例:
public void selectUser(User u);
public void a(User u);
public void addUser(..):".."表示匹配所有参数个数和类型。
示例:
public void addUser(int a);
public void addUser(int b, int c);
* com.pb.service.*.*(..):匹配com.pb.service包下所有类的所有方法
示例:
public void com.pb.service.A.a();
public String com.pb.service.B.a();
* com.pb.service..*(..):匹配com.pb.service包及子包下所有类的所有方法。
2、如何获取切入点信息
通过JoinPoint对象获取信息:
System.out.println("切入点对象:"+jp.getTarget().getClass().getSimpleName());
System.out.println("切入点方法:"+jp.getSignature());
System.out.println("切入点的参数:"+jp.getArgs()[0]);
3、特殊的前置增强 -> Advisor前置增强实现步骤
3.1 创建增强类,要求该类实现MethodBeforeAdvice接口
3.2 修改applicationContext.xml文件
(1)创建增强类对象;
(2)定义增强和切入点的关系
<aop:config>
<!-- 表达式是被切入的方法的表达式 -->
<aop:pointcut expression="execution(* biz.impl.*.*(..))" id="mypoint"/>
<aop:advisor advice-ref="增强类对象的id" pointcut-ref="切入点对象的id"/>
</aop:config>
4、使用了AspectJ依赖注解开发
spring AOP的注解方式:
注:1)增强类也需要创建对象(使用@Component)
2)要启动扫描spring注解包的代码:
<context:component-scan base-package="com.xzk"></context:component-scan>
<1> 除了启动spring的注解之处 还要启动aspectJ的注解方式
<aop:aspectj-autoproxy/>
<2> 在切面类(增强类)上添加:@Aspect
<3> 定义一个任意方法
@Pointcut("execution(* com.*.*(..))")
public void anyMethod(){}
<4> 用法:
因为@Pointcut要求要放在一个方法中:
@Pointcut("execution(* com.*.*(..))")
public void anyMethod(){}
@Before("anyMethod()")
public void log(JoinPoint){
System.out.println("myAspect....log....before");
}
@Around("anyMethod()")
public void aroundTest(ProceedingJoinPoint pjp){
System.out.println("around...before....");
try {
pjp.proceed();//执行目标方法
} catch (Throwable e) {
e.printStackTrace();
}
System.out.println("around...after....");
}
1. 没有异常情况下
环绕开始。。。。
前置增强开始执行
insert-----------
环绕结束。。。。
最终增强
后置增强开始执行
相对顺序固定,注解换位置时不影响结果顺序
2. 有异常
前置增强开始执行
insert---------------
最终增强
异常增强
注意:不要使用环绕增强,使用的话异常增强不执行
5、Spring-JDBC 数据访问
5.1 使用spring-jdbc操作数据库
主要内容:学习使用jdbcTemplate API和 如何使用Spring管理JdbcTemplate
步骤1.引入jar包
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.0.8.RELEASE</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.46</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.0.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.8.RELEASE</version>
</dependency>
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.2</version>
</dependency>
步骤2:测试
public void test1() throws Exception {
//TODO 测试jdbcTemplate简单使用
//1.创建c3p0链接池
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setDriverClass("com.mysql.jdbc.Driver");
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/springjdbc");
dataSource.setUser("root");
dataSource.setPassword("111");
//创建jdbcTemplate对象
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
//创建sql语句
String sql = "insert into role (rid , rname ,alias) value (? , ?,?);";
jdbcTemplate.update(sql,"3","visitor","游客");
}
5.2 Spring管理JdbcTemplate
注意:可以自己在RoleDaoImpl中添加JdbcTemplate变量,如果不自动装载记得添加变量的set方法,标准的操作,我们可以让RoleDaoImpl继承JdbcDaoSupport
示例:
public class RoleDaoImpl extends JdbcDaoSupport implements RoleDao {
public void save(Role role) {
String sql = "INSERT INTO role (rname,alias) value (?,?) ;";
getJdbcTemplate().update(sql,role.getRname(),role.getAlias());
}
}
配置文件:需要创建数据源和给RoleDapImpl中的JdbcTemplate赋值
<bean name="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
p:jdbcUrl="jdbc:mysql://localhost:3306/xzk" p:driverClass="com.mysql.jdbc.Driver"
p:user="root" p:password="111" />
<!-- bean jdbcTemplate -->
<bean name="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean name="roleDao" class="com.xzk.spring.dao.impl.RoleDaoImpl">
<property name="jdbcTemplate" ref="jdbcTemplate"/>
</bean>
5.3 创建db.properties属性文件
修改配置文件:
<context:property-placeholder location="db.properties">
</context:property-placeholder>
p:jdbcUrl="${jdbc.jdbcUrl}"
p:driverClass="${jdbc.driverClass}"
关于属性文件username的问题:
解决方式:
<context:property-placeholder location="db.properties" system-properties-mode="FALLBACK"/>
FALLBACK --- 默认值,不存在时覆盖
NEVER --- 不覆盖
5.4 crud
JdbcTemplate 常用方法:
JdbcTemplate.update(sql,ArgsObj....); //多个参数值时,可以使用 对象数组
// DQL 查询单个
jdbcTemplate.queryForObject(String var1,RowMapper var2,Object... var3); // 查询所有
getJdbcTemplate().query(sql,new BeanPropertyRowMapper<类名>(类名.class)); //查询(单行+多行)
实例:
处理查询结果的自定义方法(也可以自定义一个类,该类实现RowMapper接口的方式处理结果)
private Users chuliresult(ResultSet resultSet){
Users users=new Users();
try {
users.setUsername(resultSet.getString("username"));
users.setPassword(resultSet.getString("password"));
users.setUserid(resultSet.getInt("userid"));
} catch (SQLException e) {
e.printStackTrace();
}
return users;
}
单行查询:
public Users findbyid(int uid) {
String sql="select * from users where userid=?";
Users users= getJdbcTemplate().queryForObject(sql, new Object[]{uid}, new RowMapper<Users>() {
public Users mapRow(ResultSet resultSet, int i) throws SQLException {
return chuliresult(resultSet);
}
});
return users;
}
多行查询:
public List findall() {
String sql="select * from users";
List<Users> list=getJdbcTemplate().query(sql, new RowMapper<Users>() {
public Users mapRow(ResultSet resultSet, int i) throws SQLException {
// System.out.println(resultSet+",,,"+i);
return chuliresult(resultSet);
}
});
return list;
}
实例:
查询行数:
String sql="select count(*) from users";
Integer integer = getJdbcTemplate().queryForObject(sql, Integer.class);
查询多列:
Map<String, Object> map = jdbcTemplate.queryForMap("select count(*),max(roleid),min(roleid) from role");
Set<Map.Entry<String, Object>> entrySet = map.entrySet();
Iterator<Map.Entry<String, Object>> iterator = entrySet.iterator(); while(iterator.hasNext()){
System.out.println(iterator.next());
}
查询单列值:
String sql="select username from users";
List<String> list = getJdbcTemplate().queryForList(sql, String.class);
6、Spring事务管理
6.1 什么是事务(Transaction)
通过sql将逻辑相关的一组操作绑定在一起,以便服务器保持数据的完整性(准确性)。
事务通常是以begin transaction 开始,以commit或rollback结束。
事务执行的流程:开启事务 => 执行insert,update,delete => commit/rollback
设想网上购物的一次交易,其付款过程至少包括以下几步数据库操作:
1、更新客户所购商品的库存信息;
2、保存客户付款信息--可能包括与银行系统的交互
3、生成订单并且保存到数据库中
4、更新用户相关信息,例如购物数量等等
数据库事务正是用来保证这种情况下交易的平稳性和可预测性的技术
6.2 为什么要使用事务?
1、为了提高性能;
2、为了保持业务流程的完整性;
3、使用分布式事务。
6.3 事务的特性ACID
1 - 原子性(atomicity)
事务是数据库的逻辑工作单位,而且是必须是原子工作单位,对于其数据修改,要么全部执行,要么全部不执行;
2 - 一致性(consistency)
事务在完成时,必须是所有的数据都保持一致状态。在相关数据库中,所有规则都必须应用于事务的修改,以保持所有数据的完整性;
3 - 隔离性(isolation)
一个事务的执行不能被其他事务所影响。企业级的数据库每一秒钟都可能应付成千上万的并发访问,因而带来了并发控制的问题。
4 - 持久性(durability)
一个事务一旦提交,事务的操作便永久性的保存在DB中。即使此时再次执行回滚操作也不能撤销所做的更改。
6.4 事务的嵌套 => 传播行为propagation
事务的传播机制
事务的第一个方面是传播行为(propagation /,prɒpə'ɡeɪʃən/ behavior)。
当事务方法被另一个事务方法调用时,必须制定事务应该如何传播。
规定了事务方法和事务方法发生嵌套调用时事务如何进行传播。
例如:方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行。
Spring定义了七种传播行为:
事务传播行为类型 | 说明 |
PROPAGATION_REQUIRED | 如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中,这是最常见的选择。 |
PROPAGATION_SUPPORTS | 支持当前事务,如果当前没有事务,就以非事务方式执行 |
PROPAGATION_MANDATORY | 使用当前的事务,如果当前没有事务,就抛出异常 |
PROPAGATION_REQUIRES_NEW | 新建事务,如果当前存在事务,把当前事务挂起 |
PROPAGATION_NOT_SUPPORTED | 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 |
PROPAGATION_NEVER | 以非事务方式执行,如果当前存在事务,则抛出异常 |
PROPAGATION_NESTED | 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。 |
propagation_nested:
嵌套的事务可以独立于当前事务进行单独地提交或回滚。
如果当前事务不存在,那么其行为与PROPAGATION_REQUIRED一样。
注意各厂商对这种传播行为的支持是有所差异的。可以参考资源管理器的文档来确认它们是否支持嵌套事务。
事务传播行为失效的情况:
spring事务是基于代理来实现的,所以:
(1)private、final、static 方法无法被代理,所以添加事务无效;
(2)当绕过代理对象,直接调用添加事务管理的方法时,事务管理将无法生效。比如直接new出的对象。
(3)在同一个类下,有2个方法,A、B,A没有事务,B有事务,但是A调用时,方法B被标记的事务无效。究其原因,因为此类的调用对象为代理对象,代理方法A调用真正的被代理方法A后,在被代理方法A中才会去调用方法B,此时this对象为被代理的对象,所以是不会通知到代理对象,也就变成了第二种情况,绕过了代理对象,所以无效。
6.5 事务的隔离级别
MySQL数据库共定义了四种隔离级别:
1. Serializable(串行化):可避免脏读、不可重复读、幻读情况的发生;
2. Repeatable read(可重复读):可避免脏读、不可重复读情况的发生;
3. Read committed(读已提交):可避免脏读情况发生;
4. Read uncommitted(读未提交):最低级别,以上情况均无法保证。
Isolation.DEFAULT:为数据源的默认隔离级别
√:可能出现 x:不会出现
脏读 | 不可重复读 | 幻读 | |
Read uncommitted | √ | √ | √ |
Read committed | x | √ | √ |
Repeatable read | x | x | √ |
Serializable | x | x | x |
级别越高,数据越安全,但性能越低。
6.6 事务的实现
Spring XML配置声明事务
6.6.1 TransactionManager
在不同平台,操作事务的代码各不相同,因此spring提供了一个TransactionManager接口:
- DataSourceTransactionManager 用于JDBC的事务管理
- HibernateTransactionManager 用于Hibernate的事务管理
6.6.2 接口的定义
事务的属性介绍:这里定义了传播行为、隔离级别、超时时间、是否只读
package org.springframework.transaction;
public interface TransactionDefinition {
int PROPAGATION_REQUIRED = 0; //支持当前事务,如果不存在,就新建一个
int PROPAGATION_SUPPORTS = 1; //支持当前事务,如果不存在,就不使用事务
int PROPAGATION_MANDATORY = 2; //支持当前事务,如果不存在,就抛出异常
int PROPAGATION_REQUIRES_NEW = 3;//如果有事务存在,挂起当前事务,创建一个新的事物
int PROPAGATION_NOT_SUPPORTED = 4;//以非事务方式运行,如果有事务存在,挂起当前事务
int PROPAGATION_NEVER = 5;//以非事务方式运行,如果有事务存在,就抛出异常
int PROPAGATION_NESTED = 6;//如果有事务存在,则嵌套事务执行
int ISOLATION_DEFAULT = -1;//默认级别,MYSQL: 默认为REPEATABLE_READ级别 SQLSERVER: 默认为READ_COMMITTED int ISOLATION_READ_UNCOMMITTED = 1;//读取未提交数据(会出现脏读, 不可重复读) 基本不 使用
int ISOLATION_READ_COMMITTED = 2;//读取已提交数据(会出现不可重复读和幻读)
int ISOLATION_REPEATABLE_READ = 4;//可重复读(会出现幻读)
int ISOLATION_SERIALIZABLE = 8;//串行化
int TIMEOUT_DEFAULT = -1;//默认是-1,不超时,单位是秒 //事务的传播行为
int getPropagationBehavior(); //事务的隔离级别
int getIsolationLevel(); //事务超时时间
int getTimeout(); //是否只读
boolean isReadOnly();
String getName();
}
6.6.3 添加tx命名空间
6.6.4 添加事务相关配置
修改applicationContext.xml
<!-- 平台事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 通知 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!-- 传播行为:propagation 不是必须的,默认值是REQUIRED -->
<!-- REQUIRED:如果有事务,则在事务中执行;如果没有事务,则开启一个新的事务 -->
<tx:method name="save*" propagation="REQUIRED" />
<!-- SUPPORTS:如果有事务,则在事务中执行;如果没有事务,则不会开启事务 -->
<tx:method name="find*" propagation="SUPPORTS" read-only="true" />
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="txPointCut" expression="execution(* com.service.*.* (..))" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
</aop:config>
> isolation 设置隔离机制,不是必须的 默认值DEFAULT
> timeout 不是必须的 默认值-1(永不超时) 表示事务超时的时间(以秒为单位)
> read-only 不是必须的 默认值false不是只读的 表示事务是否只读?
> rollback-for 不是必须的 表示将被触发进行回滚的Exception(s); 以逗号分开
如:'com.ityhp.MyBusinessException,ServletException'
> no-rollback-for 不是必须的 表示不触发进行回滚的Exception(s);以逗号分开
如:'com.foo.MyBusinessException,ServletException'
需要包:
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.13</version>
</dependency>