美文网首页
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手动定义切面及切点学习笔

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