>>分享流行的Java框架以及开源软件,对孙卫琴的《精通Spring:Java Web开发技术详解》提供技术支持 书籍支持  卫琴直播  品书摘要  在线测试  资源下载  联系我们
发表一个新主题 开启一个新投票 回复文章 您是本文章第 21496 个阅读者 刷新本主题
 * 贴子主题:  Spring中的aop和基于XML以及注解的AOP配置 回复文章 点赞(0)  收藏  
作者:flybird    发表时间:2020-01-28 13:42:19     消息  查看  搜索  好友  邮件  复制  引用

                  
Spring中的aop和基于XML以及注解的AOP配置

1、完善我们的account案例
2、分析案例中问题
3、回顾之前讲过的一个技术:动态代理
4、动态代理另一种实现方式
5、解决案例中的问题
6、AOP的概念
7、spring中的AOP相关术语
8、spring中基于XML和注解的AOP配置

                                                    一、完善我们的account案例(分析事务问题并解决)

     解决方式:编写ConnectionUtils

     点击在新窗口中浏览原图
CTRL+鼠标滚轮放大或缩小

     1.pom
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.itheima</groupId>
    <artifactId>day03_eesy_01account</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>commons-dbutils</groupId>
            <artifactId>commons-dbutils</artifactId>
            <version>1.4</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.6</version>
        </dependency>

        <dependency>
            <groupId>c3p0</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.1.2</version>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
    </dependencies>

</project>

            2.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.itheima.factory.BeanFactory ">
        <!-- 注入service -->
        <property name= " accountService "  ref= " accountService "></property>
        <!-- 注入事务管理器 -->
        <property name= " txManager "  ref= " txManager "></property>
    </bean>

     <!-- 配置Service -->
    <bean id= " accountService "  class= " com.itheima.service.impl.AccountServiceImpl ">
        <!-- 注入dao -->
        <property name= " accountDao "  ref= " accountDao "></property>
    </bean>

    <!--配置Dao对象-->
    <bean id= " accountDao "  class= " com.itheima.dao.impl.AccountDaoImpl ">
        <!-- 注入QueryRunner -->
        <property name= " runner "  ref= " runner "></property>
        <!-- 注入ConnectionUtils -->
        <property name= " connectionUtils "  ref= " connectionUtils "></property>
    </bean>

    <!--配置QueryRunner-->
    <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= " 1234 "></property>
    </bean>

    <!-- 配置Connection的工具类 ConnectionUtils -->
    <bean id= " connectionUtils "  class= " com.itheima.utils.ConnectionUtils ">
        <!-- 注入数据源-->
        <property name= " dataSource "  ref= " dataSource "></property>
    </bean>

    <!-- 配置事务管理器-->
    <bean id= " txManager "  class= " com.itheima.utils.TransactionManager ">
        <!-- 注入ConnectionUtils -->
        <property name= " connectionUtils "  ref= " connectionUtils "></property>
    </bean>
</beans>

                                 3.IAccountDao

package com.itheima.dao;

import com.itheima.domain.Account;

import java.util.List;

/* *
* 账户的持久层接口
  */

public  interface  IAccountDao {

     /* *
     * 查询所有
     * @return
      */

    List<Account>  findAllAccount();

     /* *
     * 查询一个
     * @return
      */

    Account findAccountById(Integer accountId);

     /* *
     * 保存
     * @param account
      */

     void  saveAccount(Account account);

     /* *
     * 更新
     * @param account
      */

     void  updateAccount(Account account);

     /* *
     * 删除
     * @param acccountId
      */

     void  deleteAccount(Integer acccountId);

     /* *
     * 根据名称查询账户
     * @param accountName
     * @return  如果有唯一的一个结果就返回,如果没有结果就返回null
     *          如果结果集超过一个就抛异常
      */

    Account findAccountByName(String accountName);
}

     4.IAccountDaoImpl

package com.itheima.dao.impl;

import com.itheima.dao.IAccountDao;
import com.itheima.domain.Account;
import com.itheima.utils.ConnectionUtils;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;

import java.util.List;

/* *
* 账户的持久层实现类
  */

public  class  AccountDaoImpl implements IAccountDao {

     private  QueryRunner runner;
     private  ConnectionUtils connectionUtils;

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

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

    @Override
     public List<Account>  findAllAccount() {
         try {
             return runner.query(connectionUtils.getThreadConnection(), " select * from account ", new BeanListHandler<Account>(Account. class ));
        } catch  (Exception e) {
             throw  new  RuntimeException(e);
        }
    }

