spring3-aop的概念以及使用xml的方式实现转账功能&动态代理&专业术语

一:aop概念
即面向切面编程,通过预编译的方式和动态代理实现程序功能的统一维护的一种技术,是函数式编程的一种衍生泛型。通过对业务的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合程度降低,提高程序的可重用性,提高了开发的效率。
二:作用:不修改源码的情况下进行增加。
三:实现方式:动态代理
四:使用切面编程:案例的问题:就是我们平时转账的时候,会有事务的提交,如果不成功就会通过事务的回滚而达到数据库不会进行改变。如果不用spring则会很麻烦。
代码如下:
4.1 结构
在这里插入图片描述
4.2.工具类,作用在注入sql语句的时候,关闭自动提交事务。

/*连接的工具类,它用于从数据源中获取一个连接,并实现和线程的绑定*/
public class connectionUtils {
    
    
  private ThreadLocal<Connection> t1=  new ThreadLocal<Connection>();
    private DataSource ds;

    public void setDs(DataSource ds) {
    
    
        this.ds = ds;
    }

    //获取当前线程上的连接
    public Connection getThreadConnection(){
    
    
        Connection connection = t1.get();
        //判断是否有连接
        if (connection==null)
        {
    
    
            //从数据源中获取一个连接并且存入threadLocal中
            try {
    
    
                connection= ds.getConnection();
                t1.set(connection);

            } catch (SQLException throwables) {
    
    
                throwables.printStackTrace();
            }
        }
        return connection;
    }
    //把连接从线程上解绑
    public void removeThreadConnection(){
    
    
        t1.remove();
    }
}

4.2.2重新设置事务

public class transactionManager {
    
    
    private connectionUtils connectionUtil;

    public void setConnectionUtil(connectionUtils connectionUtil) {
    
    
        this.connectionUtil = connectionUtil;
    }

    public void beginTransaction()
    {
    
    
        //关闭自动提交
        try {
    
    
             connectionUtil.getThreadConnection().setAutoCommit(false);
        } catch (SQLException throwables) {
    
    
            throwables.printStackTrace();
        }
    }
    public void commit()
    {
    
    
        try {
    
    
            connectionUtil.getThreadConnection().commit();
        } catch (SQLException throwables) {
    
    
            throwables.printStackTrace();
        }
    }
    public void rollback()
    {
    
    
        try {
    
    
            connectionUtil.getThreadConnection().rollback();
        } catch (SQLException throwables) {
    
    
            throwables.printStackTrace();
        }
    }
    public void close()
    {
    
    
        try {
    
    
            connectionUtil.getThreadConnection().close();
            connectionUtil.removeThreadConnection();
        } catch (SQLException throwables) {
    
    
            throwables.printStackTrace();
        }
    }
}

4.3 数据库dao层

public class IaccountDaoImpl implements  IaccountDao{
    
    

    private QueryRunner runner;
    private connectionUtils connectionUtils;

    public void setConnectionUtils(utils.connectionUtils connectionUtils) {
    
    
        this.connectionUtils = connectionUtils;
    }

    public void setRunner(QueryRunner runner) {
    
    
        this.runner = runner;
    }

    public List<account> findAll() throws SQLException {
    
    
        return  runner.query(connectionUtils.getThreadConnection(),"select *from account",new BeanListHandler<account>(account.class));
    }

    public account findById(Integer id) throws SQLException {
    
    
        return runner.query(connectionUtils.getThreadConnection(),"select *from account where id=?",new BeanHandler<account>(account.class),id);
    }

    public void updateAcc(account acc) throws SQLException {
    
    
        runner.update(connectionUtils.getThreadConnection(),"update account set name=?,money=? where id=?",acc.getName(),acc.getMoney(),acc.getId());
    }

    public void deleteById(Integer id) throws SQLException {
    
    
        runner.update(connectionUtils.getThreadConnection(),"delete *from account where id =?",new BeanHandler<account>(account.class),id);
    }

