Java中的AOP思想,代理,注解

知识模块:
一.需求概述
二.AOP思想(Aspect Oriented Programming)
三.通过静态代理实现AOP
四.动态代理实现AOP
         a.注解概述 
         b.Java中常见注解 
         c.Java中的定义注解 
         d.元注解(注解上的注解) 
         e.通过反射获取类上和方法上的注解 




一.需求概述
   统计老王一天吃饭,睡觉,玩游戏的时间

import java.util.Date;

/*
定义一个老王类,实现OneDay接口,代表老王一天要干的事
分别统计老王一天吃饭,睡觉,玩游戏的时间
 */
public class LaoWang implements OneDay {
    @Override
    public void eat() {
        long startTime = new Date().getTime();

        System.out.println("用手机上的APP搜索一个吃饭的地方");
        System.out.println("用微信约一个妹纸");
        System.out.println("和妹纸一起去吃饭");
        for (int i = 0; i < 999999999; i++) {
            
        }

        long endTime = new Date().getTime();
        System.out.println("吃饭需要的时间"+(endTime-startTime));
    }

    @Override
    public void play() {
        long startTime = new Date().getTime();

        System.out.println("打开电脑");
        System.out.println("打开LOL");
        System.out.println("登录账户和密码");
        System.out.println("随机匹配");
        for (int i = 0; i < 999999999; i++) {

        }

        long endTime = new Date().getTime();
        System.out.println("玩游戏需要的时间"+(endTime-startTime));
    }


    @Override
    public void sleep() {
        long startTime = new Date().getTime();

        System.out.println("先洗个澡");
        System.out.println("换上睡衣");
        System.out.println("上床睡觉");
        for (int i = 0; i < 999999999; i++) {

        }

        long endTime = new Date().getTime();
        System.out.println("睡觉需要的时间"+(endTime-startTime));
    }

}
/*
这个接口为了约束老王这一天有什么行为
 */
public interface OneDay {
    //吃饭
    void eat();

    //玩游戏
    void play();

    //睡觉
    void sleep();
}
import org.junit.Test;

import java.lang.annotation.Target;

public class Demo {
    @Test
    public void testEat() {
        LaoWang lw = new LaoWang();
        lw.eat();                    //用手机上的APP搜索一个吃饭的地方
                                     //用微信约一个妹纸
                                    //和妹纸一起去吃饭
                                    //吃饭需要的时间2
    }
    @Test
    public void testPlay() {
        LaoWang lw = new LaoWang();
        lw.play();
    }
    @Test
    public void testSleep() {
        LaoWang lw = new LaoWang();
        lw.sleep();
    }
}
二.AOP思想(Aspect Oriented Programming)
   面向切面编程
     切面(同一层面的不同方法之间共性行为)

三.通过静态代理实现AOP
   1.定义一个代理类实现和被代理类相同的接口,相当于代理接口中这些方法
   2.代理类专注同一层面不同方法的共性行为(所有行为都要统计时间),被代理类专注于自身逻辑(玩,吃,睡)
   静态指的是编译时期已经把代码写死
   静态代理缺点:
      1.如果接口中的方法比较多,我们代理类实现大量方法,代理类代码会非常臃肿
      2.同一层面不同方法的共性行为(代码)复用性非常低,每代理一个方法,我们都要写一次;


import java.util.Date;

/*
定义一个老王类,实现OneDay接口,代表老王一天要干的事
分别统计老王一天吃饭,睡觉,玩游戏的时间
 */
public class LaoWang implements OneDay {
    @Override
    public void eat() {
        //long startTime = new Date().getTime();

        System.out.println("用手机上的APP搜索一个吃饭的地方");
        System.out.println("用微信约一个妹纸");
        System.out.println("和妹纸一起去吃饭");
        for (int i = 0; i < 999999999; i++) {
            
        }

        //long endTime = new Date().getTime();
        //System.out.println("吃饭需要的时间"+(endTime-startTime));
    }

    @Override
    public void play() {
        //long startTime = new Date().getTime();

        System.out.println("打开电脑");
        System.out.println("打开LOL");
        System.out.println("登录账户和密码");
        System.out.println("随机匹配");
        for (int i = 0; i < 999999999; i++) {

        }

        //long endTime = new Date().getTime();
        //System.out.println("玩游戏需要的时间"+(endTime-startTime));
    }