    @Override
     public  Account findAccountById(Integer accountId) {
         try {
             return runner.query(connectionUtils.getThreadConnection(), " select * from account where id = ?  ", new BeanHandler<Account>(Account. class ),accountId);
        } catch  (Exception e) {
             throw  new  RuntimeException(e);
        }
    }

    @Override
     public  void  saveAccount(Account account) {
         try {
            runner.update(connectionUtils.getThreadConnection(), " insert into account(name,money)values(?,?) " ,account.getName(),account.getMoney());
        } catch  (Exception e) {
             throw  new  RuntimeException(e);
        }
    }

    @Override
     public  void  updateAccount(Account account) {
         try {
            runner.update(connectionUtils.getThreadConnection(), " update account set name=?,money=? where id=? " ,account.getName(),account.getMoney(),account.getId());
        } catch  (Exception e) {
             throw  new  RuntimeException(e);
        }
    }

    @Override
     public  void  deleteAccount(Integer accountId) {
         try {
            runner.update(connectionUtils.getThreadConnection(), " delete from account where id=? " ,accountId);
        } catch  (Exception e) {
             throw  new  RuntimeException(e);
        }
    }

    @Override
     public  Account findAccountByName(String accountName) {
         try {
            List<Account> accounts = runner.query(connectionUtils.getThreadConnection(), " select * from account where name = ?  ", new BeanListHandler<Account>(Account. class ),accountName);
             if(accounts ==  null || accounts.size() ==  0 ){
                 return  null ;
            }
             if(accounts.size() >  1 ){
                 throw  new RuntimeException( " 结果集不唯一,数据有问题 " );
            }
             return accounts. get( 0 );
        } catch  (Exception e) {
             throw  new  RuntimeException(e);
        }
    }
}

     5.IAccountService

package com.itheima.service;

import com.itheima.domain.Account;

import java.util.List;

/* *
* 账户的业务层接口
  */

public  interface  IAccountService {

     /* *
     * 查询所有
     * @return
      */

    List<Account>  findAllAccount();

     /* *
     * 查询一个
     * @return
      */

    Account findAccountById(Integer accountId);

     /* *
     * 保存
     * @param account
      */

     void  saveAccount(Account account);

     /* *
     * 更新
     * @param account
      */

     void  updateAccount(Account account);

     /* *
     * 删除
     * @param acccountId
      */

     void  deleteAccount(Integer acccountId);

     /* *
     * 转账
     * @param sourceName        转出账户名称
     * @param targetName        转入账户名称
     * @param money             转账金额
      */

     void  transfer(String sourceName,String targetName,Float money);

     // void test(); // 它只是连接点,但不是切入点,因为没有被增强
}

     6.IAccountServiceImpl

package com.itheima.service.impl;

import com.itheima.dao.IAccountDao;
import com.itheima.domain.Account;
import com.itheima.service.IAccountService;

import java.util.List;

/* *
* 账户的业务层实现类
*
*   事务控制应该都是在业务层
*/

public  class  AccountServiceImpl implements IAccountService{

     private  IAccountDao accountDao;

     public  void  setAccountDao(IAccountDao accountDao) {
         this.accountDao =  accountDao;
    }

    @Override
     public List<Account>  findAllAccount() {
        return  accountDao.findAllAccount();
    }

    @Override
     public  Account findAccountById(Integer accountId) {
         return  accountDao.findAccountById(accountId);

    }

    @Override
     public  void  saveAccount(Account account) {
        accountDao.saveAccount(account);
    }

    @Override
     public  void  updateAccount(Account account) {
        accountDao.updateAccount(account);
    }

    @Override
     public  void  deleteAccount(Integer acccountId) {
        accountDao.deleteAccount(acccountId);
    }

    @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);
    }
}

                   7.ConnectionUtils(连接的工具类,它用于从数据源中获取一个连接,并且实现和线程的绑定)

package com.itheima.utils;

import javax.sql.DataSource;
import java.sql.Connection;

/* *
* 连接的工具类,它用于从数据源中获取一个连接,并且实现和线程的绑定
  */

public  class  ConnectionUtils {

     private ThreadLocal<Connection> tl =  new ThreadLocal<Connection> ();

     private  DataSource dataSource;

     public  void  setDataSource(DataSource dataSource) {
         this.dataSource =  dataSource;
    }

     /* *
     * 获取当前线程上的连接
     * @return
      */

     public  Connection getThreadConnection() {
         try {
             // 1.先从ThreadLocal上获取
            Connection conn = tl. get ();
             // 2.判断当前线程上是否有连接
             if (conn ==  null ) {
                 // 3.从数据源中获取一个连接,并且存入ThreadLocal中
                conn =  dataSource.getConnection();
                tl. set (conn);
            }
             // 4.返回当前线程上的连接
             return  conn;
        } catch  (Exception e){
             throw  new  RuntimeException(e);
        }
    }

     /* *
     * 把连接和线程解绑
      */

     public  void  removeConnection(){
        tl.remove();
    }
}

     8.TransactionManager(和事务管理相关的工具类,它包含了,开启事务,提交事务,回滚事务和释放连接)

           package com.itheima.utils;

/* *
* 和事务管理相关的工具类,它包含了,开启事务,提交事务,回滚事务和释放连接
  */

public  class  TransactionManager {

     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  release(){
         try  {
            connectionUtils.getThreadConnection().close(); // 还回连接池中
             connectionUtils.removeConnection();// 把连接和线程解绑
        } catch  (Exception e){
            e.printStackTrace();
        }
    }
}

     9.编写业务层和持久层事务控制代码并配置spring的ioc

     9.1 IAccountServiceImpl

package com.itheima.service.impl;

import com.itheima.dao.IAccountDao;
import com.itheima.domain.Account;
import com.itheima.service.IAccountService;
import com.itheima.utils.TransactionManager;

import java.util.List;

/* *
* 账户的业务层实现类
*
* 事务控制应该都是在业务层
  */

public  class  AccountServiceImpl_OLD implements IAccountService{

     private  IAccountDao accountDao;
       private  TransactionManager txManager;

    
public  void   setTxManager(TransactionManager txManager) {
         this.txManager =  txManager;
    }

     public  void  setAccountDao(IAccountDao accountDao) {
         this.accountDao =  accountDao;
    }

    @Override
     public List<Account>  findAllAccount() {
         try  {
             // 1.开启事务
             txManager.beginTransaction();
             // 2.执行操作
            List<Account> accounts =  accountDao.findAllAccount();
             // 3.提交事务
             txManager.commit();
             // 4.返回结果
             return  accounts;
        } catch  (Exception e){
             // 5.回滚操作
             txManager.rollback();
             throw  new  RuntimeException(e);
        } finally  {
             // 6.释放连接
             txManager.release();
        }

    }

    @Override
     public  Account findAccountById(Integer accountId) {
         try  {
             // 1.开启事务
             txManager.beginTransaction();
             // 2.执行操作
            Account account =  accountDao.findAccountById(accountId);
             // 3.提交事务
             txManager.commit();
             // 4.返回结果
             return  account;
        } catch  (Exception e){
             // 5.回滚操作
             txManager.rollback();
             throw  new  RuntimeException(e);
        } finally  {
             // 6.释放连接
             txManager.release();
        }
    }

    @Override
     public  void  saveAccount(Account account) {
         try  {
             // 1.开启事务
             txManager.beginTransaction();
             // 2.执行操作
             accountDao.saveAccount(account);
             // 3.提交事务
             txManager.commit();
        } catch  (Exception e){
             // 4.回滚操作
             txManager.rollback();
        } finally  {
             // 5.释放连接
             txManager.release();
        }

    }

    @Override
     public  void  updateAccount(Account account) {
         try  {
             // 1.开启事务
             txManager.beginTransaction();
             // 2.执行操作
             accountDao.updateAccount(account);
             // 3.提交事务
             txManager.commit();
        } catch  (Exception e){
             // 4.回滚操作
             txManager.rollback();
        } finally  {
             // 5.释放连接
             txManager.release();
        }

    }

    @Override
     public  void  deleteAccount(Integer acccountId) {
         try  {
             // 1.开启事务
             txManager.beginTransaction();
             // 2.执行操作
             accountDao.deleteAccount(acccountId);
             // 3.提交事务
             txManager.commit();
        } catch  (Exception e){
             // 4.回滚操作
             txManager.rollback();
        } finally  {
             // 5.释放连接
             txManager.release();
        }

    }

