Spring里的aop实现方式和源码分析详解编程语言

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>

 

原创文章,作者:Maggie-Hunter,如若转载,请注明出处:https://blog.ytso.com/15712.html

(0)
上一篇 2021年7月19日
下一篇 2021年7月19日

相关推荐

发表回复

登录后才能评论