    @Override
    public void sleep() {
        //long startTime = new Date().getTime();

        System.out.println("先洗个澡");
        System.out.println("换上睡衣");
        System.out.println("上床睡觉");
        for (int i = 0; i < 999999999; i++) {

        }

        //long endTime = new Date().getTime();
        //System.out.println("睡觉需要的时间"+(endTime-startTime));
    }

}

import proxy01.OneDay;

import java.util.Date;

/*
 *定义一个代理类,相当于老王的私人教练
 *代理主要完成需求:
 *    老王专注于吃饭,睡觉,玩游戏,私人教练专门负责统计老王的吃饭,睡觉,玩游戏的时间
 *为了让代理类知道要对哪些方法做时间统计,我们需要让代理类和被代理类实现相同接口
 * 类比:
 *    为了让私人教练教练知道对老王哪些行为做时间统计,我们需要让私人教练和老王实现相同的接口
 *
 */
public class LaoWangProxy implements OneDay {
    private LaoWang lw;

    public LaoWangProxy(LaoWang lw) {
        this.lw = lw;
    }

    @Override
    public void eat() {
        long startTime = new Date().getTime();

        lw.eat();

        long endTime = new Date().getTime();
        System.out.println("吃饭需要的时间" + (endTime - startTime));
    }

    @Override
    public void play() {
        long startTime = new Date().getTime();

        lw.play();

        long endTime = new Date().getTime();
        System.out.println("玩游戏需要的时间" + (endTime - startTime));
    }

    @Override
    public void sleep() {
        long startTime = new Date().getTime();

        lw.sleep();

        long endTime = new Date().getTime();
        System.out.println("睡觉需要的时间" + (endTime - startTime));
    }
}

/*
这个接口为了约束老王这一天有什么行为
 */
public interface OneDay {
    //吃饭
    void eat();

    //玩游戏
    void play();

    //睡觉
    void sleep();
}
//代码实现的地方
import org.junit.Test;

public class Demo {
    @Test
    public void testEat() {
        LaoWangProxy lwp = new LaoWangProxy(new LaoWang());
        lwp.eat();
    }

    @Test
    public void testPlay() {
        LaoWangProxy lwp = new LaoWangProxy(new LaoWang());
        lwp.play();
    }

    @Test
    public void testSleep() {
        LaoWangProxy lwp = new LaoWangProxy(new LaoWang());
        lwp.sleep();
    }
}
四.动态代理实现AOP
     /*
      *动态代理实现AOP:
      *    动态指的是:在程序运行时,有JVM为我么自动生成代理类(隐形代理)
      *
      * 用到的Class类中的方法:
      *      ClassLoader getClassLoader()
                   返回该类的类加载器。
      *
      *      Class[] getInterfaces()
                   确定此对象所表示的类或接口实现的接口。
      *
      *我们需要通过Proxy类的方法来实现动态代理
      *     static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)
      *         返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。
      *
      *     第一个参数:传递一个类加载器,用来加载JVM生成的代理类,我们一般使用和被代理类相同的加载器
      *     第二个参数:传递代理类和被代理类实现的接口(一个类实现的接口又可能多个,所以用数组接受)
      *     第三个参数:传递一个InvocationHandler接口实现类对象,这个InvocationHandler实现类的对象绑定在嗲李磊对象上
      *     返回值:底层会返回由JVM生成的代理类的一个实例
      *
      *     JVM隐式生擦横的代理类:
      *        class $Proxy1 implements OneDay{
      *            public void eat(){}
      *
      *            public void play(){}
      *
      *            public void sleep(){}
      *             }
      *
      */

      动态代理的优点:
         1.不用我呢自定义一个代理类实现和被代理类相同的接口,实现就口中的大量方法
           由JVM帮我们创建这个代理类,简化代码
         2.对于同一层面不同方法之间的共性行为我们只需要写一次,从而提高代码复用性


/*
定义一个老王类,实现OneDay接口,代表老王一天要干的事
分别统计老王一天吃饭,睡觉,玩游戏的时间
 */