    public void insert(account acc) throws SQLException {
    
    
        runner.update(connectionUtils.getThreadConnection(),"insert into account values(?,?)",acc.getName(),acc.getMoney());
    }

    public account findByNme(String name) throws SQLException {
    
    
      List<account> accounts= runner.query(connectionUtils.getThreadConnection(),"select *from account where name=?",new BeanListHandler<account>(account.class),name);
      if (accounts.size()==0||accounts==null)
      {
    
    
          return null;
      }if (accounts.size()>1)
        {
    
    
            throw new RuntimeException("结果集不唯一");
        }
        return accounts.get(0);
    }
}

4.4service层,可以清楚的感受到代码的冗余

@Component("accountService")
public class IaccountServiceImpl implements IaccountService
{
    
    
    private IaccountDao accDao;
    private transactionManager ts;

    public void setTs(transactionManager ts) {
    
    
        this.ts = ts;
    }

    public void setAccDao(IaccountDao accDao) {
    
    
        this.accDao = accDao;
    }
    //还有其他方法 ...省略实现了,跟下面一样
        public void insert(account acc) throws SQLException {
    
    

        try {
    
    
            //开启事务
            ts.beginTransaction();
            //执行操作
            accDao.insert(acc);
            //提交事务
            ts.commit();
            //返回结果

        }catch (Exception E){
    
    
            //回滚
            ts.rollback();
            throw new RuntimeException(E);
        }finally {
    
    
//            释放
            ts.close();
        }

    }
    //模拟转账操作
    public void transfer(String sourceName , String targetName ,float money) throws SQLException {
    
    

        try {
    
    
            //开启事务
            ts.beginTransaction();
            //执行操作
            //获取转账人的钱数
            account aa = accDao.findByNme(sourceName);
            account bb = accDao.findByNme(targetName);
            //转账
            aa.setMoney(aa.getMoney()-money);
            bb.setMoney(bb.getMoney()+money);
            //更新
            accDao.updateAcc(aa);
            //int a=1/0;
            accDao.updateAcc(bb);
            //提交事务
            ts.commit();
            //返回结果

        }catch (Exception E){
    
    
            //回滚
            ts.rollback();
            throw new RuntimeException(E);
        }finally {
    
    
//            释放
            ts.close();
        }

    }

4.5 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">
<!--配置dao-->
    <!--配置service-->
    <bean id="accountService" class="service.IaccountServiceImpl">
        <property name="accDao" ref="accountDao"/>
        <property name="ts" ref="transactionManager"></property>
    </bean>
    <bean id="accountDao" class="dao.IaccountDaoImpl">
        <property name="runner" ref="runner1"/>
        <property name="connectionUtils" ref="connectionUtils"></property>
    </bean>
    <!--事务管理-->
    <bean id="transactionManager" class="utils.transactionManager">
        <property name="connectionUtil" ref="connectionUtils"></property>
    </bean>

    <bean id="connectionUtils" class="utils.connectionUtils">
        <property name="ds" ref="dataSource"></property>
    </bean>


    <!--  此处我们只注入了数据源,表明每条语句独立事务-->
    <bean id="runner1" class="org.apache.commons.dbutils.QueryRunner" scope="prototype">
    </bean>
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="com.mysql.cj.jdbc.Driver"></property>
        <property name="jdbcUrl" value="jdbc:mysql://127.0.0.1:3306/springOne?serverTimezone=UTC"></property>
        <property name="user" value="root"></property>
        <property name="password" value="admin"></property>
    </bean>
</beans>

4.6 导入pom坐标

