spring中IOP的配置

1 案例
需求: 完成转账业务, 事务的支持:

1.1开发数据库表:Account id name money、
开发实体类: Account
public class Account {
private Integer id;
private String name;
private double money;
}
1.2开发AccountDao ; 查询, 更新方法:
package com.yidongxueyuan.spring.dao;

import com.yidongxueyuan.spring.pojo.Account;

public interface AccountDao {
/**
* 查询用户:
* @param accountName
* @return
*/
Account findAccountByName(String accountName);

/**
 * 更新操作: 
 * @param newAccount
 */
void updateAccount(Account newAccount);

}
1.3开发AccountDaoImpl ; DBUtils链接数据库。
package com.yidongxueyuan.spring.dao.impl;

import java.sql.SQLException;

import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;

import com.yidongxueyuan.spring.dao.AccountDao;
import com.yidongxueyuan.spring.pojo.Account;
import com.yidongxueyuan.spring.utils.TransactionManager;

public class AccountDaoImpl implements AccountDao {

//获得对象, 不需要给出数据源: 
	private QueryRunner qr= new QueryRunner();

	@Override
	public Account findAccountByName(String accountName) {
		
		try {
			String sql ="select * from account where name =?";
			Account account = qr.query(TransactionManager.getConnection(), sql, new BeanHandler<Account>(Account.class),accountName);
			return account;
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return null; 
	}

	@Override
	public void updateAccount(Account newAccount) {
		try {
			String sql ="update account set money = ? where name =?";
			qr.update(TransactionManager.getConnection(), sql, newAccount.getMoney(),newAccount.getName());
		} catch (SQLException e) {
			e.printStackTrace();
		}
		
	}

}
1.4开发AccountService ; 定义业务方法: transfer
package com.yidongxueyuan.spring.service;

public interface AccountService {
/**
* 业务方法, 实现转账
* @param sourceAccount 来源账户
* @param targetAccount 目标账户
* @param money 转账金额
*/
public void transfer(String sourceAccount,String targetAccount,float money);
}

1.5开发AccountServiceImpl实现类:
package com.yidongxueyuan.spring.service.impl;

import com.yidongxueyuan.spring.dao.AccountDao;
import com.yidongxueyuan.spring.dao.impl.AccountDaoImpl;
import com.yidongxueyuan.spring.pojo.Account;
import com.yidongxueyuan.spring.service.AccountService;

public class AccountServiceImpl implements AccountService{
private AccountDao dao= new AccountDaoImpl();
@Override
public void transfer(String sourceAccount, String targetAccout, float money) {

		//开启事务: 
		
		//根据来源账户: 
		Account sourceAcc = dao.findAccountByName(sourceAccount);//aaa
		//查询目标账户: 
		Account targetAcc = dao.findAccountByName(targetAccout);// ccc
		//来源账户减钱
		sourceAcc.setMoney(sourceAcc.getMoney()-money); 
		//目标账户增钱:
		targetAcc.setMoney(targetAcc.getMoney()+money);
		

       //调用dao层的方法: 将变化后的数据保存哎数据库当中: 
		dao.updateAccount(sourceAcc);
		
		//模拟异常的发生: 
		int i=1/0;
		dao.updateAccount(targetAcc);
		
	
}

}

1.6工具类: 获得数据源的工具类
package com.yidongxueyuan.spring.utils;

import java.sql.Connection;
import java.sql.SQLException;

import javax.sql.DataSource;

import com.mchange.v2.c3p0.ComboPooledDataSource;

public class C3P0Util {
/**
* 初始化一个对象:
* ComboPooledDataSource 是DataSource类的子类对象:
*/
private static DataSource ds = new ComboPooledDataSource();

/**
 * 返回一个数据源: 
 * @return
 */
public static DataSource getDataSource(){
	return ds; 
}

/**
 * 获得一个链接对象: 
 * @return Connection链接对象: 
 */
public static Connection getConnection(){
	 try {
		return ds.getConnection();
	} catch (SQLException e) {
		e.printStackTrace();
	}
	 return null; 
}

}

1.7工具类依赖的配置文件

<?xml version="1.0" encoding="UTF-8"?> com.mysql.jdbc.Driver jdbc:mysql:///customer root root 10 30 100 10 200

1.8测试
public class TestAccountService {
@Test
public void test1() {
//直接获得被代理对象:
AccountService service = new AccountServiceImpl();
service.transfer(“aaa”, “ccc”, 100);
}

}

测试总结: 在没有加入事务的前提下, 转账的过程当中: aaa 用户-100 但是ccc用户钱并没有增加: 不符合实际情况。 所以加入事务的支持。 保证转账的一个逻辑单元要么都成功, 要么都失败。

1.9 使用aop的方式事务
获得UserServiceImpl类的代理类对象; 在执行业务方法的时候, 在动态的加入事务的支持。

package com.yidongxueyuan.spring.utils;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import com.yidongxueyuan.spring.service.AccountService;
import com.yidongxueyuan.spring.service.impl.AccountServiceImpl;

/**

  • 定义一个工厂: 获得对象的代理对象:
  • @author Mrzhang

*/
public class BeanFactory {

/**
 * 定义了一个方法: 能够获得业务对象的代理对象: 
 * @return
 */
public static AccountService getProxy(){
	
	//被代理的对象: 
	final AccountService service = new AccountServiceImpl();//只专注于转账:
	
	//获得代理类: 
	AccountService proxy= (AccountService)Proxy.newProxyInstance(
			//代理类的类加载器
			service.getClass().getClassLoader(), 
			//代理类的接口当中所有的方法: 
			service.getClass().getInterfaces(),
			//InvocationHandler 接口: 定义代理类和被代理类的具体的代理策略: 
			 new InvocationHandler() {
				
				//proxy:代理类引用: 
				// method:当前执行的方法: 
				// args:执行方法的参数
				//Object 调用方法的返回值: : 
				@Override
				public Object invoke(Object proxy, Method method, Object[] args)
						throws Throwable {
					Object obj =null; 
					
					try {
						//事务开启: 
						TransactionManager.startTransaction();
						//调用业务方法: 
						obj = method.invoke(service, args);
						//事务的提交: 
						TransactionManager.commit();
						
					} catch (Exception e) {
						TransactionManager.rollback();
						e.printStackTrace();
					} finally{
						//释放资源:
					    
					}
					return obj;
				}
			});
	
	return proxy;
}

}
1.10测试:
@Test
public void test1() {
//直接获得被代理对象:
AccountService service = new AccountServiceImpl();
//获得service类的代理类对象:
AccountService proxy = BeanFactory.getProxy();
proxy.transfer(“aaa”, “ccc”, 100);
}

2AOP
2.1 aop概述
Aop 面向切面编程,底层是动态代理实现的。 是oop的延伸。 解决了oop开发过程当中遇到的一些问题。
Aop可以解决: 事务管理 日志的记录 权限的校验 性能的监控。

2.2 代理模式
代理模式: 分类静态代理&动态代理:
静态代理:
动态代理:分类:
基于接口的动态代理: JDK
基于普通Bean的代理: CGLIB (第三方,使用时候导入jar)

2.3 spring当中AOP
Aop 面向切面编程的思想, 这思想并不是spring提出的。 spring当中引用了aop。

Spring框架: aop的实现:
(1)spring当中对aop进行了具体的实现: (废弃)
(2)Spring引用了 AspectJ 对aop进行了很好的实现。(广泛使用)

2.4 aop相关的数据

public interface UserDao {
	
	public User findUserById(int id);
	
	public void saveUser(User user);
	
	public void deleteUser(int id); 
	
	public void updateUser(User user) ;
	
}

连接点(join point ): 可以被拦截到的点就是连接点。
增删改查这些方法都有机会可以被进行功能性的增强, 这些方法都是连接点。

切入点(PonitCut): 真正被拦截到的点。
真正被增强的方法, 就称之为切入点。 在实际开发过程当中, saveUser方法被增强了, 此时saveUser这个方法就是切入点

通知(advice) :增强(方法层面的增强)
在执行saveUser方法之前, 要执行checkPri这个方法,此时checkPri这个方法就称之为通知, 也叫作增强。

引介: (Introduction) : 类层面的增强。

目标:(Target) : 被增强的对象:
在开发当中, UserDao 需要被增强, UserDao就是增强的对象。

织入:(Weaving) :将通知(advice)应用到目标(target)的过程
saveUser方法需要在执行之前加入checkPri()进行权限的校验,这个过程就是织入。

代理对象(Proxy): 目标被增强后,就是一个代理对象。

切面: (aspect): 多个通知和多个切入点的集合。 多个通知的集合。

2.5 aop的入门案例
在这里插入图片描述
2.5.1 引入aop相关的jar包:
IOC: 4个核心+ 2个日志:
注解式开发: spring3.x 不需要导入其他的》
Spring4.x 引来了aop
Aop的开发: 4个:

在这里插入图片描述
第一个jar: aop联盟
第二个jar: aspectweaving
第三个jar: aop
第四个jar: spring 整合aspect

Spring当中自己提供了一个套实现: 开发只需要两个: aop , aopalliance
Spring引入了aspect后: aspectJ spring-aspect :

2.5.2 核心配置文件当中引入相关的约束:

<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">
</bean>

2.5.3 定义接口 UserDao
public interface UserDao {

public User findUserById(int id);


public void saveUser(User user);

public void deleteUser(int id); 

public void updateUser(User user) ;

}
2.5.4 定义接口的实现:

 UserDaoImpl
package com.yidongxueyuan.spring.dao.impl;

import org.springframework.stereotype.Repository;

import com.yidongxueyuan.spring.dao.UserDao;
import com.yidongxueyuan.spring.pojo.User;


public class UserDaoImpl implements UserDao {

	@Override
	public User findUserById(int id) {
		System.out.println("根据id 进行用户的查询:");
		return null;
	}

	@Override
	public void saveUser(User user) {
		System.out.println("save...");
	}

	@Override
	public void deleteUser(int id) {
		System.out.println("delete...");
	}

	@Override
	public void updateUser(User user) {
		System.out.println("update...");
	}

}

2.5.5 定义切面类 并且常见对象: (增强的集合)
package com.yidongxueyuan.spring.pojo;

/**

  • 定义一个切面: (通知的集合)
  • @author Mrzhang

*/
public class MyAspectXml {

/**
 *  定义了一个通知: (增强)
 */
public void checkPri() {
	System.out.println("权限的校验");
}

}

2.5.6 通过配置的方法, 实现对UserDaoImpl当中的方法进行功能的增强。
ApplicationContext.xml当中进行配置L:

 <!-- 配置切面类: -->
    <bean id="myAspectXml" class="com.yidongxueyuan.spring.pojo.MyAspectXml"></bean> 
    <!-- 目标类:  -->
    <bean id="userDao" class="com.yidongxueyuan.spring.dao.impl.UserDaoImpl"></bean>
    
    <!-- aop的配置:  
    	aop: config 标签代表aop配置标签: 
    	aop:aspect: 开始配置切面: 通知和切点集合: 
    	id: 配置该切面的唯一标识: 自定义的: 
    	ref:引入切面类: 
    	aop:before: 在切点之前执行: 
    	method: 指定增强的方法: 
    	pointCut: 定义切点: 基于execution编写: 
    -->
    <aop:config>
        <!-- 配置一个切点: -->
         
        <!-- 配置切面: 通知和切入点的集合 -->
        <aop:aspect id="myAspect" ref="myAspectXml">
            <aop:before method="checkPri"  pointcut="execution( * com.yidongxueyuan.spring.dao.impl.UserDaoImpl.saveUser(..))" />
        </aop:aspect>
    </aop:config>·

总结配置: 将切面类当中的增强应用在目标类的切点上。

2.5.7测试 (spring整合Junit后)

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestUserDao {
	
	//整合后的单元测试: 
	@Resource(name="userDao")
	private UserDao userDao;
	
	@Test
	public void test2() throws Exception {
		
		userDao.saveUser(new User());
	}
	
}

2.6 spring整合junit
(1)加入单元测试的包:
在这里插入图片描述
(2)使用JUnit:

package com.yidongxueyuan.spring.test;

import javax.annotation.Resource;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import com.yidongxueyuan.spring.dao.UserDao;
import com.yidongxueyuan.spring.pojo.User;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestUserDao {
	
	//整合后的单元测试: 
	@Resource(name="userDao")
	private UserDao userDao;
	
	@Test
	public void test2() throws Exception {
		userDao.saveUser(new User());
	}
	
	//传统的单元测试: 
	@Test
	public void test1() throws Exception {
		ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
		UserDao dao = (UserDao)context.getBean("userDao");
		dao.saveUser(new User());
	}
	
	
}

2.7 spring当中提供的通知的类型:
五种通知类型:
2.7.1前置通知:

