Spring中的AOP
Written by Tony Jiang @ 2012-1-18
(转)何为AOP
AOP,面向切面编程。
在不改动代码的前提下,灵活的在现有代码的执行顺序前后,添加进新规机能。
来一个简单的Sample:
目标类:
- package com.hyron.tony;
- public class CustomerService {
- private String name;
- private String url;
- public void setName(String name) {
- this.name = name;
- }
- public void setUrl(String url) {
- this.url = url;
- }
- public void printName() {
- System.out.println("Customer name : " + this.name);
- }
- public void printURL() {
- System.out.println("Customer website : " + this.url);
- }
- public void printThrowException() {
- throw new IllegalArgumentException();
- }
- }
advice:只以Around advice为例
- import java.util.Arrays;
- import org.aopalliance.intercept.MethodInterceptor;
- import org.aopalliance.intercept.MethodInvocation;
- public class HijackAroundMethod implements MethodInterceptor {
- @Override
- public Object invoke(MethodInvocation methodInvocation) throws Throwable {
- System.out.println("Method name : "
- + methodInvocation.getMethod().getName());
- System.out.println("Method arguments : "
- + Arrays.toString(methodInvocation.getArguments()));
- // same with MethodBeforeAdvice
- System.out.println("HijackAroundMethod : Before method hijacked!");
- try {
- // proceed to original method call
- Object result = methodInvocation.proceed();
- // same with AfterReturningAdvice
- System.out.println("HijackAroundMethod : Before after hijacked!");
- return result;
- } catch (IllegalArgumentException e) {
- // same with ThrowsAdvice
- System.out
- .println("HijackAroundMethod : Throw exception hijacked!");
- throw e;
- }
- }
- }
编织切入关系的配置文件:
- <bean id="customerService" class="com.mkyong.customer.services.CustomerService">
- <property name="name" value="Yong Mook Kim" />
- <property name="url" value="http://www.mkyong.com" />
- </bean>
- <bean id="hijackAroundMethodBean" class="com.mkyong.aop.HijackAroundMethod" />
- <bean id="customerServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
- <property name="target" ref="customerService" />
- <property name="interceptorNames">
- <list>
- <value>hijackAroundMethodBean</value>
- </list>
- </property>
- </bean>
Sample的启动:
- import org.springframework.context.ApplicationContext;
- import org.springframework.context.support.ClassPathXmlApplicationContext;
- import com.mkyong.customer.services.CustomerService;
- public class App {
- public static void main(String[] args) {
- ApplicationContext appContext = new ClassPathXmlApplicationContext(
- new String[] { "Spring-Customer.xml" });
- CustomerService cust = (CustomerService) appContext
- .getBean("customerServiceProxy");
- System.out.println("*************************");
- cust.printName();
- System.out.println("*************************");
- cust.printURL();
- System.out.println("*************************");
- try {
- cust.printThrowException();
- } catch (Exception e) {
- }
- }
- }
以上代码,用customerServiceProxy代理CustomerService的执行
在customerServiceProxy的配置中,定义了用hijackAroundMethodBean作为方法拦截器,在hijackAroundMethodBean中利用invoke方法,拦截住所有的方法调用,塞入自己的逻辑业务。
AOP的两种实现
上面看到的是Spring的Sample。
其实,Spring的AOP也是调用了其他开源技术实现。
比较常用的是JDK自己的Proxy,和开源的CGLIB
两者的区别,Proxy需要Advice必须从接口继承过来。如果切入的目标物是实体类,则无法使用。
CGLIB则可以用于直接覆盖实体类的方法。
Spring对以上两种都有支持。
Spring的底层实现
Spring在配置文件中,通过ProxyFactoryBean编织和实现了切面的构成。
我们在执行以下这行话的时候
CustomerService cust = (CustomerService) appContext
.getBean("customerServiceProxy");
其实是将动态对象的生成委托给了ProxyFactoryBean
当配置文件中 <bean>的class属性配置的实现类是FactoryBean时,通过getBean方法返回的不是FactoryBean本身,而是 FactoryBean#getObject()方法所返回的对象,相当于FactoryBean#getObject()代理了getBean()方 法。
执行顺序如下:
1. ProxyFactoryBean中的getObject
- /**
- * Return a proxy. Invoked when clients obtain beans from this factory bean.
- * Create an instance of the AOP proxy to be returned by this factory.
- * The instance will be cached for a singleton, and create on each call to
- * <code>getObject()</code> for a proxy.
- * @return a fresh AOP proxy reflecting the current state of this factory
- */
- public Object getObject() throws BeansException {
- initializeAdvisorChain();
- if (isSingleton()) {
- return getSingletonInstance();
- }
- else {
- if (this.targetName == null) {
- logger.warn("Using non-singleton proxies with singleton targets is often undesirable. " +
- "Enable prototype proxies by setting the 'targetName' property.");
- }
- return newPrototypeInstance();
- }
- }
2. ProxyFactoryBean中的initializeAdvisorChain
从配置文件中的advice list中取得interceptorNames,并将其加入advisorChain
- for (String name : this.interceptorNames) {
- if (logger.isTraceEnabled()) {
- logger.trace("Configuring advisor or advice '" + name + "'");
- }
- if (name.endsWith(GLOBAL_SUFFIX)) {
- if (!(this.beanFactory instanceof ListableBeanFactory)) {
- throw new AopConfigException(
- "Can only use global advisors or interceptors with a ListableBeanFactory");
- }
- addGlobalAdvisor((ListableBeanFactory) this.beanFactory,
- name.substring(0, name.length() - GLOBAL_SUFFIX.length()));
- }
3. 被切面改造过的instance的生成
我们以单例举例:
- /**
- * Return the singleton instance of this class's proxy object,
- * lazily creating it if it hasn't been created already.
- * @return the shared singleton proxy
- */
- private synchronized Object getSingletonInstance() {
- if (this.singletonInstance == null) {
- this.targetSource = freshTargetSource();
- if (this.autodetectInterfaces && getProxiedInterfaces().length == 0 && !isProxyTargetClass()) {
- // Rely on AOP infrastructure to tell us what interfaces to proxy.
- Class targetClass = getTargetClass();
- if (targetClass == null) {
- throw new FactoryBeanNotInitializedException("Cannot determine target class for proxy");
- }
- setInterfaces(ClassUtils.getAllInterfacesForClass(targetClass, this.proxyClassLoader));
- }
- // Initialize the shared singleton instance.
- super.setFrozen(this.freezeProxy);
- this.singletonInstance = getProxy(createAopProxy());
- }
- return this.singletonInstance;
- }
AopProxy是最终生成instance的地方,但是它是接口,和框架分离开来了
接口的生成在父类的ProxyCreatorSupport中
- /**
- * Subclasses should call this to get a new AOP proxy. They should <b>not</b>
- * create an AOP proxy with <code>this</code> as an argument.
- */
- protected final synchronized AopProxy createAopProxy() {
- if (!this.active) {
- activate();
- }
- return getAopProxyFactory().createAopProxy(this);
- }
- createAopProxy的实际运行代码:
- 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()) {
- return new JdkDynamicAopProxy(config);
- }
- if (!cglibAvailable) {
- throw new AopConfigException(
- "Cannot proxy target class because CGLIB2 is not available. " +
- "Add CGLIB to the class path or specify proxy interfaces.");
- }
- return CglibProxyFactory.createCglibProxy(config);
- }
- else {
- return new JdkDynamicAopProxy(config);
- }
- }
大家注意一下,这里根据targetClass的类型判断,采用JDK Proxy还是CGLIB模式生成动态对象
4. JDK Proxy的生成
如果大家使用过原生的JDK PROXY,下面的代码是在熟悉不过了
JdkDynamicAopProxy中实例化instance的
- public Object getProxy(ClassLoader classLoader) {
- if (logger.isDebugEnabled()) {
- logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());
- }
- Class[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised);
- findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
- return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
- }
JdkDynamicAopProxy中的invoke方法
- // Get the interception chain for this method.
- List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
- // Check whether we have any advice. If we don't, we can fallback on direct
- // reflective invocation of the target, and avoid creating a MethodInvocation.
- if (chain.isEmpty()) {
- // We can skip creating a MethodInvocation: just invoke the target directly
- // Note that the final invoker must be an InvokerInterceptor so we know it does
- // nothing but a reflective operation on the target, and no hot swapping or fancy proxying.
- retVal = AopUtils.invokeJoinpointUsingReflection(target, method, args);
- }
- else {
- // We need to create a method invocation...
- invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
- // Proceed to the joinpoint through the interceptor chain.
- retVal = invocation.proceed();
- }
Proceed的真实代码,把对象本身的invoke和拦截器的invoke交织在一起
- 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);
- }
- }
拦截器是如何编织的
我们在JdkDynamicAopProxy中的invoke方法中看到如下拦截器链条的生成
- // Get the interception chain for this method.
- List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
它对应的代码是DefaultAdvisorChainFactory中的getInterceptorsAndDynamicInterceptionAdvice方法
- AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
- for (Advisor advisor : config.getAdvisors()) {
- if (advisor instanceof PointcutAdvisor) {
- // Add it conditionally.
- PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
- if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(targetClass)) {
- MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
最终advisor的登记由DefaultAdvisorAdapterRegistry完成
我们可以看到DefaultAdvisorAdapterRegistry中首先登记了所有的AdviceAdapter
- /**
- * Create a new DefaultAdvisorAdapterRegistry, registering well-known adapters.
- */
- public DefaultAdvisorAdapterRegistry() {
- registerAdvisorAdapter(new MethodBeforeAdviceAdapter());
- registerAdvisorAdapter(new AfterReturningAdviceAdapter());
- registerAdvisorAdapter(new ThrowsAdviceAdapter());
- }
在如下代码中按照AdviceAdapter的类型塞入责任练中
- public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {
- List<MethodInterceptor> interceptors = new ArrayList<MethodInterceptor>(3);
- Advice advice = advisor.getAdvice();
- if (advice instanceof MethodInterceptor) {
- interceptors.add((MethodInterceptor) advice);
- }
- for (AdvisorAdapter adapter : this.adapters) {
- if (adapter.supportsAdvice(advice)) {
- interceptors.add(adapter.getInterceptor(advisor));
- }
- }
- if (interceptors.isEmpty()) {
- throw new UnknownAdviceTypeException(advisor.getAdvice());
- }
- return interceptors.toArray(new MethodInterceptor[interceptors.size()]);
- }
这种做法类似于:
1. 首先为切入Adivsor建立责任链模式
2. 其次将每个Adivsor委托给DefaultAdvisorAdapterRegistry登录
3. 在DefaultAdvisorAdapterRegistry中封装Adivsor到各个专门的Advisor适配器中
比如,AfterReturningAdviceAdapter的代码如下:
- /**
- * Adapter to enable {@link org.springframework.aop.AfterReturningAdvice}
- * to be used in the Spring AOP framework.
- *
- * @author Rod Johnson
- * @author Juergen Hoeller
- */
- class AfterReturningAdviceAdapter implements AdvisorAdapter, Serializable {
- public boolean supportsAdvice(Advice advice) {
- return (advice instanceof AfterReturningAdvice);
- }
- public MethodInterceptor getInterceptor(Advisor advisor) {
- AfterReturningAdvice advice = (AfterReturningAdvice) advisor.getAdvice();
- return new AfterReturningAdviceInterceptor(advice);
- }
- }
4. 在适配器中,将Advisor的方法和目标类方法交织在一起
- **
- * Interceptor to wrap am {@link org.springframework.aop.AfterReturningAdvice}.
- * Used internally by the AOP framework; application developers should not need
- * to use this class directly.
- *
- * @author Rod Johnson
- */
- public class AfterReturningAdviceInterceptor implements MethodInterceptor, AfterAdvice, Serializable {
- private final AfterReturningAdvice advice;
- /**
- * Create a new AfterReturningAdviceInterceptor for the given advice.
- * @param advice the AfterReturningAdvice to wrap
- */
- public AfterReturningAdviceInterceptor(AfterReturningAdvice advice) {
- Assert.notNull(advice, "Advice must not be null");
- this.advice = advice;
- }
- public Object invoke(MethodInvocation mi) throws Throwable {
- Object retVal = mi.proceed();
- this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());
- return retVal;
- }
- }