public class LaoWang implements OneDay {
    @Override
    public void eat() {


        System.out.println("用手机上的APP搜索一个吃饭的地方");
        System.out.println("用微信约一个妹纸");
        System.out.println("和妹纸一起去吃饭");
        for (int i = 0; i < 999999999; i++) {
            
        }


    }

    @Override
    public void play() {

        System.out.println("打开电脑");
        System.out.println("打开LOL");
        System.out.println("登录账户和密码");
        System.out.println("随机匹配");
        for (int i = 0; i < 999999999; i++) {

        }
    }


    @Override
    public void sleep() {

        System.out.println("先洗个澡");
        System.out.println("换上睡衣");
        System.out.println("上床睡觉");
        for (int i = 0; i < 999999999; i++) {

        }


    }

}

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class HandlerImpl implements InvocationHandler {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //System.out.println("invoke方法被调用");
        System.out.println(proxy.getClass());//获取代理类
        System.out.println(proxy.getClass().getSuperclass());//代理类继承的父类
        for (Class i : proxy.getClass().getInterfaces()) {//获取代理类实现的接口
            System.out.println(i);
        }
        System.out.println(method);
        System.out.println(args);

        return null;
    }
}
/*
这个接口为了约束老王这一天有什么行为
 */
public interface OneDay {
    //吃饭
    void eat();

    //玩游戏
    void play();

    //睡觉
    void sleep();
}
import org.junit.Test;

import java.lang.reflect.Proxy;

/*
 *动态代理实现AOP:
 *    动态指的是:在程序运行时,有JVM为我么自动生成代理类(隐形代理)
 *
 * 用到的Class类中的方法:
 *      ClassLoader getClassLoader()
              返回该类的类加载器。
 *
 *      Class[] getInterfaces()
              确定此对象所表示的类或接口实现的接口。
 *
 *我们需要通过Proxy类的方法来实现动态代理
 *     static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)
 *         返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。
 *
 *     第一个参数:传递一个类加载器,用来加载JVM生成的代理类,我们一般使用和被代理类相同的加载器
 *     第二个参数:传递代理类和被代理类实现的接口(一个类实现的接口又可能多个,所以用数组接受)
 *     第三个参数:传递一个InvocationHandler接口实现类对象,这个InvocationHandler实现类的对象绑定在嗲李磊对象上
 *     返回值:底层会返回由JVM生成的代理类的一个实例
 *
 *     JVM隐式生擦横的代理类:
 *        class $Proxy1 implements OneDay{
 *            public void eat(){}
 *
 *            public void play(){}
 *
 *            public void sleep(){}
 *             }
 *
 */
public class Demo {
    @Test
    public void testProxy() {
        //1.传递一个类加载器
        ClassLoader cl = LaoWang.class.getClassLoader();

        //2.代理类和被代理类实现的接口
        Class[] interfaces = LaoWang.class.getInterfaces();

        //3.创建InvocationHandler实现类对象
        HandlerImpl ih = new HandlerImpl();

        //4.调用 newProxyInstance()方法
        OneDay od = (OneDay) Proxy.newProxyInstance(cl, interfaces, ih);//OneDay接口指向底层代理类对象,底层代理类实现了OneDay接口
                                                                       //多态形式
        od.eat();//通过代理类对象调用的方法最终都被转调InvocationHandler实现类的invoke方法
        System.out.println("--------------");
        od.sleep();
        System.out.println("------------");
        od.play();

    }
}
四.Java中的注解
    a.注解概述
      注解相当于是一种标记,交给Java编译器或者程序来解析,当java百年一起和程序解析到相应的注解就会执行相应的操作
      KFC    肯德基  (一种标记被人脑解析了)

    b.Java中常见注解
         /*
           @Override   交给编译器解析,百年一起会检查该方法是否重写父类对应的方法,如果该方法没有重写父类的方法,百衲衣直接报错
           @Deprecated 交给编译器解析,代表该方法已过时,当我们调用该方法的时候,会在方法名上加一个删除线
                       已过时的方法可以使用,但是不推荐使用
           @param      文档注释相关的注解 可以在后面对该参数进行信息描述
           @return     文档注释相关的注解 可以在后面对该方法的返回值进行描述
           @throws     文档注释相关的注解 可以在后面对该方法声明的异常进行描述
          */