    @Override
     public  void  transfer(String sourceName, String targetName, Float money) {
         try  {
             // 1.开启事务
             txManager.beginTransaction();
             // 2.执行操作

             // 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);
             // 3.提交事务
             txManager.commit();

        } catch  (Exception e){
             // 4.回滚操作
             txManager.rollback();
            e.printStackTrace();
        } finally  {
             // 5.释放连接
             txManager.release();
        }

        }
}

     9.2bean.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.itheima.factory.BeanFactory ">
        <!-- 注入service -->
        <property name= " accountService "  ref= " accountService "></property>
        <!-- 注入事务管理器 -->
        <property name= " txManager "  ref= " txManager "></property>
    </bean>

     <!-- 配置Service -->
    <bean id= " accountService "  class= " com.itheima.service.impl.AccountServiceImpl ">
        <!-- 注入dao -->
        <property name= " accountDao "  ref= " accountDao "></property>
    </bean>

    <!--配置Dao对象-->
    <bean id= " accountDao "  class= " com.itheima.dao.impl.AccountDaoImpl ">
        <!-- 注入QueryRunner -->
        <property name= " runner "  ref= " runner "></property>
        <!-- 注入ConnectionUtils -->
        <property name= " connectionUtils "  ref= " connectionUtils "></property>
    </bean>

    <!--配置QueryRunner-->
    <bean id= " runner "  class= " org.apache.commons.dbutils.QueryRunner " scope= " prototype "></bean>   //queryRunner不再往dataSource中拿数据连接,而是往我们自定义的ConnectionUtils中拿数据链接

    <!-- 配置数据源 -->
    <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= " 1234 "></property>
    </bean>

    <!-- 配置Connection的工具类 ConnectionUtils -->
    <bean id= " connectionUtils "  class= " com.itheima.utils.ConnectionUtils ">
        <!-- 注入数据源-->
        <property name= " dataSource "  ref= " dataSource "></property> //往我们自定义的ConnectionUtils中注入连接
    </bean>

    <!-- 配置事务管理器-->
    <bean id= " txManager "  class= " com.itheima.utils.TransactionManager ">
        <!-- 注入ConnectionUtils -->
        <property name= " connectionUtils "  ref= " connectionUtils "></property>
    </bean>
</beans>

     9.3 IAccountDaoImpl

package com.itheima.dao.impl;

import com.itheima.dao.IAccountDao;
import com.itheima.domain.Account;
import com.itheima.utils.ConnectionUtils;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;

import java.util.List;

/* *
* 账户的持久层实现类
  */

public  class  AccountDaoImpl implements IAccountDao {

     private  QueryRunner runner;
      private  ConnectionUtils connectionUtils;

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

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

    @Override
     public List<Account>  findAllAccount() {
         try {
             return runner.query(connectionUtils.getThreadConnection(), " select * from account ", new BeanListHandler<Account>(Account. class ));
        } catch  (Exception e) {
             throw  new  RuntimeException(e);
        }
    }

    @Override
     public  Account findAccountById(Integer accountId) {
         try {
             return runner.query( connectionUtils.getThreadConnection(), " select * from account where id = ?  ", new BeanHandler<Account>(Account. class ),accountId);
        } catch  (Exception e) {
             throw  new  RuntimeException(e);
        }
    }

    @Override
     public  void  saveAccount(Account account) {
         try {
            runner.update(connectionUtils.getThreadConnection(), " insert into account(name,money)values(?,?) " ,account.getName(),account.getMoney());
        } catch  (Exception e) {
             throw  new  RuntimeException(e);
        }
    }

    @Override
     public  void  updateAccount(Account account) {
         try {
            runner.update(connectionUtils.getThreadConnection(), " update account set name=?,money=? where id=? " ,account.getName(),account.getMoney(),account.getId());
        } catch  (Exception e) {
             throw  new  RuntimeException(e);
        }
    }

    @Override
     public  void  deleteAccount(Integer accountId) {
         try {
            runner.update(connectionUtils.getThreadConnection(), " delete from account where id=? " ,accountId);
        } catch  (Exception e) {
             throw  new  RuntimeException(e);
        }
    }

    @Override
     public  Account findAccountByName(String accountName) {
         try {
            List<Account> accounts = runner.query(connectionUtils.getThreadConnection(), " select * from account where name = ?  ", new BeanListHandler<Account>(Account. class ),accountName);
             if(accounts ==  null || accounts.size() ==  0 ){
                 return  null ;
            }
             if(accounts.size() >  1 ){
                 throw  new RuntimeException( " 结果集不唯一,数据有问题 " );
            }
             return accounts. get( 0 );
        } catch  (Exception e) {
             throw  new  RuntimeException(e);
        }
    }
}

     10.aop的引入

     点击在新窗口中浏览原图
