1. 程式人生 > >Spring裡的aop實現方式和原始碼分析 java中代理,靜態代理,動態代理以及spring aop代理方式,實現原理統一彙總

Spring裡的aop實現方式和原始碼分析 java中代理,靜態代理,動態代理以及spring aop代理方式,實現原理統一彙總

使用"橫切"技術,AOP把軟體系統分為兩個部分:核心關注點橫切關注點。業務處理的主要流程是核心關注點,與之關係不大的部分是橫切關注點。橫切關注點的一個特點是,他們經常發生在核心關注點的多處,而各處基本相似,比如許可權認證、日誌、事務。AOP的作用在於分離系統中的各種關注點,將核心關注點和橫切關注點分離開來。

AOP核心概念

1、橫切關注點

對哪些方法進行攔截,攔截後怎麼處理,這些關注點稱之為橫切關注點

2、切面(aspect)

類是對物體特徵的抽象,切面就是對橫切關注點的抽象

3、連線點(joinpoint)

被攔截到的點,因為Spring只支援方法型別的連線點,所以在Spring中連線點指的就是被攔截到的方法,實際上連線點還可以是欄位或者構造器

4、切入點(pointcut)

對連線點進行攔截的定義

5、通知(advice)

所謂通知指的就是指攔截到連線點之後要執行的程式碼,通知分為前置、後置、異常、最終、環繞通知五類

6、目標物件

代理的目標物件

7、織入(weave)

將切面應用到目標物件並導致代理物件建立的過程

8、引入(introduction)

在不修改程式碼的前提下,引入可以在執行期為類動態地新增一些方法或欄位

使用ProxyFactoryBean實現AOP

Spring自己的AOP實現在於ProxyFactoryBean。

介面

package com.shoudongdaili;

public interface IPerson { void say(); }

實現類

package com.shoudongdaili;

public class Person3 implements IPerson {

    private String name3;
    private int age3;
    
    public String getName3() {
        return name3;
    }
    public void setName3(String name3) {
        this.name3 = name3;
    }
    
public int getAge3() { return age3; } public void setAge3(int age3) { this.age3 = age3; } @Override public String toString() { return "Person3 [name3=" + name3 + ", age3=" + age3 + "]"; } @Override public void say() { System.out.println(toString()); } }

通知類

package com.shoudongdaili;

import java.lang.reflect.Method;

import org.aopalliance.intercept.MethodInterceptor;
import org.springframework.aop.MethodBeforeAdvice;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;

public class MyAdvice3 implements MethodBeforeAdvice {
    
    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        //arg0 是 目標類的方法     arg1是目標類的入引數   arg2是目標類例項  發生異常則拋給Throwable
        System.out.println("before my advice3...");
    }
    
}

bean.xml

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:aop="http://www.springframework.org/schema/aop" 
    xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:context="http://www.springframework.org/schema/context"
        xsi:schemaLocation="
            http://www.springframework.org/schema/beans 
            http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
            http://www.springframework.org/schema/context  
            http://www.springframework.org/schema/context/spring-context-3.2.xsd
            http://www.springframework.org/schema/tx 
            http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
            http://www.springframework.org/schema/aop
             http://www.springframework.org/schema/aop/spring-aop.xsd">
   <!-- 代理前原物件 -->
    <bean id="person3" class="com.shoudongdaili.Person3"></bean>
    
  <!-- 通知類 --> <bean id="myAdvice3" class="com.shoudongdaili.MyAdvice3"></bean>
  <!-- 代理物件 -->   <bean id="proxyPerson3" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="proxyInterfaces" value="com.shoudongdaili.IPerson"></property> <property name="target" ref="person3"></property> <property name="interceptorNames"> <list> <value>myAdvice3</value> </list> </property> </bean> </beans>

測試類

