美文网首页
114、【JavaEE】【Spring】Spring AOP(2

114、【JavaEE】【Spring】Spring AOP(2

作者: yscyber | 来源:发表于2021-10-26 04:32 被阅读0次

    1、概述

    • AOP,面向切面编程,利用 AOP 可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
    1. 在程序运行期间,在不修改源码的情况下对方法进行功能增强
    2. 逻辑清晰,开发核心业务的时候,不必关注增强业务的代码
    3. 减少重复代码,提高开发效率,便于后期维护
    • AOP 的底层是通过 Spring 提供的的动态代理技术实现的。在运行期间,Spring 通过动态代理技术动态的生成代理对象,代理对象方法执行时进行增强功能的介入,在去调用目标对象的方法,从而完成功能的增强。

    • AOP 相关术语:

    Spring-27
    • AOP 步骤:
    1. 编写核心业务代码(目标类的目标方法),明确切入点
    2. 把公用代码抽取出来,制作成通知(增强功能方法) 通知
    3. 在配置文件或使用注解中,声明切入点与通知间的关系,即切面
    • 在 Spring 中,框架会根据目标类是否实现了接口来决定采用哪种动态代理的方式:
      1、当 bean 实现接口时,会用 JDK 代理模式。
      2、当 bean 没有实现接口,用 cglib 实现。
      3、可以强制使用 cglib(在 Spring 配置文件中加入<aop:aspectj autoproxy proxyt-target-class=”true”/>)。

    2、XML 方式 AOP

    2.1、举例

    • 步骤:
    Spring-28
    • 1、引入 Maven 依赖
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-context</artifactId>
                <version>5.3.8</version>
            </dependency>
    
            <dependency>
                <groupId>org.aspectj</groupId>
                <artifactId>aspectjweaver</artifactId>
                <version>1.9.7</version>
            </dependency>
    
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.12</version>
                <scope>test</scope>
            </dependency>
    
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-test</artifactId>
                <version>5.3.8</version>
                <scope>test</scope>
            </dependency>
    

    Spring AOP 的依赖是spring-aopspring-context中已经包括了spring-aop。AspectJ 是基于 Java 的 AOP 框架,是一个完全可以独立于 Spring 的 AOP 框架。Spring AOP 与 AspectJ 结合使用,使得整个 AOP 开发更加便捷。

    • 2、创建目标类,明确切入点,即需要增强的方法。
    public class MyTarget {
    
        public void myTargetMethod() {
            System.out.println("切入点,待增强的方法");
        }
    
    }
    
    • 3、创建通知类,编写通知方法,即增强的逻辑。
    public class MyAdvice {
    
        public void myBeforeAdvice() {
            System.out.println("前置增强(通知)");
        }
    
    }
    
    • 3、目标类、通知类,交给 Spring 容器管理。
    <?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 https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
    
        <bean id="MyTarget" class="com.yscyber.spring.six.MyTarget"/>
        
        <bean id="MyAdvice" class="com.yscyber.spring.six.MyAdvice"/>
    
    </beans>
    
    • 4、在 Spring 核心配置文件中,配置织入。
    <?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 https://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
    
        <bean id="MyTarget" class="com.yscyber.spring.six.MyTarget"/>
    
        <bean id="MyAdvice" class="com.yscyber.spring.six.MyAdvice"/>
    
        <aop:config>
            <!-- <aop:aspect ref=""> 指定通知类 -->
            <aop:aspect ref="MyAdvice">
                <!-- <aop:before method="" pointcut=""> 指定通知方法、需增强的方法 -->
                <aop:before method="myBeforeAdvice" pointcut="execution(public void com.yscyber.spring.six.MyTarget.myTargetMethod())"/>
            </aop:aspect>
        </aop:config>
        
    </beans>
    
    • 5、测试。
    import org.junit.Test;
    import org.junit.runner.RunWith;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(locations = {"classpath:applicationContext.xml"})
    public class MyTest {
    
        @Autowired
        private MyTarget myTarget;
    
        @Test
        public void test1() {
            myTarget.myTargetMethod();
        }
    
    }
    
    /*
    前置增强(通知)
    切入点,待增强的方法
    */
    

    2.2、切点表达式

    • <aop:before method="myBeforeAdvice" pointcut="execution(public void com.yscyber.spring.six.MyTarget.myTargetMethod())"/>中的pointcut定义切点表达式。切点表达式定义了哪些方法需要被增强。
    Spring-29
    • 切点表达式抽取,使得切点表达式能够复用,<aop:pointcut id="", expression="">
    <?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 https://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
    
        <bean id="MyTarget" class="com.yscyber.spring.six.MyTarget"/>
    
        <bean id="MyAdvice" class="com.yscyber.spring.six.MyAdvice"/>
    
        <aop:config>
            <aop:pointcut id="myPointcut" expression="execution(public void com.yscyber.spring.six.MyTarget.myTargetMethod())"/>
    
            <!-- <aop:aspect ref=""> 指定通知类 -->
            <aop:aspect ref="MyAdvice">
                <!-- <aop:before method="" pointcut=""> 指定通知方法、需增强的方法 -->
                <aop:before method="myBeforeAdvice" pointcut-ref="myPointcut"/>
            </aop:aspect>
        </aop:config>
    
    </beans>
    

    2.3、通知类型

    Spring-30
    • 异常通知与后置通知,执行过程中只有1个生效。

    • 环绕通知比较特殊,一般单独使用。一个环绕通知可以完全实现其他类型的通知。

    public class MyTarget {
    
        public void myTargetMethod() {
            System.out.println("切入点,待增强的方法");
        }
    
    }
    
    import org.aspectj.lang.ProceedingJoinPoint;
    
    public class MyAdvice {
    
        public void myAroundAdvice(ProceedingJoinPoint proceedingJoinPoint) {
            try {
                System.out.println("前置通知");
    
                // 执行被增强的方法,即 MyTarget.myTargetMethod
                proceedingJoinPoint.proceed();
    
                System.out.println("后置通知");
            } catch (Throwable throwable) {
                System.out.println("异常通知");
                throwable.printStackTrace();
            } finally {
                System.out.println("最终通知");
            }
        }
    
    }
    
    <?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 https://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
    
        <bean id="MyTarget" class="com.yscyber.spring.six.MyTarget"/>
    
        <bean id="MyAdvice" class="com.yscyber.spring.six.MyAdvice"/>
    
        <aop:config>
            <aop:pointcut id="myPointcut" expression="execution(public void com.yscyber.spring.six.MyTarget.myTargetMethod())"/>
    
            <aop:aspect ref="MyAdvice">
                <aop:around method="myAroundAdvice" pointcut-ref="myPointcut"/>
            </aop:aspect>
        </aop:config>
    
    </beans>
    
    import org.junit.Test;
    import org.junit.runner.RunWith;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(locations = {"classpath:applicationContext.xml"})
    public class MyTest {
    
        @Autowired
        private MyTarget myTarget;
    
        @Test
        public void test1() {
            myTarget.myTargetMethod();
        }
    
    }
    
    /*
    前置通知
    切入点,待增强的方法
    后置通知
    最终通知
    */
    

    3、注解方式 AOP

    3.1、举例 — 部分注解

    Spring-31
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-context</artifactId>
                <version>5.3.8</version>
            </dependency>
    
            <dependency>
                <groupId>org.aspectj</groupId>
                <artifactId>aspectjweaver</artifactId>
                <version>1.9.7</version>
            </dependency>
    
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.12</version>
                <scope>test</scope>
            </dependency>
    
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-test</artifactId>
                <version>5.3.8</version>
                <scope>test</scope>
            </dependency>
    
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    
    import org.springframework.stereotype.Component;
    
    @Component
    @Aspect // @Aspect 通知类
    public class MyAdvice {
    
        
        @Before("execution(public void com.yscyber.spring.seven.MyTarget.myTargetMethod())")
        public void myBeforeAdvice() {
            System.out.println("前置通知");
        }
    
    }
    
    import org.springframework.stereotype.Component;
    
    @Component
    public class MyTarget {
    
        public void myTargetMethod() {
            System.out.println("切入点,待增强的方法");
        }
    
    }
    
    <?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 https://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd
           http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
    
        <context:component-scan base-package="com.yscyber.spring.seven"/>
    
        <!-- 相当于识别:@Aspect 等 AspectJ 注解 -->
        <aop:aspectj-autoproxy/>
    
    </beans>
    
    import org.junit.Test;
    import org.junit.runner.RunWith;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(locations = {"classpath:applicationContext.xml"})
    public class MyTest {
    
        @Autowired
        private MyTarget myTarget;
    
        @Test
        public void test1() {
            myTarget.myTargetMethod();
        }
    
    }
    
    /*
    前置通知
    切入点,待增强的方法
    */
    

    3.2、举例 — 完全注解

    import org.springframework.stereotype.Component;
    
    @Component
    public class MyTarget {
    
        public void myTargetMethod() {
            System.out.println("切入点,待增强的方法");
        }
    
    }
    
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    
    import org.springframework.stereotype.Component;
    
    @Component
    @Aspect // @Aspect 通知类
    public class MyAdvice {
    
    
        @Before("execution(public void com.yscyber.spring.seven.MyTarget.myTargetMethod())")
        public void myBeforeAdvice() {
            System.out.println("前置通知");
        }
    
    }
    
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.EnableAspectJAutoProxy;
    
    @Configuration
    @ComponentScan(basePackages = {"com.yscyber.spring.seven"})
    @EnableAspectJAutoProxy // 替代 XML 配置文件中的 <aop:aspectj-autoproxy/>
    public class SpringConfig {
    }
    
    import org.junit.Test;
    import org.junit.runner.RunWith;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(classes = {com.yscyber.spring.seven.SpringConfig.class})
    public class MyTest {
    
        @Autowired
        private MyTarget myTarget;
    
        @Test
        public void test1() {
            myTarget.myTargetMethod();
        }
    
    }
    

    3.3、抽取切点表达式

    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    
    import org.aspectj.lang.annotation.Pointcut;
    import org.springframework.stereotype.Component;
    
    @Component
    @Aspect // @Aspect 通知类
    public class MyAdvice {
    
        // 抽取切点表达式
        @Pointcut("execution(public void com.yscyber.spring.seven.MyTarget.myTargetMethod())")
        public void myPointcut() {}
    
    
        @Before("myPointcut()")
        public void myBeforeAdvice() {
            System.out.println("前置通知");
        }
    
    }
    

    3.4、通知类型

    Spring-32

    相关文章

      网友评论

          本文标题:114、【JavaEE】【Spring】Spring AOP(2

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