Java的代理实现模式有三种,分别为
静态代理、Proxy动态代理、cglib动态代理
Spring AOP面向切面编程就是用动态代理来实现的,在目标类的基础上,生成增强目标类(目标函数执行之前BeforeAdviseInterceptor
、执行之后AfterAdviseInterceptor
、抛出异常是执行ThrowsAdviseInterceptor
)。
1.静态代理
/**
* 用户管理
* 静态代理接口
*/
public interface UserManager {
void findUser();
}
/**
*用户管理实现类
*/
public class UserManagerImpl implements UserManager{
@Override
public void findUser() {
System.out.println("*****UserManagerImpl实现类执行*******");
}
}
/**
* 静态代理类
*/
public class UserMangerProxy implements UserManager{
private UserManagerImpl userManager;
public UserMangerProxy(UserManagerImpl userManager){
this.userManager=userManager;
}
@Override
public void findUser() {
System.out.println("-------执行前调-------------");
userManager.findUser();
System.out.println("-------执行后调-------------");
}
public static void main(String[] args) {
UserManager userManager = new UserMangerProxy(new UserManagerImpl());
userManager.findUser();
}
}
2.Proxy动态代理
借助java内部的反射机制
来实现的
2.1 Proxy举例
/**
* 用户管理
* 静态代理接口
*/
public interface UserManager {
void findUser();
}
**
*用户管理实现类
*/
public class UserManagerImpl implements UserManager {
@Override
public void findUser() {
System.out.println("*****UserManagerImpl实现类执行*******");
}
}
/**
*动态代理类只能代理接口(不支持抽象类),
* 代理类都需要实现InvocationHandler类,实现invoke方法。
* 该invoke方法就是调用被代理接口的所有方法时需要调用的,该invoke方法返回的值是被代理接口的一个实现类
*/
public class UserMangerProxy implements InvocationHandler {
private Object targetObject;
//绑定关系,也就是关联到哪个接口(与具体的实现类绑定)的哪些方法将被调用时,执行invoke方法。
public Object newProxyInstance(Object targetObject){
this.targetObject=targetObject;
//该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例
//第一个参数指定产生代理对象的类加载器,需要将其指定为和目标对象同一个类加载器
//第二个参数要实现和目标对象一样的接口,所以只需要拿到目标对象的实现接口
//第三个参数表明这些被拦截的方法在被拦截时需要执行哪个InvocationHandler的invoke方法根据传入的目标返回一个代理对象
return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),
targetObject.getClass().getInterfaces(),this);
}
@Override
//关联的这个实现类的方法被调用时将被执行
/*InvocationHandler接口的方法,proxy表示代理,method表示原对象被调用的方法,args表示方法的参数*/
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("start-->>");
//打印方法名,参数值
System.out.println("The runing method name is:"+method.getName());
if(args!=null){
for(int i=0;i<args.length;i++){
System.out.println(args[i]);
}
}
Object ret=null;
try{
/*原对象方法调用前处理日志信息*/
System.out.println("satrt-->>");
//调用目标方法
ret=method.invoke(targetObject, args);
/*原对象方法调用后处理日志信息*/
System.out.println("success-->>");
}catch(Exception e){
e.printStackTrace();
System.out.println("error-->>");
throw e;
}
return ret;
}
public static void main(String[] args){
UserMangerProxy logHandler=new UserMangerProxy();
UserManager userManager=(UserManager)logHandler.newProxyInstance(new UserManagerImpl());
userManager.findUser();
}
}
Proxy.newProxyInstance(*,*,*)
该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例,传入的三个参数
- 第一个参数指定产生代理对象的类加载器,需要将其指定为和目标对象同一个类加载器
- 第二个参数要实现和目标对象一样的接口,所以只需要拿到目标对象的实现接口
- 第三个参数表明这些被拦截的方法在被拦截时需要执行哪个InvocationHandler的
invoke
方法,根据传入的目标返回一个代理对象
3.CGLIB动态代理
Cglib是一个优秀的动态代理框架,它的底层使用ASM在内存中动态的生成被代理类的子类,使用CGLIB即使代理类没有实现任何接口也可以实现动态代理功能。CGLIB具有简单易用,它的运行速度要远远快于JDK的Proxy动态代理:
cglib有两种可选方式,继承和引用
。第一种是基于继承实现的动态代理,所以可以直接通过super调用target方法,但是这种方式在spring中是不支持的,因为这样的话,这个target对象就不能被spring所管理,所以cglib还是才用类似jdk的方式,通过持有target对象来达到拦截方法的效果。
CGLIB的核心类:
- net.sf.cglib.proxy.Enhancer – 主要的增强类
- net.sf.cglib.proxy.MethodInterceptor – 主要的方法拦截类,它是Callback接口的子接口,需要用户自定义实现
- net.sf.cglib.proxy.MethodProxy – JDK的java.lang.reflect.Method类的代理类,可以方便的实现对源对象方法的调用,如Object o = methodProxy.invokeSuper(proxy, args);//虽然第一个参数是被代理对象,也不会出现死循环的问题。
/**
*用户管理实现类
*/
public class UserManagerImpl {
public void findUser() {
System.out.println("*****UserManagerImpl实现类执行*******");
}
}
public class UserMangerProxy implements MethodInterceptor {
/**
* @param obj 代理对象
* @param method 委托类方法
* @param args 方法参数
* @param proxy 代理方法的MethodProxy对象
* @return
* @throws Throwable
*/
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("Before:" + method);
Object object = proxy.invokeSuper(obj, args);
System.out.println("After:" + method);
return object;
}
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserManagerImpl.class);
enhancer.setCallback(new UserMangerProxy());
UserManagerImpl userMangerProxy = (UserManagerImpl)enhancer.create();
userMangerProxy.findUser();
}
}
代理方式 | 实现 | 优点 | 缺点 | 特点 |
---|---|---|---|---|
静态代理 | 代理类与委托类实现同一接口,并且在代理类中需要硬编码接口 | 实现简单,容易理解 | 代理类需要硬编码接口,在实际应用中可能会导致重复编码,浪费存储空间并且效率很低 | |
JDK动态代理 | 代理类与委托类实现同一接口,主要是通过代理类实现InvocationHandler并重写invoke方法来进行动态代理的,在invoke方法中将对方法进行增强处理 | 不需要硬编码接口,代码复用率高 | 只能够代理实现了接口的委托类 | 底层使用反射机制 进行方法的调用 |
CGLIB动态代理 | 代理类将委托类作为自己的父类并为其中的非final 委托方法创建两个方法 ,一个是与委托方法签名相同的方法,它在方法中会通过super调用委托方法;另一个是代理类独有的方法。在代理方法中,它会判断是否存在实现了MethodInterceptor接口的对象,若存在则将调用intercept方法对委托方法进行代理 | 可以在运行时对类或者是接口进行增强操作,且委托类无需实现接口 | 不能对final类以及final方法进行代理 | 底层将方法全部存入一个数组中,通过数组索引 直接进行方法调用 |
反射机制在生成类的过程中比较高效,而asm在生成类之后的相关执行过程中比较高效(可以通过将asm生成的类进行缓存,这样解决asm生成类过程低效问题)
文章参考地址:
https://blog.youkuaiyun.com/heyutao007/article/details/49738887
CGLIB动态代理反编译源码:
https://www.jianshu.com/p/13aa63e1ac95
import net.sf.cglib.core.Signature;
import net.sf.cglib.core.ReflectUtils;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.Factory;
//
// Decompiled by Procyon v0.5.30
//
public class UserService$$EnhancerByCGLIB$$394dddeb extends UserService implements Factory
{
private boolean CGLIB$BOUND;
private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
private static final Callback[] CGLIB$STATIC_CALLBACKS;
private MethodInterceptor CGLIB$CALLBACK_0;
private static final Method CGLIB$add$0$Method;
private static final MethodProxy CGLIB$add$0$Proxy;
private static final Object[] CGLIB$emptyArgs;
static void CGLIB$STATICHOOK2() {
CGLIB$THREAD_CALLBACKS = new ThreadLocal();
CGLIB$emptyArgs = new Object[0];
final Class<?> forName = Class.forName("UserService$$EnhancerByCGLIB$$394dddeb");
final Class<?> forName3;
CGLIB$add$0$Method = ReflectUtils.findMethods(new String[] { "add", "()V" }, (forName3 = Class.forName("UserService")).getDeclaredMethods())[0];
CGLIB$add$0$Proxy = MethodProxy.create((Class)forName3, (Class)forName, "()V", "add", "CGLIB$add$0");
}
final void CGLIB$add$0() {
super.add();
}
public final void add() {
MethodInterceptor cglib$CALLBACK_2;
MethodInterceptor cglib$CALLBACK_0;
if ((cglib$CALLBACK_0 = (cglib$CALLBACK_2 = this.CGLIB$CALLBACK_0)) == null) {
CGLIB$BIND_CALLBACKS(this);
cglib$CALLBACK_2 = (cglib$CALLBACK_0 = this.CGLIB$CALLBACK_0);
}
if (cglib$CALLBACK_0 != null) {
cglib$CALLBACK_2.intercept((Object)this, UserService$$EnhancerByCGLIB$$394dddeb.CGLIB$add$0$Method, UserService$$EnhancerByCGLIB$$394dddeb.CGLIB$emptyArgs, UserService$$EnhancerByCGLIB$$394dddeb.CGLIB$add$0$Proxy);
return;
}
super.add();
}
static {
CGLIB$STATICHOOK2();
}
}