在我另一篇博客SSM之Spring 02 —— 动态代理、AOP、Spring-MyBatis、声明式事务中有简单描述代理模式和AOP原理,当时自己也还是有些没明白,后面在面试过程中被问到,这里详细说明一下。
AOP是什么就不再说明了,主要解释下AOP的原理——动态代理。
动态代理
和静态代理不同,动态代理是动态生成代理对象,克服了静态代理的问题——不同目标对象都需要写一个代理类。
动态代理主要有两种方式:JDK的动态代理和cglib代理。
JDK的动态代理
它是对对象进行代理,底层利用反射机制实现,同时需要我们自定义一个实现了InvocationHandler接口的类,这个接口中的invoke方法就是AOP的本质。
下面我们通过代码来进行理解。
1、定义好UserDao以及实现类,这是我们常见的方式。
//为了方便就不写参数了
public interface UserDao {
boolean addUser();
boolean deleteUser();
}
public class UserDaoImpl implements UserDao {
@Override
public boolean addUser() {
System.out.println("addUser();");
return false;
}
@Override
public boolean deleteUser() {
System.out.println("deleteUser();");
return true;
}
}
2、写一个测试,具体解释看注解。可以看到Proxy和InvocationHandler都是reflect包下的,说明底层肯定是反射。
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class Test {
public static void main(String[] args) {
//创建好目标对象
UserDao ud = new UserDaoImpl();
//这个就是我们自定义的一个实现类,里面invoke方法就是AOP的原理。
InvocationHandler ih = new MyInvocationHandler(ud);//将ud作为目标对象传进去
/*
这个方法返回一个代理对象,我们会在代理对象中做一些其他操作
newProxyInstance(ClassLoader, interfaces, InvocationHandler)
1、ClassLoader:目标对象的类加载器,代码是固定的。
2、interfaces:目标对象的接口数组,代码也是固定的,都是通过反射获得。
3、InvocationHandler:这是关键点,AOP能实现方法的增强就在这个接口里。需要自己定义。
*/
Object newProxyInstance = Proxy.newProxyInstance(ud.getClass().getClassLoader(),
ud.getClass().getInterfaces(), ih);
//执行代理对象被增强后的方法(增强的意思是在原有方法的基础上添加了其他效果,同时不影响原有业务,同Spring AOP的事务)
UserDao proxyUserDao = (UserDao)newProxyInstance;
proxyUserDao.addUser();
System.out.println("--------------");
proxyUserDao.deleteUser();
}
}
3、自定义的实现类,这是实现AOP增强方法的关键。
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class MyInvocationHandler implements InvocationHandler {
private Object object;
public MyInvocationHandler(Object object) {
this.object = object;
}
/**
*
* @param proxy 代理对象,就是我们从外部传进来的,但一般不用这个,会用我们自定义的object成员。
* @param method 目标对象的方法对象,是JDK传过来的。
* @param args 目标对象方法的参数对象。
* @return 对应于我们目标对象的方法的返回值
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String before = "";
String after = "";
//这里通过反射来判断UserDao的两个方法,如果不判断,那么就会针对目标对象的所有方法进行增强。
if(method.getName().equals("addUser")){
before = "前置通知:addUser()方法执行 之前";
after = "后置通知:addUser()方法执行 之后";
}
if(method.getName().equals("deleteUser")){
before = "前置通知:deleteUser()方法执行 之前";
after = "后置通知:deleteUser()方法执行 之后";
}
//下面这样就实现了和Spring AOP 一样的效果:方法的增强,即在方法执行前做一些其他事,且不影响到原有业务。
System.out.println(before);
Object result = method.invoke(object, args);//通过反射调用目标对象的方法
System.out.println(after);
return result;
}
}
4、运行结果:
cglib
和JDK动态代理不同,cglib(code generator library 代码生成库)代理的是字节码对象(即Class),而JDK动态代理代理的是对象。
步骤:
- cglib生成一个字节码对象(即Class对象),内容是空的。
- 设置字节码对象的父类为目标对象,即UserDaoImpl。
- 通过字节码对象中继承了目标对象的方法,即addUser,进行回调,调用父类的addUser,并在执行其方法前完成方法增强。
- 创建代理对象,通过代理对象执行Class的方法。
注意:cglib是Spring中的,所以需要导入Spring核心包才能使用。
导入进来
下面进行代码说明:
这里面CallBack就是AOP的原理,它通过子类调用父类UserDaoImpl的addUser方法,并在调用时完成增强。
package test02_AOP.cglib;
import org.springframework.cglib.proxy.Callback;
import org.springframework.cglib.proxy.Enhancer;
public class Test {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();//空的字节码对象
enhancer.setSuperclass(UserDaoImpl.class);//设置父类
Callback callback = new MyCallback();
enhancer.setCallback(callback);//设置回调
UserDao userDao = (UserDao) enhancer.create();//用代理类Enhancer 创建代理对象
userDao.addUser();
}
}
package test02_AOP.cglib;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
//本来是继承Callback接口,表示回调
//但Callback接口里什么都没有,只是起到一个标志作用,所以继承其子类MethodInterceptor
//MethodInterceptor接口仅有一个方法,拦截我们待增强的方法
public class MyCallback implements MethodInterceptor {
/**
*
* @param proxy 代理对象,即前面图里面的 class XX extends UserDaoImpl的XX对象
* @param method 目标对象的方法
* @param args 目标对象中的 方法对象 的参数对象(相当于addUser方法的参数)
* @param methodProxy 代理对象中的 方法代理对象(相当于父类UserDaoImpl的addUser方法)
* @return 返回代理对象方法的结果
* @throws Throwable
*/
@Override
public Object intercept(
Object proxy, Method method, Object[] args, MethodProxy methodProxy
) throws Throwable {
System.out.println("前置通知");
//执行原来的方法
Object object = methodProxy.invokeSuper(proxy, args);//这里不能用invoke,相当于前面图里super.addUser()
System.out.println("后置通知");
return object;
}
}
结果: