Spring -- AOP 面向切面编程

面向切面编程(AOP)

作用: 在程序运行期间,不修改源码对已有方法进行增强
​优势: 减少重复代码、提高开发效率、维护方便
​AOP 的实现方式:使用动态代理技术

Spring 中 AOP 的相关术语

1. JoinPoint 连接点:指那些被拦截到的点(方法)
2. pointcut 切入点:指那些被增强的连接点(方法)
   注:所有的切入点都是连接点,但连接点不一定是切入点
3. Advice 通知:增强的方法
4. Aspect 切面:由切入点和通知组成
5. Weaving 织入:指把增强应用到目标对象,创建新的代理对象的过程

AOP的 xml 配置

1. Spring 中基于 xml 的 AOP 配置步骤
1. 把通知 bean 交给 Spring 来管理
2. 使用 aop:config 标签来表明开始 AOP 的配置
3. 在 aop:config 标签内,使用 aop:aspect 标签配置切面
		id 属性:切面的唯一标识
		ref 属性:指定通知类的 bean 对象
4. 配置切入点表达式(aop:pointcut)
		id 属性用于指定表达式的唯一标识,expression 属性用于指定表达式内容
		此标签写在 aop:aspect 标签内部只能当前切面使用,在其外部则所有切面均可用
5. 在 aop:aspect 标签的内部使用对应标签配置通知的类型
		aop:before 标识前置通知
				method 属性:用于指定类中哪个方法是前置通知
				pointcut 属性:用于指定切入点表达式,即对哪些方法增强
2. Spring 常用通知类型
Spring 常用通知类型
	前置通知(aop:before):在切入点方法执行之前执行
	后置通知(aop:after-returning):在切入点方法正常执行之后执行,它和异常通知只能执行一个
	异常通知(aop:after-throwing):在切入点方法执行产生异常之后执行,它和后置通知只能执行一个
	最终通知(aop:after):无论切入点方法是否正常执行,它都会在其后面执行
	环绕通知:用户自定义增强代码的位置
3. 切入点表达式的书写规则
切入点表达式的写法:
	关键字:execution (表达式)
	表达式的标准写法:访问修饰符 + 返回值 + 包名.类名.方法名(参数列表)
		举例:public void com.demo.service.impl.IAccountServiceImpl.saveAccount()
	全通配写法:* * ..*.*(..)
	全通配的书写规则:
		1. 访问修饰符可以省略
		2. 返回值可以使用通配符,表示任意返回值
		3. 包名可以使用通配符,表示任意包,但是有几级包就需要写几个 *.
		4. 包名可以使用 .. 表示当前包和子包
		5. 类名和方法名都可以使用 * 来实现通配
		6. 参数列表:
			6.1 可以直接使写数据类型:
				基本类型直接写名称(如 int )
				引用类型写包名.类名的方式 (如 java.lang.String )
			6.2 可以使用通配符 * 表示任意类型,但是方法必须有参数
			6.3 可以使用 .. 表示有无参数即可,有参数可以是任意类型
	实际开发中切入点表达式的通常写法:
		业务层类下的所有方法:* com.demo.service.impl.*.*(..)
4. AOP 的 xml 配置示例
<!-- 配置 AOP -->
    <aop:config>
    	<!-- 配置切入点表达式 -->
        <aop:pointcut id="pt1" expression="execution(* com.demo.service.impl.*.*(..))"></aop:pointcut>
        <!-- 配置切面 -->
        <aop:aspect id="logAdvice" ref="logger">
            <!-- 配置前置通知:在切入点方法执行之前执行-->
            <aop:before method="beforePrintLog" pointcut-ref="pt1" ></aop:before>

            <!-- 配置后置通知:在切入点方法正常执行之后值。它和异常通知只能执行一个-->
            <aop:after-returning method="afterReturningPrintLog" pointcut-ref="pt1"></aop:after-returning>

            <!-- 配置异常通知:在切入点方法执行产生异常之后执行。它和后置通知只能执行一个-->
            <aop:after-throwing method="afterThrowingPrintLog" pointcut-ref="pt1"></aop:after-throwing>

            <!-- 配置最终通知:无论切入点方法是否正常执行它都会在其后面执行-->
            <aop:after method="afterPrintLog" pointcut-ref="pt1"></aop:after>

            <!-- 配置环绕通知:环绕通知需要用户在代码中定义增强的内容 -->
            <aop:around method="aroundPringLog" pointcut-ref="pt1"></aop:around>
        </aop:aspect>
    </aop:config>

