源码地址
https://github.com/yixuaz/myspring
在写AOP之前,我们必须要先会写,JAVA的动态代理,要知道代理模式。所以我们先把准备工作给做好。
我们会按照这个顺序来讲
1.什么是动态代理
2.JDK动态代理
3.CGLIB 动态代理
4.Objenesis的使用
5.bean后置处理器的使用
1. 什么是动态代理
在讲动态代理前,我们先说下什么是代理模式。
比如你MAIN方法想要去调用DBQUERY的request方法。但是你给MAIN的是一个接口。如下图。
那么MAIN方法是不知道实现这个接口的是代理类还是类本身。
所以代理类可以封装了原类之后,对REQUEST方法做一个加强。比如在前面做一些安全效验。在后面做一个日志记录等。
那么动态代理的意思,就是这个代理类我们是不用自己去写这样一个类。而是由程序自动为我们生成,运行时生成,这就是动态代理了。
好处就是有很多类,比如FILE QUERY,都要有代理。就不用每个类都写一个代理类。
JDK 动态代理
JDK动态代理为我们提供的了一个INVOCATION HANDLER的接口。
下面我们就手把手来实现下JDK动态代理。
首先我们需要一个IDBQuery 的Interface
public interface IDBQuery {
String request(String id);
}
写一个DBQuery 来实现它
public class DBQuery implements IDBQuery{
@Override
public String request(String id) {
return "request id";
}
}
实现一个代理类 实现JDK的INVOCATION HANDLER的接口
public class DBQueryProxy implements InvocationHandler {
private Object tar;//要代理的对象
public Object bind(Object obj){
this.tar = obj;
//取得代理对象
return Proxy.newProxyInstance(tar.getClass().getClassLoader(),tar.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("query Id:"+args[0]);//增强代码
Object result = method.invoke(tar,args);
return result;
}
}
最后写个MAIN函数做测试
public class JdkProxyMain {
public static void main(String[] args) {
DBQueryProxy proxy = new DBQueryProxy();
IDBQuery query = (IDBQuery) proxy.bind(new DBQuery());
System.out.println(query.request("1"));
System.out.println("class:"+query.getClass());
}
}
我们发现CLASS变了,已经编程代理类了。
CGLIB 动态代理
cglib 底层是通过字节码操作,来实现生成一个代理类,比JDK提供的方式更加灵活。
弥补了JDK动态代理智能代理接口的不足。
操作字节码是通过一个ASM的库。
我们先来看一下一个ASM库可以实现什么效果
下面这段代码就是通过ASM在运行时来动态生成一个Week11的class,这个类里有个MAIN方法。算(6+7)*3 然后打印出来。
public static void main(String[] args) throws Exception {
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS
| ClassWriter.COMPUTE_FRAMES);
cw.visit(Opcodes.V1_7, Opcodes.ACC_PUBLIC, "Week11", null,
"java/lang/Object", null);
// 方法开始init
MethodVisitor mw = cw.visitMethod(Opcodes.ACC_PUBLIC, "", "()V",
null, null);
mw.visitVarInsn(Opcodes.ALOAD, 0); // this 入栈
mw.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "",
"()V");
mw.visitInsn(Opcodes.RETURN);
mw.visitMaxs(0, 0);
mw.visitEnd(); // 方法init结束
// main方法开始
mw = cw.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "main",
"([Ljava/lang/String;)V", null, null);
/**
* 使用ASM,通过字节码 完成以下代码: int a=6; int b=7; int c=(a+b)*3;
* System.out.println(c);
*/
// 把变量放入局部变量表里
mw.visitIntInsn(Opcodes.BIPUSH, 6);
mw.visitIntInsn(Opcodes.BIPUSH, 7);
// 操作数栈
mw.visitInsn(Opcodes.IADD);
mw.visitIntInsn(Opcodes.BIPUSH, 3);
mw.visitInsn(Opcodes.IMUL);
mw.visitVarInsn(Opcodes.ISTORE, 2);
// 打印出来
mw.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out",
"Ljava/io/PrintStream;");
mw.visitVarInsn(Opcodes.ILOAD, 2);
mw.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream",
"println", "(I)V");
mw.visitInsn(Opcodes.RETURN);
mw.visitMaxs(0, 0);
mw.visitEnd(); // main方法结束
final byte[] code = cw.toByteArray();
ASMPlay loader = new ASMPlay();
Class> exampleClass = loader.defineClass("Week11", code, 0,
code.length);
exampleClass.getMethods()[0].invoke(null, new Object[] { null });
}
cglib主要通过methodInterceptor
JDK缺陷,要使用代理必须要有接口。CGLIB没有这个限制。
代码详解
DBQuery 类不用接口了
public class DBQuery {
public String request(String id) {
return "request id";
}
public String request2(String id) {
return "request id2";
}
}
代理器
System.out.println("query id:"+args[0]); 为增强方法。
后面就是执行代理方法。
public class DBQueryInterceptor implements MethodInterceptor {
Object tar;
public DBQueryInterceptor(Object tar){
this.tar = tar;
}
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("query id:"+args[0]);
Object result = methodProxy.invoke(tar,args);
return result;
}
}
MAIN
public class CglibProxyMain {
public static void main(String[] args) {
DBQuery query = createProxy(new DBQuery());
System.out.println(query.request("1"));
System.out.println(query.request2("2"));
System.out.println(query.getClass());
}
private static T createProxy(Object obj) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(obj.getClass());
enhancer.setCallback(new DBQueryInterceptor(obj));
return (T) enhancer.create();
}
}
打印结果,我们发现这个类下面所有的方法都被增强了。
query id:1
request id
query id:2
request id2
class cglib.DBQuery$$EnhancerByCGLIB$$4a3cce79
那有没有方法可以选择增强部分方法呢?
这要在INTECEPT里面判断哪些方法用METHOD,哪些方法用METHOD PROXY 就好了
public class DBQueryInterceptor implements MethodInterceptor {
Object tar;
public DBQueryInterceptor(Object tar){
this.tar = tar;
}
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
if(!method.getName().equals("request2")){
return method.invoke(tar,args);
}
System.out.println("query id:"+args[0]);
Object result = methodProxy.invoke(tar,args);
return result;
}
}
可以在ENHANCER的地方设置CALLBACK FILTER,来指定使用不同的代理方法。
filter 里面就是告诉CGLIB,我应该用CALLBACKS[] 里的第几个CALLBACK。
public class CglibProxyMain {
public static void main(String[] args) {
DBQuery query = createProxy(new DBQuery());
System.out.println(query.request("1"));
System.out.println(query.request2("2"));
System.out.println(query.getClass());
}
private static T createProxy(Object obj) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(obj.getClass());
enhancer.setCallbacks(new Callback[]{
new DBQueryInterceptor(obj), new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("second");
return methodProxy.invokeSuper(o,args);
}
}
});
enhancer.setCallbackFilter(new CallbackFilter() {
@Override
public int accept(Method method) {
if(method.getName().equals("request2"))
return 1;
return 0;
}
});
return (T) enhancer.create();
}
Objenesis
Objenesis是专门用于实例化一些特殊java对象的一个工具,如私有构造方法,带参数的构造等不能通过class.newInstance()实例化的,通过它可以轻松完成。
首先写一个没有默认构造函数的类。
public class User {
String name;
private User(){
}
private User(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
然后用OBJENESIS把它构造出来
import org.springframework.objenesis.Objenesis;
import org.springframework.objenesis.ObjenesisStd;
import org.springframework.objenesis.instantiator.ObjectInstantiator;
public class ObjenesisMain {
public static void main(String[] args) {
Objenesis objenesis = new ObjenesisStd();
ObjectInstantiator thingyInstantiator = objenesis.getInstantiatorOf(User.class);
User user = thingyInstantiator.newInstance();
user.setName("hello");
System.out.println(user.getName());
}
}
Bean 后置处理器
通过SPRING源码 可以看到这个调用顺序
我们在来看下Bean 后置处理器 在哪里注册上去的。 下图是一个SPRING 的 APLLICATION CONTEXT的一个流程图。
我们可以看到标红的那一步就是。
下面标红的那一步就是上面贴的代码来源。
最后用BEAN POST PROCESSOR来模拟一个简单的AOP实现
public class AopBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if(bean.getClass().equals(DBQuery.class)) {
return createProxy(bean);
}
return bean;
}
private T createProxy(Object obj) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(obj.getClass());
enhancer.setCallback(new DBQueryInterceptor(obj));
return (T) enhancer.create();
}
}
接下来我们就按照这个思路来实现自己的SPRING AOP。