动态代理实现增强,转账功能,AOP的使用,通知的类型,注解实现AOP
要实现转账功能,就需要一个数据库表,
这里使用第二天的DBUtils和C3P0来连接数据库,
涉及到转账操作,就要用到事务的相关知识了,首先传统的使用事务的方法,可以创建一个连接工具类,
public class ConnectionUtils {
private ThreadLocal<Connection> tl = new ThreadLocal<Connection>();
private DataSource dataSource;
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
/**
* 获取当前线程上的连接
*/
public Connection getThreadConnection(){
try {
Connection conn = tl.get();
if(conn == null){
conn = dataSource.getConnection();//如果连接为空则从线程池中获取连接
tl.set(conn);
}
return conn;
}catch (Exception e){
throw new RuntimeException(e);
}
}
/**
* 把连接和线程解绑
*/
public void removeConnection(){
tl.remove();
}
}
使用ThreadLocal从线程池中获取连接,并且注入DataSource,
事务的代码提取到transactionCommit 中
/**
* 开启事务,提交事务,回滚事务,释放连接
*/
public class transactionCommit {
private ConnectionUtils connectionUtils;
public void setConnectionUtils(ConnectionUtils connectionUtils) {
this.connectionUtils = connectionUtils;
}
/**
* 开启事务
*/
public void beginTransaction(){
try {
connectionUtils.getThreadConnection().setAutoCommit(false);
}catch (Exception e){
e.printStackTrace();
}
}
/**
* 提交事务
*/
public void commit(){
try {
connectionUtils.getThreadConnection().commit();
}catch (Exception e){
e.printStackTrace();
}
}
/**
* 回滚事务
*/
public void rollBack(){
try {
connectionUtils.getThreadConnection().rollback();
}catch (Exception e){
e.printStackTrace();
}
}
/**
* 释放连接
*/
public void releaseConn(){
try {
connectionUtils.getThreadConnection().close();
connectionUtils.removeConnection();
}catch (Exception e){
e.printStackTrace();
}
}
}
这样就可以在每个方法中手动的加入事务的功能。
public class AccountServiceImpl_OLD implements AccountService {
public AccountDao accountDao;
public transactionCommit tc;
public void setTc(transactionCommit tc) {
this.tc = tc;
}
//注入
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
public List<Account> findAllAccount() {
try{
tc.beginTransaction();
List<Account> accounts = accountDao.findAllAccount();
tc.commit();
return accounts;
}catch (Exception e){
tc.rollBack();
throw new RuntimeException(e);
}finally {
tc.releaseConn();
}
}
public Account findAccountById(Integer id) {
try{
tc.beginTransaction();
Account account = accountDao.findAccountById(id);
tc.commit();
return account;
}catch (Exception e){
tc.rollBack();
throw new RuntimeException(e);
}finally {
tc.releaseConn();
}
}
public void saveAccount(Account account) {
try{
tc.beginTransaction();
accountDao.saveAccount(account);
tc.commit();
}catch (Exception e){
tc.rollBack();
throw new RuntimeException(e);
}finally {
tc.releaseConn();
}
}
public void updateAccount(Account account) {
try{
tc.beginTransaction();
accountDao.updateAccount(account);
tc.commit();
}catch (Exception e){
tc.rollBack();
throw new RuntimeException(e);
}finally {
tc.releaseConn();
}
}
public void deleteAccount(Integer id) {
try{
tc.beginTransaction();
accountDao.deleteAccount(id);
tc.commit();
}catch (Exception e){
tc.rollBack();
throw new RuntimeException(e);
}finally {
tc.releaseConn();
}
}
public void transfer(String sourceName, String tragetName, Float money) {
try{
tc.beginTransaction();
Account source = accountDao.findAccountByName(sourceName);
Account traget = accountDao.findAccountByName(tragetName);
source.setMoney(source.getMoney()-money);
//int i=1/0;
traget.setMoney(traget.getMoney()+money);
accountDao.updateAccount(source);
accountDao.updateAccount(traget);
tc.commit();
}catch (Exception e){
tc.rollBack();
throw new RuntimeException(e);
}finally {
tc.releaseConn();
}
}
}
此时代码冗余度太大,我们希望在AccountServiceImpl中的各方法中的代码简洁一点,于是我们使用代理来增强
动态代理实现增强
-
动态代理:
特点:字节码随用随创建,随用随加载
作用:不修改源码的基础上对方法增强 -
分类:
基于接口的动态代理
基于子类的动态代理 -
基于接口的动态代理:
涉及的类:Proxy
提供者:JDK官方 -
如何创建代理对象:
使用Proxy类中的newProxyInstance方法
创建代理对象的要求:
被代理类最少实现一个接口,如果没有则不能使用 -
newProxyInstance方法的参数:
ClassLoader:类加载器
它是用于加载代理对象字节码的。和被代理对象使用相同的类加载器。固定写法。
Class[]:字节码数组
它是用于让代理对象和被代理对象有相同方法。固定写法。
InvocationHandler:用于提供增强的代码
它是让我们写如何代理。我们一般都是些一个该接口的实现类,通常情况下都是匿名内部类,但不是必须的。
此接口的实现类都是谁用谁写。 -
基于子类的动态代理:
涉及的类:Enhancer
提供者:第三方cglib库 -
如何创建代理对象:
使用Enhancer类中的create方法 -
创建代理对象的要求:
被代理类不能是最终类
create方法的参数:
Class:字节码
它是用于指定被代理对象的字节码。
Callback:用于提供增强的代码
它是让我们写如何代理。我们一般都是些一个该接口的实现类,通常情况下都是匿名内部类,但不是必须的。
此接口的实现类都是谁用谁写。
我们一般写的都是该接口的子接口实现类:MethodInterceptor
要将AccountService中的每个方法都加强(每个方法都使用事务),可以创建一个BeanFactory工厂类,且这个类要使用到AccountService和transactionCommit,将这两个类注入工厂,
public class BeanFactory {
private AccountService accountService;
public transactionCommit tc;
//注入
public void setTc(transactionCommit tc) {
this.tc = tc;
}
//注入,匿名内部类要使用到,所以用final
public final void setAccountService(AccountService accountService) {
this.accountService = accountService;
}
/**
* 获取service代理,将其增强,然后返回增强的service
* 基于接口的动态代理
* @return
*/
public AccountService getAccountService(){
return (AccountService)Proxy.newProxyInstance(accountService.getClass().getClassLoader(), accountService.getClass().getInterfaces(), new InvocationHandler() {
//三个参数分别为,要增强的类的字节码类加载器,要增强的类的字节码接口加载器,---
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//method指要增强的类中的所有方法,**这里还理解的不透彻**
if("test".equals(method.getName())){
return method.invoke(accountService,args);
}
Object rtValue=null;
try{
tc.beginTransaction();
rtValue = method.invoke(accountService,args);
tc.commit();
return rtValue;
}catch (Exception e){
tc.rollBack();
throw new RuntimeException(e);
}finally {
tc.releaseConn();
}
}
});
}
}
然后将工厂类注入,这里要使用factory-bean属性和factory-method属性,从而将得到增强的方法,这里要在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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 配置代理的service-->
<bean id="proxyAccountService" factory-bean="beanFactory" factory-method="getAccountService"></bean>
<!-- 配置BeanFactory-->
<bean id="beanFactory" class="com.Factory.BeanFactory">
<property name="accountService" ref="accountService"></property>
<!-- 注入事务管理器-->
<property name="tc" ref="transactionCommit"></property>
</bean>
<!-- 注入service,要用到accountDao-->
<bean id="accountService" class="com.service.impl.AccountServiceImpl">
<property name="accountDao" ref="accountDao"></property>
</bean>
<!-- 提供dao的ref -->
<bean id="accountDao" class="com.dao.impl.AccountDaoImpl">
<property name="runner" ref="runner"></property>
<property name="connectionUtils" ref="connectionUtils"></property>
</bean>
<!-- 此时runner没有注入datasource,而是交给工具类connectionUtils来管理 -->
<bean id="runner" class="org.apache.commons.dbutils.QueryRunner" scope="prototype">
</bean>
<!-- 配置数据源 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<!-- 连接数据库的信息 -->
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/eesy"></property>
<property name="user" value="root"></property>
<property name="password" value="123456"></property>
</bean>
<bean id="connectionUtils" class="com.utils.ConnectionUtils">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 配置事务管理器 -->
<bean id="transactionCommit" class="com.utils.transactionCommit">
<property name="connectionUtils" ref="connectionUtils"></property>
</bean>
</beans>
现在就可以将AccountService改的简洁很多了
public class AccountServiceImpl implements AccountService {
//public transactionCommit tc;已经交给工具类了,通过代理增强实现事务
public AccountDao accountDao;
//注入
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
public List<Account> findAllAccount() {
List<Account> accounts = accountDao.findAllAccount();
return accounts;
}
public Account findAccountById(Integer id) {
Account account = accountDao.findAccountById(id);
return account;
}
public void saveAccount(Account account) {
accountDao.saveAccount(account);
}
public void updateAccount(Account account) {
accountDao.updateAccount(account);
}
public void deleteAccount(Integer id) {
accountDao.deleteAccount(id);
}
public void transfer(String sourceName, String tragetName, Float money) {
Account source = accountDao.findAccountByName(sourceName);
Account traget = accountDao.findAccountByName(tragetName);
source.setMoney(source.getMoney()-money);
//int i=1/0;
traget.setMoney(traget.getMoney()+money);
accountDao.updateAccount(source);
accountDao.updateAccount(traget);
}
}
动态代理在连接池中使用和全栈处理乱码的时候使用,
这里只展示了基于接口的动态代理,基于子类的在下篇展示。
注解实现AOP,关键标签
@Aspect:定义在切面上
@Pointcut:定义切入点表达式
@Before
@AfterReturning
@AfterThrowing
@After