JdbcTemplate 简介

jdbcTemplate 是用来操作关系型数据库的模板,位于 spring-jdbc-5.0.2.RELEASE.jar 中,一般还需导入 spring-tx-5.0.2.RELEASE.jar 来进行事务管理。

使用 jdbc 模板时需要进行的配置
<!-- 配置 JdbcTemplate -->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!-- 配置数据源:Spring 自带数据源 DriverManagerDataSource -->
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
        <property name="url" value="jdbc:mysql://localhost:3306/eesy"></property>
        <property name="username" value="root"></property>
        <property name="password" value="1234"></property>
    </bean>

事务控制

Spring 框架为我们提供了一组事务控制的接口,这组接口在 spring-tx-5.0.2.RELEASE.jar中。PlatformTransactionManager 就是 Spring 提供的事务管理器的接口,该接口提供了我们常用的操作事务的方法,它的实现类是 org.springframework.jdbc.datasource.DataSourceTransactionManager

---------------------------------------事物属性-------------------------------------

1. 隔离级别(isolation):默认是数据库的隔离级别
2. 传播行为(propagation):介绍两种经常使用的传播行为: REQUIRED 和 SUPPORTS 
	REQUIRED:如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。
	SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行。
3. 回滚规则:
	rollback-for:用于指定一个异常,当执行产生该异常时,事务回滚;产生其他异常,事务不回滚。没有默认值,任何异常都回滚。 
	no-rollback-for:用于指定一个异常,当产生该异常时,事务不回滚;产生其他异常时,事务回滚。没有默认值,任何异常都回滚。
4. 事务超时(timeout)int getTimeOut() 默认值是-1,没有超时限制。如果有,以秒为单位进行设置。
5. 是否只读(read-only):是否是只读事务。默认值 false,读写型。

基于 xml 的声明式事务控制的基本案例

1. 转账的 dao 层接口
public interface IAccountDao {
	/**
	* 根据Id查询账户
	* @param accountId
	* @return
	*/
	Account findAccountById(Integer accountId);
	
	/**
	* 根据名称查询账户
	* @param accountName
	* @return
	*/
	Account findAccountByName(String accountName);
	
	/**
	* 更新账户
	* @param account
	*/
	void updateAccount(Account account);
}
2. 转账的 dao 层实现类
public class AccountDaoImpl extends JdbcDaoSupport implements IAccountDao {	
	@Override
	public Account findAccountById(Integer accountId) {
		List<Account> accounts = super.getJdbcTemplate().query("select * from account where id = ?",new BeanPropertyRowMapper<Account>(Account.class),accountId);
		return accounts.isEmpty()?null:accounts.get(0);
	}
	
	@Override
	public Account findAccountByName(String accountName) {
		List<Account> accounts = super.getJdbcTemplate().query("select * from account where name = ?",new BeanPropertyRowMapper<Account>(Account.class),accountName);
		if(accounts.isEmpty()) {
		return null;
		}
		if(accounts.size() > 1) {
		throw new RuntimeException("结果集不唯一");
		}
		return accounts.get(0);
	}
	
	@Override
	public void updateAccount(Account account) {
		super.getJdbcTemplate().update("update account set name=?,money=? where id=?",account.getName(),account.getMoney(),account.getId());
	}
}
3. 转账的 service 层接口
public interface IAccountService {

