遇一山,过一山,处处有风景;只要勇敢向前,一路尽是繁花盛开。 | (点击查看→)【测试干货】python/java自动化、持续集成、性能、测开、简历、笔试面试等

AOP思想(面向切面编程)、注解版通知示例

 

AOP介绍

  AOP(Aspect Oriented Programming,即面向切面编程),是OOP(Object Oriented Programming,面向对象编程)的补充和完善。AOP利用一种称为"横切"的技术,剖开封装的对象,将那些影响了多个类的公共行为封装到一个可重用模块,减少系统的重复代码,降低模块之间的耦合度。
  使用"横切"技术,AOP把软件系统分为两个部分:核心关注点和横切关注点。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。横切关注点经常发生在核心关注点的多处,而各处基本相似,比如权限认证、日志打印等,如下图:

示意图一

 

示意图二

 

示意图三

 

AOP是在不改变原程序的基础上为代码段增加新的功能,底层使用的是代理设计模式实现的。

 

AOP术语

1.target:目标类,需要被代理的类,例如:UserServiceImpl

2.Joinpoint:连接点,哪些方法可以被拦截

3.PointCut:切入点,被增强的连接点(通过切入点表达式选择),也就是指我们要对哪些Joinpoint进行拦截

4.advice:通知/增强,拦截到Joinpoint之后所要做的事情就是通知,是一段代码

5.Weaving:织入,是指把aspect应用到目标对象target来创建新的代理对象proxy的过程,是一个动作

6.proxy:融合了原来类和增强逻辑的代理类

7.Aspect(切面):由切入点pointcut和通知advice组成

 

通知类型

前置通知:目标方法运行之前调用
后置通知:在目标方法运行之后调用;如果出现异常不会调用
环绕通知:在目标方法之前和之后都调用
异常拦截通知:如果出现异常,就会调用
最终通知:在目标方法运行之后调用;无论是否出现异常,都会调用

  

注解版通知环境配置

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.qzcsbj.myspring</groupId>
    <artifactId>myspring</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <spring.version>4.3.14.RELEASE</spring.version>
        <junit.version>4.12</junit.version>
        <log4j.version>1.2.17</log4j.version>
    </properties>

    <dependencies>
        <!-- spring需要的jar包 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <!--<dependency>-->
            <!--<groupId>org.springframework</groupId>-->
            <!--<artifactId>spring-context-support</artifactId>-->
            <!--<version>${spring.version}</version>-->
        <!--</dependency>-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-expression</artifactId>
            <version>${spring.version}</version>
        </dependency>


        <!--aop相关-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>${spring.version}</version>
        </dependency>


        <!--日志-->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>${log4j.version}</version>
        </dependency>

        <!--Spring测试模块-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <!--单元测试-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>${junit.version}</version>
            <scope>test</scope>
        </dependency>

        <!--lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.16.18</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>RELEASE</version>
            <scope>compile</scope>
        </dependency>
    </dependencies>
</project>

 

实体类

package com.qzcsbj.bean;

/**
 * @公众号 : 全栈测试笔记
 * @博客 : www.cnblogs.com/uncleyong
 * @微信 : ren168632201
 * @描述 : <>
 */
public class User {
    private String name;
    private String sex;

    public User() {
    }

    public User(String name, String sex) {
        this.name = name;
        this.sex = sex;
    }

    public String getName() {
        return name;
    }

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

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

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

dao层

dao接口

package com.qzcsbj.dao;

import com.qzcsbj.bean.User;

/**
 * @公众号 : 全栈测试笔记
 * @博客 : www.cnblogs.com/uncleyong
 * @微信 : ren168632201
 * @描述 : <>
 */
public interface UserDao {
    public int addUser(User user);

    public int deleteUser(int id);

    public int updateUser(User user);
}

  

dao实现类(这里只是演示,没有真的操作数据库,而且,mybatis也不需要实现类)

package com.qzcsbj.dao.impl;

import com.qzcsbj.bean.User;
import com.qzcsbj.dao.UserDao;
import org.springframework.stereotype.Repository;

/**
 * @公众号 : 全栈测试笔记
 * @博客 : www.cnblogs.com/uncleyong
 * @微信 : ren168632201
 * @描述 : <>
 */
@Repository
public class UserDaoImpl implements UserDao {