CTRL+鼠标滚轮放大或缩小

          二、动态代理
案例的引入

点击在新窗口中浏览原图
CTRL+鼠标滚轮放大或缩小

             2.1 基于接口的动态代理(必须要实现接口)

1.Producer类(一个生产厂家)

package com.itheima.proxy;

/* *
* 一个生产者
  */

public  class  Producer implements IProducer{

     /* *
     * 销售
     * @param money
      */

     public  void saleProduct( float money){
        System. out.println( " 销售产品,并拿到钱: "+ money);
    }

     /* *
     * 售后
     * @param money
      */

     public  void afterService( float money){
        System. out.println( " 提供售后服务,并拿到钱: "+ money);
    }
}

     2. IProducer(对生产厂家要求的接口)

package com.itheima.proxy;

/* *
* 对生产厂家要求的接口
  */

public  interface  IProducer {

     /* *
     * 销售
     * @param money
      */

         public  void saleProduct( float money);

     /* *
     * 售后
     * @param money
      */

     public  void afterService( float money);
}

     3.Client(消费者)

package com.itheima.proxy;

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

/* *
* 模拟一个消费者
  */

public  class  Client {

     public  static  void  main(String[] args) {
        final Producer producer =  new  Producer();

         /* *
         * 动态代理:
         *  特点:字节码随用随创建,随用随加载
         *  作用:不修改源码的基础上对方法增强
         *  分类:
         *      基于接口的动态代理
         *      基于子类的动态代理
         *  基于接口的动态代理:
         *      涉及的类:Proxy
         *      提供者:JDK官方
         *  如何创建代理对象:
         *      使用Proxy类中的newProxyInstance方法
         *  创建代理对象的要求:
         *      被代理类最少实现一个接口,如果没有则不能使用
         *  newProxyInstance方法的参数:
         *      ClassLoader:类加载器
         *          它是用于加载代理对象字节码的。和被代理对象使用相同的类加载器。固定写法。
         *      Class[]:字节码数组
         *          它是用于让代理对象和被代理对象有相同方法。固定写法。
         *      InvocationHandler:用于提供增强的代码
         *          它是让我们写如何代理。我们一般都是些一个该接口的实现类,通常情况下都是匿名内部类,但不是必须的。
         *          此接口的实现类都是谁用谁写。
          */

       IProducer proxyProducer =  (IProducer) Proxy.newProxyInstance(producer.getClass().getClassLoader(),
                producer.getClass().getInterfaces(),
                 new  InvocationHandler() {
                     /* *
                     * 作用:执行被代理对象的任何接口方法都会经过该方法
                     * 方法参数的含义
                     * @param proxy   代理对象的引用
                     * @param method  当前执行的方法
                     * @param args    当前执行方法所需的参数
                     * @return        和被代理对象方法有相同的返回值
                     * @throws Throwable
                      */

                    @Override
                     public  Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                         // 提供增强的代码
                        Object returnValue =  null ;

                         // 1.获取方法执行的参数
                        Float money = (Float)args[ 0 ];
                         // 2.判断当前方法是不是销售
                         if( " saleProduct " .equals(method.getName())) {
                            returnValue = method.invoke(producer, money* 0.8f );
                        }
                         return  returnValue;
                    }
                });
        proxyProducer.saleProduct(10000f);
    }
}

 2.2 基于子类的动态代理(需要外部导入jar包)

     1.导入jar包

     <dependencies>
        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>2.1_3</version>
        </dependency>
    </dependencies>

     2.Client

package  com.itheima.cglib;

import  net.sf.cglib.proxy.Enhancer;
import  net.sf.cglib.proxy.MethodInterceptor;
import  net.sf.cglib.proxy.MethodProxy;

import  java.lang.reflect.Method;

/**
* 模拟一个消费者
  */

public  class  Client {