package com.shoudongdaili;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class BeanTest3 {
    
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("zidongdaili/shoudongdaili-bean.xml");
        // IPerson的實現類有Person3和proxyPerson3代理類這兩個,注意這裡是使用proxyPerson3
        IPerson person3 = (IPerson) context.getBean("proxyPerson3");
        person3.say();
    }
    
}

原始碼解讀

  然後我們就要原始碼分析下這一過程,先看下是如何產生代理物件的,在ProxyFactoryBean的getObject方法中:

複製程式碼
public class ProxyFactoryBean extends ProxyCreatorSupport
        implements FactoryBean<Object>, BeanClassLoaderAware, BeanFactoryAware {
    @Override
    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();
        }
    }
}
複製程式碼

  重點1:就是根據我們配置的interceptorNames來獲取對應的bean,並卻轉化成Advisor。

  this.advisorChainInitialized:標示是否已進行過初始化,若以初始化則不再進行初始化。然後就是將interceptorNames轉化成Advisor。根據interceptorNames所包含的字串到容器中進行查詢,如果含有*則,則表示進行一定的匹配,符合的都會納入。

  如下: 

複製程式碼
    private synchronized void initializeAdvisorChain() throws AopConfigException, BeansException {
        if (this.advisorChainInitialized) {
            return;
        }

        if (!ObjectUtils.isEmpty(this.interceptorNames)) {
            if (this.beanFactory == null) {
                throw new IllegalStateException("No BeanFactory available anymore (probably due to serialization) " +
                        "- cannot resolve interceptor names " + Arrays.asList(this.interceptorNames));
            }

            // Globals can't be last unless we specified a targetSource using the property...
            if (this.interceptorNames[this.interceptorNames.length - 1].endsWith(GLOBAL_SUFFIX) &&
                    this.targetName == null && this.targetSource == EMPTY_TARGET_SOURCE) {
                throw new AopConfigException("Target required after globals");
            }

            // Materialize interceptor chain from bean names.
            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()));
                }

                else {
                    // If we get here, we need to add a named interceptor.
                    // We must check if it's a singleton or prototype.
                    Object advice;
                    if (this.singleton || this.beanFactory.isSingleton(name)) {
                        // Add the real Advisor/Advice to the chain.
                        advice = this.beanFactory.getBean(name);
                    }
                    else {
                        // It's a prototype Advice or Advisor: replace with a prototype.
                        // Avoid unnecessary creation of prototype bean just for advisor chain initialization.
                        advice = new PrototypePlaceholderAdvisor(name);
                    }
                    addAdvisorOnChainCreation(advice, name);
                }
            }
        }

        this.advisorChainInitialized = true;
    }
複製程式碼

  這中間頁經過了Advice到Advisor的轉換,如下: 

複製程式碼
    private void addAdvisorOnChainCreation(Object next, String name) {
        // We need to convert to an Advisor if necessary so that our source reference
        // matches what we find from superclass interceptors.
        Advisor advisor = namedBeanToAdvisor(next);
        if (logger.isTraceEnabled()) {
            logger.trace("Adding advisor with name '" + name + "'");
        }
        addAdvisor(advisor);
    }
複製程式碼 複製程式碼
    private Advisor namedBeanToAdvisor(Object next) {
        try {
            return this.advisorAdapterRegistry.wrap(next);
        }
        catch (UnknownAdviceTypeException ex) {
            // We expected this to be an Advisor or Advice,
            // but it wasn't. This is a configuration error.
            throw new AopConfigException("Unknown advisor type " + next.getClass() +
                    "; Can only include Advisor or Advice type beans in interceptorNames chain except for last entry," +
                    "which may also be target or TargetSource", ex);
        }
    }
複製程式碼 複製程式碼
    public Advisor wrap(Object adviceObject) throws UnknownAdviceTypeException {
        if (adviceObject instanceof Advisor) {
            return (Advisor) adviceObject;
        }
        if (!(adviceObject instanceof Advice)) {
            throw new UnknownAdviceTypeException(adviceObject);
        }
        Advice advice = (Advice) adviceObject;
        if (advice instanceof MethodInterceptor) {
            // So well-known it doesn't even need an adapter.
            return new DefaultPointcutAdvisor(advice);
        }
        for (AdvisorAdapter adapter : this.adapters) {
            // Check that it is supported.
            if (adapter.supportsAdvice(advice)) {
                return new DefaultPointcutAdvisor(advice);
            }
        }
        throw new UnknownAdviceTypeException(advice);
    }
