AOP的动态代理原理

一直好奇~所以看了一些博客~总结一下:

https://blog.csdn.net/dreamrealised/article/details/12885739


AOP的源码中用到了两种动态代理来实现拦截切入功能:jdk动态代理和cglib动态代理。两种方法同时存在,各有优劣。

jdk动态代理是由java内部的反射机制来实现的,cglib动态代理底层则是借助asm来实现的。

总的来说,反射机制在生成类的过程中比较高效,而asm在生成类之后的相关执行过程中比较高效(可以通过将asm生成的类进行缓存,这样解决asm生成类过程低效问题)。

还有一点必须注意:jdk动态代理的应用前提,必须是目标类基于统一的接口。如果没有上述前提,jdk动态代理不能应用。由此可以看出,jdk动态代理有一定的局限性,cglib这种第三方类库实现的动态代理应用更加广泛,且在效率上更有优势。

Jdk动态代理

        

JavaJDK1.3后引入的动态代理机制,使我们可以在运行期动态的创建代理类

使用动态代理实现AOP需要有四个角色:被代理的类,被代理类的接口,织入器,和InvocationHandler,而织入器使用接口反射机制生成一个代理类,然后在这个代理类中织入代码。被代理的类是AOP里所说的目标,InvocationHandler是切面,它包含了AdvicePointcut

动态代理的核心其实就是代理对象的生成,即Proxy.newProxyInstance(classLoader, proxyInterface, handler)

动态代理在运行期通过接口动态生成代理类,这为其带来了一定的灵活性,但这个灵活性却带来了两个问题

第一代理类必须实现一个接口,如果没实现接口会抛出一个异常。

第二性能影响,因为动态代理使用反射的机制实现的,首先反射肯定比直接调用要慢,经过测试大概每个代理类比静态代理多出10几毫秒的消耗。其次 使用反射大量生成类文件可能引起Full GC造成性能影响,因为字节码文件加载后会存放在JVM运行时区的方法区(或者叫持久代)中,当方法区满的时候,会引起Full GC,所以当你大量使用动态代理时,可以将持久代设置大一些,减少Full GC次数。

一、jdk动态代理实现AOP拦截

1、为目标类(target)定义统一的接口类Service,这个是jdk动态代理必须的前提。

package jdkproxy;
 
/**
 * 该类是所有被代理类的接口类,jdk实现的代理要求被代理类基于统一的接口
 */
public interface Service {
	/**
	 * add方法
	 */
	public void add();
 
	/**
	 * update方法
	 */
	public void update();
}

2、目标类AService,我们的实验目标就是在AService中add和update方法的前后实现拦截,加入自定义切面逻辑advise

package jdkproxy;
 
/**
 * 被代理类,即目标类target
 */
public class AService implements Service {
	/*
	 * (non-Javadoc)
	 * 
	 * @see jdkproxy.Service#add()
	 */
	public void add() {
		System.out.println("AService add>>>>>>>>>>>>>>>>>>");
	}
 
	/*
	 * (non-Javadoc)
	 * 
	 * @see jdkproxy.Service#update()
	 */
	public void update() {
		System.out.println("AService update>>>>>>>>>>>>>>>");
	}
}

3、实现动态代理类MyInvocationHandler,实现InvocationHandler接口,并且实现接口中的invoke方法。仔细看invoke方法,就是在该方法中加入切面逻辑的。目标类方法的执行是由mehod.invoke(target,args)这条语句完成。

package jdkproxy;
 
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
 public class MyInvocationHandler implements InvocationHandler {
	private Object target;
 
	MyInvocationHandler() {
		super();
	}
 
	MyInvocationHandler(Object target) {
		super();
		this.target = target;
	}
 
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		// 程序执行前加入逻辑,MethodBeforeAdviceInterceptor
		System.out.println("before-----------------------------");
		// 程序执行
		Object result = method.invoke(target, args);
		// 程序执行后加入逻辑,MethodAfterAdviceInterceptor
		System.out.println("after------------------------------");
		return result;
	}
 
}

4、测试类,其中增强的目标对象是由Proxy.newProxyInstance(aService.getClass().getClassLoader(), aService.getClass().getInterfaces(), handler);来生成的。

package jdkproxy;
 
import java.lang.reflect.Proxy;

public class Test {
	public static void main(String[] args) {
		Service aService = new AService();
		MyInvocationHandler handler = new MyInvocationHandler(aService);
		// Proxy为InvocationHandler实现类动态创建一个符合某一接口的代理实例
		Service aServiceProxy = (Service) Proxy.newProxyInstance(aService
				.getClass().getClassLoader(), aService.getClass()
				.getInterfaces(), handler);
		// 由动态生成的代理对象来aServiceProxy 代理执行程序,其中aServiceProxy 符合Service接口
		aServiceProxy.add();
		System.out.println();
		aServiceProxy.update();
		// 以下是对B的代理
		// Service bService = new BService();
		// MyInvocationHandler handler = new MyInvocationHandler(bService);
		// Service bServiceProxy = (Service) Proxy.newProxyInstance(bService
		// .getClass().getClassLoader(), bService.getClass()
		// .getInterfaces(), handler);
		// bServiceProxy.add();
		// System.out.println();
		// bServiceProxy.update();
	}
}

自此,jdk动态代理来实现AOP拦截机制的代码已经实现,下面我们看一下拦截的结果,程序输出结果如下:
before—————————–
AService add>>>>>>>>>>>>>>>>>>
after——————————
before—————————–
AService update>>>>>>>>>>>>>>>
after——————————

另一个例子:

https://blog.csdn.net/fuzhongmin05/article/details/61615149

    public interface IHello {
        /**
         * 业务处理A方法
         * @param name
         */
        void sayHello(String name);
        /**
         * 业务处理B方法
         * @param name
         */
        void sayGoogBye(String name);
    

        Hello.java代码如下:

    public class Hello implements IHello {
        public void sayHello(String name) {
            System.out.println("Hello " + name);
        }
        public void sayGoogBye(String name) {
            System.out.println(name+" GoodBye!");
        }
    }

        我们一样的去写一个代理类。只不过,让这个类去实现java.lang.reflect.InvocationHandler接口,代码如下:


    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    public class DynaProxyHello implements InvocationHandler {
        /**
         * 要处理的对象(也就是我们要在方法的前后加上业务逻辑的对象,如例子中的Hello)
         */
        private Object delegate;
        /**
         * 动态生成方法被处理过后的对象 (写法固定)
         * 
         * @param delegate
         * @param proxy
         * @return
         */
        public Object bind(Object delegate) {
            this.delegate = delegate;
            return Proxy.newProxyInstance(
                    this.delegate.getClass().getClassLoader(), this.delegate
                            .getClass().getInterfaces(), this);
        }
        /**
         * 要处理的对象中的每个方法会被此方法送去JVM调用,也就是说,要处理的对象的方法只能通过此方法调用
         * 此方法是动态的,不是手动调用的
         */
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            Object result = null;
            try {
                //执行原来的方法之前记录日志
                Logger.logging(Level.DEBUGE, method.getName() + " Method end .");
                
                //JVM通过这条语句执行原来的方法(反射机制)
                result = method.invoke(this.delegate, args);
                //执行原来的方法之后记录日志
                Logger.logging(Level.INFO, method.getName() + " Method Start!");
            } catch (Exception e) {
                e.printStackTrace();
            }
            //返回方法返回值给调用者
            return result;
        }
    

        上面类中出现的Logger类和Level枚举还是和上一上例子的实现是一样的,这里就不贴出代码了。


    public class Test {
        public static void main(String[] args) {
            IHello hello = (IHello)new DynaProxyHello().bind(new Hello());
            hello.sayGoogBye("Double J");
            hello.sayHello("Double J");
        }
    

        运行输出的结果如下:

  1. Tue Mar 04 21: 24: 03 CST 2008 sayGoogBye Method end .
  2. Double J GoodBye!
  3. 200834 21: 24: 03 sayGoogBye Method Start!
  4. Tue Mar 04 21: 24: 03 CST 2008 sayHello Method end .
  5. Hello Double J
  6. 200834 21: 24: 03 sayHello Method Start!


只要你是采用面向接口编程,那么,你的任何对象的方法执行之前要加上记录日志的操作都是可以的。

(DynaPoxyHello)自动去代理执行被代理对象(Hello)中的每一个方法,一个java.lang.reflect.InvocationHandler接口就把我们的代理对象和被代理对象解藕了。


二、cglib动态代理实现AOP拦截

1、目标类,cglib不需要定义目标类的统一接口

package cglibproxy;
 
/**
 * 被代理类,即目标对象target
 */
public class Base {
	/**
	 * 一个模拟的add方法
	 */
	public void add() {
		System.out.println("add ------------");
	}
}

2、实现动态代理类CglibProxy,需要实现MethodInterceptor接口,实现intercept方法。该代理中在add方法前后加入了自定义的切面逻辑,目标类add方法执行语句为proxy.invokeSuper(object, args);

package cglibproxy;
 
import java.lang.reflect.Method;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
 
/**
 * 此为代理类,用于在pointcut处添加advise
 */
public class CglibProxy implements MethodInterceptor {
 
	public Object intercept(Object object, Method method, Object[] args,
			MethodProxy proxy) throws Throwable {
		// 添加切面逻辑(advise),此处是在目标类代码执行之前,即为MethodBeforeAdviceInterceptor。
		System.out.println("before-------------");
		// 执行目标类add方法
		proxy.invokeSuper(object, args);
		// 添加切面逻辑(advise),此处是在目标类代码执行之后,即为MethodAfterAdviceInterceptor。
		System.out.println("after--------------");
		return null;
	}
 
}

3、获取增强的目标类的工厂Factory,其中增强的方法类对象是有Enhancer来实现的,

package cglibproxy;
 
import net.sf.cglib.proxy.Enhancer;
 
/**
 * 工厂类,生成增强过的目标类(已加入切入逻辑)
 */
public class Factory {
	/**
	 * 获得增强之后的目标类,即添加了切入逻辑advice之后的目标类
	 * 
	 * @param proxy
	 * @return
	 */
	public static Base getInstance(CglibProxy proxy) {
		Enhancer enhancer = new Enhancer();
		enhancer.setSuperclass(Base.class);
		//回调方法的参数为代理类对象CglibProxy,最后增强目标类调用的是代理类对象CglibProxy中的intercept方法
		enhancer.setCallback(proxy);
		// 此刻,base不是单纯的目标类,而是增强过的目标类
		Base base = (Base) enhancer.create();
		return base;
	}
}

4、测试类

package cglibproxy;
public class Test {
	public static void main(String[] args) {
		CglibProxy proxy = new CglibProxy();
		// base为生成的增强过的目标类
		Base base = Factory.getInstance(proxy);
		base.add();
	}
}

程序执行结果如下:

before————-
add ————
after————–

可以看到,在目标类Base的add方法前后已经加入了自定义的切面逻辑,AOP拦截机制生效了。


ps:cglib动态代理用到了第三方类库,需要在项目中引入两个jar包:cglib.jar和asm.jar。

    原文作者:AOP
    原文地址: https://blog.csdn.net/duoduo18up/article/details/81058477
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