spring第三天AOP(上)

动态代理实现增强,转账功能,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:用于提供增强的代码
    它是让我们写如何代理。我们一般都是些一个该接口的实现类,通常情况下都是匿名内部类,但不是必须的。
    此接口的实现类都是谁用谁写。

    扫描二维码关注公众号,回复: 9243829 查看本文章
  • 基于子类的动态代理:
    涉及的类: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

发布了19 篇原创文章 · 获赞 4 · 访问量 1243

猜你喜欢

转载自blog.csdn.net/weixin_43427728/article/details/104097777