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(两包即可)