zoukankan      html  css  js  c++  java
  • SpringAOP03 项目脚手架、自定义注解、织入切面、引介增强

    1 项目脚手架

      利用 Maven 进行创建

      1.1 利用IDEA创建一个Maven原型项目

        技巧01:原型Maven项目是没有webapp文件夹和resources项目文件夹的,需要自己手动创建;创建完后需要进行模块配置 file -> project structure -> modules

        

        

      1.2 配置 pom.xml 文件

        需要引入一些 spring 和 aop 相关的依赖

    <?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>cn.xiangxu.com</groupId>
      <artifactId>aop_base_demo</artifactId>
      <version>1.0-SNAPSHOT</version>
    
      <name>aop_base_demo</name>
      <!-- FIXME change it to the project's website -->
      <url>http://www.example.com</url>
    
      <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.7</maven.compiler.source>
        <maven.compiler.target>1.7</maven.compiler.target>
        <file.encoding>UTF-8</file.encoding>
        <spring.version>4.2.2.RELEASE</spring.version>
        <testng.version>6.8.7</testng.version>
        <asm.version>4.0</asm.version>
        <cglib.version>3.0</cglib.version>
        <aspectj.version>1.8.1</aspectj.version>
        <aopalliance.version>1.0</aopalliance.version>
        <commons-codec.version>1.9</commons-codec.version>
        <slf4j.version>1.7.5</slf4j.version>
      </properties>
    
      <dependencies>
        <!-- spring 依赖-->
        <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>
    
    
        <!-- asm/cglib依赖(spring依赖) -->
        <dependency>
          <groupId>org.ow2.asm</groupId>
          <artifactId>asm</artifactId>
          <version>${asm.version}</version>
        </dependency>
        <dependency>
          <groupId>org.ow2.asm</groupId>
          <artifactId>asm-util</artifactId>
          <version>${asm.version}</version>
        </dependency>
        <dependency>
          <groupId>cglib</groupId>
          <artifactId>cglib</artifactId>
          <version>${cglib.version}</version>
          <exclusions>
            <exclusion>
              <artifactId>asm</artifactId>
              <groupId>org.ow2.asm</groupId>
            </exclusion>
          </exclusions>
        </dependency>
        <dependency>
          <groupId>org.aspectj</groupId>
          <artifactId>aspectjrt</artifactId>
          <version>${aspectj.version}</version>
        </dependency>
        <dependency>
          <groupId>org.aspectj</groupId>
          <artifactId>aspectjweaver</artifactId>
          <version>${aspectj.version}</version>
        </dependency>
        <dependency>
          <groupId>aopalliance</groupId>
          <artifactId>aopalliance</artifactId>
          <version>${aopalliance.version}</version>
        </dependency>
        <dependency>
          <groupId>commons-codec</groupId>
          <artifactId>commons-codec</artifactId>
          <version>${commons-codec.version}</version>
        </dependency>
    
    
    
        <dependency>
          <groupId>org.testng</groupId>
          <artifactId>testng</artifactId>
          <version>${testng.version}</version>
          <scope>test</scope>
        </dependency>
        <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-test</artifactId>
          <version>${spring.version}</version>
          <scope>test</scope>
        </dependency>
      </dependencies>
    
      <build>
        <plugins>
          <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-surefire-plugin</artifactId>
            <version>2.7.2</version>
            <configuration>
              <forkMode>once</forkMode>
              <threadCount>10</threadCount>
              <argLine>-Dfile.encoding=UTF-8</argLine>
            </configuration>
          </plugin>
        </plugins>
      </build>
    
    </project>
    pom.xml

      1.3 刷新maven下载相关依赖

        技巧01:最好不要使用自带的maven仓库,使用自己修改过仓库地址的maven

        

        

    2 自定义注解

      2.1 利用 @interface 创建

        技巧01:@Retention 用来指定注解有效范围,@Target 用来指定注解的使用范围

    package cn.xiangxu.com.annotations;
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD)
    public @interface NeedTest {
        boolean value() default true;
    }
    /*
     * Copyright (c) 2003, 2004, Oracle and/or its affiliates. All rights reserved.
     * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
     *
     *
     *
     */
    
    package java.lang.annotation;
    
    /**
     * Annotation retention policy.  The constants of this enumerated type
     * describe the various policies for retaining annotations.  They are used
     * in conjunction with the {@link Retention} meta-annotation type to specify
     * how long annotations are to be retained.
     *
     * @author  Joshua Bloch
     * @since 1.5
     */
    public enum RetentionPolicy {
        /**
         * Annotations are to be discarded by the compiler.
         */
        SOURCE,
    
        /**
         * Annotations are to be recorded in the class file by the compiler
         * but need not be retained by the VM at run time.  This is the default
         * behavior.
         */
        CLASS,
    
        /**
         * Annotations are to be recorded in the class file by the compiler and
         * retained by the VM at run time, so they may be read reflectively.
         *
         * @see java.lang.reflect.AnnotatedElement
         */
        RUNTIME
    }
    RetentionPolicy.java
    /*
     * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
     * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
     *
     *
     *
     *
     *
     *
     *
     *
     *
     *
     *
     *
     *
     *
     *
     *
     *
     *
     *
     *
     */
    
    package java.lang.annotation;
    
    /**
     * The constants of this enumerated type provide a simple classification of the
     * syntactic locations where annotations may appear in a Java program. These
     * constants are used in {@link Target java.lang.annotation.Target}
     * meta-annotations to specify where it is legal to write annotations of a
     * given type.
     *
     * <p>The syntactic locations where annotations may appear are split into
     * <em>declaration contexts</em> , where annotations apply to declarations, and
     * <em>type contexts</em> , where annotations apply to types used in
     * declarations and expressions.
     *
     * <p>The constants {@link #ANNOTATION_TYPE} , {@link #CONSTRUCTOR} , {@link
     * #FIELD} , {@link #LOCAL_VARIABLE} , {@link #METHOD} , {@link #PACKAGE} ,
     * {@link #PARAMETER} , {@link #TYPE} , and {@link #TYPE_PARAMETER} correspond
     * to the declaration contexts in JLS 9.6.4.1.
     *
     * <p>For example, an annotation whose type is meta-annotated with
     * {@code @Target(ElementType.FIELD)} may only be written as a modifier for a
     * field declaration.
     *
     * <p>The constant {@link #TYPE_USE} corresponds to the 15 type contexts in JLS
     * 4.11, as well as to two declaration contexts: type declarations (including
     * annotation type declarations) and type parameter declarations.
     *
     * <p>For example, an annotation whose type is meta-annotated with
     * {@code @Target(ElementType.TYPE_USE)} may be written on the type of a field
     * (or within the type of the field, if it is a nested, parameterized, or array
     * type), and may also appear as a modifier for, say, a class declaration.
     *
     * <p>The {@code TYPE_USE} constant includes type declarations and type
     * parameter declarations as a convenience for designers of type checkers which
     * give semantics to annotation types. For example, if the annotation type
     * {@code NonNull} is meta-annotated with
     * {@code @Target(ElementType.TYPE_USE)}, then {@code @NonNull}
     * {@code class C {...}} could be treated by a type checker as indicating that
     * all variables of class {@code C} are non-null, while still allowing
     * variables of other classes to be non-null or not non-null based on whether
     * {@code @NonNull} appears at the variable's declaration.
     *
     * @author  Joshua Bloch
     * @since 1.5
     * @jls 9.6.4.1 @Target
     * @jls 4.1 The Kinds of Types and Values
     */
    public enum ElementType {
        /** Class, interface (including annotation type), or enum declaration */
        TYPE,
    
        /** Field declaration (includes enum constants) */
        FIELD,
    
        /** Method declaration */
        METHOD,
    
        /** Formal parameter declaration */
        PARAMETER,
    
        /** Constructor declaration */
        CONSTRUCTOR,
    
        /** Local variable declaration */
        LOCAL_VARIABLE,
    
        /** Annotation type declaration */
        ANNOTATION_TYPE,
    
        /** Package declaration */
        PACKAGE,
    
        /**
         * Type parameter declaration
         *
         * @since 1.8
         */
        TYPE_PARAMETER,
    
        /**
         * Use of a type
         *
         * @since 1.8
         */
        TYPE_USE
    }
    ElementType.java

      2.2 使用注解

        2.2.1 随便创建一个服务类

          技巧01:在该服务类中的方法中使用自定义的注解即可

    package cn.xiangxu.com.service;
    
    import cn.xiangxu.com.annotations.NeedTest;
    import org.springframework.stereotype.Service;
    
    /**
     * @author 王杨帅
     * @create 2018-05-04 10:28
     * @desc 自定义注解测试类
     **/
    @Service
    public class AnnotationTestService {
        @NeedTest
        public void deleteUser(Integer userId) {
            System.out.println("根据用户ID删除用户所有信息:" + userId);
        }
    
        @NeedTest(value = false)
        public void deleteUserAddress(String address) {
            System.out.println("删除用户地址信息:" + address);
        }
    }

        2.2.2 创建一个测试类

          该测试类主要功能是获取注解对象信息

          技巧01:getClass() 获取运行时的class信息,getDeclaredMethods() 获取已经声明的方法,getAnnotation 用来获取注解对象信息

    package cn.xiangxu.com.service;
    
    import cn.xiangxu.com.annotations.NeedTest;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.testng.annotations.Guice;
    import org.testng.annotations.Test;
    
    import javax.annotation.Resource;
    import java.lang.reflect.Method;
    
    import static org.testng.Assert.*;
    
    public class AnnotationTestServiceTest {
    
        @Test
        public void tool() {
            // 01 获取到AnnotationTestService对象
            AnnotationTestService annotationTestService = new AnnotationTestService();
    
            // 02 获取到AnnotationTestService中所有的Method数组
            Method [] methods = annotationTestService.getClass().getDeclaredMethods();
            System.out.println(methods.length);
    
            // 03 获取到方法上的注解信息
            for (Method method : methods) {
                NeedTest needTest = method.getAnnotation(NeedTest.class);
                if (needTest != null) {
                    if (needTest.value()) {
                        System.out.println(method.getName() + "()需要进行单元测试");
                    } else{
                        System.out.println(method.getName() + "()不需要进行单元测试");
                    }
                }
            }
    
        }
    
        @Test
        public void testDeleteUser() throws Exception {
        }
    
        @Test
        public void testDeleteUserAddress() throws Exception {
        }
    
    }
    AnnotationTestServiceTest.java
        /**
         * Returns the runtime class of this {@code Object}. The returned
         * {@code Class} object is the object that is locked by {@code
         * static synchronized} methods of the represented class.
         *
         * <p><b>The actual result type is {@code Class<? extends |X|>}
         * where {@code |X|} is the erasure of the static type of the
         * expression on which {@code getClass} is called.</b> For
         * example, no cast is required in this code fragment:</p>
         *
         * <p>
         * {@code Number n = 0;                             }<br>
         * {@code Class<? extends Number> c = n.getClass(); }
         * </p>
         *
         * @return The {@code Class} object that represents the runtime
         *         class of this object.
         * @jls 15.8.2 Class Literals
         */
        public final native Class<?> getClass();
    getClass()
        /**
         *
         * Returns an array containing {@code Method} objects reflecting all the
         * declared methods of the class or interface represented by this {@code
         * Class} object, including public, protected, default (package)
         * access, and private methods, but excluding inherited methods.
         *
         * <p> If this {@code Class} object represents a type that has multiple
         * declared methods with the same name and parameter types, but different
         * return types, then the returned array has a {@code Method} object for
         * each such method.
         *
         * <p> If this {@code Class} object represents a type that has a class
         * initialization method {@code <clinit>}, then the returned array does
         * <em>not</em> have a corresponding {@code Method} object.
         *
         * <p> If this {@code Class} object represents a class or interface with no
         * declared methods, then the returned array has length 0.
         *
         * <p> If this {@code Class} object represents an array type, a primitive
         * type, or void, then the returned array has length 0.
         *
         * <p> The elements in the returned array are not sorted and are not in any
         * particular order.
         *
         * @return  the array of {@code Method} objects representing all the
         *          declared methods of this class
         * @throws  SecurityException
         *          If a security manager, <i>s</i>, is present and any of the
         *          following conditions is met:
         *
         *          <ul>
         *
         *          <li> the caller's class loader is not the same as the
         *          class loader of this class and invocation of
         *          {@link SecurityManager#checkPermission
         *          s.checkPermission} method with
         *          {@code RuntimePermission("accessDeclaredMembers")}
         *          denies access to the declared methods within this class
         *
         *          <li> the caller's class loader is not the same as or an
         *          ancestor of the class loader for the current class and
         *          invocation of {@link SecurityManager#checkPackageAccess
         *          s.checkPackageAccess()} denies access to the package
         *          of this class
         *
         *          </ul>
         *
         * @jls 8.2 Class Members
         * @jls 8.4 Method Declarations
         * @since JDK1.1
         */
        @CallerSensitive
        public Method[] getDeclaredMethods() throws SecurityException {
            checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
            return copyMethods(privateGetDeclaredMethods(false));
        }
    getDeclaredMethods()
        /**
         * {@inheritDoc}
         * @throws NullPointerException  {@inheritDoc}
         * @since 1.5
         */
        public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
            return super.getAnnotation(annotationClass);
        }
    getAnnotation()

    3 切面样例

      3.1 创建所需的服务接口和服务类

        说明:Waiter 是一个接口利用有两个方法,其中greetTo方法有@NeedTest注解;NaiveWaiter是Waiter接口的实现类

    package cn.xiangxu.com.service;
    
    import cn.xiangxu.com.annotations.NeedTest;
    
    public interface Waiter {
        @NeedTest
        public void greetTo(String clientName);    
        public void serveTo(String clientName);
    }
    Waiter.java
    package cn.xiangxu.com.service;
    
    
    public class NaiveWaiter implements Waiter {
        public void greetTo(String clientName) {
            System.out.println("NaiveWaiter:greet to "+clientName+"...");
        }    
        public void serveTo(String clientName){
            System.out.println("NaiveWaiter:serving "+clientName+"...");
        }
        public void smile(String clientName,int times){
            System.out.println("NaiveWaiter:smile to  "+clientName+ times+"times...");
        }    
    }
    NaiveWaiter.java

      3.2 创建切面类

        技巧01:一个完整的切面类必须包含: 切点、增强、横切逻辑

    package cn.xiangxu.com.aops.example;
    
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    
    /**
     * @author 王杨帅
     * @create 2018-05-04 11:09
     * @desc
     **/
    @Aspect
    public class PreGreetingAspect {
        @Before("execution(* greetTo(..))")
        public void beforeGreeting() {
            System.out.println("How are you");
        }
    }
    PreGreetingAspect.java

      3.3 织入切面

        3.3.1 通过编程方式

          利用 AspectJProxyFactory 织如基于 @AspectJ注解的类

    package cn.xiangxu.com.aops.example;
    
    import cn.xiangxu.com.service.NaiveWaiter;
    import cn.xiangxu.com.service.Waiter;
    import org.springframework.aop.aspectj.annotation.AspectJProxyFactory;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    import org.testng.annotations.Test;
    
    import javax.annotation.Resource;
    
    import static org.testng.Assert.*;
    
    public class PreGreetingAspectTest {
        @Test
        public void testBeforeGreeting() throws Exception {
            // 01 生成目标对象实例
            Waiter target = new NaiveWaiter();
            // 02 实例化工厂
            AspectJProxyFactory factory = new AspectJProxyFactory();
            // 03 设置目标对象
            factory.setTarget(target);
            // 04 添加切面类
            factory.addAspect(PreGreetingAspect.class);
    
            // 05 生成植入切面的代理对象
            Waiter proxy = factory.getProxy();
            // 06 利用代理对象调用方法
            proxy.greetTo("Warrior");
            System.out.println("parting line");
            proxy.serveTo("Warrior");
    
        }
    
    }
    PreGreetingAspectTest.java

        3.3.2 利用Spring配置的方式

          》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 http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
               http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">
        <aop:aspectj-autoproxy/>
        <!--bean class="org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator"/-->
        <bean id="waiter" class="cn.xiangxu.com.service.NaiveWaiter" />
        <bean class="cn.xiangxu.com.aops.example.PreGreetingAspect" />
    </beans>
    View Code

          》测试类

    package cn.xiangxu.com.aops.example;
    
    import cn.xiangxu.com.service.NaiveWaiter;
    import cn.xiangxu.com.service.Waiter;
    import javafx.application.Application;
    import org.springframework.aop.aspectj.annotation.AspectJProxyFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    import org.testng.annotations.Test;
    
    import javax.annotation.Resource;
    
    import static org.testng.Assert.*;
    
    public class PreGreetingAspectTest {
    
        /**
         * 利用 编码的方式织入切面
         * @throws Exception
         */
        @Test
        public void testBeforeGreeting() throws Exception {
            // 01 生成目标对象实例
            Waiter target = new NaiveWaiter();
            // 02 实例化工厂
            AspectJProxyFactory factory = new AspectJProxyFactory();
            // 03 设置目标对象
            factory.setTarget(target);
            // 04 添加切面类
            factory.addAspect(PreGreetingAspect.class);
    
            // 05 生成植入切面的代理对象
            Waiter proxy = factory.getProxy();
            // 06 利用代理对象调用方法
            proxy.greetTo("Warrior");
            System.out.println("parting line");
            proxy.serveTo("Warrior");
    
        }
    
        /**
         * 利用Spring配置的方式织入切面
         */
        @Test
        public void testBeforeGreeting02() {
            // 01 配置文件路径
            String configPath = "aops/example/beans.xml";
    
            // 02 获取应用上下文
            ApplicationContext ac = new ClassPathXmlApplicationContext(configPath);
    
            // 03 利用应用上下文获取对象
            Waiter waiter = (Waiter)ac.getBean("waiter");
    
            waiter.greetTo("fury");
            System.out.println("===parting line===");
            waiter.serveTo("warrior");
    
        }
    
    }
    PreGreetingAspectTest.java

    4 引介增强

      引介增强最主要的目的是为类A添加一个需要实现的接口B,并指定实现类C(解释:C是B的实现类);简而言之,类A可以通过引介增强来实现接口B,实现的方式是利用了类C

      4.1 创建接口B

    package cn.xiangxu.com.service;
    
    public interface Seller {
      int sell(String goods, String clientName);
    }
    Seller.java

      4.2 创建接口B的实现类C

    package cn.xiangxu.com.service;
    
    public class SmartSeller implements Seller {
    
        public int sell(String goods,String clientName) {
            System.out.println("SmartSeller: sell "+goods +" to "+clientName+"...");
            return 100;
        }
        
        public void checkBill(int billId){
            if(billId == 1) throw new IllegalArgumentException("iae Exception");
            else throw new RuntimeException("re Exception");
        }
    }
    SmartSeller.java

      4.3 创建切面类

        该切面类主要实现引介增强

    package cn.xiangxu.com.aops.basic;
    
    import cn.xiangxu.com.service.Seller;
    import cn.xiangxu.com.service.SmartSeller;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.DeclareParents;
    
    @Aspect
    public class EnableSellerAspect {
        @DeclareParents(value="cn.xiangxu.com.service.NaiveWaiter",
                defaultImpl=SmartSeller.class)
        public  Seller seller;
    }
    EnableSellerAspect.java

      4.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 http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
               http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">
        <aop:aspectj-autoproxy/>
        <bean id="waiter" class="cn.xiangxu.com.service.NaiveWaiter"/>
        <bean class="cn.xiangxu.com.aops.basic.EnableSellerAspect"/>
    </beans>
    View Code

      4.5 编写测试类

    package cn.xiangxu.com.aops.basic;
    
    import cn.xiangxu.com.service.Seller;
    import cn.xiangxu.com.service.Waiter;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    import org.testng.annotations.Test;
    
    public class EnableSellerAspectTest {
    
        @Test
        public void test01() {
            // 01 配置文件路径
            String configPath = "aops/basic/beans.xml";
            // 02 实例化应用上下文
            ApplicationContext ac = new ClassPathXmlApplicationContext(configPath);
            // 03 获取bean对象
            Waiter waiter = (Waiter)ac.getBean("waiter");
    
            // 04 调用实例方法
            waiter.greetTo("fury");
    
            // 05 类型强转
            Seller seller = (Seller)waiter;
            seller.sell("hotpot", "王杨帅");
        }
    
    }
    EnableSellerAspectTest.java

       

    5 本博文源代码

      点击前往

        

      

        

  • 相关阅读:
    MRF能量优化
    Django:model中的ForeignKey理解
    Django:常见的orm操作
    Django:在模板中获取当前url信息
    Django:haystack全文检索详细教程
    Django:全文检索功能可参考博客
    看电影学英语
    Markdown中怎么上传图片
    Mosquitto的安装、配置、测试
    Django:评论文章后局部刷新评论区
  • 原文地址:https://www.cnblogs.com/NeverCtrl-C/p/8990665.html
Copyright © 2011-2022 走看看