zoukankan      html  css  js  c++  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>
  • 相关阅读:
    如何把项目中经常使用的信息放在全局的对象里,随取随用?
    优秀代码
    gcc编译C代码后,输出乱码
    mybatis !=null的一个坑
    String转int[]
    插值算法的公式 mid=low+(key-a[low])/(a[high]-a[low])*(high-low) 是怎么来的
    关于Leetcode的交替打印FooBar,我的答案一直超时
    git找回前几个版本删除的某个文件
    Google 此手机号无法用于验证 解决方法
    Postgresql 一对多如何将原本用,隔开的id替换为name
  • 原文地址:https://www.cnblogs.com/shamo89/p/9932089.html
Copyright © 2011-2022 走看看