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()));
-
-
- System.out.println("HijackAroundMethod : Before method hijacked!");
-
- try {
-
- Object result = methodInvocation.proceed();
-
-
- System.out.println("HijackAroundMethod : Before after hijacked!");
-
- return result;
-
- } catch (IllegalArgumentException e) {
-
- 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
-
-
-
-
-
-
-
- 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的生成
我们以单例举例:
-
-
-
-
-
- private synchronized Object getSingletonInstance() {
- if (this.singletonInstance == null) {
- this.targetSource = freshTargetSource();
- if (this.autodetectInterfaces && getProxiedInterfaces().length == 0 && !isProxyTargetClass()) {
-
- Class targetClass = getTargetClass();
- if (targetClass == null) {
- throw new FactoryBeanNotInitializedException("Cannot determine target class for proxy");
- }
- setInterfaces(ClassUtils.getAllInterfacesForClass(targetClass, this.proxyClassLoader));
- }
-
- super.setFrozen(this.freezeProxy);
- this.singletonInstance = getProxy(createAopProxy());
- }
- return this.singletonInstance;
- }
AopProxy是最终生成instance的地方,但是它是接口,和框架分离开来了
接口的生成在父类的ProxyCreatorSupport中
-
-
-
-
- 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方法
-
- List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
-
-
- if (chain.isEmpty()) {
-
-
-
- retVal = AopUtils.invokeJoinpointUsingReflection(target, method, args);
- }
- else {
-
- invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
-
- retVal = invocation.proceed();
- }
Proceed的真实代码,把对象本身的invoke和拦截器的invoke交织在一起
- public Object proceed() throws Throwable {
-
- if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
- return invokeJoinpoint();
- }
-
- Object interceptorOrInterceptionAdvice =
- this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
- if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
-
-
- InterceptorAndDynamicMethodMatcher dm =
- (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
- if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
- return dm.interceptor.invoke(this);
- }
- else {
-
-
- return proceed();
- }
- }
- else {
-
-
- return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
- }
- }
拦截器是如何编织的
我们在JdkDynamicAopProxy中的invoke方法中看到如下拦截器链条的生成
-
- List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
它对应的代码是DefaultAdvisorChainFactory中的getInterceptorsAndDynamicInterceptionAdvice方法
- AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
- for (Advisor advisor : config.getAdvisors()) {
- if (advisor instanceof PointcutAdvisor) {
-
- PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
- if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(targetClass)) {
- MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
最终advisor的登记由DefaultAdvisorAdapterRegistry完成
我们可以看到DefaultAdvisorAdapterRegistry中首先登记了所有的AdviceAdapter
-
-
-
- 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的代码如下:
-
-
-
-
-
-
-
- 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;
-
-
-
-
-
-
- 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;
- }
-
- }