     public  static  void  main(String[] args) {
         final Producer producer =  new  Producer();

         /**
         * 动态代理:
         *  特点:字节码随用随创建,随用随加载
         *  作用:不修改源码的基础上对方法增强
         *  分类:
         *      基于接口的动态代理
         *      基于子类的动态代理
         *  基于子类的动态代理:
         *      涉及的类:Enhancer
         *      提供者:第三方cglib库
         *  如何创建代理对象:
         *      使用Enhancer类中的create方法
         *  创建代理对象的要求:
         *      被代理类不能是最终类
         *  create方法的参数:
         *      Class:字节码
         *          它是用于指定被代理对象的字节码。
         *
         *      Callback:用于提供增强的代码
         *          它是让我们写如何代理。我们一般都是些一个该接口的实现类,通常情况下都是匿名内部类,但不是必须的。
         *          此接口的实现类都是谁用谁写。
         *          我们一般写的都是该接口的子接口实现类:MethodInterceptor
          */

        Producer cglibProducer = (Producer)Enhancer.create(producer.getClass(),  new  MethodInterceptor() {
             /**
             * 执行北地阿里对象的任何方法都会经过该方法
             *  @param  proxy
             *  @param  method
             *  @param  args
             *    以上三个参数和基于接口的动态代理中invoke方法的参数是一样的
             *  @param  methodProxy :当前执行方法的代理对象
             *  @return
             *  @throws  Throwable
              */

            @Override
             public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy)  throws  Throwable {
                 // 提供增强的代码
                Object returnValue =  null ;

                 // 1.获取方法执行的参数
                Float money = (Float)args[0 ];
                 // 2.判断当前方法是不是销售
                 if("saleProduct" .equals(method.getName())) {
                    returnValue = method.invoke(producer, money*0.8f );
                }
                 return  returnValue;
            }
        });
        cglibProducer.saleProduct(12000f);
    }
}

     2.producer

package  com.itheima.cglib;

/**
* 一个生产者
  */

public  class  Producer {

     /**
     * 销售
     *  @param  money
      */

     public  void saleProduct( float money){
        System.out.println("销售产品,并拿到钱:"+ money);
    }

     /**
     * 售后
     *  @param  money
      */

     public  void afterService( float money){
        System.out.println("提供售后服务,并拿到钱:"+ money);
    }
}

   三、spring中的aop

     1.1基于xml的aop配置

<?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">

    <!-- 配置srping的Ioc,把service对象配置进来-->
    <bean id="accountService"  class="com.itheima.service.impl.AccountServiceImpl"></bean>

    <!-- spring中基于XML的AOP配置步骤
        1 、把通知Bean也交给spring来管理
        2 、使用aop:config标签表明开始AOP的配置
        3 、使用aop:aspect标签表明配置切面
                id属性:是给切面提供一个唯一标识
                ref属性:是指定通知类bean的Id。
        4 、在aop:aspect标签的内部使用对应标签来配置通知的类型
               我们现在示例是让printLog方法在切入点方法执行之前之前:所以是前置通知
               aop:before:表示配置前置通知
                    method属性:用于指定Logger类中哪个方法是前置通知
                    pointcut属性:用于指定切入点表达式,该表达式的含义指的是对业务层中哪些方法增强

            切入点表达式的写法:
                关键字:execution(表达式)
                表达式:
                    访问修饰符  返回值  包名.包名.包名...类名.方法名(参数列表)
                标准的表达式写法:
                     public  void  com.itheima.service.impl.AccountServiceImpl.saveAccount()
                访问修饰符可以省略
                     void  com.itheima.service.impl.AccountServiceImpl.saveAccount()
                返回值可以使用通配符,表示任意返回值
                    *  com.itheima.service.impl.AccountServiceImpl.saveAccount()
                包名可以使用通配符,表示任意包。但是有几级包,就需要写几个* .
                    * *.*.*.* .AccountServiceImpl.saveAccount())
                包名可以使用..表示当前包及其子包
                    * * ..AccountServiceImpl.saveAccount()
                类名和方法名都可以使用* 来实现通配
                    * *..*.* ()
                参数列表:
                    可以直接写数据类型:
                        基本类型直接写名称            int
                        引用类型写包名.类名的方式   java.lang.String
                    可以使用通配符表示任意类型,但是必须有参数
                    可以使用..表示有无参数均可,有参数可以是任意类型
                全通配写法:
                    * *..*.* (..)

                实际开发中切入点表达式的通常写法:
                    切到业务层实现类下的所有方法
                        * com.itheima.service.impl.*.* (..)
    -->

    <!-- 配置Logger类 -->
    <bean id="logger"  class="com.itheima.utils.Logger"></bean>

    <!--配置AOP-->
    <aop:config>
        <!--配置切面 -->
        <aop:aspect id="logAdvice" ref="logger">
            <!-- 配置通知的类型,并且建立通知方法和切入点方法的关联-->
            <aop:before method="printLog" pointcut="execution(* com.itheima.service.impl.*.*(..))"></aop:before>
        </aop:aspect>
    </aop:config>

</beans>

                   1.2四种常用的通知类型