   <artifactId>spring-context</artifactId>
    <artifactId>spring-test</artifactId>
    <artifactId>c3p0</artifactId>
     <artifactId>commons-dbutils</artifactId>
     <artifactId>junit</artifactId>
      <artifactId>mysql-connector-java</artifactId>

4.7 测试类

public class Spring_curd1 {
    
    
    @Test
    public void findAll() throws SQLException {
    
    
       ApplicationContext cc = new ClassPathXmlApplicationContext("bean.xml");
        IaccountService accountService = cc.getBean("accountService", IaccountService.class);
        accountService.transfer("aa","bb",100);
    }

}

**由此可以明显的看出问题,业务层方法变得臃肿了,里面充斥着很多重复代码。并且业务层方法和事务控制方法耦合了。 试想一下,如果我们此时提交,回滚,释放资源中任何一个方法名变更,都需要修改业务层的代码。 **

五:更新后

学习 spring 中的 AOP 要明确的事 : a、开发阶段(我们做的) 编写核心业务代码(开发主线):大部分程序员来做,要求熟悉业务需求。 把公用代码抽取出来,制作成通知。(开发阶段最后再做):AOP 编程人员来做。 在配置文件中,声明切入点与通知间的关系,即切面。:AOP 编程人员来做。 b、运行阶段(Spring框架完成的) Spring 框架监控切入点方法的执行。一旦监控到切入点方法被运行,使用代理机制,动态创建目标对 象的代理对象,根据通知类别,在代理对象的对应位置,将通知对应的功能织入,完成完整的代码逻辑运行。

5.1 结构图
在这里插入图片描述
5.2
不变的是:
工具类在这里是connectionUtils和transactionManager不变
dao层不变
pojo层不变
自然测试类不变
变的是因为臃肿的service而导致的变化
service

@Component("accountService")
public class IaccountServiceImpl implements IaccountService
{
    
    

    private IaccountDao accDao;

    public void setAccDao(IaccountDao accDao) {
    
    
        this.accDao = accDao;
    }
    public List<account> findAll() throws SQLException {
    
    
        return accDao.findAll();
    }
    public account findById(Integer id) throws SQLException {
    
    
        return accDao.findById(id);
    }
    public void updateAcc(account acc) throws SQLException {
    
    
        accDao.updateAcc(acc);
    }

    public void deleteById(Integer id) throws SQLException {
    
    
        accDao.deleteById(id);
    }

    public void insert(account acc) throws SQLException {
    
    
        accDao.insert(acc);
    }

    public void transfer(String sourceName, String targetName, float money) throws SQLException {
    
    
        //执行操作
        //获取转账人的钱数
        account aa = accDao.findByNme(sourceName);
        account bb = accDao.findByNme(targetName);
        //转账
        aa.setMoney(aa.getMoney()-money);
        bb.setMoney(bb.getMoney()+money);
        //更新
        accDao.updateAcc(aa);
//    int a=1/0;
        accDao.updateAcc(bb);
    }
}

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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                      http://www.springframework.org/schema/beans/spring-beans.xsd
             http://www.springframework.org/schema/aop
                           http://www.springframework.org/schema/aop/spring-aop.xsd">
    <!--配置service-->
    <bean id="accountService" class="lml.service.IaccountServiceImpl">
        <property name="accDao" ref="accountDao"></property>
    </bean>
    <!--配置dao-->
    <bean id="accountDao" class="lml.dao.IaccountDaoImpl">
        <property name="runner" ref="runner1"></property>
        <property name="connectionUtils" ref="connectionUtils"></property>

    </bean>
    <bean id="connectionUtils" class="config.connectionUtils">
        <property name="ds" ref="dataSource"></property>
    </bean>
    <bean id="tsmanager" class="config.transactionManager">
        <property name="connectionUtil" ref="connectionUtils"></property>
    </bean>
    <!-- 配置 runner此处我们只注入了数据源,表明每条语句独立事务-->
    <bean id="runner1" class="org.apache.commons.dbutils.QueryRunner" scope="prototype">
      <constructor-arg name="ds" ref="dataSource"></constructor-arg>
    </bean>
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="com.mysql.cj.jdbc.Driver"></property>
        <property name="jdbcUrl" value="jdbc:mysql://127.0.0.1:3306/springOne?serverTimezone=UTC"></property>
        <property name="user" value="root"></property>
        <property name="password" value="admin"></property>
    </bean>