/*
  @Override   交给编译器解析,百年一起会检查该方法是否重写父类对应的方法,如果该方法没有重写父类的方法,百衲衣直接报错
  @Deprecated 交给编译器解析,代表该方法已过时,当我们调用该方法的时候,会在方法名上加一个删除线
              已过时的方法可以使用,但是不推荐使用
  @param      文档注释相关的注解 可以在后面对该参数进行信息描述
  @return     文档注释相关的注解 可以在后面对该方法的返回值进行描述
  @throws     文档注释相关的注解 可以在后面对该方法声明的异常进行描述
 */
public class Son extends Father {
    @Override
    public void method() {
        System.out.println("Son类的method方法");
    }
@Deprecated
    public void method02() {
        System.out.println("Son类的method02方法");
    }

    /**
     *
     * @param a  代表求和的第一个参数
     * @param b  代表求和的第二个参数
     * @return   最终返回两个整数的和
     * @throws Exception   可能抛出Exception类型的异常对象
     */
    public int getSum(int a, int b) throws Exception{
        return a + b;
    }
}
    c.Java中的定义注解
      格式:
         元注解(可写可不写)
         public @interface  注解名称{
              //注解属性
              数据类型 属性名称();
              数据类型 属性名称() default 默认值;//代表当前属性的默认值
         }
         注解中属性的数据类型:
            1.Java中八大基本类型
            2.String,Class,enum,注解类型
            3.以上所有类型的一堆数组
      使用:
         public @interface MyAnnotation {
             String name();//定义了一个String类型的属性
             int num() default 0;//定义了一个int类型的num属性,并且默认值为0

             String[] colors() default {"while", "black"};//定义了一个String[]类型的color属性,并且默认值为{"white","black"}
         }

         public @interface MyAnnotation02 {
             int value();

             String name() default "老王";

             Color color()default Color.GREEN;
         }
/*
     元注解(可写可不写)
         public @interface  注解名称{
              //注解属性
              数据类型 属性名称();
              数据类型 属性名称() default 默认值;//代表当前属性的默认值
         }
         注解中属性的数据类型:
            1.Java中八大基本类型
            2.String,Class,enum,注解类型
            3.以上所有类型的一堆数组
   java中的注解在不指明位置的情况下,可以用在包上,类上,方法的形参上等
 */
public @interface MyAnnotation {

    String name();//定义了一个String类型的属性
    int num() default 0;//定义了一个int类型的num属性,并且默认值为0

    String[] colors() default {"while", "black"};//定义了一个String[]类型的color属性,并且默认值为{"white","black"}

}

/*

  @注解名(给注解中的属性赋值)

 */
@MyAnnotation(name = "老王")//在类上使用注解
public class Person {

    @MyAnnotation(name = "老李", num = 13)//在字段上使用注解
    private int age;

    @MyAnnotation(name = "老李", num = 13,colors = {"green","while", "black"})//在方法上使用注解
    public int getAge() {
        return age;
    }

    public void setAge(@MyAnnotation(name="菲菲",num=25) int age) {//用在形参age上
        this.age = age;
    }
}
    d.元注解(注解上的注解)
      @Target: 指定该注解能使用的位置,默认定义的注解可以使用在任意位置
             TYPE:表明该类注解可以以使用在类上
             FIELD:表明该类注解可以以使用在成员变量上
             METHOD:表明该注解可以使用在成员方法上
             PARAMETER:表明该注解可以使用在参数上

             CONSTRUCTOR:表明该注解可以使用在构造方法上
             LOCAL_VARIABLE:表明该注解可以使用在局部变量
             ANNOTATION_TYPE:表明该注解可以使用在其他注解上
             PACKAGE:表明该注解可以使用在包上
             TYPE_PARAMETER:表明该注解可以使用在泛型上
             TYPE_USE:表明该注解可以使用在任意位置上

             MODULE,

             RECORD_COMPONENT;

      @Retention:指定注解的生命周期(存活时间),不指定默认生命周期时 Retention.CLASS

                 Retention.SOURCE:指定该注解只存在于源码(.java文件)阶段,编译器编译该.java文件生成的.class文件中就不再有该注解
                                   例如@Override
                 Retention.CLASS:指定该注解存在与源码(.java)阶段,编译器编译该.java文件后生成的.class文件中依然有该注解
                                  当程序运行的时候(把这个.class加载到jvm时)会弄丢该注解
                                  如果该注解指明为这个RetentionPolicy.CLASS,那么这个类对应的字节码对象中就没有该注解
                 Retention.RUNTIME:
                                   指定该注解存在源码(.java)阶段,编译器编译该.java文件后生成的.class文件中依然有该注解
                                   而且程序运行时(把这个.class加载到jvm时),依然存在该注解

    e.通过反射获取类上和方法上的注解
            /*
              获取类上和方法上的注解
              Class类中的方法:
                  boolean isAnnotationPresent(Class annotationClass)
                      判断该类上是否有指定的注解,如果有返回true,否则返回false

                  public  A getAnnotation(Class annotationClass)
                      获取该类上指定的注解

              Method类中:
                  public boolean isAnnotationPresent(Class annotationClass)
                             判断该方法上是否有指定的注解,如果有返回true,否则返回false

                  public  T getAnnotation(Class annotationClass)
                             获取该方法上指定的注解

             */