package  com.itheima.utils;

import  org.aspectj.lang.ProceedingJoinPoint;

/**
* 用于记录日志的工具类,它里面提供了公共的代码
  */

public  class  Logger {

     /**
     * 前置通知
      */

     public   void  beforePrintLog(){
        System.out.println("前置通知Logger类中的beforePrintLog方法开始记录日志了。。。" );
    }

     /**
     * 后置通知
      */

     public   void  afterReturningPrintLog(){
        System.out.println("后置通知Logger类中的afterReturningPrintLog方法开始记录日志了。。。" );
    }
     /**
     * 异常通知
      */

     public   void  afterThrowingPrintLog(){
        System.out.println("异常通知Logger类中的afterThrowingPrintLog方法开始记录日志了。。。" );
    }

     /**
     * 最终通知
      */

     public   void  afterPrintLog(){
        System.out.println("最终通知Logger类中的afterPrintLog方法开始记录日志了。。。" );
    }

     /**
     * 环绕通知
     * 问题:
     *      当我们配置了环绕通知之后,切入点方法没有执行,而通知方法执行了。
     * 分析:
     *      通过对比动态代理中的环绕通知代码,发现动态代理的环绕通知有明确的切入点方法调用,而我们的代码中没有。
     * 解决:
     *      Spring框架为我们提供了一个接口:ProceedingJoinPoint。该接口有一个方法proceed(),此方法就相当于明确调用切入点方法。
     *      该接口可以作为环绕通知的方法参数,在程序执行时,spring框架会为我们提供该接口的实现类供我们使用。
     *
     * spring中的环绕通知:
     *      它是spring框架为我们提供的一种可以在代码中手动控制增强方法何时执行的方式。
      */

     public  Object aroundPringLog(ProceedingJoinPoint pjp){
        Object rtValue =  null ;
         try {
            Object[] args = pjp.getArgs(); // 得到方法执行所需的参数

            System.out.println("Logger类中的aroundPringLog方法开始记录日志了。。。前置" );

            rtValue = pjp.proceed(args); // 明确调用业务层方法(切入点方法)

            System.out.println("Logger类中的aroundPringLog方法开始记录日志了。。。后置" );

             return  rtValue;
        } catch  (Throwable t){
            System.out.println("Logger类中的aroundPringLog方法开始记录日志了。。。异常" );
             throw  new  RuntimeException(t);
        } finally  {
            System.out.println("Logger类中的aroundPringLog方法开始记录日志了。。。最终" );
        }
    }
}

2.基于注解的aop配置

2.1pom文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.itheima</groupId>
    <artifactId>day03_eesy_05annotationAOP</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.7</version>
        </dependency>
    </dependencies>

</project>

     2.2 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:context="http://www.springframework.org/schema/context"
       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
        http: // www.springframework.org/schema/context
        http: // www.springframework.org/schema/context/spring-context.xsd">

    <!-- 配置spring创建容器时要扫描的包-->
    <context:component-scan base- package="com.itheima"></context:component-scan>

    <!-- 配置spring开启注解AOP的支持 -->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>

     2.3 acountServiceImpl

package  com.itheima.service.impl;

import  com.itheima.service.IAccountService;
import  org.springframework.stereotype.Service;

/**
* 账户的业务层实现类
  */

@Service("accountService" )
public  class AccountServiceImpl  implements  IAccountService{

    @Override
     public  void  saveAccount() {
        System.out.println("执行了保存" );
         int i=1/0 ;
    }

    @Override
     public  void updateAccount( int  i) {
        System.out.println("执行了更新"+ i);

    }

    @Override
     public  int  deleteAccount() {
        System.out.println("执行了删除" );
         return 0 ;
    }
}

     2.4 通知类

package  com.itheima.utils;

import  org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.* ;
import  org.springframework.stereotype.Component;

/**
* 用于记录日志的工具类,它里面提供了公共的代码
  */
  
@Component("logger" )
@Aspect // 表示当前类是一个切面类
public  class  Logger {

    @Pointcut("execution(* com.itheima.service.impl.*.*(..))" )
     private  void  pt1(){}

     /**
     * 前置通知
      */

//     @Before("pt1()")
     public   void  beforePrintLog(){
        System.out.println("前置通知Logger类中的beforePrintLog方法开始记录日志了。。。" );
    }