    public int addUser(User user){
        System.out.println("============新增用户:" + user);
        // System.out.println(1/0);  // 异常
        return 1;
    }

    public int deleteUser(int id){
        System.out.println("============删除用户:" + id);
        return 1;
    }

    public int updateUser(User user){
        System.out.println("============更新用户:" + user);
        return 1;
    }
}

 

 

日志:log4j.properties

log4j.rootLogger = INFO,console,file

log4j.appender.console = org.apache.log4j.ConsoleAppender
log4j.appender.console.Target = System.out
log4j.appender.console.layout = org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern = %d{HH:mm:ss SSS} [%t] %-5p method: %l----%m%n

log4j.appender.file = org.apache.log4j.DailyRollingFileAppender
log4j.appender.file.File = target/qzcsbj.log
log4j.appender.file.Append = true
log4j.appender.file.Threshold = warn
log4j.appender.file.layout = org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern = %d{yyyy-MM-dd HH:mm:ss} method: %l - [ %p ] ----%m%n

 

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

    <!--扫描包-->
    <context:component-scan base-package="com.qzcsbj.*"/>

    <!--配置使用注解的方式将增强织入目标对象-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>

  

注解版示例:前置通知、后置通知、异常通知、最终通知

通知类:添加@Component和@Aspect

package com.qzcsbj.adviser;

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

/**
 * @公众号 : 全栈测试笔记
 * @博客 : www.cnblogs.com/uncleyong
 * @微信 : ren168632201
 * @描述 : <>
 */
@Component
@Aspect  // 表示这是一个切面类
public class TransactionAdviser {
    // 前置通知
    @Before("execution(* com.qzcsbj.dao.impl.*.*(..))")
    public void openTx(){
        System.out.println("================开启事务");
    }

    // 后置通知
    @AfterReturning(value = "execution(* com.qzcsbj.dao.impl.*.*(..))", returning = "val")
    public void afterRetrunAdviser(Object val){
        System.out.println("================提交事务,方法返回值是:" + val);
    }

    // 异常通知
    @AfterThrowing(value = "execution(* com.qzcsbj.dao.impl.*.*(..))", throwing = "ex")
    public void exceptionAdviser(Exception ex){
        System.out.println("================回滚事务,异常信息是:" + ex.getMessage());
    }

    // 最终增强
    @After(value = "execution(* com.qzcsbj.dao.impl.*.*(..))")
    public void commitTx(){
        System.out.println("================最终增强");
    }
}

 

运行顺序:

无异常,最终增强在后置增强前面

 

有异常,最终增强在异常增强前面

    public int addUser(User user){
        System.out.println("============新增用户:" + user);
        System.out.println(1/0);  // 异常
        return 1;
    }

  

结果:只执行了新增方法

 

调整dao实现类:在deleteUser方法中加异常,addUser放最后

package com.qzcsbj.dao.impl;

import com.qzcsbj.bean.User;
import com.qzcsbj.dao.UserDao;
import org.springframework.stereotype.Repository;

/**
 * @公众号 : 全栈测试笔记
 * @博客 : www.cnblogs.com/uncleyong
 * @微信 : ren168632201
 * @描述 : <>
 */
@Repository
public class UserDaoImpl implements UserDao {
    public int deleteUser(int id){
        System.out.println("============删除用户:" + id);
        System.out.println(1/0);
        return 1;
    }