   <!-- 配置切面: 通知和切入点的集合 -->
        <aop:aspect id="myAspect" ref="myAspectXml">
            <aop:before method="checkPri"  pointcut="execution( * com.yidongxueyuan.spring.dao.impl.UserDaoImpl.saveUser(..))" />
        </aop:aspect>

2.7.2 后置增强:

总结: 当异常通知执行了, 后置通知就不再执行了。
2.7.3 最终通知

2.7.4 异常抛出通知

总结: 如果最后通知执行, 异常抛出通知不执行。

异常抛出通知, 能够获得异常信息:

public void afterThrow(Throwable e) {// e的名称必须和配置文件当中抛出的名称一致。
	System.out.println("异常抛出通知:  "+e.getMessage());//获得抛出的异常信息。
}

总结: 前置, 后置, 最终, 异常抛出通知, 以上四个通知不可能通知执行。 后置通知和异常抛出通知只能执行一个。
当没有遇到异常的时候, 执行后置通知。
遇到了异常信息, 执行异常抛出通知。

2.7.5 环绕通知
切面当中增强的定义:

/**
	 * 测试了代码: 环绕通知执行了, 但是业务代码没有执行: 
	 * 
	 * spring当中提供了一个对象: ProceedingJoinPoint 放行业务代码:  方法有返回值: 
	 */
	public Object around(ProceedingJoinPoint joinPoint) {
		
		//放行业务代码: 
		Object obj= null; 
		try {
			System.out.println("前置通知。");
			obj= joinPoint.proceed();
			System.out.println("后置通知。");
		} catch (Throwable e) {
			System.out.println("异常抛出通知: ");
			e.printStackTrace();
		} finally {
			System.out.println("最终通知");
		}
		return obj; 
		
	}
核心配置文件当中配置环绕通知: 
          <aop:around method="around" pointcut="execution( * com.yidongxueyuan.spring.dao.impl.UserDaoImpl.saveUser(..))"  />

测试。。。 
总结: 环绕通知可以模拟前置,后置, 异常, 最终通知。 

2.8 通用化切点的配置
  <aop:config>
        <!-- 配置一个切点: 任何的切面都可以引用该切点:  -->
         <aop:pointcut expression="execution( * com.yidongxueyuan.spring.dao.impl.UserDaoImpl.saveUser(..))" id="ponit1"/> 
         
        <!-- 配置切面: 通知和切入点的集合 -->
        <aop:aspect id="myAspect" ref="myAspectXml">
            <!-- 前置通知  -->
             <aop:before method="checkPri"  pointcut-ref="ponit1"/>
       		
             <!-- 后置通知:  -->
       		<aop:after-returning method="writeLogger" pointcut-ref="ponit1" /> 
        	
            <!-- 最终通知:  -->
    	     <aop:after method="after" pointcut-ref="ponit1"  />  
            <!-- 异常抛出通知 -->
        	<aop:after-throwing method="afterThrow" throwing="e" pointcut-ref="ponit1"  />
        
         
        </aop:aspect>
    </aop:config>

2.9 execution 表达式的书写

  <aop:pointcut expression="execution( void com.yidongxueyuan.spring.dao.impl.UserDaoImpl.saveUser(..))" id="ponit1"/> 

Expression: 基于execution的书写:

语法:
访问权限修饰符 返回值 包名.类名.方法的名称(方法参数的类型)

访问权限可以省略的:
void com.yidongxueyuan.spring.dao.impl.UserDaoImpl.saveUser(…))

返回值: 可以写成具体的返回值类型: void
返回值类型可以使用通配符: * :带表任何的返回值。

包: 可以使用统配符: * 每一个就代表一级包。
execution( * ..
...UserDaoImpl.saveUser(…))"
*… 代表当前当前包,
expression=“execution( * *…UserDaoImpl.saveUser(…))”
类: 可以使用统配: * 代表所有的类。
方法: 可以同时通配符: * 代表任意的方法。
方法的参数:
如果写具体的类型: 基本数据类型: int double
如果是引用类型: 必须写全限定类型: java.lang.String.class Java.lang.Integer.class
方法的参数可以使用通配符:
*:带表有参数:
() 匹配不带参数;
(…) 匹配带参数的和不带参数的。

总结: 在实际开发当中, 不需要写统配方法:
统配的写法:
expression=“execution( * .*(…))”

建议:

  • com.yidongxueyuan.xx.impl..(…)
    业务包下的所有的实现类。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值