Spring AOP 动态代理

一、静态代理与动态代理。

1、代理的优点

① 可实现日志或事物控制等与业务的解耦。

② 在不修改源码的前提下对方法进行增强,即在方法执行前后添加一些操作。

2、代理的原理和特征

① 原理:用一个代理将原始对象包装起来,然后使用该代理对象代替原始对象。此时任何对原始对象的调用都要经过代理对象,由代理对象决定是否且何时转到原始对象上。

② 特征:代理的委托类和代理类具有相同的接口,代理类主要负责为委托类进行消息预处理、消息转发和消息传递后处理等操作,且代理类对象本身不实现服务,而是调用委托类的对象方法来实现。

③ 分类:根据代理类的创建时期,可将其分为静态代理和动态代理两种模式。

3、静态代理

① 代理类在程序运行前就已存在。

② 静态代理事先知道要代理的是什么;通常只代理一个类。

4、动态代理

① 在程序运行时, 由反射机制动态创建代理类。

② 事先不知道要代理的是什么,只有在运行时才知道;通常代理一个接口下面的多个类。

③ 实现动态代理的方法:

| 实现JDK中InvocationHandler接口的invoke方法,由于代理的是接口,所以业务类需要实现接口,通过proxy的newProxyInstance方法来获取代理对象。

| 通过CGLIB实现动态代理,CGLIB代理的是类,不需业务类实现接口,通过派生子类实现代理,且在运行时动态修改字节码来修改类。


二、静态代理和动态代理的实现。

静态代理的实现

定义接口:

package DynamicProxy;

public interface Subject {
	public void helloWorld();
}

接口实现类:

package DynamicProxy;

public class RealSubject implements Subject {
	@Override
	public void helloWorld() {
		System.out.println("Hello My World!");
	}
}

静态代理实现方法增强:

package DynamicProxy;

public class StaticProxy implements Subject{
	// 代理的目标对象
	private Subject subject;
	
	public StaticProxy(Subject subject) {
		this.subject = subject;
	}
	
	@Override
	public void helloWorld() {
		System.out.println("---Before---");
		subject.helloWorld();
		System.out.println("---After---");
	}
}

测试类:

package DynamicProxy;

public class StaticClient {
	public static void main(String[] args) {
		// 创建需要被代理的目标对象
		Subject realSubject = new RealSubject();
		StaticProxy staticProxy = new StaticProxy(realSubject);
		staticProxy.helloWorld();
		// 查看staticProxy对象的类型
		System.out.println(staticProxy.getClass().getName());
	}
}

控制台输出:

---Before---
Hello My World!
---After---
DynamicProxy.StaticProxy

若此接口有多个方法,且每个方法前后都需要进行相同内容增强,则使用静态代理需要每个都写一遍,此时使用动态代理更为方便简洁。


JDK实现动态代理

接口和接口实现类同静态代理。

动态代理类【实现InvocationHandler接口】:

package DynamicProxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class DynamicProxy implements InvocationHandler{
	// 代理的目标对象
	private Object subject;						
	
	// 构造方法,为目标对象赋值
	public DynamicProxy(Object subject) {		
		this.subject = subject;
	}
	
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		// 代理目标对象前,可以加一些其它操作,即在不改变原方法的基础上增强原方法
		System.out.println("Hello DynamicProxy!");
		// 打印method方法
		System.out.println("Method:"+method);
		// 代理对象通过自动跳转到其关联的Handler对象的invoke方法来调用目标对象方法
		Object object = method.invoke(subject, args);
		// 代理目标对象后,也可以加一些其它操作,即在不改变原方法的基础上增强原方法
		System.out.println("Bye DynamicProxy!");
		return object;
	}
}

测试类Client:

package DynamicProxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

public class Client {
	public static void main(String[] args) {
		// 创建需要被代理的目标对象
		Subject realSubject = new RealSubject();
		// 将其传到动态代理实现类中
		InvocationHandler handler = new DynamicProxy(realSubject);
		// 调用DynamicProxy中的invoke方法
		Subject subject = (Subject)Proxy.newProxyInstance(RealSubject.class.getClassLoader(),RealSubject.class.getInterfaces(),handler);
		subject.helloWorld();
		// 查看subject对象的类型
		System.out.println(subject.getClass().getName());
	}
}

控制台输出:

Hello DynamicProxy!
Method:public abstract void DynamicProxy.Subject.helloWorld()
Hello My World!
Bye DynamicProxy!
com.sun.proxy.$Proxy0

由控制台输出结果可知,当代理对象调用目标对象的方法时,会自动跳转到代理对象关联的handler对象的invoke方法来调用。

其中,newProxyInstance方法参数及返回值如下:

Object java.lang.reflect.Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException

类加载器loader为目标对象的类加载器,且代理类和目标对象都继承同一接口,最后需传递InvocationHandler使代理类能对目标对象进行调用。

AOP面向切面编程使用切面对关注点进行模块化,通过AOP配置XML,使com.aop.HelloWorld类下面的所有方法运行前都执行showTime()函数,运行后执行showLog()函数【都在切面timeHandler类中定义】,即对多个方法做预处理和后续的处理。

<aop:config>  
    <aop:aspect id="time" ref="timeHandler">  
        <aop:pointcut id="addAllMethod" expression="execution(* com.aop.HelloWorld.*(..))" />  
        <aop:before method="showTime" pointcut-ref="addAllMethod" />  
        <aop:after method="showLog" pointcut-ref="addAllMethod" />  
    </aop:aspect>  
</aop:config>


CGLIB动态代理【转】

由于JDK动态代理只能代理实现了接口的目标对象,且基于反射效率相对较低,于是出现了基于字节码实现的CGLIB(Code Generation Library)动态代理技术,大大扩展了试用范围而且效率相对JDK更高,但由于CGLIB动态代理需要生成目标对象的子类,所以CGLIB不能代理final方法。

具体参考https://blog.csdn.net/danchu/article/details/70238002。


猜你喜欢

转载自blog.csdn.net/a515557595_xzb/article/details/80069003