美文网首页
2021-06-14_SpringAop手动定义切面及切点学习笔

2021-06-14_SpringAop手动定义切面及切点学习笔

作者: kikop | 来源:发表于2021-06-14 21:15 被阅读0次

20210614_SpringAop手动定义切面及切点学习笔记

1概述

本节主要学习下自定义切面及切点,完成spring aop代理的功能。主要内容如下:

  1. 自定义切点Pointcut
  2. 自定义增强器 MethodInterceptor
  3. 自定义切面advisor(依赖pointcut和advice)
  4. 定义Configuration,实现SpringIOC容器的管理。

1.1AbstractAutoProxyCreator类图

[图片上传失败...(image-2c48ff-1623676489083)]

1.2maven项目依赖

<?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">
    <parent>
        <artifactId>technicaltools</artifactId>
        <groupId>com.kikop</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>myaopcustomadvisor2demo</artifactId>

    <name>myaopcustomadvisor2demo</name>
    <!-- FIXME change it to the project's website -->
    <url>http://www.example.com</url>
    <description>myaopcustomadvisor2demo</description>

    <properties>

        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>

        <!--springframework-->
        <springframework.version>5.0.0.RELEASE</springframework.version>

        <!-- mybatis -->
        <mybatis.version>3.2.3</mybatis.version>
        <mybatis-spring.version>1.2.5</mybatis-spring.version>

        <!-- mysql jdbc -->
        <mysql-connector-java.version>5.1.38</mysql-connector-java.version>

        <!--jedis-->
        <jedis.version>3.3.0</jedis.version>


        <!--redisson对应 spring-boot-->
        <!--<redisson.version>3.11.5</redisson.version>-->
        <redisson.version>3.13.1</redisson.version>

        <!--<redisson.version>3.10.6</redisson.version>-->

        <!--fastjson-->
        <fastjson.version>1.2.29</fastjson.version>

        <!-- slf4j 日志文件管理包版本 -->
        <slf4j.version>1.7.12</slf4j.version>
        <!-- log4j2日志文件管理包版本 -->
        <log4j.version>2.1</log4j.version>

        <!--lombok-->
        <!--<lombok.version>1.16.10</lombok.version>-->
        <lombok.version>1.18.10</lombok.version>

    </properties>

    <dependencies>

        <!--1.junit高版本-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13</version>
            <scope>test</scope>
        </dependency>

        <!--2.引入spring依赖-->
        <!--spring对web的支持,依赖 spring-webmvc,此时jar包会自动下载-->
        <!--spring-aop、spring-beans、spring-context-->
        <!--spring-core、spring-expressionweb、spring-web-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>${springframework.version}</version>
        </dependency>

        <!--2.1.spring-test-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>${springframework.version}</version>
        </dependency>

        <!--2.2.spring-jdbc-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>${springframework.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>${springframework.version}</version>
        </dependency>

        <!--10.log4j2 相关配置开始-->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-api</artifactId>
            <version>${log4j.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>${log4j.version}</version>
        </dependency>

        <!--兼容log4j1.x版本-->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-1.2-api</artifactId>
            <version>${log4j.version}</version>
        </dependency>

        <!-- 桥接:告诉commons logging 使用Log4j2 -->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-jcl</artifactId>
            <version>${log4j.version}</version>
        </dependency>

        <!--清理web日志资源-->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-web</artifactId>
            <version>${log4j.version}</version>
            <scope>runtime</scope>
        </dependency>

        <!--log4j2相关配置结束-->

        <!--11.slf4j-->
        <!--begin_slf4j api-->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>${slf4j.version}</version>
        </dependency>

        <!-- 桥接:告诉Slf4j使用 Log4j2 -->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-slf4j-impl</artifactId>
            <version>${log4j.version}</version>
        </dependency>
        <!--end_slf4j api-->


        <!--12.公共模块中 lombok插件,其他模块用到时填-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>${lombok.version}</version>
            <optional>true</optional>
        </dependency>

    </dependencies>

    <build>

        <!--resources-->
        <resources>
            <resource>
                <directory>src/main/resources</directory>
                <filtering>false</filtering>
            </resource>
        </resources>


        <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
            <plugins>
                <!-- clean lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#clean_Lifecycle -->
                <plugin>
                    <artifactId>maven-clean-plugin</artifactId>
                    <version>3.1.0</version>
                </plugin>
                <!-- default lifecycle, jar packaging: see https://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging -->
                <plugin>
                    <artifactId>maven-resources-plugin</artifactId>
                    <version>3.0.2</version>
                </plugin>
                <plugin>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.8.0</version>
                </plugin>
                <plugin>
                    <artifactId>maven-surefire-plugin</artifactId>
                    <version>2.22.1</version>
                </plugin>
                <plugin>
                    <artifactId>maven-jar-plugin</artifactId>
                    <version>3.0.2</version>
                </plugin>
                <plugin>
                    <artifactId>maven-install-plugin</artifactId>
                    <version>2.5.2</version>
                </plugin>
                <plugin>
                    <artifactId>maven-deploy-plugin</artifactId>
                    <version>2.8.2</version>
                </plugin>
                <!-- site lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#site_Lifecycle -->
                <plugin>
                    <artifactId>maven-site-plugin</artifactId>
                    <version>3.7.1</version>
                </plugin>
                <plugin>
                    <artifactId>maven-project-info-reports-plugin</artifactId>
                    <version>3.0.0</version>
                </plugin>
            </plugins>
        </pluginManagement>
    </build>
</project>

2代码实战

2.1MyCachePointCutWithFind

package com.kikop.aop2;

import org.springframework.aop.ClassFilter;
import org.springframework.aop.MethodMatcher;
import org.springframework.aop.Pointcut;

import java.lang.reflect.Method;

/**
 * @author kikop
 * @version 1.0
 * @project Name: toparchdesigndemo2
 * @file Name: MyCachePointCutWithQuery
 * @desc 自定义一个切点(基于方法上), 不需注入 @Bean.代理所有类的 findVersion方法。
 * @date 2021/6/05
 * @time 18:00
 * @by IDE: IntelliJ IDEA
 */
public class MyCachePointCutWithFind implements Pointcut, MethodMatcher {


    //    begin_MethodMatcher

    /**
     * 定义方法匹配规则
     *
     * @param method
     * @param targetClass
     * @return
     */
    @Override
    public boolean matches(Method method, Class<?> targetClass) {

        String methodName = method.getName();
        if ("findVersion".equals(methodName)) {
            return true;
        }
        return false;

    }

    @Override
    public boolean isRuntime() {
        return false;
    }

    @Override
    public boolean matches(Method method, Class<?> targetClass, Object... args) {
        return false;
    }

//    end_MethodMatcher


    //    begin_Pointcut

    /**
     * 类匹配规则
     *
     * @return
     */
    @Override
    public ClassFilter getClassFilter() {
        return ClassFilter.TRUE;
    }

    /**
     * 方法匹配规则
     *
     * @return
     */
    @Override
    public MethodMatcher getMethodMatcher() {
        return this;
    }
//    end_Pointcut
}

2.2MyCacheAdvice2

package com.kikop.aop2;


import lombok.extern.slf4j.Slf4j;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.util.StringUtils;

/**
 * @author kikop
 * @version 1.0
 * @project Name: toparchdesigndemo2
 * @file Name: MyCacheAdvice
 * @desc 自定义一个增强
 * @date 2021/6/05
 * @time 18:00
 * @by IDE: IntelliJ IDEA
 */
@Slf4j
public class MyCacheAdvice2 implements MethodInterceptor, ApplicationContextAware {

    // applicationContext
    private ApplicationContext applicationContext;


    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {

        // 缓存无数据
        Object proceedResult = invocation.proceed();
        if (!StringUtils.isEmpty(proceedResult)) {
            if (log.isWarnEnabled()) {
                log.warn("query data from db!");
            }
        }
        // 类型转换
        return proceedResult;
    }



    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

}

2.3MyCacheAdvisor2

package com.kikop.aop2;


import org.springframework.aop.Pointcut;
import org.springframework.aop.support.AbstractBeanFactoryPointcutAdvisor;

/**
 * @author kikop
 * @version 1.0
 * @project Name: toparchdesigndemo2
 * @file Name: MyCacheAdvisor 依赖 pointcut切点、advice增强
 * @desc 自定义一个切面
 * @date 2021/6/05
 * @time 18:00
 * @by IDE: IntelliJ IDEA
 */
public class MyCacheAdvisor2 extends AbstractBeanFactoryPointcutAdvisor {


    // Advisor分两大类:
    // IntroductionAdvisor(引介通知器)和PointcutAdvisor(切点通知器)。
    // 两类Advisor都是为了增强targetClass,但是作用不一样。
    // 1.IntroductionAdvisor主要为了给targetClass追加接口(或者说追加更多的方法),这种增强属于类级别的增强;

    // 2.PointcutAdvisor主要为了拦截方法,这种增强属于方法级别的增强。
    // DefaultPointcutAdvisor 通用的,最强大的Advisor,
    // 它是Spring提供的通用的,也被认为是最强大的Advisor。它可以把任意的两个Advice和Pointcut放在一起

    private Pointcut pointcut = new MyCachePointCutWithFind();


    /**
     * 定义一个切点
     *
     * @return
     */
    @Override
    public Pointcut getPointcut() {
        //  <aop:pointcut id="mypointcut" expression="execution(* com.kikop.myspringaspects..*.*(..))">
        //  </aop:pointcut>
        return pointcut;
    }
}


// https://www.jianshu.com/p/d3e0aa9111e8
// https://blog.csdn.net/elim168/article/details/78445908

// 配置使用自定义的Advisor

// AbstractAdvisorAutoProxyCreator 子类有4个 DefaultAdvisorAutoProxyCreator InfrastructureAdvisorAutoProxyCreator
// <aop:config>就是自动定义了 AspectJAwareAdvisorAutoProxyCreator 类型的bean。
// <aop:aspectj-autoproxy/>就是自动定义了 AnnotationAwareAspectjAutoProxyCreator 类型的bean,

// 其实为了能够在创建目标bean的时候能够自动创建基于我们自定义的Advisor实现类的代理对象,
// 我们的bean容器中只要有AbstractAutoProxyCreator类型的bean定义即可,当然了你实现自己的BeanPostProcessor,
// 在其postProcessAfterInitialization方法中创建自己的代理对象也是可以的。

//    @Bean
//    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
//        return new DefaultAdvisorAutoProxyCreator();
//    }

2.4MyCacheAppConfig

package com.kikop.config;


import com.kikop.aop2.MyCacheAdvice2;
import com.kikop.aop2.MyCacheAdvisor2;
import com.kikop.usermodule.UserService;
import com.kikop.usermodule.impl.UserServiceImpl;
import org.springframework.aop.framework.autoproxy.InfrastructureAdvisorAutoProxyCreator;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Role;

/**
 * @author kikop
 * @version 1.0
 * @project Name: mycustomadvisordemo
 * @file Name: AppConfig
 * @desc 基于Redis实现的缓存接口
 * @date 2021/6/05
 * @time 18:00
 * @by IDE: IntelliJ IDEA
 */
@Configuration
// 指定扫描的组件包,@Bean,作为框架使用时,此处不需要
//@ComponentScan("com.kikop")
public class MyCacheAppConfig {

    // 必须加:ROLE_INFRASTRUCTURE,否则不生成代理
    @Bean
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public MyCacheAdvice2 myCacheAdvice2() {
        MyCacheAdvice2 myCacheAdvice2 = new MyCacheAdvice2();
        return myCacheAdvice2;
    }


    // 必须加:ROLE_INFRASTRUCTURE,否则不生成代理
    @Bean
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public MyCacheAdvisor2 myCacheAdvisor2() {
        MyCacheAdvisor2 myCacheAdvisor2 = new MyCacheAdvisor2();
        myCacheAdvisor2.setAdvice(myCacheAdvice2());
        return myCacheAdvisor2;
    }


    @Bean
    public UserService userService() {
        return new UserServiceImpl();
    }

    /**
     * 实现自己的 BeanPostProcessor
     *
     * @return
     */
    @Bean
    public InfrastructureAdvisorAutoProxyCreator infrastructureAdvisorAutoProxyCreator() {
        return new InfrastructureAdvisorAutoProxyCreator();
    }

}

2.5模拟业务模块

package com.kikop.usermodule;


/**
 * @author kikop
 * @version 1.0
 * @project Name: mycustomadvisordemo
 * @file Name: UserService
 * @desc
 * @date 2021/6/05
 * @time 18:00
 * @by IDE: IntelliJ IDEA
 */
public interface UserService {

    String queryVersion(String userId);

    String findVersion(String userId);
}
package com.kikop.usermodule.impl;

import com.kikop.usermodule.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;


/**
 * @author kikop
 * @version 1.0
 * @project Name: mycustomadvisordemo
 * @file Name: UserServiceImpl
 * @desc 一般该Service类在业务系统模块
 * @date 2021/6/05
 * @time 18:00
 * @by IDE: IntelliJ IDEA
 */
@Slf4j
// 作为框架使用时,此处不需要
//@Service
public class UserServiceImpl implements UserService {


    /**
     * 查询用户的版本
     *
     * @param userId
     * @return
     */
    @Override
    public String queryVersion(String userId) {
        log.info("begin queryVersion");
        log.info("end queryVersion");
        return "query";
    }

    @Override
    public String findVersion(String userId) {
        return "find";
    }
}

2.6测试

2.6.1简单测试

package com.kikop;

import com.kikop.config.MyCacheAppConfig;
import com.kikop.usermodule.UserService;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

/**
 * @author kikop
 * @version 1.0
 * @project Name: mycustomadvisordemo
 * @file Name: AdvisorAppTest
 * @desc
 * @date 2021/6/05
 * @time 18:00
 * @by IDE: IntelliJ IDEA
 */
public class AdvisorAppTest {

    public static void main(String[] args) {

        AnnotationConfigApplicationContext annotationConfigApplicationContext =
                new AnnotationConfigApplicationContext(MyCacheAppConfig.class);

        UserService userService1 = annotationConfigApplicationContext.getBean("userService",UserService.class);
        String queryResult = userService1.queryVersion("1000");
        System.out.println(queryResult);

        String findResult = userService1.findVersion("2000");
        System.out.println(findResult);
    }
}

2.6.2单元测试

2.6.2.1BaseJunit4Test

package com.kikop;


import com.kikop.config.MyCacheAppConfig;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

/**
 * @author kikop
 * @version 1.0
 * @project Name: mycustomadvisordemo
 * @file Name: BaseJunit4Test
 * @desc
 * @date 2021/6/05
 * @time 18:00
 * @by IDE: IntelliJ IDEA
 */
@RunWith(SpringJUnit4ClassRunner.class) //使用junit4进行测试
//@ContextConfiguration(locations={"classpath:applicationContext.xml"}) // 加载xml配置文件
@ContextConfiguration(classes = {MyCacheAppConfig.class}) // 加载 JavaConfig
public class BaseJunit4Test {

}

2.6.2.2CacheTest

package com.kikop;


import com.kikop.usermodule.UserService;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;

/**
 * @author kikop
 * @version 1.0
 * @project Name: mycustomadvisordemo
 * @file Name: CacheTest
 * @desc
 * @date 2021/6/05
 * @time 18:00
 * @by IDE: IntelliJ IDEA
 */
public class CacheTest extends BaseJunit4Test {

    @Autowired
    private UserService userService;

    @Test
    public void userServiceTest() {
        System.out.println("begin_测试Spring整合Junit4进行单元测试");

        String version = userService.findVersion("0001");
        System.out.println(version);

        System.out.println("endn_测试Spring整合Junit4进行单元测试");

    }
}

3总结

参考

1Spring Aop(十四)——Aop自动创建代理对象的原理

https://blog.csdn.net/elim168/article/details/78445835

相关文章

  • 2021-06-14_SpringAop手动定义切面及切点学习笔

    20210614_SpringAop手动定义切面及切点学习笔记 1概述 本节主要学习下自定义切面及切点,完成spr...

  • 浅谈java切面

    一、切面定义 切面 = 切点+通知 切点(pointcut): 定义执行切面的入口点 ,这里切入点指示符 通知:...

  • AOP要点概念理解

    通知(Advice):通知定义了切面是什么以及何时调用。 切点(Pointcut):切点定义了切面在何处。 切面(...

  • 实际项目中 Spring AOP 的应用场景案例

    一. 定义切点-pointcut 二. 定义切面-aspect 1. 切面定义 2. 自定义逻辑 案例1 案例2 ...

  • spring aop配置总结

    引言 Spring aop支持@AspectJ注解的方式来配置切面,相比于手动配置xml且手动实现增强、切点的方式...

  • Spring AOP源码01 - 切点的定义

    Pointcut 切点,定义切面的位置。Spring 中的切点可以在class 或者method 上。所有的Poi...

  • AOP实现登陆检测

    为何想到AOP AOP 项目完整代码 如何接入 AspectJ 具体实现 自定义切点 切面 使用如何 获取切点中的...

  • AOP

    一个切面包含多个切点,为每个切点设置通知 1. 切面(aspect) 2. 切点(pointcut) 参考:Spr...

  • Spring AOP 简化笔记(一)

    切点表达式 编写配置 编写切点 编写切面(@Aspect注解)简化切面编码(@Pointcut注解)环绕通知(@A...

  • Spring切面、切点、通知

    切面(AopConfig ):切点,通知所在的类就是切面切点(Pointcut):需要增强的方法集合通知(Befo...

网友评论

      本文标题:2021-06-14_SpringAop手动定义切面及切点学习笔

      本文链接:https://www.haomeiwen.com/subject/ignoeltx.html