public class PersonServiceImpl implements PersonService {
@Log
@Override
public String get() {
System.out.println("execute get method");
return "Sayi";
}
}
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
Class<?> targetClass = config.getTargetClass();
if (targetClass == null) {
throw new AopConfigException("TargetSource cannot determine target class: " +
"Either an interface or a target is required for proxy creation.");
}
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
return new ObjenesisCglibAopProxy(config);
}
else {
return new JdkDynamicAopProxy(config);
}
}
public Object proceed() throws Throwable {
// We start with an index of -1 and increment early.
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
return invokeJoinpoint();
}
Object interceptorOrInterceptionAdvice =
this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
// Evaluate dynamic method matcher here: static part will already have
// been evaluated and found to match.
InterceptorAndDynamicMethodMatcher dm =
(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
return dm.interceptor.invoke(this);
}
else {
// Dynamic matching failed.
// Skip this interceptor and invoke the next in the chain.
return proceed();
}
}
else {
// It's an interceptor, so we just invoke it: The pointcut will have
// been evaluated statically before this object was constructed.
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}
public ConstructionProxy<T> create() throws ErrorsException {
if (interceptors.isEmpty()) {
return new DefaultConstructionProxyFactory<T>(injectionPoint).create();
}
@SuppressWarnings("unchecked")
Class<? extends Callback>[] callbackTypes = new Class[callbacks.length];
for (int i = 0; i < callbacks.length; i++) {
if (callbacks[i] == net.sf.cglib.proxy.NoOp.INSTANCE) {
callbackTypes[i] = net.sf.cglib.proxy.NoOp.class;
} else {
callbackTypes[i] = net.sf.cglib.proxy.MethodInterceptor.class;
}
}
// Create the proxied class. We're careful to ensure that all enhancer state is not-specific
// to this injector. Otherwise, the proxies for each injector will waste PermGen memory
try {
Enhancer enhancer = BytecodeGen.newEnhancer(declaringClass, visibility);
enhancer.setCallbackFilter(new IndicesCallbackFilter(methods));
enhancer.setCallbackTypes(callbackTypes);
return new ProxyConstructor<T>(enhancer, injectionPoint, callbacks, interceptors);
} catch (Throwable e) {
throw new Errors().errorEnhancingClass(declaringClass, e).toException();
}
}
AOP是依赖注入框架的一个完善,本文将会对Spring和Guice的AOP部分进行详细分析。
Spring AOP
我们直接看看AOP的使用示例。
public class PersonServiceImpl implements PersonService { @Log @Override public String get() { System.out.println("execute get method"); return "Sayi"; } }
import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component;
@Aspect public class LogAspect {
@Around("@annotation(com.deepoove.diexample.annotation.Log)") public Object doLog(ProceedingJoinPoint pjp) throws Throwable { System.out.println(System.currentTimeMillis() + " Log Aspect before"); Object obj = pjp.proceed(); System.out.println(System.currentTimeMillis() + " Log Aspect after"); return obj; }
}
一切完毕,可以写个单元测试检验下:
AOP源码解析
在《依赖注入(二)Spring Dependency injection》文章中详细说明过Bean的初始化过程,我们知道AOP其实是Spring的一个扩展,而BeanPostProcessor的设计为实现这个扩展提供了便捷。关于如何扫描XML配置,如何解析@Around注解生成拦截器Advice这里不作介绍,我们直接看为Bean生成代理的源码AbstractAutoProxyCreator,处理链为先调用postProcessAfterInitialization方法再调用wrapIfNecessary方法,wrapIfNecessary方法核心代码如下:
我们对上面代码做个简单解释:
如果拦截器不为NULL,则会创建代理
可以从源码看出来,生成代理的核心类为ProxyFactory,接下来会详细阐述它的细节。
ProxyFatory编程
Spring为代理模式提供了一个工厂类ProxyFatory,支持对象的DK动态代理和Cglib代理。如果目标对象至少实现了一个接口,那么优先使用JDK动态代理所有接口,否则会使用Cglib,如果需要强制使用Cglib,可以通过配置实现:
我们先来看看如何使用ProxyFatory编程:
ProxyFactory构造器支持传入Object对象或者接口Class,通过addAdvice方法增加拦截器org.aopalliance.intercept.Interceptor的实现,AopProxy接口定义了获取代理类的方法,获取AopProxy实例的源码在DefaultAopProxyFactory中,如下:
AopProxy有两个实现,分别是JdkDynamicAopProxy和CglibAopProxy,我们可以实现接口,实现自己的代理策略。
深入JdkDynamicAopProxy的实现,我们发现Spring也是通过实现AOP盟约的MethodInvocation,完成对拦截器链的调用,具体实现类是ReflectiveMethodInvocation,构造完ReflectiveMethodInvocation后,通过其核心方法递归遍历拦截器:
通过源码我们发现,所有JDK动态代理的对象除了代理对象接口外,还实现了接口: SpringProxy.class、Advised.class和DecoratingProxy.class,我们可以利用这一特性,获得更多的代理信息。
ProxyFatory应用之Spring-remoting
RPC框架客户端依赖服务接口,调用远程服务实现,HttpInvoker是Spring的一个基于HTTP协议和Java序列化的远程调用框架。参见《写一个极简的RPC和Hessian的设计 》
Spring Http Invoker客户端的实现原理是基于动态代理,把接口的调用代理至HTTP服务,同时利用了FactoryBean的扩展技术。
AOP应用
Spring中AOP的应用还有很多,比如@Transcation、@Cache等,后面有时间会作为一个主题单独分析。
Guice AOP
Guice AOP的设计相对比较简单:切入点匹配和拦截器绑定。 我们先看一个示例,首先在Module中指定切入点和拦截器(这里采用了官方文档的代码,Matchers类用来生成匹配逻辑或者不匹配逻辑),所有使用了注解NotOnWeekends的方法都将会被WeekendBlocker拦截。
如果有某个Bean的方法加上了注解NotOnWeekends,那么在周末执行的时候就会抛错。
源码解析
我们直接看
ConstructorBindingImpl
代码来分析如何实现Bean的生成,Guice是在构造Bean的时候直接生成代理的,com.google.inject.internal.ConstructorBindingImpl.initialize方法初始化了ConstructorInjector对象,这个对象包含了ConstructionProxy对象,而ConstructionProxy对象是由内部隐藏的一个代理工厂com.google.inject.internal.ProxyFactory<T>
类生成的。ConstructionProxy有三个实现FastClassProxy、ReflectiveProxy和ProxyConstructor,在有拦截器时,采用ProxyConstructor实例化对象。
总结
Spring很好的利用扩展机制实现了AOP,Guice采用了简单优雅的方式使用AOP。
在研究实现代理模式的源码,我们发现基本上所有的框架都会有一个类
ProxyFactory
,它隐藏了JDK动态代理和Cglib动态代理的实现,对外提供一个代理对象。