Spring Aop知识及理解

1、AOP

1.1 什么是AOP

  • 概念:面向切面编程,AOP是OOP的一个延伸

  • OOP:面向对象编程

    • 三大特征:封装、继承、多态

    • 特点:纵向抽取和继承体系

      例如:
      	Animal类:动物类
      		eat()吃饭
      		run()奔跑
      	Dog类:继承Animal类
      	Pig类:继承Animal类
      
  • AOP特点:横向切面编程

    日志记录
    权限校验
    性能监控
    事务
    

1.2 什么是横向逻辑

  • 出现原因:多个纵向流程中出现的相同的子流程逻辑

  • 存在问题:

    1.横切逻辑往往在很多方法中,它是重复的
    2.横切逻辑和业务代码混杂在一起,让业务代码变得很臃肿,业务代码应该只处理业务逻辑
    3.OOP已经不能处理横切逻辑的问题了
    
  • 解决方法

    1.AOP独辟蹊径的提出了横向抽取机制,将业务逻辑和横切逻辑分离
    2.难点:分离之后怎么把横切逻辑再融合到原有业务逻辑上,达到和原来一样的效果
    

1.3 AOP特性

1.3.1 AOP思想
面向切面编程:
	1.要解决问题:在不改变业务逻辑基础上增强横切逻辑
	2.解决过程:业务逻辑不能改变,只能面向横切逻辑
	3.具体方式:增强横切逻辑
1.3.2 AOP的实现方式
  • 动态代理技术
1.3.3 AOP的优势
  • 作用:不改变业务逻辑的情况下进行方法增强
  • 优势:减少重复代码,提高开发效率,业务逻辑和增强的横切逻辑分离便于维护

2、AOP的具体应用

案例:模拟转账(并且模拟转账异常)
	汇款人账户减少一定的金额
	收款人账户增加一定的金额
	计算之后,更新数据库
问题:模拟转账异常(人为制造异常,在两次update之间造了异常)
问题:模拟转账异常(人为制造异常,在两次update之间造了异常)

2.1 转账编码

步骤:
2.1.1 引入依赖
<?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>lyq.com</groupId>
    <artifactId>Spring_day03_01_transfer</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <maven.compiler.compilerVersion>1.8</maven.compiler.compilerVersion>
    </properties>

    <dependencies>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.46</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.10</version>
        </dependency>
        <dependency>
            <groupId>commons-dbutils</groupId>
            <artifactId>commons-dbutils</artifactId>
            <version>1.6</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <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>
    </dependencies>
</project>
2.1.2 持久层
@Repository
public class AccountDaoImpl implements AccountDao {
    //依赖注入
    @Autowired
    private QueryRunner queryRunner;
    @Override
    public Account findByName(String name) {
        String sql = "select * from account where name=?";
        try {
            return queryRunner.query(sql, new BeanHandler<Account>(Account.class),name);
        } catch (SQLException e) {
            throw new RuntimeException();
        }
    }
    @Override
    public void update(Account account) {
        String sql = "update account set name=?,money=? where id=?";
        try {
            Object[] objects = {account.getName(), account.getMoney(), account.getId()};
            queryRunner.update(sql, objects);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}
2.1.3 业务逻辑层
@Service
public class AccountServiceImpl implements AccountService {
    //依赖注入
    @Autowired
    private AccountDao accountDao;
    //转账
    public void transfer(String forName, String toName, Float money) {
        try {
            //开启事务

            //获得转账人,收取人
            Account inName = accountDao.findByName(forName);
            Account outName = accountDao.findByName(toName);
            //转账
            inName.setMoney(inName.getMoney() - money);
            outName.setMoney(outName.getMoney() + money);
            //更新账户余额
            accountDao.update(inName);
            accountDao.update(outName);
            //提交事务

        } catch (Exception e) {
            //回滚事务

            e.printStackTrace();
        }finally {
            //释放资源

        }
    }
}
2.1.4 配置文件ApplicationContext.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: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/context http://www.springframework.org/schema/context/spring-context.xsd">

    <!--扫描注解-->
    <context:component-scan base-package="com"></context:component-scan>
    <!--创建QueryRunner-->
    <bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner">
        <!--依赖注入druid-->
        <constructor-arg name="ds" ref="druidDataSource"></constructor-arg>
    </bean>
    <!--Druid数据源-->
    <bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <!--赋值-->
        <property name="driverClassName" value="${jdbc.driverClassName}"></property>
        <property name="url" value="${jdbc.url}"></property>
        <property name="username" value="${jdbc.username}"></property>
        <property name="password" value="${jdbc.password}"></property>
    </bean>
    <!--读取数据文件-->
    <context:property-placeholder location="druid.properties"></context:property-placeholder>
</beans>
2.1.5 测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:ApplicationContext.xml")
public class AccountServiceImplTest {
    //依赖注入
    @Autowired
    private AccountService accountService;
    @Test
    public void transfer() {
        accountService.transfer("bbb", "aaa", 1F);
    }
}
2.1.6 问题

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OMLUkYNz-1582266849798)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\1571627852683.png)]