複製程式碼
public class DefaultPointcutAdvisor extends AbstractGenericPointcutAdvisor implements Serializable {
    public DefaultPointcutAdvisor(Advice advice) {
        this(Pointcut.TRUE, advice);
    }
}

這個包裹過程已經見過很多遍了,採用了介面卡的模式

之後又是和其他的AOP方式接軌了,設定一些列要實現的介面和引數,使用DefaultAopProxyFactory先創建出AopProxy,要麼是JdkDynamicAopProxy,要麼是CglibAopProxy,然後就可以呼叫AopProxy的getProxy方法來獲取代理物件。

具體JdkDynamicAopProxy和CglibAopProxy的區別聯絡,參閱java中代理,靜態代理,動態代理以及spring aop代理方式,實現原理統一彙總

這種方式實現的AOP還是比較麻煩的,同時配置一個ProxyFactoryBean僅能實現對一個目標物件的攔截,要想攔截多個目標物件,需要配置多個ProxyFactoryBean所以大部分還是使用Spring引進的aspectj的AOP方式來進行AOP程式設計。 

使用DefaultAdvisorAutoProxyCreator實現自動代理完成AOP

介面

package com.zidongdaili;

public interface IPerson4 {

    void sayhi();
    
}

實現類

package com.zidongdaili;


public class Person4 implements IPerson4 {

    private String name4;
    private int age4;
    
    public String getName4() {
        return name4;
    }
    public void setName4(String name4) {
        this.name4 = name4;
    }
    public int getAge4() {
        return age4;
    }
    public void setAge4(int age4) {
        this.age4 = age4;
    }
    @Override
    public String toString() {
        return "Person4 [name4=" + name4 + ", age4=" + age4 + "]";
    }
    
    @Override
    public void sayhi() {
        System.out.println(toString());
    }
    
}

通知類

package com.zidongdaili;

import java.lang.reflect.Method;
import org.springframework.aop.MethodBeforeAdvice;

public class MyAdvice4 implements MethodBeforeAdvice {
    
    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        //arg0 是 目標類的方法     arg1是目標類的入引數   arg2是目標類例項  發生異常則拋給Throwable
        System.out.println("before my advice4...");
    }
    
}

bean.xml

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:aop="http://www.springframework.org/schema/aop" 
    xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:context="http://www.springframework.org/schema/context"
        xsi:schemaLocation="
            http://www.springframework.org/schema/beans 
            http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
            http://www.springframework.org/schema/context  
            http://www.springframework.org/schema/context/spring-context-3.2.xsd
            http://www.springframework.org/schema/tx 
            http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
            http://www.springframework.org/schema/aop
             http://www.springframework.org/schema/aop/spring-aop.xsd">

    <bean id="person4" class="com.zidongdaili.Person4"></bean>
    
    <bean id="myAdvice4" class="com.zidongdaili.MyAdvice4"></bean>
    
    <bean class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> <!-- id="advisor" 可以不寫 -->
        <property name="pattern">
            <value>.*say.+</value> <!-- 業務實現方法名匹配 -->
        </property>
        <property name="advice">
            <ref bean="myAdvice4" />
        </property>
    </bean>
    
  <!-- 自動代理 --> <bean id="autoProxyCreator" class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"> </bean> </beans>

測試類

package com.zidongdaili;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class BeanTest4 {
    
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("zidongdaili/zidongdaili-bean.xml");
        
        IPerson4 person4 = (IPerson4) context.getBean("person4");
        person4.sayhi();
        
    }
    
}

使用BeanNameAutoProxyCreator實現自動代理完成AOP

