AOP含义
AOP即面向切面编程(Aspect Oriented Program),将那些与业务无关、但是却为业务模块所共同调用的逻辑(日志、事务、异常处理等)封装起来,便于减少重复代码,降低模块间耦合度,提升系统的可扩展性和可维护性。
AOP实现
AOP实现的核心是:在目标类的基础上增加切面逻辑,生成增强后的代理类。
AOP代理可以分为静态代理和动态代理。
- 静态代理在编译期就生成代理类。
- 动态代理则是在运行期生成代理类。
动态代理又可以分为两种主要实现方式:JDK动态代理和CGLIB动态代理。
- JDK动态代理是通过
Java反射机制来实现,需要目标类实现一个接口。 - CGLIB动态代理通过
修改字节码,来生成目标类的子类,通过继承的方式对目标类型进行增强。 Spring AOP默认采用JDK动态代理(要求目标类实现接口),当目标类未实现接口时会采用CGLIB动态代理。也可以在配置文件中指定使用CGLIB动态代理。
JDK动态代理实现示例
JDK动态代理的核心类是InvocationHandler和Proxy,实现主要步骤可以分为3步:
- 实现InvocationHandler接口,并在其中invoke方法实现对目标对象的代理;
- 使用Proxy.newProxyInstance()创建代理对象;
- 将代理对象转成接口类型,并调用其中方法。
(1)准备目标类和接口
创建接口:
public interface HelloService {
void sayHello();
}
创建目标类实现接口
public class HelloServiceImpl implements HelloService {
private Integer id = 0;
public HelloServiceImpl() {
}
public HelloServiceImpl(int id) {
this.id = id;
}
public void sayHello() {
System.out.println(id + " Hello, world!");
}
}
(2)实现InvocationHandler接口,并实现代理方法
要求构造器传入目标对象,并在invoke()方法中应该对目标对象方法进行增强。
public class JdkProxyFactory implements InvocationHandler {
private Object targetObject;
public JdkProxyFactory(Object targetObject) {
this.targetObject = targetObject;
}
// proxy表示当前代理对象,调用proxy的方法会造成无限循环代理,应该该调用原对象的方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Jdk proxy, 该方法被代理了,这里是前序工作");
Object result = method.invoke(targetObject, args);
// 调用代理对象方法是错误的
// Object result = method.invoke(proxy, args);
System.out.println("Jdk proxy, 该方法被代理了,这里是后序工作");
return result;
}
}
(3)使用Proxy创建代理对象,并调用方法
JdkProxyFactory jdkProxyFactory = new JdkProxyFactory(new HelloServiceImpl(3));
/** Proxy.newProxyInstance()需要传入3个参数。生成代理对象的过程也可以直接封装在调用处理实现类中。
* 参数1:类加载器,该参数与原对象、代理对象等都无关,如下甚至可以使用线程对象的类加载器,不过一般建议使用原对象的类加载器
* 参数2:代理类要实现的接口数组
* 参数3:InvocationHandler接口实现类对象
*/
HelloService helloService1 = (HelloService) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
new Class[] {HelloService.class}, jdkProxyFactory);
helloService1.sayHello();
上面示例代码中将创建代理对象的过程放在JdkProxyFactory类外部,也可以将该逻辑封装到JdkProxyFactory内部,调用方不关心创建代理对象内部逻辑。 在JdkProxyFactory中增加获取代理对象的方法:
public Object getProxyInstance() {
return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(), targetObject.getClass().getInterfaces(), this);
}
此时外部调用逻辑如下:
HelloService helloService2 = (HelloService) jdkProxyFactory.getProxyInstance();
helloService2.sayHello();
CGLIB动态代理实现
CGLIB动态代理实现核心类是MethodInterceptor和Enhancer,其实现步骤也可以分为主要3步:
- 实现MethodInterceptor接口,并在intercept()方法中实现对目标类的增强。
- 使用Enhancer创建代理对象。
- 将代理对象转成目标类型,并调用其中方法。
(0)引入相应库
可能需要引入Spring AOP相应maven库,这里由于直接创建了Springboot程序,省略这一步。
(1)实现MethodInterceptor接口,并实现代理方法
要求构造器传入目标对象,并在intercept()方法中对目标对象的方法进行增强。
public class CglibProxyFactory implements MethodInterceptor {
private Object targetObject;
public CglibProxyFactory(Object obj) {
this.targetObject = obj;
}
// object是被代理对象(子类对象),method是原方法,methodProxy是代理方法
@Override
public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("Cglib proxy, 该方法被代理了,这里是前序工作");
method.invoke(targetObject, args);
// 这是使用子类对象调用父类的方法,生成的代理对象其实是一个新的对象,没有原来对象的属性,所以这种方式是错误的
// methodProxy.invokeSuper(object, args);
System.out.println("Cglib proxy, 该方法被代理了,这里是后序工作");
return null;
}
}
(2)使用Enhancer创建代理对象,并调用方法
CglibProxyFactory cglibProxyFactory = new CglibProxyFactory(new HelloServiceImpl(3));
// cglib动态代理是生成了目标类的一个子类,所以Enhancer需要设置父类型,同时需要设置MethodInterceptor实现类对象
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(HelloServiceImpl.class);
enhancer.setCallback(cglibProxyFactory);
HelloServiceImpl helloService1 = (HelloServiceImpl) enhancer.create();
helloService1.sayHello();
同样上面示例代码中将创建代理对象的过程放在CglibProxyFactory类外部,也可以将该逻辑封装到CglibProxyFactory内部。 在CglibProxyFactory中增加获取代理对象的方法:
public Object getProxyInstance() {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(targetObject.getClass());
//回调方法的参数为代理类对象CglibProxy,最后增强目标类调用的是代理类对象CglibProxy中的intercept方法
enhancer.setCallback(this);
//增强后的目标类
Object proxyObj = enhancer.create();
// 返回代理对象
return proxyObj;
}
外部调用逻辑:
HelloServiceImpl helloService2 = (HelloServiceImpl) cglibProxyFactory.getProxyInstance();
helloService2.sayHello();
总结
Spring AOP实现的核心机制是动态代理,实现方式可以分为JDK动态代理和CGLIB动态代理。
- JDK动态代理基于反射,要求目标类必须实现接口;
- CGLIB动态代理基于继承,要求目标类不能被final修饰、可以被继承。