	Account findAccountById(Integer accountId);
	
	void transfer(String sourceName, String targetName, Float Money);
}
4. 转账的 service 层实现类
public class AccountServiceImpl implements IAccountService	{
	private IAccountDao accountDao;
	
	public void setAccountDao(IAccountDao accountDao) {
		this.accountDao = accountDao;
	}
	
	@Override
	public Account findAccountById(Integer accountId) {
		return accountDao.findAccountById(accountId);
	}
	
	@Override
	public void transfer(String sourceName, String targetName, Float money) {
		System.out.println("transfer....");
		//2.1根据名称查询转出账户
		Account source = accountDao.findAccountByName(sourceName);
		//2.2根据名称查询转入账户
		Account target = accountDao.findAccountByName(targetName);
		//2.3转出账户减钱
		source.setMoney(source.getMoney()-money);
		//2.4转入账户加钱
		target.setMoney(target.getMoney()+money);
		//2.5更新转出账户
		accountDao.updateAccount(source);
		int i=1/0;
		//2.6更新转入账户
		accountDao.updateAccount(target);
	}
}
5. account 账户的实体类
public class Account implements Serializable {
	private Integer id;
	private String name;
	private Float money;
  
	public Integer getId() {
		return id;
	}
	
	public void setId(Integer id) {
		this.id = id;
	}
	
	public String getName() {
		return name;
	}
	
	public void setName(String name) {
		this.name = name;
	}
	
	public Float getMoney() {
		return money;
	}
	
	public void setMoney(Float money) {
		this.money = money;
	}
	
	@Override
	public String toString() {
		return "Account{" +
			"id=" + id +
			", name='" + name + '\'' +
			", money=" + money +
			'}';
	}
}
6. 测试类
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:bean.xml")
public class AccountServiceTest {
	@Autowired
	private  IAccountService as;
	
	@Test
	public  void testTransfer(){
		as.transfer("aaa","bbb",100f);
	}
}
7. bean.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: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">
</beans>

<!-- 配置 service --> 
<bean id="accountService" class="com.demo.service.impl.AccountServiceImpl">
    <property name="accountDao" ref="accountDao"></property>
</bean>

<!-- 配置 dao --> 
<bean id="accountDao" class="com.demo.dao.impl.AccountDaoImpl">
	<!-- 注入 dataSource -->
    <property name="dataSource" ref="dataSource"></property> 
</bean> 

<!-- 配置数据源 --> 
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> 
        <property name="driverClassName" value="com.mysql.jdbc.Driver">
        </property> <property name="url" value="jdbc:mysql://localhost:3306/eesy">
        </property> <property name="username" value="root">
        </property> <property name="password" value="1234"></property> 
</bean>

<!-- 配置一个事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
	<!-- 注入 DataSource --> 
    <property name="dataSource" ref="dataSource"></property> 
</bean>

<!-- 事务的配置 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
	<!-- 配置事务的属性 -->
	<tx:attributes>
		<tx:method name="*" read-only="false" propagation="REQUIRED"/> 
		<tx:method name="find*" read-only="true" propagation="SUPPORTS"/> 
	</tx:attributes>
</tx:advice>

<!-- 配置 aop -->
<aop:config>
    <!-- 配置切入点表达式 --> 
    <aop:pointcut expression="execution(* com.demo.service.impl.*.*(..))" id="pt1"/>
	<!-- 建立事务的通知和切入点表达式的关系 --> 
	<aop:advisor advice-ref="txAdvice" pointcut-ref="pt1"/>
</aop:config>
8. pom 文件
<!--添加依赖-->
<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.0.2.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>5.0.2.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-tx</artifactId>
        <version>5.0.2.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.6</version>
    </dependency>
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>1.8.7</version>
    </dependency>
    
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-text</artifactId>
        <version>5.0.2.RELEASE</version>
    </dependency>
</dependencies>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值