    <!--配置切面编程-->

    <aop:config>
        <aop:aspect id="txAdvice" ref="tsmanager">
            <!--前置通知-->
            <aop:before method="beginTransaction" pointcut-ref="pt"></aop:before>
            <!--后置-->
            <aop:after-returning method="commit" pointcut-ref="pt"></aop:after-returning>
            <!--异常-->
            <aop:after-throwing method="rollback" pointcut-ref="pt"></aop:after-throwing>
            <!--最后-->
            <aop:after method="close" pointcut-ref="pt"></aop:after>
            <aop:pointcut id="pt" expression="execution(* lml.service.*.*(..))"/>
        </aop:aspect>
    </aop:config>
</beans>

pom.xml
在上面的基础上导入aop的包

  <artifactId>aopalliance</artifactId>
  <artifactId>aspectjweaver</artifactId>

六:动态代理:字节码随用随创建,随用随加载。
理解:原本:a销售c。动态代理:a通过b代理,顺便b还送c礼品,流程 a-b-c;
1.基于接口(a)的增强
2.基于子类(b)的增强

1.1 基于接口(a)的增强
提供者:JDK 官方的 Proxy 类。
要求:被代理类最少实现一个接口

  创建的方式    
     Proxy.newProxyInstance(三个参数) 
        * 参数含义:  
          ClassLoader:和被代理对象使用相同的类加载器。   
           Interfaces:和被代理对象具有相同的行为。实现相同的接口。
           InvocationHandler:如何代理。  
     Object one= Proxy.newProxyInstance(    
              a.getClass().getClassLoader(),   
              a.getClass().getInterfaces(),  
      		  new InvocationHandler() {
    
     
      		   //执行被代理对象的任何方法,都会经过该方法
      		   // proxy:代理对象的引用。不一定每次都用得到 
      		   //method:当前执行的方法对象 
      		   // args:执行方法所需的参数 
                        @Override     
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
       增强...}

2.1基于子类(b)的增强
提供者:第三方的 CGLib,如果报 asmxxxx 异常,需要导入 asm.jar。
要求:被代理类不能用 final 修饰的类(最终类)。

 用到的类:  
  *   Enhancer 
  *  用到的方法:
  *     create(Class, Callback) 
  *  方法的参数:  
  *  *   Class:被代理对象的字节码 
  *   *   Callback:如何代理 
Object one= Enhancer.create(a.getClass(),new MethodInterceptor() {
    
      
  /* 执行被代理对象的任何方法,都会经过该方法。在此方法内部就可以对被代理对象的任何 方法进行增强。    
   参数:
   前三个和基于接口的动态代理是一样的。
   MethodProxy:当前执行方法的代理对象。 
 */    @Override    
 public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
    
     
 ...}

七:
Joinpoint(连接点): 所谓连接点是指那些被拦截到的点。在 spring 中,这些点指的是方法,因为 spring 只支持方法类型的 连接点。
Pointcut(切入点): 所谓切入点是指我们要对哪些 Joinpoint 进行拦截的定义。
Advice(通知/增强): 所谓通知是指拦截到 Joinpoint 之后所要做的事情就是通知。 通知的类型:前置通知,后置通知,异常通知,最终通知,环绕通知。
Introduction(引介): 引介是一种特殊的通知在不修改类代码的前提下, Introduction 可以在运行期为类动态地添加一些方 法或 Field。
Target(目标对象): 代理的目标对象。
Weaving(织入): 是指把增强应用到目标对象来创建新的代理对象的过程。 spring 采用动态代理织入,而 AspectJ 采用编译期织入和类装载期织入。
Proxy(代理): 一个类被 AOP 织入增强后,就产生一个结果代理类。 Aspect(切面): 是切入点和通知(引介)的结合。

猜你喜欢

转载自blog.csdn.net/weixin_46809332/article/details/115746945