AOP和动态代理之间的关系

1.什么是AOP

AOP的全称是Aspect-Oriented Programming,即面向切面编程(也称面向方面编程)。它是面向对象编程(OOP)的一种补充,目前已成为一种比较成熟的编程方式。

在传统的业务处理代码中,通常都会进行事务处理、日志记录等操作。虽然使用OOP可以通过组合或者继承的方式来达到代码的重用,但如果要实现某个功能(如日志记录),同样的代码仍然会分散到各个方法中。这样,如果想要关闭某个功能,或者对其进行修改,就必须要修改所有的相关方法。这不但增加了开发人员的工作量,而且提高了代码的出错率。

为了解决这一问题,AOP思想随之产生。AOP采取横向抽取机制,将分散在各个方法中的重复代码提取出来,然后在程序编译或运行时,再将这些提取出来的代码应用到需要执行的地方。这种采用横向抽取机制的方式,采用传统的OOP思想显然是无法办到的,因为OOP只能实现父子关系的纵向的重用。虽然AOP是一种新的编程思想,但却不是OOP的替代品,它只是OOP的延伸和补充。

而Spring中的AOP是基于动态代理实现的,即 JDK动态代理和Cglib动态代理。

2.JDK动态代理

JDK动态代理是java.lang.reflect.*包提供的方式,它必须借助一个接口才能产生代理对象,所以在使用代理之前,先要有一个接口,然后定义一个类实现接口。

2.1 定义接口

interface HelloWorld {
	void sayHelloWorld();
	void sayGoodBye();
}

2.2 实现接口

class HelloWorldImpl implements HelloWorld {

	@Override
	public void sayHelloWorld() {
		// TODO Auto-generated method stub
		System.out.println("Hello World");
	}

	@Override
	public void sayGoodBye() {
		// TODO Auto-generated method stub
		System.out.println("GoodBye");
	}
}

2.3 动态代理绑定和代理逻辑实现

public class Demo01_Proxy implements InvocationHandler {
	//真实对象
	private Object target = null;
	
	/**
	 * 建立代理对象和真实对象的代理关系,并返回代理对象
	 */
	public Object bind(Object target) {
		this.target = target;
		return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
	}
	
	/**
	 * 代理方法逻辑
	 *  porxy 代理对象
	 *  method 当前调度方法
	 *  args 当前方法参数
	 */
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		System.out.println("进入代理逻辑方法");
		System.out.println("在调度真实对象之前的服务");
		Object obj = method.invoke(target, args); //相当于调用sayHelloWorld方法
		System.out.println("在调度真实对象之后的服务");
		return obj;
	}
	/**
	*测试代码
	*/
	@Test
	public void testJdkProxy() {
		Demo01_Proxy jdk = new Demo01_Proxy();
		//绑定关系,因为挂在HelloWorld接口下,所有声明代理对象HelloWorld proxy
		HelloWorld proxy = (HelloWorld) jdk.bind(new HelloWorldImpl());
		//注意,此时proxy已经是代理对象,他会进入代理的逻辑方法invoke中
		proxy.sayGoodBye();
	}
	
}

实现过程:第一步,建立代理对象和真实对象的关系,这里使用了bind方法去完成,方法中首先用类的属性target保存了真实对象,然后通过如下代码建立并生成代理对象并返回。

Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);

第二步,实现代理逻辑方法,invoke方法可以实现代理逻辑,当我们使用了代理对象调度方法后,它就会进入invoke方法里面。即测试代码的proxy.sayGoodBye();执行就会进入invoke方法中,实现在调用实现类的每个方法之前和之后都加上的相关操作,实现AOP思想。

3.CGLIB动态代理

JDK动态代理必须实现接口才能使用,在一些不能提供接口的环境中,只能采用其他第三方技术,即CGLIB动态代理,它的优势在于不用提供接口,只要一个非抽象类就可以实现动态代理。

3.1 定义一个类

/**
 * 被代理对象
 * @author Administrator
 *
 */
class Reflect {
	public void print(String name) {
		System.out.println("hello" + name);
	}
}

3.2 编写动态代理代码

public class Demo02_Proxy implements MethodInterceptor {
	
	/**
	 * 生成Cglib代理对象
	 * @param cls
	 * @return
	 */
	public Object getProxy(Class cls) {
		//enhancer 增强类对象
		Enhancer enhancer = new Enhancer();
		//设置增强类型
		enhancer.setSuperclass(cls);
		//定义代理逻辑对象为当前对象,要求当前对象实现MethodInterceptor方法
		enhancer.setCallback(this);
		//生成并返回代理对象
		return enhancer.create();
	}
	/**
	 * 代理逻辑方法
	 * @param proxy 代理对象
	 * @param method 方法
	 * @param args 方法参数
	 * @param methodProxy 方法代理
	 * @return 代理逻辑返回
	 * @throws Throwable
	 */
	@Override
	public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
		System.out.println("调用真实对象前");
		Object result = methodProxy.invokeSuper(proxy, args);
		System.out.println("调用真实对象后");
		return result;
	}
	/**
	*测试代码
	*/
	@Test
	public void testCglibProxy() {
		Demo02_Proxy proxy = new Demo02_Proxy();
		Reflect reflect = (Reflect) proxy.getProxy(Reflect.class);
		reflect.print("张三");
	}
}

这里使用了CGLIB的加强者Enhancer,通过设置超类的方法(setSuperClass),然后通过setCallback方法设置哪个类为它的代理类。其中,参数this就意味着当前对象,那就要求用this这个对象实现接口MethodInterceptor的方法-intercept,然后返回代理对象。那么此时当前类中的intercept方法就是其代理逻辑方法,在真实对象方法气候进行打印,是通过如下代码实现。

Object result = methodProxy.invoke(proxy, args);

最后关于Cglib导包问题做一下简述:asm.jar,cglib-2.1.3.jar(两包即可)

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