1. JDK代理和Cglib代理两种方式的区别
JDK
动态代理使用的是运行期间使用反射
CGlib
使用的是动态生成class
文件字节码,为原先的类生成子类class
文件,运行的时候使用相应的子类。
传统的代理模式缺陷和动态代理的优势
代理模式是两个类同时实现一个接口,一个类持有另一个类的实例,然后自定义逻辑以增强。
为什么在AOP
中不使用传统的代理模式,是因为传统的代理模式,针对同样一个接口中同样的一个方法,需要写不同的类来进行代理,会增加代码的复杂度。(针对与同一个JoinPoint
,要写不同的逻辑来实现)(《Spring揭秘》p138-p139)
JDK代理标准实现
实现InvocationHandler
接口,Proxy.newInstance
生成
public class RequestCtrlInvocationHandler implements InvocationHandler {
private Object target;
public RequestCtrlInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().equals("request")) {
System.out.println("invoke method");
return method.invoke(target, args);
}
return null;
}
}
RequestAble requestAble = (RequestAble) Proxy.newProxyInstance(
JDKProxy.class.getClassLoader(),
new Class[]{RequestAble.class},
new RequestCtrlInvocationHandler(new RequestImpl())
);
上述的这种方式又被成为Around Advice
,可以在方法调用之前和调用之后分别使用插入不同的逻辑。
CgLib代理标准实现
实现MethodInterceptor
接口,Enhancer
实现
public class RequestCtrlCallback implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println(method);
return proxy.invokeSuper(obj, args);
}
}
public class Main {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(RequestAble.class);
enhancer.setCallback(new RequestCtrlCallback());
RequestAble proxy = (RequestAble) enhancer.create();
proxy.request();
}
}
生成的方式不同,Cglib
可以对任意类进行增强和代理,但是JDK
动态代理的Proxy.newInstance
方法的存在只能对实现了某一接口的类实现动态代理。
两种实现方式中,拦截方法的第一个参数的含义
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
官方文档的解释是
proxy the proxy instance that the method was invoked on
我的理解是生成的那个代理对象。具体的用途是链式调用。
https://stackoverflow.com/questions/22930195/understanding-proxy-arguments-of-the-invoke-method-of-java-lang-reflect-invoca
proxy
对象是com.sun.proxy.$Proxy0
链式调用的例子:
需要被代理的对象:
public class RequestImpl implements RequestAble {
@Override
public RequestAble request() {
System.out.println("invoke request");
return this;
}
}
链式调用方法:
public static void main(String[] args) {
RequestAble requestAble = (RequestAble) Proxy.newProxyInstance(
JDKProxy.class.getClassLoader(),
new Class[]{RequestAble.class},
new RequestCtrlInvocationHandler(new RequestImpl())
);
requestAble.request().request();
}
如果要对requestAble.request()
第一次调用拦截和第二次调用拦截,就需要返回这个Proxy
对象。
如果使用这种方法:
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().equals("request")) {
System.out.println("invoke method");
return method.invoke(target, args);
}
return null;
}
那么requestAble.request()
返回值是requestImpl
中的this
,即requestImpl
对象本身,这个对象是原始对象,不包括任何横切逻辑。即不会被代理。即跟以下方式一样。
public class RequestCtrlInvocationHandler implements InvocationHandler { private Object target; public RequestCtrlInvocationHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (method.getName().equals("request")) { System.out.println("invoke method"); method.invoke(target, args); return target; } return null; }}
如果return proxy
的话,第二次调用的时候使用的就是被代理的对象,就会执行代理逻辑。
2. AOP基础概念
AOP
全名为Aspect Oriented Programming
,如果说传统的开发方式是由上到下的,由抽象到具体的化,AOP
则是在代码中插入横切逻辑,即对代码进行水平扩展。即诞生了几个基本概念。
JoinPoint
JoinPoint
描述的是在何处插入横切逻辑,比如以下情况:
- 构造方法之前
- 某set,get方法之前或者之后
- 某方法执行之前
即插入横切逻辑的位置
PointCut
PointCut
则是JoinPoint
的表述方式。Spring
中表示的接口如下:
public interface Pointcut { ClassFilter getClassFilter(); MethodMatcher getMethodMatcher();}
大概含义就是满足了某Class
要求,并且满足方法限定的要求的JoinPoint
Advice
有了上面的描述之后,Advice
就是在这些个JoinPoint
处需要做的事情,分为以下几种:
before advice
:在JoinPoint
位置之前执行的类型after advice
:在JointPoint
位置之后执行的advice
类型around advice
:能环绕执行。上述代理方式中的JDK
动态代理和CGlib
动态代理,都可以看作是around advice
Aspect
将PointCut
和Advice
这两个独立的概念封装在一起,Aspect
可以直接表述在某一个JoinPoint
执行的逻辑。
3. 两个MethodInterceptor
org.aopalliance.intercept.MethodInterceptor
public interface MethodInterceptor extends Interceptor { /** * Implement this method to perform extra treatments before and * after the invocation. Polite implementations would certainly * like to invoke {@link Joinpoint#proceed()}. * * @param invocation the method invocation joinpoint * @return the result of the call to {@link * Joinpoint#proceed()}, might be intercepted by the * interceptor. * * @throws Throwable if the interceptors or the * target-object throws an exception. */ Object invoke(MethodInvocation invocation) throws Throwable;}
这个MethodInterceptror
是来自AOP
联盟,是Java
自带的一个接口。
MethodInvocation
的含义是可以表示方法的调用逻辑,可以获取到类,主要目的是使用MethodInvocation.proceed()
方法,让被拦截的方法继续执行。
MethodInvocation
的一个实例
public class ReflectiveMethodInvocation implements MethodInvocation { protected final Object target; protected final Method method; protected final Object[] arguments; public ReflectiveMethodInvocation(Object target, Method method, Object[] arguments) { this.target = target; this.method = method; this.arguments = arguments; } @Override public Method getMethod() { return this.method; } @Override public Object[] getArguments() { return this.arguments; } @Override public Object proceed() throws Throwable { return this.method.invoke(target, arguments); } @Override public Object getThis() { return target; } @Override public AccessibleObject getStaticPart() { return method; }}
net.sf.cglib.proxy.MethodInterceptor
public interface MethodInterceptorextends Callback{ /** * All generated proxied methods call this method instead of the original method. * The original method may either be invoked by normal reflection using the Method object, * or by using the MethodProxy (faster). * @param obj "this", the enhanced object * @param method intercepted Method * @param args argument array; primitive types are wrapped * @param proxy used to invoke super (non-intercepted method); may be called * as many times as needed * @throws Throwable any exception may be thrown; if so, super method will not be invoked * @return any value compatible with the signature of the proxied method. Method returning void will ignore this value. * @see MethodProxy */ public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args, MethodProxy proxy) throws Throwable;}
这个接口是为了CGlib
代理。和上面的不一样,注意区分。
4. AOP实现
第一步实现
public void testDynamicProxy() { IUserService userService = new UserService(); TargetSource source = new TargetSource(userService); AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut("execution(* com.csu.springframework.test.aop.IUserService.queryUserInfo(..))"); AdvisedSupport advisedSupport = new AdvisedSupport(); advisedSupport.setTargetSource(source); advisedSupport.setMethodMatcher(pointcut); advisedSupport.setMethodInterceptor(new UserServiceInterceptor()); IUserService proxyJDK = (IUserService) new JDKDynamicAopProxy(advisedSupport).getProxy(); proxyJDK.queryUserInfo(); proxyJDK.register("chenyu"); IUserService proxyCGLIB= (IUserService) new Cglib2AopProxy(advisedSupport).getProxy(); proxyCGLIB.queryUserInfo(); proxyCGLIB.register("chenyu");}
第二步实现
将上面的步骤整合到Spring
周期中并且支持beforeAdvice
。
AOP
的实现本质上是在初始化bean
之前,判断该类是否为需要代理的类。(PointCut
过滤)
其实也就是那么将这么几步融合在一起:
- 创建
PointCut
: 控制切面,在哪里插入切面 - 创建
AdvisorSupport
:advisor
中主要管在切面出执行什么逻辑 - 然后根据
AdvisorSupport
:创建代理对象 - 将上面的三部分集成到一个
PostProcessor
中,只不过这个PostProcessor
需要重新创建一个,要在对象初始化之前执行。
最后的实现效果就是,只需要继承beforeAdvice
,就可以实现相应对象的动态代理。
5. 总结
设计模式
这部分的设计模式主要涉及到的是代理模式,但是由于传统的代理模式的扩展性问题的存在,使用了JDK
和CGlib
动态代理。
其他
剩下的部分就是根据AOP
的基础概念,将整个AOP
的概念扩展到Spring
框架中并与Spring
集成。