2.2 解决问题

2.2.1 引入工具类Connection.java
步骤:
1.创建线程对象
2.创建数据库对象
3.获取连接对象
  • 获取线程中的连接对象
  • 判断连接对象是否为空
  • 如果为空,获取链接,并将连接存入到线程中
  • 如果不为空,抛出异常
4.关闭资源
public class ConnectionUtils {
    //创建线程对象
    private ThreadLocal<Connection> threadLocal =  new ThreadLocal<Connection>();
    //数据库
    @Autowired
    private DataSource dataSource;

    //获取连接对象
    public Connection getThreadConnection(){
        //获取线程中的连接对象
        Connection connection = threadLocal.get();
        //判断连接对象是否为空
        if (connection == null){
            //从连接池中获取对象
            try {
                connection = dataSource.getConnection();
                //将连接存入线程
                threadLocal.set(connection);
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        return connection;
    }
    //关闭资源
    public void close(){
        if (threadLocal.get() != null){
            try {
                //关闭连接,将连接归还给连接池
                threadLocal.get().close();
                //移除线程中绑定的对象
                threadLocal.remove();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}
2.2.2 引入事务管理工具类TransactionManager.java
步骤:
1.开启事务
2.提交事务
3.回滚事务
4.释放资源
@Component
public class TransactionManager {
    //依赖注入
    @Autowired
    private ConnectionUtils connectionUtils;
    //开启事务
    public void beginTransaction(){
        try {
            connectionUtils.getThreadConnection().setAutoCommit(false);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    //提交事务
    public void commit(){
        try {
            connectionUtils.getThreadConnection().commit();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    //回滚事务
    public void rollback(){
        try {
            connectionUtils.getThreadConnection().rollback();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    //释放资源
    public void release(){
        try {
            connectionUtils.getThreadConnection().setAutoCommit(true);
            connectionUtils.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}
2.2.4 持久层
@Repository
public class AccountDaoImpl implements AccountDao {
    //依赖注入
    @Autowired
    private QueryRunner queryRunner;
    @Autowired
    private ConnectionUtils connectionUtils;
    @Override
    public Account findByName(String name) {
        String sql = "select * from account where name=?";
        try {
            return queryRunner.query(connectionUtils.getThreadConnection(), sql, new BeanHandler<Account>(Account.class),name);
        } catch (SQLException e) {
            throw new RuntimeException();
        }
    }
    @Override
    public void update(Account account) {
        String sql = "update account set name=?,money=? where id=?";
        try {
            Object[] objects = {account.getName(), account.getMoney(), account.getId()};
            queryRunner.update(connectionUtils.getThreadConnection(), sql, objects);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}
2.2.3 业务层
@Service
public class AccountServiceImpl implements AccountService {
    //依赖注入
    @Autowired
    private AccountDao accountDao;
    @Autowired
    private TransactionManager transactionManager;
    //转账
    @Override
    public void transfer(String forName, String toName, Float money) {
        try {
            //开启事务
            transactionManager.beginTransaction();
            //获得转账人,收取人
            Account inName = accountDao.findByName(forName);
            Account outName = accountDao.findByName(toName);
            //转账
            inName.setMoney(inName.getMoney() - money);
            outName.setMoney(outName.getMoney() + money);
            //更新账户余额
            accountDao.update(inName);
            int i = 1/0;
            accountDao.update(outName);
            //提交事务
            transactionManager.commit();
        } catch (Exception e) {
            e.printStackTrace();
            //回滚事务
            transactionManager.rollback();
        }finally {
            //释放资源
            transactionManager.release();
        }
    }
}
2.2.4 测试结果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AQIJkMU5-1582266849800)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\1571630872177.png)]

2.2.5 问题
1.业务层代码臃肿
2.技术与业务整合,比较混乱

2.3 动态代理

2.3.1 jdk动态代理
  • 三要素:真实对象(被代理对象)、代理对象、共同的接口

  • 特点:

    1.jdk基于接口的动态代理
    2.java自带的代理
    3.创建好的代理对象时接口的一个实现类
    
  • 代理对象的创建:Proxy

    创建代理对象:
       参数1:类加载器
       参数2:真实对象实现的所有接口   new Class(){Sale.class}
       参数3:实现InvocationHandler接口的实现类对象
       返回值, 代理对象
    例如:
    	SaleDao saleProxy = (SaleDao) Proxy.newProxyInstance(
    						SaleDao.getClass().getClassLoader(), 
    						SaleDao.getClass().getInterfaces(), 
    		new InvocationHandler() {
    		//代理执行, 要执行的方法
    		//proxy 代理对象, 没用
    		//method 真实要执行的方法
    		//args 真实要执行方法 所接收的参数
       		@Override
        	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            	//代码
        	});
        	//采用代理对象执行
        	saleProxy.saleProduct(100F);
    
1. pojo实现类
public class Product {
    private String name;
    private Float money;

    @Override
    public String toString() {
        return "Product{" +
                "name='" + name + '\'' +
                ", money=" + money +
                '}';
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Float getMoney() {
        return money;
    }

    public void setMoney(Float money) {
        this.money = money;
    }
}
2. 产品工厂
public class ProductFactory {
    //制造工厂
    public Product make(){
        //创建商品对象
        Product product = new Product();
        product.setName("小米");
        product.setMoney(2999F);
        return product;
    }
}
3. 销售接口
public interface SaleDao {
    //销售商品
    public void saleProduct(Float money);
}
4. 销售接口实现类
public class SaleDaoImpl implements SaleDao {
    @Override
    public void saleProduct(Float money) {
        //创建工厂对象
        ProductFactory productFactory = new ProductFactory();
        //制造产品
        Product product = productFactory.make();
        //以指定价格卖出
        product.setMoney(money);
        System.out.println("product:" + product);
    }

5. 测试类
public class SaleDaoImplTest {

    //销售商品
    @Test
    public void test() {
        SaleDao saleDao = new SaleDaoImpl();
        saleDao.saleProduct(3999F);
    }

    //jdk动态代理,销售商品
    @Test
    public void test01() {
        //真实对象
        SaleDao saleDao = new SaleDaoImpl();
        /*
         * 创建代理对象
         * 参数1:类加载器
         * 参数2:真实对象实现的所有接口   new Class(){Sale.class}
         * 参数3:实现InvocationHandler接口的实现类对象
         * 返回值, 代理对象
         */
        SaleDao saleProxy = (SaleDao) Proxy.newProxyInstance(
                saleDao.getClass().getClassLoader(),
                saleDao.getClass().getInterfaces(),
                new InvocationHandler() {
                    /*
                     * 代理执行, 要执行的方法
                     * @param proxy 代理对象, 没用
                     * @param method 真实要执行的方法
                     * @param args 真实要执行方法 所接收的参数
                     */
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        //创建工厂对象
                        ProductFactory productFactory = new ProductFactory();
                        //生产工厂
                        Product product = productFactory.make();
                        //获取商品的成本价
                        Float money = product.getMoney();
                        System.out.println(money);
                        //获取指定销售价格
                        Float price = (Float) args[0];
                        System.out.println(price);
                        //两个价格对比
                        if (money > price) {
                            System.out.println("成本价高于销售价格,赔钱,不卖!!!");
                            return null;
                        } else {
                            System.out.println("赚了" + (price - money) + "元,可以卖!");
                            return product;
                        }

                    }
                });
        //采用代理对象执行
        saleProxy.saleProduct(3999F);
    }
}
2.3.2 cglib:基于类的动态代理
1.第三方的代理,需要引入依赖
2.创建好的代理对象时真实对象的子类
  • 创建cglib代理对象

    1. 创建增强对象
    	Enhancer enhancer = new Enhancer();
    2. 指定代理对象的父类(真实对象)
    	enhancer.setSuperclass(真实对象.getClass());
    3. 指定方法拦截器(代理执行的方法)
    	enhancer.setCallback(new MethodInterceptor(){
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                //代码
            }
    	});
    4. 创建代理对象
    	enhancer.create();
    
1. 导入依赖
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib-nodep</artifactId>
    <version>2.2</version>
</dependency>
2. 测试类
public class SaleDaoImplTest {

    //cglib的动态代理
    @Test
    public void test() {
        //创建真实对象
        SaleDao saleDao = new SaleDaoImpl();
         /*
            cglib代理对象
            1. 创建增强对象
            2. 指定代理对象的父类(真实对象)
            3. 指定方法拦截器(代理执行的方法)
            4. 创建代理对象
         */
         //创建增强对象
        Enhancer enhancer = new Enhancer();
        //指定代理对象的父类
        enhancer.setSuperclass(saleDao.getClass());
        //指定方法拦截器
        enhancer.setCallback(new MethodInterceptor() {
            /*
             * 代理执行, 要执行的方法
             * @param o 代理对象, 没用
             * @param method 真实要执行的方法
             * @param objects 真实要执行方法 所接收的参数
             * @param MethodProxy 代理方法, 没用
             */
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                //创建工厂
                ProductFactory productFactory = new ProductFactory();
                //生产产品
                Product product = productFactory.make();
                //获取成本价
                Float money = product.getMoney();
                //获取销售价
                Float price = (Float) objects[0];
                //两个价格对比
                if (money > price) {
                    System.out.println("成本价高于销售价格,赔钱,不卖!!!");
                    return null;
                }else {
                    System.out.println("赚了"+(price -money) + "元,可以卖!");
                    return product;
                }
            }
        });
        //创建代理对象
        SaleDaoImpl proxy = (SaleDaoImpl) enhancer.create();
        proxy.saleProduct(3000F);
    }
}

2.4 动态代理解决问题–业务代码与技术代码分离

业务层

@Service
public class AccountServiceImpl implements AccountService {
    @Autowired
    private AccountDao accountDao;
    @Override
    public void transfer(String forName, String toName, Float money) {
        try {
            //获取转账人对象、收取人对象
            Account inAccount = accountDao.findByName(forName);
            Account outAccount = accountDao.findByName(toName);
            //转账
            inAccount.setMoney(inAccount.getMoney() - money);
            outAccount.setMoney(outAccount.getMoney() + money);
            //更新账户信息
            accountDao.update(inAccount);
            accountDao.update(outAccount);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
2.4.1 jdk动态代理解决问题
 @Test
    public void transferJdk() {
        //创建jdk动态代理对象
        //参数:1.类加载器  2.接口中的所有方法  3.实现类对象
        AccountService proxy = (AccountService) Proxy.newProxyInstance(
                accountService.getClass().getClassLoader(),
                accountService.getClass().getInterfaces(),
                new InvocationHandler() {
                    //参数:1.动态代理对象 2.接口中的方法transfer()  3.方法中的参数的值
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        try {
                            //开启事务
                            transactionManager.beginTransaction();
                            //调用方法,编写业务代码,通过反射技术调用
                            //参数:1.接口对象 2. 接口中方法的transfer()中的参数
                            Object obj = method.invoke(accountService, args);
                            // 提交事务
                            transactionManager.commitTransaction();
                            return obj;
                        } catch (Exception e) {
                            //回滚事务
                            transactionManager.rollback();
                            e.printStackTrace();
                        }finally {
                            //关闭事务
                            transactionManager.release();
                        }
                        return null;
                    }
                });
        accountService.transfer("bbb", "aaa", 1F);
    }

2.4.2 cglib动态代理解决问题
//cglib动态代理
    @Test
    public void transferCglib(){
        //创建增强对象
        Enhancer enhancer = new Enhancer();
        //指定真实对象的父类--->AccountServiceImpl
        enhancer.setSuperclass(accountService.getClass());
        //指定方法拦截器,代理要执行的方法
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object o,
                                    Method method,
                                    Object[] objects,
                                    MethodProxy methodProxy) throws Throwable {
                try {
                    //开启事务
                    transactionManager.beginTransaction();
                    //调用方法,编写业务代码,通过反射技术调用
                    //参数:1.接口对象 2. 接口中方法的transfer()中的参数
                    Object obj = method.invoke(accountService, objects);
                    // 提交事务
                    transactionManager.commitTransaction();
                    return obj;
                } catch (Exception e) {
                    e.printStackTrace();
                    //回滚事务
                    transactionManager.rollback();
                }finally {
                    //关闭事务
                    transactionManager.release();
                }
                return null;
            }
        });
        //创建代理对象,执行方法
        AccountServiceImpl proxy = (AccountServiceImpl) enhancer.create();
        proxy.transfer("aaa", "bbb", 1F);
    }

2.5 Spring关于Jdk/Cglib动态代理的选择

  • jdk动态代理------->涉及到接口
  • cglib动态代理------->不涉及动态代理

3、Spring中AOP的使用

3.1 术语

3.1.1 连接点---->Joinpoint
1.横切程序执行的特定位置:比如类开始初始化前,类初始化之后,类中某个方法调用前、调用后,方法抛出异常后等,这些代码中的特定点就称为“连接点”。
2.Spring仅支持方法的连接点:即仅能在方法调用前、方法调用后、方法抛出异常时以及方法调用前后这些程序执行点织入增强。
3.AOP:可以理解为是一个黑客(因为它要向目前类中嵌入额外的代码逻辑),连接点就是AOP向目标类打入楔子的候选点。
3.1.2 切入点---->Pointcut
1.切点:从一个类中的多个方法中定位需要使用的方法
2.注意:切点只定位到某个方法上,所以如果希望定位到具体连接点上,还需要提供方位信息  
	比如:如果把一个方法理解成数据表中的一条记录的话,那么切入点就好比你select语句的where条件 ,就可以定位到你感兴趣的方法
3.1.3 通知/增强---->Advice
1.增强的第一层意思:横切逻辑代码(增强逻辑代码)
2.在Spring中,增强除了用于描述横切逻辑外,包含另一层意思就是横切逻辑执行的方位信息。刚刚说了切点只能定位到方法,在进一步使用方位信息就可以定位到我们感兴趣的连接点了(方法调用前、方法调用后还是方法抛出异常时等)。
3.1.4 目标对象---->Target
增强逻辑的织入目标类。比如未添加任何事务控制的AccountServiceImpl类  
3.1.5 织入---->Weaving
1.织入是将增强逻辑/横切逻辑添加到目标类具体连接点上的过程,AOP像一台织布机,将目标类、增强 通过AOP(其实就是动态代理技术)编织(捆绑)到一起。
2.Spring采用动态代理织入。  
  Proxy(代理):一个类被AOP织入增强后,就产出了一个结果类,它是融合了原类和增强逻辑的代理类。  
3.1.6 切面---->Acpect
1.切面由切点和增强组成。
2.切面=切点+增强
3.切点+方位信息+横切逻辑
4.连接点+横切逻辑
5.最终切面完成:把横切逻辑织入到哪些方法的方法前/后等
6.本质:把横切逻辑增强到连接点(切点和方位信息都是为了确定连接点)上

3.2 基于XML的AOP配置

3.2.1 AOP配置文件
  • <aop:config>:所有aop配置的根标签

  • <aop:aspect id="" ref="">:配置切面对象

    id:自定义名称
    ref:横切逻辑类的<bean>标签id值
    
  • <aop:pointcut id="" expression>:配置切入点

    id:自定义值
    expression:匹配方法的表达式
    
  • <aop:before method="" pointcut-ref="">:被切入的方法前执行

    method:printBeforeMethod切入的方法名
    pointcut-ref:切入点标签的id属性值
    
3.2.2 增强/通知
前置增强:连接点执行前
后置增强:连接点执行后
异常增强:连接带你抛出异常
返回增强:连接正常执行完毕,当要进行方法放回时执行;如果出现异常,则不执行
环绕增强:前置增强+后置增强+异常增强+返回增强
注意:异常增强与返回增强只能执行到其中一个

代码演示:

需求:在Service层代码的不同方法的不同连接点JoinPoint织入日志
- 把Account表的service层进行crud模拟(dao层就不需要了)
1. pom.xml
 <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.2</version>
        </dependency>
    </dependencies>
2. pojo
public class Account {
    private Integer id;
    private String name;
    private Float money;
}
3. 业务层
//接口
public interface AccountService {
    //模拟保存账户
    int saveAccount(Account account);
    //模拟更新账户
    int updateAccountById(Account account);
    //模拟删除账户
    int deleteAccountById(int id);
    //模拟查询账户
    Account queryAccountById(int id);
}
//实现类
@Service
public class AccountServiceImpl implements AccountService {
    @Override
    public int saveAccount(Account account) {
        int i = 1/0;
        System.out.println("模拟保存账户");
        return 0;
    }

    @Override
    public int updateAccountById(Account account) {
        System.out.println("模拟更新账户");
        return 0;
    }

    @Override
    public int deleteAccountById(int id) {
        System.out.println("模拟删除账户");
        return 0;
    }

    @Override
    public Account queryAccountById(int id) {
        System.out.println("模拟查询账户");
        return null;
    }
}
4. 配置文件
<!--开启注解扫描-->
<context:component-scan base-package="com.itheima"></context:component-scan>
<!--创建增强对象,配置横切逻辑-->
    <bean id="log" class="com.utils.LogUtil"></bean>
    <!--配置aop-->
    <aop:config>
        <!--切面配置-:把增强应用到目标对象,也就是切面-->
        <aop:aspect id="logAspect" ref="log">
            <!--配置切入点:定义哪些方法是连接点-->
            <aop:pointcut id="point" expression="execution(* com.service.impl.*.*(..))"></aop:pointcut>
            <!--开始织入-->
            <!--执行前-->
            <aop:before method="beforeMethod" pointcut-ref="point"></aop:before>
            <!--执行后-->
            <aop:after method="afterMethod" pointcut-ref="point"></aop:after>
            <!--异常-->
            <aop:after-throwing method="afterThrowing" pointcut-ref="point"></aop:after-throwing>
            <!--正常-->
            <aop:after-returning method="afterReturning" pointcut-ref="point"></aop:after-returning>
        </aop:aspect>

    </aop:config>
5. 横切逻辑
public class LogUtil {
    //前置增强
    public void beforeMethod(){
        System.out.println("方法执行前");
    }
    //后置增强
    public void afterMethod(){
        System.out.println("方法执行后");
    }
    //异常增强
    public void afterThrowing(){
        System.out.println("方法异常执行时");
    }
    //返回增强
    public void afterReturning(){
        System.out.println("方法正常执行时");
    }
}
6. 测试类
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:ApplicationContext.xml")
public class TestAop {

    @Autowired
    private AccountService accountService;

    @Test
    public void test(){
        Account account = new Account();
        accountService.saveAccount(account);

        System.out.println("--------------");
        accountService.queryAccountById(1);
    }
}
7. 测试结果

异常增强

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kPsDAEw6-1582266849802)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\1571881291404.png)]

正常增强

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yETHi1MV-1582266849802)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\1571881536379.png)]

3.3 环绕增强

  • 什么是环绕增强:

    它是spring为我们提供的一种可以在代码中手动控制增强方法何时执行的方式,灵活度比较高,设置可以控制原业务逻辑是否执行。
    
  • 注意:环绕通知都是独立使用,不能和上面四种通知类型混合使用

3.3.1 环绕增强AOP的配置
  • <aop:around method="" pointcut-ref=""></aop:around>:环绕通知配置

    method:printBeforeMethod切入的方法名
    pointcut-ref:切入点标签的id属性值
    
3.3.2 XML的环绕增强配置
1. 配置文件:ApplicationContext.xml
<!--环绕增强-->
            <aop:around method="around" pointcut-ref="point"></aop:around>
2. 横切逻辑
//环绕增强
    public Object around(ProceedingJoinPoint proceedingJoinPoint){
        Object result = null;

        try {
            System.out.println("环绕增强前--------------");
            //执行原有业务方法
            result = proceedingJoinPoint.proceed();
            System.out.println("环绕增强后--------------");
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            System.out.println("环绕增强执行后,出现异常的情况: 想做的增强内容");
        }
        return result;
    }

3.4 基于AOP的注解配置

  • 扫描注解:

    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
    
  • AOP的注解:

    1.@Aspect:配置切面对象
    
    1.@Before:前置增强
    2.@After:后置增强
    ------------------------------
    异常增强与返回增强只能执行到其中一个
    ------------------------------
    3.@AfterThrowing:异常增强
    4.@AfterReturning:返回增强
    ------------------------------
    环绕增强独立使用
    ------------------------------
    5.@Around:环绕增强
    
    1.@Pointcut:配置切入点
    
    3.4.1 基本配置
    1. 配置文件
    <!--扫描AOP注解-->
        <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
    
    2. 横切逻辑
    @Component
    @Aspect
    public class LogUtil {
        //前置增强
        @Before("execution(* com.service.impl.*.*(..))")
        public void beforeMethod(){
            System.out.println("方法执行前");
        }
        //后置增强
        @After("execution(* com.service.impl.*.*(..))")
        public void afterMethod(){
            System.out.println("方法执行后");
        }
        //异常增强
        @AfterThrowing("execution(* com.service.impl.*.*(..))")
        public void afterThrowing(){
            System.out.println("方法异常执行时");
        }
        //返回增强
        @AfterReturning("execution(* com.service.impl.*.*(..))")
        public void afterReturning(){
            System.out.println("方法正常执行时");
        }
    }
    

    3.5 AOP注解的横切逻辑对象抽取(配置全局的切点匹配)

  • 注解:

    @Pointcut("execution(* 包名.*.*(..))")
    public void Pointcut(){}
    @Around("Pointcut()")
    
1. 横切逻辑
@Component
@Aspect
public class LogUtil01 {
    //配置全局的切点匹配, 设置匹配的表达式
    @Pointcut("execution(* com.service.impl.*.*(..))")
    public void Pointcut(){

    }
    //环绕增强
    @Around("Pointcut()")
    public Object around(ProceedingJoinPoint proceedingJoinPoint){
        Object result = null;

        try {
            System.out.println("环绕增强前--------------");
            //执行原有业务方法
            result = proceedingJoinPoint.proceed();
            System.out.println("环绕增强后--------------");
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            System.out.println("环绕增强执行后,出现异常的情况: 想做的增强内容");
        }
        return result;
    }
}

intln(“方法执行后”);
}
//异常增强
@AfterThrowing(“execution(* com.service.impl..(…))”)
public void afterThrowing(){
System.out.println(“方法异常执行时”);
}
//返回增强
@AfterReturning(“execution(* com.service.impl..(…))”)
public void afterReturning(){
System.out.println(“方法正常执行时”);
}
}


### 3.5 AOP注解的横切逻辑对象抽取(配置全局的切点匹配)

- 注解:

```properties
@Pointcut("execution(* 包名.*.*(..))")
public void Pointcut(){}
@Around("Pointcut()")
1. 横切逻辑
@Component
@Aspect
public class LogUtil01 {
    //配置全局的切点匹配, 设置匹配的表达式
    @Pointcut("execution(* com.service.impl.*.*(..))")
    public void Pointcut(){

    }
    //环绕增强
    @Around("Pointcut()")
    public Object around(ProceedingJoinPoint proceedingJoinPoint){
        Object result = null;

        try {
            System.out.println("环绕增强前--------------");
            //执行原有业务方法
            result = proceedingJoinPoint.proceed();
            System.out.println("环绕增强后--------------");
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            System.out.println("环绕增强执行后,出现异常的情况: 想做的增强内容");
        }
        return result;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值