知识模块:
一.需求概述
二.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 extends Annotation> annotationClass)
判断该类上是否有指定的注解,如果有返回true,否则返回false
public A getAnnotation(Class annotationClass)
获取该类上指定的注解
Method类中:
public boolean isAnnotationPresent(Class extends Annotation> 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 extends Annotation> annotationClass)
判断该类上是否有指定的注解,如果有返回true,否则返回false
public A getAnnotation(Class annotationClass)
获取该类上指定的注解
Method类中:
public boolean isAnnotationPresent(Class extends Annotation> 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);
}
}
}
}