    public int updateUser(User user){
        System.out.println("============更新用户:" + user);
        return 1;
    }
    public int addUser(User user){
        System.out.println("============新增用户:" + user);
        // System.out.println(1/0);  // 异常
        return 1;
    }
}

 

结果:虽然上面addUser放最下面,但是下面先执行addUser,然后执行deleteUser,说明方法执行顺序是按照ascii来排序的

 

注解版示例:环绕通知

package com.qzcsbj.adviser;

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

/**
 * @公众号 : 全栈测试笔记
 * @博客 : www.cnblogs.com/uncleyong
 * @微信 : ren168632201
 * @描述 : <>
 */
@Component
@Aspect  // 表示这是一个切面类
public class TransactionAdviser {
    // 环绕增强
    @Around(value = "execution(* com.qzcsbj.dao.impl.*.*(..))")
    public Object aroundMethod(ProceedingJoinPoint joinPoint) {  // 连接点
        Object result = null;
        try {
            System.out.println("--------------开启事务------------");
            //调用目标方法
            result = joinPoint.proceed();
            System.out.println("--------------提交事务--------方法的返回值:" + result);
        } catch (Throwable throwable) {
            System.out.println("--------------回滚事务----------异常信息是:" + throwable.getMessage());
            //throwable.printStackTrace();
        } finally {
            System.out.println("--------------最终增强------------");
        }
        return result;
    }
}

  

只配置环绕,可以控制顺序,最终增强都是在最后面:前置增强 -- 后置增强/异常增强 -- 最终增强
 
无异常
package com.qzcsbj.dao.impl;

import com.qzcsbj.bean.User;
import com.qzcsbj.dao.UserDao;
import org.springframework.stereotype.Repository;

/**
 * @公众号 : 全栈测试笔记
 * @博客 : www.cnblogs.com/uncleyong
 * @微信 : ren168632201
 * @描述 : <>
 */
@Repository
public class UserDaoImpl implements UserDao {
    public int deleteUser(int id){
        System.out.println("============删除用户:" + id);
        // System.out.println(1/0);
        return 1;
    }

    public int updateUser(User user){
        System.out.println("============更新用户:" + user);
        return 1;
    }
    public int addUser(User user){
        System.out.println("============新增用户:" + user);
        // System.out.println(1/0);  // 异常
        return 1;
    }
}
 
结果

 

有异常
package com.qzcsbj.dao.impl;

import com.qzcsbj.bean.User;
import com.qzcsbj.dao.UserDao;
import org.springframework.stereotype.Repository;

/**
 * @公众号 : 全栈测试笔记
 * @博客 : www.cnblogs.com/uncleyong
 * @微信 : ren168632201
 * @描述 : <>
 */
@Repository
public class UserDaoImpl implements UserDao {
    public int deleteUser(int id){
        System.out.println("============删除用户:" + id);
        System.out.println(1/0);
        return 1;
    }

    public int updateUser(User user){
        System.out.println("============更新用户:" + user);
        return 1;
    }
    public int addUser(User user){
        System.out.println("============新增用户:" + user);
        // System.out.println(1/0);  // 异常
        return 1;
    }
}
 
结果

  

总结:执行顺序

无异常,最终增强在后置增强前面,环绕在最终增强前面
有异常,最终增强在异常增强前面,环绕在最终增强前面

 

补充:测试类

package com.qzcsbj.test;

import com.qzcsbj.bean.User;
import com.qzcsbj.dao.UserDao;
import com.qzcsbj.dao.impl.UserDaoImpl;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import javax.annotation.Resource;

/**
 * @公众号 : 全栈测试笔记
 * @博客 : www.cnblogs.com/uncleyong
 * @微信 : ren168632201
 * @描述 : <>
 */
@RunWith(SpringJUnit4ClassRunner.class)  // 表示Spring和JUnit整合测试
@ContextConfiguration("classpath:applicationContext2.xml")
public class Test {
    // @Autowired  // 对象只有一个,所以这里直接写@Resource也可以
    @Resource
    UserDao userDao;

    @org.junit.Test
    public void test(){
        userDao.addUser(new User("jack","男"));
        userDao.deleteUser(1);
        userDao.updateUser(new User("jack","女"));
    }
}

 

说明:以上非运行结果图片来自百度图片

 

【bak1】

【bak2】

原文会持续更新,原文地址:https://www.cnblogs.com/uncleyong/p/17023866.html

 

posted @ 2023-01-04 07:02  全栈测试笔记  阅读(250)  评论(0编辑  收藏  举报
浏览器标题切换
浏览器标题切换end