Java反射,代理,Spring中的AOP

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/about4years/article/details/77688991

Java代理

1.RTTI机制

RTTI, 全称是Run-Time Type Identification,译为运行时类型识别。最经典的例子就是Java中的多态机制,父类引用指向子类的对象,JVM总是能够找到对应的子类去执行对应的方法。例如:

class Instruments {
    public void play(){
        System.out.println("Instruments is playing")
    }
}

class surnai extends Instruments {
    public void play() {
        System.out.println("surnai is playing")
    }
}

class piano extends Instruments {
    public void play() {
        System.out.println("piano is playing")
    }
}

//测试
public class test {
    public static void main(String[] args) {
        Instruments piano = new piano();
        Instruments surnai = new surnai();
        piano.play();   //output: piano is playing
        surnai.play();  //output: surnai is playing
    }
}

很显然,运行时JVM其实是知道这个引用所对应的具体的对象的类型的,并且清楚这个类的所有信息,这块信息就保存在class对象中。也就是说一个类会有一个对应的class对象,并且只会有一个,由Jvm加载,不是一次性就全部加载到Jvm中,加载时机是当我们第一次使用时,例如: Instruments piano = new piano();更准确的说,当第一次引用该类的静态成员时,会加载对应类的class对象(就是读取对应类的.class文件,将字节码读取到jvm中),这恰恰证明了其实类的构造函数也是静态方法,尽管没有static修饰。

一旦这个类的class对象加载完成,那么这个类的所有对象就由这个类的class对象来生成。
我们可以通过三种获取到类的class对象,Class类提供了很多强大的API,具体参见JDK。

反射

上文提到三种方式获取类的class对象,分别是如下三种:

piano p = new piano();
Class c = piano.class;
Class c2 = p.getClass();
Class c3 = Class.forName("xx.xx.xx");  //需要handle 异常:ClassNotFoundException

第一第二种很好理解,第三种有点特殊之处,显而易见的是它需要处理一个异常:ClassNotFoundException,而第一第二种不需要,这是因为第一第二种我们已经知道了piano这个类的存在,而第三种我们需要传入一个变量(变量通常是一个class文件的路径),很显然我们可能传入一个错误的路径,jvm无法找到对应的class文件。自然也生成不了对应的class对象了。

所以,对于Class.forName这种方式,我们是无法在编译时获取到这个类的class文件的,也就是运行时检查.class文件,这个.class文件可能是在我们的磁盘中,也可能是通过网络传输获取到。一但获取到,我们拿到了对应的class对象,那么这个类的一切信息都暴露出来了,我们甚至可以通过invoke去调用对应的方法。

代理

如果说我们可以通过一个类的class对象,最终调用到这个类的某个实例的具体方法,那么是不是可以理解为我们可以代理这个对象,所谓代理,那么我们就可以做很多其他的事,只要最终不影响到方法的调用即可。

静态代理

先从静态代理开始:

public interface Task {
    void setData(String data);
    
}

public class TaskImpl implements Task {
    @Override
    public void setData(String data) {
        System.out.println(data+ " Data is saved");
    }
    
}

public class TaskStaticProxy implements Task {
    private Task realObj;

    public TaskStaticProxy(Task task) {
        this.realObj = task;
    }

    @Override
    public void setData(String data) {
        System.out.println("执行真实对象的setData方法之前,我做了一些其他事...");
        realObj.setData(data);
        System.out.println("执行真实对象的setData方法之后,我做了一些其他事...");
    }

}

//test
public class Test {
    public static void main(String[] args) {
        TaskStaticProxy taskStaticProxy = new TaskStaticProxy(new TaskImpl());
        taskStaticProxy.setData("data");
    }
}
//output:
执行真实对象的setData方法之前,我做了一些其他事...
data Data is saved
执行真实对象的setData方法之后,我做了一些其他事...

这就是所谓的静态代理,很容易理解。那么真实情况是如果需要实现一个类的代理,那么就需要创建一个新的代理类,所谓的静态,十分不方便,有没有办法可以动态的生成这个代理类呢?

动态代理(JDK)

JDK提供了动态代理的机制:

public class MyInvocationHandler implements InvocationHandler {

    private Object target;

    public MyInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy,  Method m, Object[] args) throws Throwable {
        Object result;
        System.out.println("执行真实对象的setData方法之前,我做了一些其他事...");
        result = m.invoke(target, args);
        System.out.println("执行真实对象的setData方法之后,我做了一些其他事...");
        return result;
    }
}
//test
public class Test {
    public static void main(String[] args) {
        Task task = (Task) Proxy.newProxyInstance(new TaskImpl().getClass().getClassLoader(), new Class<?>[] { Task.class },new MyInvocationHandler(new TaskImpl()) );
        task.setData("data");
    }
}
//output:
执行真实对象的setData方法之前,我做了一些其他事...
data Data is saved
执行真实对象的setData方法之后,我做了一些其他事...

使用了Proxy的一个静态方法,传入classloader,Class数组,以及自己实现的MyInvocationHandler对象:

  • ClassLoader:类加载器,需要一个类加载器来生成对应的类,注意这里的类加载器需要传入AppClassLoader而不能是其他ClassLoader,你只需要知道我们自己编写的类获取到的classLoader就是AppClassLoader,而像Object,String这些类对应的classLoader就是另外一个classLoader了,这里推荐一篇文章:http://blog.csdn.net/briblue/article/details/54973413。
  • Class<?>[]:接口数组,传入该类实现的所有的接口
  • InvocationHandler:传入自己实现的InvocationHandler,这个类中来编写代理前后的逻辑。

Spring中结合注解使用代理(AOP)

AOP是Spring提供的一个特性,其实现就是依托于代理,所谓的AOP就简单的理解为面向切面编程,可以想象一下一个个方法垂直排列,我们往中间横切一刀,产生了一个切面,这样所有的方法都需要经过这个面,而我们就可以在这个面上处理一些自己的逻辑。

结合注解,Spring使用AOP十分方便,例如现在我们对项目中所有的Rest接口的调用做一个打印Log的操作。
首先定义注解:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface ApiLog {
    String desc() default "";
}

接着,编写对应的切面

@Aspect
@Component
public class ApiLogAopHandler {

    @Before("within(com.zysaaa.aopdemo.controller.*) && @annotation(apiLog)")
    public void apiLogHandler(final JoinPoint joinPoint, ApiLog apiLog) {
        //args
        Object[] args = joinPoint.getArgs();
        //apiLog
        apiLog.desc();
    }

对应的测试代码:

@RestController
public class ApiController {

    @GetMapping("/book")
    @ApiLog(desc = "获取book By Book ID")
    public Object book(Long id) {

        return null;
    }

}

启动项目后,我们调用对应的api,例如:http://localhost:8082/book?id=10,DEBUG模式下,会进入到我们的切面中,
此时,一切信息都能获取到。

猜你喜欢

转载自blog.csdn.net/about4years/article/details/77688991
今日推荐