/*

  @注解名(给注解中的属性赋值)

 */

@MyAnnotation(name = "张三")
public class Person {

    //@MyAnnotation(name="张三")
    private int age;

@MyAnnotation(name="李四",num=13)
    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}


import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/*
     元注解(可写可不写)
         public @interface  注解名称{
              //注解属性
              数据类型 属性名称();
              数据类型 属性名称() default 默认值;//代表当前属性的默认值
         }
         注解中属性的数据类型:
            1.Java中八大基本类型
            2.String,Class,enum,注解类型
            3.以上所有类型的一堆数组
   java中的注解在不指明位置的情况下,可以用在包上,类上,方法的形参上等
 */
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)//由于我们需要通过反射来获取注解,反射其实就是解剖字节码对象,我们需要保证在字节码对象中依然有该注解
                                   //所以需要使用RetentionPolicy。RUNTIME
public @interface MyAnnotation {

    String name();//定义了一个String类型的属性
    int num() default 0;//定义了一个int类型的num属性,并且默认值为0

    String[] colors() default {"while", "black"};//定义了一个String[]类型的color属性,并且默认值为{"white","black"}

}

import org.junit.Test;

import java.lang.reflect.Method;

/*
  获取类上和方法上的注解
  Class类中的方法:
      boolean isAnnotationPresent(Class annotationClass)
          判断该类上是否有指定的注解,如果有返回true,否则返回false

      public  A getAnnotation(Class annotationClass)
          获取该类上指定的注解

  Method类中:
      public boolean isAnnotationPresent(Class annotationClass)
                 判断该方法上是否有指定的注解,如果有返回true,否则返回false

      public  T getAnnotation(Class annotationClass)
                 获取该方法上指定的注解
 */
public class Demo {
    @Test
    public void testGetClassAnnotation() {
        //1.获取Person类的字节码对象
        Class personClass = Person.class;
        //2.判断该类上是否有@MyAnnotation该注解,如果有再去获取,如果没有什么操作都不做
        boolean flag = personClass.isAnnotationPresent(MyAnnotation.class);
        //3.判断flag为真,如果flag为真再去获取注解
        if (flag) {
            //4.获取类上注解
            MyAnnotation annotation = personClass.getAnnotation(MyAnnotation.class);
            //5.获取当前注解上的属性值
            System.out.println(annotation.name());
            System.out.println(annotation.num());
            for (String color : annotation.colors()) {
                System.out.println(color);
            }
        }
    }

    @Test
    public void testGetMethodAnnotation() throws Exception{
        //1.获取Person类的字节码对象
        Class personClass = Person.class;
        //2.判断该方法上是否有@MyAnnotation该注解,如果有再去获取,如果没有什么操作都不做
        Method method = personClass.getMethod("getAge");
        if (method.isAnnotationPresent(MyAnnotation.class)) {
            //3.获取方法上的注解
            MyAnnotation annotation = method.getAnnotation(MyAnnotation.class);

            //4.获取注解上的属性值
            System.out.println(annotation.name());
            System.out.println(annotation.num());
            for (String color : annotation.colors()) {
                System.out.println(color);
            }
        }

    }
}

你可能感兴趣的:(java,开发语言)