反射:有了反射Java才被可以称之为动态语言,反射允许程序在执行期借助于Reflection API取得任何类的內部信息,并能直接操作任意对象的内部属性及方法。
某种意义上来讲,反射其实是破坏了Java的安全性。
//Test.java
public class Test {
private String name="不可见的";
}
//Fps.java
public class Fps {
public static void main(String[] args) throws Exception{
Test test = new Test();
//获取Test的Class对象,反向的调用test
Class testClass = test.getClass(); //得到 test 对象的字节码 对象
Field field = testClass.getDeclaredField("name");
field.setAccessible(true);
System.out.println("输出结果:"+field.get(test));
}
}
执行结果:“输出结果:不可见的”
我们通过反射的方法得到了Test私有的属性的值,当然它不仅仅能做到这些,它的能量超乎想象!
Class是一个很特殊的类,它是一个运行期的信息类,通过它,你可以得到 类名,包名,构造方法等等,通过Class可以得到类的全部信息,包括类的对象。
重点不是AOP的动态代理吗,跑题了?
言归正传,大部分和Java相关的框架都是使用的反射,可以说反射就是框架的灵魂,一点也毫不夸张。
AOP的动态代理实现方案有两种,JDK的动态代理和CGLIB动态代理,区别在于JDK自带的动态代理,必须要有接口,而CGLIB动态代理有没有接口都可以。
CGLIB动态代理
先看一个简单的类,我们来一步一步的实现动态代理
//Test.java
public class Test {
public void test() {
System.out.println("执行test");
}
}
和得到私有对象一样,得到Test方法然后执行。
//FPS.java
public class Fps {
public static void main(String[] args) throws Exception{
Test test = new Test();
//获取Test的Class对象,反向的调用test
Class testClass = test.getClass(); //得到 test 对象的字节码 对象
Method m = testClass.getMethod("test");
m.invoke(test);
}
}
执行结果:“执行test”
然后我们创建TestA
//TestA .java
public class TestA implements MethodInterceptor {
/**
* 代理方法, 每次调用目标方法时都会进到这里
*/
@Override
public Object intercept(Object obj, Method method,
Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("Before TestA");
return methodProxy.invokeSuper(obj, args);
}
}
模拟CGLIB的实现方案
//FPS.java
public class Fps {
public static void main(String[] args) throws Exception{
Test test = new Test();
//获取Test的Class对象,反向的调用test
Class testClass = test.getClass(); //得到 test 对象的字节码 对象
// Method m = testClass.getMethod("test");
//m.invoke(test);
//创建代理对象
Test test1 = (Test) Enhancer.create(testClass,new TestA());
test1.test();
}
}
执行结果:“Before TestA 执行test”
Enhancer 是CGLib中的一个字节码增强器
Test test1 = (Test) Enhancer.create(testClass,new TestA());
等同于
Enhancer test1= new Enhancer(); //生成代理对象
test1 setSuperclass(testClass); //设置testClass为代理对象的父类
test1 setCallback(new TestA()); //设置回调对象为TestA
Test test2= (Test)test1.create();//生成一个代理类对象
CGLIB产生代理对象的原理:实际上是将一个新的类作为它的子类,我们在使用它时,就相当我们把一个类给了它,它返回给我们这个类的子类
我们将TestA的功能扩充下,就可以实现前置拦截,后置拦截,异常拦截,等等
//TestA.java
public class TestA implements MethodInterceptor {
/**
* 代理方法, 每次调用目标方法时都会进到这里
*/
@Override
public Object intercept(Object obj, Method method,
Object[] args, MethodProxy methodProxy) throws Throwable {
Object obj1= null;
begin();
if (isIntercept(method, args)) {
before();
obj1= methodProxy.invokeSuper(obj, args);
after();
} else {
obj1= methodProxy.invokeSuper(obj,args);
}
end();
return obj1;
}
//开始
public void begin() {}
//切面
public boolean isIntercept(Method method, Object[] args) throws Throwable {
return true;}
//前置
public void before() throws Throwable {}
//后置
public void after() throws Throwable {}
//结束
public void end() {}
}
//spring 还有个异常,有兴趣的朋友可以取了解下
到这里基本就结束了,可以看到,只要在对应方法中加入想要的方案就就可以进行动态插入了,但是这里有个问题就是不可能所有的逻辑都写在TestA中啊,朋友们想到了什么?没错,继承啊,当我们需要一个新的业务时,继承TestA就行了
public class TestB extends TestA{
//切面
public boolean isIntercept(Method method, Object[] args) throws Throwable {
return method.getName().equals("test");
}
//前置
public void before() throws Throwable {
System.out.println("还没开始");
}
//后置
public void after() throws Throwable {
System.out.println("已经结束了");
}
}
以上就是Spring AOP的实现方案,只不过Spring定义一个工厂类来创建代理,对于我们而言有无皆可,相信朋友们肯定已经了解了CGLIB了,了解了CGLIB,回头看下JDK动态代理,你会发现原理差不多,除了适用范围不同外。
代理 | 代理类继承 | 代理类实现方法 | 原理 | 适用范围 |
---|---|---|---|---|
CGLIB | MethodInterceptor | intercept | 生成一个类的子类,然后在在其子类上加入前置后置等等方案 | ALL |
JDK动态代理 | InvocationHandler | invoke | 生成接口的实现类,然后再实现类上加入前置后置等等方案 | 接口 |