     /**
     * 后置通知
      */

//     @AfterReturning("pt1()")
     public   void  afterReturningPrintLog(){
        System.out.println("后置通知Logger类中的afterReturningPrintLog方法开始记录日志了。。。" );
    }
     /**
     * 异常通知
      */

//     @AfterThrowing("pt1()")
     public   void  afterThrowingPrintLog(){
        System.out.println("异常通知Logger类中的afterThrowingPrintLog方法开始记录日志了。。。" );
    }

     /**
     * 最终通知
      */

//     @After("pt1()")
     public   void  afterPrintLog(){
        System.out.println("最终通知Logger类中的afterPrintLog方法开始记录日志了。。。" );
    }

     /**
     * 环绕通知
     * 问题:
     *      当我们配置了环绕通知之后,切入点方法没有执行,而通知方法执行了。
     * 分析:
     *      通过对比动态代理中的环绕通知代码,发现动态代理的环绕通知有明确的切入点方法调用,而我们的代码中没有。
     * 解决:
     *      Spring框架为我们提供了一个接口:ProceedingJoinPoint。该接口有一个方法proceed(),此方法就相当于明确调用切入点方法。
     *      该接口可以作为环绕通知的方法参数,在程序执行时,spring框架会为我们提供该接口的实现类供我们使用。
     *
     * spring中的环绕通知:
     *      它是spring框架为我们提供的一种可以在代码中手动控制增强方法何时执行的方式。
      */

    @Around("pt1()" )
     public  Object aroundPringLog(ProceedingJoinPoint pjp){
        Object rtValue =  null ;
         try {
            Object[] args = pjp.getArgs(); // 得到方法执行所需的参数

            System.out.println("Logger类中的aroundPringLog方法开始记录日志了。。。前置" );

            rtValue = pjp.proceed(args); // 明确调用业务层方法(切入点方法)

            System.out.println("Logger类中的aroundPringLog方法开始记录日志了。。。后置" );

             return  rtValue;
        } catch  (Throwable t){
            System.out.println("Logger类中的aroundPringLog方法开始记录日志了。。。异常" );
             throw  new  RuntimeException(t);
        } finally  {
            System.out.println("Logger类中的aroundPringLog方法开始记录日志了。。。最终" );
        }
    }
}

     2.5 测试类

package  com.itheima.test;

import  com.itheima.service.IAccountService;
import  org.springframework.context.ApplicationContext;
import  org.springframework.context.support.ClassPathXmlApplicationContext;

/**
* 测试AOP的配置
  */

public  class  AOPTest {

     public  static  void  main(String[] args) {
         // 1.获取容器
        ApplicationContext ac =  new ClassPathXmlApplicationContext("bean.xml" );
         // 2.获取对象
        IAccountService as = (IAccountService)ac.getBean("accountService" );
         // 3.执行方法
         as.saveAccount();
    }
}

原文链接:https://www.cnblogs.com/lsk-130602/p/12210249.html

程序猿的技术大观园:www.javathinker.net



[这个贴子最后由 flybird 在 2020-01-29 11:20:48 重新编辑]
  Java面向对象编程-->集合(下)
  JavaWeb开发-->Web运作原理(Ⅳ)
  JSP与Hibernate开发-->JPA API的高级用法
  Java网络编程-->安全网络通信
  精通Spring-->通过Vuex进行状态管理
  Vue3开发-->Vue组件开发高级技术
  Netty初识
  Java POI的使用方法(基于POI将Word、Excel、PPT转换为html)之...
  Redis 超详细总结笔记
  如何编写优雅的Spring架构API
  9种设计模式在Spring中的运用,一定要非常熟练!
  Redis夺命连环11问
  使用Spring MVC多次读取请求Request Body的内容
  SpringMVC下的JUnit4单元测试
  Spring Boot JPA @OneToOne
  超详细使用Maven 搭建 Spring MVC 本地部署Tomcat 实现 Hell...
  Spring MVC的国际化
  SpringBoot构建Restful接口
  spring cloud分布式微服务的概览
  vue项目编译75%时很慢的解决办法
  Axis、Axis2和CXF比较
  更多...
 IPIP: 已设置保密
树形列表:   
[url=http://www.zhent.com]... nihaota 2022-10-28 22:00:56
1页 1条记录 当前第1
发表一个新主题 开启一个新投票 回复文章


中文版权所有: JavaThinker技术网站 Copyright 2016-2026 沪ICP备16029593号-2
荟萃Java程序员智慧的结晶,分享交流Java前沿技术。  联系我们
如有技术文章涉及侵权,请与本站管理员联系。