BeanNameAutoProxyCreator是自動代理建立器的三種(BeanNameAutoProxyCreator,DefaultAdvisorAutoProxyCreator,AbstractAdvisorAutoProxyCreator)之一.它是根據攔截器和設定的Bean的名稱表示式做匹配來建立代理.下面是個例子

1.主要依賴(略)

2.宣告一個環繞通知(攔截器)

public class MyMethodInterceptor implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        System.out.println(getClass()+"呼叫方法前");
        Object ret=invocation.proceed();
        System.out.println(getClass()+"呼叫方法後");
        return ret;
    }
}

3.要建立代理的目標類與介面

public interface UserService {
    void print();
}
public class UserServiceImpl implements UserService {
    public void print(){
        System.out.println(getClass()+"#print");
    }
}

4.配置

@Configuration
public class AppConfig {
    //要建立代理的目標Bean
    @Bean
    public UserService userService(){
        return new UserServiceImpl();
    }
    //建立Advice或Advisor
    @Bean
    public Advice myMethodInterceptor(){
        return new MyMethodInterceptor();
    }
    //使用BeanNameAutoProxyCreator來建立代理
    @Bean
    public BeanNameAutoProxyCreator beanNameAutoProxyCreator(){
        BeanNameAutoProxyCreator beanNameAutoProxyCreator=new BeanNameAutoProxyCreator();
        //設定要建立代理的那些Bean的名字
        beanNameAutoProxyCreator.setBeanNames("userSer*");
        //設定攔截鏈名字(這些攔截器是有先後順序的)
        beanNameAutoProxyCreator.setInterceptorNames("myMethodInterceptor");
        return beanNameAutoProxyCreator;
    }
}

5.測試

public class Main {
    public static void main(String[] args) {
        ApplicationContext applicationContext=new AnnotationConfigApplicationContext(AppConfig.class);
        UserService userService= applicationContext.getBean(UserService.class);
        userService.print();
    }
}

原始碼分析

BeanNameAutoProxyCreator是一個BeanPostProcessor.它在Bean例項化隨後,呼叫回撥org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#postProcessAfterInitialization進行後期處理來完成代理的建立.
其中AbstractAutoProxyCreator是BeanNameAutoProxyCreator的超類,BeanNameAutoProxyCreator沒有重寫postProcessAfterInitialization方法.下面看看這個方法:

public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    if (bean != null) {
        Object cacheKey = getCacheKey(bean.getClass(), beanName);
        if (!this.earlyProxyReferences.contains(cacheKey)) {
            //關鍵程式碼在這裡
            return wrapIfNecessary(bean, beanName, cacheKey);
        }
    }
    return bean;
}

再看看wrapIfNecessary方法:

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
    if (beanName != null && this.targetSourcedBeans.contains(beanName)) {
        return bean;
    }
    if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
        return bean;
    }
    if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
        this.advisedBeans.put(cacheKey, Boolean.FALSE);
        return bean;
    }
 
    //這個bean是否匹配要建立代理也是在這個方法.
    Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
    if (specificInterceptors != DO_NOT_PROXY) {
        this.advisedBeans.put(cacheKey, Boolean.TRUE);
        //關鍵程式碼在這裡
        Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
        this.proxyTypes.put(cacheKey, proxy.getClass());
        return proxy;
    }
 
    this.advisedBeans.put(cacheKey, Boolean.FALSE);
    return bean;
}

再看看createProxy方法:

protected Object createProxy(
        Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {
    ProxyFactory proxyFactory = new ProxyFactory();
    proxyFactory.copyFrom(this);
    if (!proxyFactory.isProxyTargetClass()) {
        if (shouldProxyTargetClass(beanClass, beanName)) {
            proxyFactory.setProxyTargetClass(true);
        }
        else {
            evaluateProxyInterfaces(beanClass, proxyFactory);
        }
    }
    Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
    for (Advisor advisor : advisors) {
        proxyFactory.addAdvisor(advisor);
    }
    proxyFactory.setTargetSource(targetSource);
    customizeProxyFactory(proxyFactory);
    proxyFactory.setFrozen(this.freezeProxy);
    if (advisorsPreFiltered()) {
        proxyFactory.setPreFiltered(true);
    }
    //關鍵程式碼看這裡
    return proxyFactory.getProxy(getProxyClassLoader());
}

再看看org.springframework.aop.framework.ProxyFactory#getProxy(java.lang.ClassLoader)如下:

public Object getProxy(ClassLoader classLoader) {
    return createAopProxy().getProxy(classLoader);
}

再看看org.springframework.aop.framework.ProxyCreatorSupport#createAopProxy

public Object getProxy(ClassLoader classLoader) {
    return createAopProxy().getProxy(classLoader);
}

再看看createAopProxy方法

protected final synchronized AopProxy createAopProxy() {
    if (!this.active) {
        activate();
    }
    return getAopProxyFactory().createAopProxy(this);
}

剩下的就與ProxyFactoryBean建立代理類似了.

手動實現自動代理實現AOP

 

我們也可以寫一個類,來實現DefaultAdvisorAutoProxyCreator自動代理的功能!

首先,我們需要實現一個介面,也就是BeanPostProcessor介面。
BeanPostProcessor介面作用是:如果我們需要在Spring容器完成Bean的例項化、配置和其他的初始化前後新增一些自己的邏輯處理,我們就可以定義一個或者多個BeanPostProcessor介面的實現,然後註冊到容器中。

而我們想要在原型物件bean被建立之後就代理了,就必須在原來的容器中拿到原來的原型物件,需要拿到原來spring容器中的切面物件,這個時候,我們就需要原來的容器,這個時候就需要另一個介面,也就是ApplicationContextAware介面!

通過這2個介面,我們就可以實現自動代理了。

package cn.hncu.xmlImpl;

import org.springframework.aop.Advisor;
import org.springframework.aop.framework.ProxyFactoryBean;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

public class MyAutoProxy implements BeanPostProcessor,ApplicationContextAware{
    private ApplicationContext applicationContext=null;

    //bean建立之前呼叫
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName)
            throws BeansException {
        return bean;//在這裡,我們直接放行
    }

    //bean建立之後呼叫
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName)
            throws BeansException {
        ProxyFactoryBean factory = new ProxyFactoryBean();
        //把原型物件放入代理工廠
        factory.setTarget(bean);
        //在這裡
        Advisor adv = applicationContext.getBean(Advisor.class);
        factory.addAdvisor(adv);
        //返回被代理後的物件
        return factory.getObject();
    }

    //拿到原來的spring中的容器
    @Override
    public void setApplicationContext(ApplicationContext applicationContext)
            throws BeansException {
        this.applicationContext=applicationContext;
    }

}

bean.xml

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:aop="http://www.springframework.org/schema/aop" 
    xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:context="http://www.springframework.org/schema/context"
        xsi:schemaLocation="
            http://www.springframework.org/schema/beans 
            http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
            http://www.springframework.org/schema/context  
            http://www.springframework.org/schema/context/spring-context-3.2.xsd
            http://www.springframework.org/schema/tx 
            http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
            http://www.springframework.org/schema/aop
             http://www.springframework.org/schema/aop/spring-aop.xsd">

    <bean id="person4" class="com.zidongdaili.Person4"></bean>
    
    <bean id="myAdvice4" class="com.zidongdaili.MyAdvice4"></bean>
    
    <bean class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> <!-- id="advisor" 可以不寫 -->
        <property name="pattern">
            <value>.*say.+</value> <!-- 業務實現方法名匹配 -->
        </property>
        <property name="advice">
            <ref bean="myAdvice4" />
        </property>
    </bean>
    
    <!-- 自己寫的自動代理 --> 
<bean class="cn.hncu.xmlImpl.MyAutoProxy"></bean>
</beans>