精华内容
下载资源
问答
  • AOP如何实现及实现原理

    万次阅读 多人点赞 2018-11-24 10:41:03
    最近在开发中遇到了一个刚好可以用AOP实现的例子,就顺便研究了AOP的实现原理,把学习到的东西进行一个总结。文章中用到的编程语言为kotlin,需要的可以在IDEA中直接转为java。 这篇文章将会按照如下目录展开: AOP...

    概述:

    最近在开发中遇到了一个刚好可以用AOP实现的例子,就顺便研究了AOP的实现原理,把学习到的东西进行一个总结。文章中用到的编程语言为kotlin,需要的可以在IDEA中直接转为java。 这篇文章将会按照如下目录展开:

    • AOP简介
    • 代码中实现举例
    • AOP实现原理
    • 部分源码解析

    1. AOP简介

    相信大家或多或少的了解过AOP,都知道它是面向切面编程,在网上搜索可以找到很多的解释。这里我用一句话来总结:AOP是能够让我们在不影响原有功能的前提下,为软件横向扩展功能。 那么横向扩展怎么理解呢,我们在WEB项目开发中,通常都遵守三层原则,包括控制层(Controller)->业务层(Service)->数据层(dao),那么从这个结构下来的为纵向,它具体的某一层就是我们所说的横向。我们的AOP就是可以作用于这某一个横向模块当中的所有方法。

    我们在来看一下AOP和OOP的区别:AOP是OOP的补充,当我们需要为多个对象引入一个公共行为,比如日志,操作记录等,就需要在每个对象中引用公共行为,这样程序就产生了大量的重复代码,使用AOP可以完美解决这个问题。

    接下来介绍一下提到AOP就必须要了解的知识点:

    • 切面:拦截器类,其中会定义切点以及通知
    • 切点:具体拦截的某个业务点。
    • 通知:切面当中的方法,声明通知方法在目标业务层的执行位置,通知类型如下:
      1. 前置通知:@Before 在目标业务方法执行之前执行
      2. 后置通知:@After 在目标业务方法执行之后执行
      3. 返回通知:@AfterReturning 在目标业务方法返回结果之后执行
      4. 异常通知:@AfterThrowing 在目标业务方法抛出异常之后
      5. 环绕通知:@Around 功能强大,可代替以上四种通知,还可以控制目标业务方法是否执行以及何时执行

    2. 代码中实现举例

    上面已经大概的介绍了AOP中需要了解的基本知识,也知道了AOP的好处,那怎么在代码中实现呢?给大家举个例子:我们现在有个学校管理系统,已经实现了对老师和学生的增删改,又新来个需求,说是对老师和学生的每次增删改做一个记录,到时候校长可以查看记录的列表。那么问题来了,怎么样处理是最好的解决办法呢?这里我罗列了三种解决办法,我们来看下他的优缺点。

     

     

    -最简单的就是第一种方法,我们直接在每次的增删改的函数当中直接实现这个记录的方法,这样代码的重复度太高,耦合性太强,不建议使用。

     

    -其次就是我们最长使用的,将记录这个方法抽离出来,其他的增删改调用这个记录函数即可,显然代码重复度降低,但是这样的调用还是没有降低耦合性。

    -这个时候我们想一下AOP的定义,再想想我们的场景,其实我们就是要在不改变原来增删改的方法,给这个系统增加记录的方法,而且作用的也是一个层面的方法。这个时候我们就可以采用AOP来实现了。

    我们来看下代码的具体实现:

    1. 首先我定义了一个自定义注解作为切点

    @Target(AnnotationTarget.FUNCTION)  //注解作用的范围,这里声明为函数
    @Order(Ordered.HIGHEST_PRECEDENCE)  //声明注解的优先级为最高,假设有多个注解,先执行这个
    annotation class Hanler(val handler: HandlerType)  //自定义注解类,HandlerType是一个枚举类型,里面定义的就是学生和老师的增删改操作,在这里就不展示具体内容了

    2. 接下来就是要定义切面类了

    @Aspect   //该注解声明这个类为一个切面类
    @Component
    class HandlerAspect{
    
     @Autowired
     private lateinit var handlerService: HandlerService
    
    @AfterReturning("@annotation(handler)")   //当有函数注释了注解,将会在函数正常返回后在执行我们定义的方法
    fun hanler(hanler: Hanler) {
        handlerService.add(handler.operate.value)   //这里是真正执行记录的方法
    }
    }

    3. 最后就是我们本来的业务方法了

    /**
    * 删除学生方法
    */
    @Handler(operate= Handler.STUDENT_DELETE)   //当执行到删除学生方法时,切面类就会起作用了,当学生正常删除后就会执行记录方法,我们就可以看到记录方法生成的数据
    fun delete(id:String) {
       studentService.delete(id)
    }

    3. AOP实现原理

    我们现在了解了代码中如何实现,那么AOP实现的原理是什么呢?之前看了一个博客说到,提到AOP大家都知道他的实现原理是动态代理,显然我之前就是不知道的,哈哈,但是相信阅读文章的你们一定是知道的。

    讲到动态代理就不得不说代理模式了, 代理模式的定义:给某一个对象提供一个代理,并由代理对象控制对原对象的引用。代理模式包含如下角色:subject:抽象主题角色,是一个接口。该接口是对象和它的代理共用的接口; RealSubject:真实主题角色,是实现抽象主题接口的类; Proxy:代理角色,内部含有对真实对象RealSubject的引用,从而可以操作真实对象。代理对象提供与真实对象相同的接口,以便代替真实对象。同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装。如下图所示:

     

     

     

    那么代理又分为静态代理和动态代理,这里写两个小的demo,动态代理采用的就是JDK代理。举个例子就是现在一个班上的学生需要交作业,现在由班长代理交作业,那么班长就是代理,学生就是被代理的对象。

    3.1 静态代理

    首先,我们创建一个Person接口。这个接口就是学生(被代理类),和班长(代理类)的公共接口,他们都有交作业的行为。这样,学生交作业就可以让班长来代理执行。

    /**
     * Created by Mapei on 2018/11/7
     * 创建person接口
     */
    public interface Person {
        //交作业
        void giveTask();
    }

    Student类实现Person接口,Student可以具体实施交作业这个行为。

    /**
     * Created by Mapei on 2018/11/7
     */
    public class Student implements Person {
        private String name;
        public Student(String name) {
            this.name = name;
        }
    
        public void giveTask() {
            System.out.println(name + "交语文作业");
        }
    }

    StudentsProxy类,这个类也实现了Person接口,但是还另外持有一个学生类对象,那么他可以代理学生类对象执行交作业的行为。

    /**
     * Created by Mapei on 2018/11/7
     * 学生代理类,也实现了Person接口,保存一个学生实体,这样就可以代理学生产生行为
     */
    public class StudentsProxy implements Person{
        //被代理的学生
        Student stu;
    
        public StudentsProxy(Person stu) {
            // 只代理学生对象
            if(stu.getClass() == Student.class) {
                this.stu = (Student)stu;
            }
        }
    
        //代理交作业,调用被代理学生的交作业的行为
        public void giveTask() {
            stu.giveTask();
        }
    }

    下面测试一下,看代理模式如何使用:

    /**
     * Created by Mapei on 2018/11/7
     */
    public class StaticProxyTest {
        public static void main(String[] args) {
            //被代理的学生林浅,他的作业上交有代理对象monitor完成
            Person linqian = new Student("林浅");
    
            //生成代理对象,并将林浅传给代理对象
            Person monitor = new StudentsProxy(linqian);
    
            //班长代理交作业
            monitor.giveTask();
        }
    }

    运行结果:

     

     

     

    这里并没有直接通过林浅(被代理对象)来执行交作业的行为,而是通过班长(代理对象)来代理执行了。这就是代理模式。代理模式就是在访问实际对象时引入一定程度的间接性,这里的间接性就是指不直接调用实际对象的方法,那么我们在代理过程中就可以加上一些其他用途。比如班长在帮林浅交作业的时候想告诉老师最近林浅的进步很大,就可以轻松的通过代理模式办到。在代理类的交作业之前加入方法即可。这个优点就可以运用在spring中的AOP,我们能在一个切点之前执行一些操作,在一个切点之后执行一些操作,这个切点就是一个个方法。这些方法所在类肯定就是被代理了,在代理过程中切入了一些其他操作。

    3.2 动态代理

    动态代理和静态代理的区别是,静态代理的的代理类是我们自己定义好的,在程序运行之前就已经变异完成,但是动态代理的代理类是在程序运行时创建的。相比于静态代理,动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类中的方法。比如我们想在每个代理方法之前都加一个处理方法,我们上面的例子中只有一个代理方法,如果还有很多的代理方法,就太麻烦了,我们来看下动态代理是怎么去实现的。

    首先还是定义一个Person接口:

    /**
     * Created by Mapei on 2018/11/7
     * 创建person接口
     */
    public interface Person {
        //交作业
        void giveTask();
    }

    接下来是创建需要被代理的实际类,也就是学生类:

    /**
     * Created by Mapei on 2018/11/7
     */
    public class Student implements Person {
        private String name;
        public Student(String name) {
            this.name = name;
        }
    
        public void giveTask() {
            System.out.println(name + "交语文作业");
        }
    }

    创建StuInvocationHandler类,实现InvocationHandler接口,这个类中持有一个被代理对象的实例target。InvocationHandler中有一个invoke方法,所有执行代理对象的方法都会被替换成执行invoke方法。

    /**
     * Created by Mapei on 2018/11/7
     */
    public class StuInvocationHandler<T> implements InvocationHandler {
        //invocationHandler持有的被代理对象
        T target;
    
        public StuInvocationHandler(T target) {
            this.target = target;
        }
    
        /**
         * proxy:代表动态代理对象
         * method:代表正在执行的方法
         * args:代表调用目标方法时传入的实参
         */
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("代理执行" +method.getName() + "方法");
            Object result = method.invoke(target, args);
            return result;
        }
    }

    那么接下来我们就可以具体的创建代理对象了。

    /**
     * Created by Mapei on 2018/11/7
     * 代理类
     */
    public class ProxyTest {
        public static void main(String[] args) {
    
            //创建一个实例对象,这个对象是被代理的对象
            Person linqian = new Student("林浅");
    
            //创建一个与代理对象相关联的InvocationHandler
            InvocationHandler stuHandler = new StuInvocationHandler<Person>(linqian);
    
            //创建一个代理对象stuProxy来代理linqian,代理对象的每个执行方法都会替换执行Invocation中的invoke方法
            Person stuProxy = (Person) Proxy.newProxyInstance(Person.class.getClassLoader(), new Class<?>[]{Person.class}, stuHandler);
    
            //代理执行交作业的方法
            stuProxy.giveTask();
        }
    }

    我们执行代理测试类,首先我们创建了一个需要被代理的学生林浅,将林浅传入stuHandler中,我们在创建代理对象stuProxy时,将stuHandler作为参数,那么所有执行代理对象的方法都会被替换成执行invoke方法,也就是说,最后执行的是StuInvocationHandler中的invoke方法。所以在看到下面的运行结果也就理所当然了。

     

     

     

    那么到这里问题就来了,为什么代理对象执行的方法都会通过InvocationHandler中的invoke方法来执行,带着这个问题,我们需要看一下动态代理的源码,对他进行简单的分析。

    上面我们使用Proxy类的newProxyInstance方法创建了一个动态代理对象,看一下他的源码:

    public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
            throws IllegalArgumentException
        {
            Objects.requireNonNull(h);
    
            final Class<?>[] intfs = interfaces.clone();
            final SecurityManager sm = System.getSecurityManager();
            if (sm != null) {
                checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
            }
    
            /*
             * Look up or generate the designated proxy class.
             */
            Class<?> cl = getProxyClass0(loader, intfs);
    
            /*
             * Invoke its constructor with the designated invocation handler.
             */
            try {
                if (sm != null) {
                    checkNewProxyPermission(Reflection.getCallerClass(), cl);
                }
    
                final Constructor<?> cons = cl.getConstructor(constructorParams);
                final InvocationHandler ih = h;
                if (!Modifier.isPublic(cl.getModifiers())) {
                    AccessController.doPrivileged(new PrivilegedAction<Void>() {
                        public Void run() {
                            cons.setAccessible(true);
                            return null;
                        }
                    });
                }
                return cons.newInstance(new Object[]{h});
            } catch (IllegalAccessException|InstantiationException e) {
                throw new InternalError(e.toString(), e);
            } catch (InvocationTargetException e) {
                Throwable t = e.getCause();
                if (t instanceof RuntimeException) {
                    throw (RuntimeException) t;
                } else {
                    throw new InternalError(t.toString(), t);
                }
            } catch (NoSuchMethodException e) {
                throw new InternalError(e.toString(), e);
            }
      }

    然后,我们需要重点关注Class<?> cl = getProxyClass0(loader, intfs)这句代码,这里产生了代理类,这个类就是动态代理的关键,由于是动态生成的类文件,我们将这个类文件打印到文件中。

            byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0", Student.class.getInterfaces());
            String path = "/Users/mapei/Desktop/okay/65707.class";
    
            try{
                FileOutputStream fos = new FileOutputStream(path);
                fos.write(classFile);
                fos.flush();
                System.out.println("代理类class文件写入成功");
            }catch (Exception e) {
                System.out.println("写文件错误");
            }

    对这个class文件进行反编译,我们看看jdk为我们生成了什么样的内容:

    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    import java.lang.reflect.UndeclaredThrowableException;
    import proxy.Person;
    
    public final class $Proxy0 extends Proxy implements Person
    {
      private static Method m1;
      private static Method m2;
      private static Method m3;
      private static Method m0;
      
      /**
      *注意这里是生成代理类的构造方法,方法参数为InvocationHandler类型,看到这,是不是就有点明白
      *为何代理对象调用方法都是执行InvocationHandler中的invoke方法,而InvocationHandler又持有一个
      *被代理对象的实例,就可以去调用真正的对象实例。
      */
      public $Proxy0(InvocationHandler paramInvocationHandler)
        throws 
      {
        super(paramInvocationHandler);
      }
      
      //这个静态块本来是在最后的,我把它拿到前面来,方便描述
       static
      {
        try
        {
          //看看这儿静态块儿里面的住giveTask通过反射得到的名字m3,其他的先不管
          m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
          m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
          m3 = Class.forName("proxy.Person").getMethod("giveTask", new Class[0]);
          m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
          return;
        }
        catch (NoSuchMethodException localNoSuchMethodException)
        {
          throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
        }
        catch (ClassNotFoundException localClassNotFoundException)
        {
          throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
        }
      }
     
      /**
      * 
      *这里调用代理对象的giveMoney方法,直接就调用了InvocationHandler中的invoke方法,并把m3传了进去。
      *this.h.invoke(this, m3, null);我们可以对将InvocationHandler看做一个中介类,中介类持有一个被代理对象,在invoke方法中调用了被代理对象的相应方法。通过聚合方式持有被代理对象的引用,把外部对invoke的调用最终都转为对被代理对象的调用。
      */
      public final void giveTask()
        throws 
      {
        try
        {
          this.h.invoke(this, m3, null);
          return;
        }
        catch (Error|RuntimeException localError)
        {
          throw localError;
        }
        catch (Throwable localThrowable)
        {
          throw new UndeclaredThrowableException(localThrowable);
        }
      }
    
    }

    看完了动态代理的源码,我们接下来就要看一下Spring中AOP实现的源码是怎样的?

    4. 部分源码解析

    aop创建代理的源码分析

    1. 看一下bean如何被包装为proxy

           	protected Object createProxy(
       		Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {
       		
       	if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
       		AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
       	}
    
           // 1.创建proxyFactory,proxy的生产主要就是在proxyFactory做的
       	ProxyFactory proxyFactory = new ProxyFactory();
       	proxyFactory.copyFrom(this);
    
       	if (!proxyFactory.isProxyTargetClass()) {
       		if (shouldProxyTargetClass(beanClass, beanName)) {
       			proxyFactory.setProxyTargetClass(true);
       		}
       		else {
       			evaluateProxyInterfaces(beanClass, proxyFactory);
       		}
       	}
    
           // 2.将当前bean适合的advice,重新封装下,封装为Advisor类,然后添加到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);
       	}
    
           // 3.调用getProxy获取bean对应的proxy
       	return proxyFactory.getProxy(getProxyClassLoader());
       }

    2. 创建何种类型的Proxy?JDKProxy还是CGLIBProxy?

    	public Object getProxy(ClassLoader classLoader) {
    		return createAopProxy().getProxy(classLoader);
    	}
        // createAopProxy()方法就是决定究竟创建何种类型的proxy
    	protected final synchronized AopProxy createAopProxy() {
    		if (!this.active) {
    			activate();
    		}
            // 关键方法createAopProxy()
    		return getAopProxyFactory().createAopProxy(this);
    	}
    	
        // createAopProxy()
    	public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
            // 1.config.isOptimize()是否使用优化的代理策略,目前使用与CGLIB
            // config.isProxyTargetClass() 是否目标类本身被代理而不是目标类的接口
            // hasNoUserSuppliedProxyInterfaces()是否存在代理接口
    		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.");
    			}
                
                // 2.如果目标类是接口类(目标对象实现了接口),则直接使用JDKproxy
    			if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
    				return new JdkDynamicAopProxy(config);
    			}
                
                // 3.其他情况则使用CGLIBproxy
    			return new ObjenesisCglibAopProxy(config);
    		}
    		else {
    			return new JdkDynamicAopProxy(config);
    		}
    	}

    3. getProxy()方法

       final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable// JdkDynamicAopProxy类结构,由此可知,其实现了InvocationHandler,则必定有invoke方法,来被调用,也就是用户调用bean相关方法时,此invoke()被真正调用
       // getProxy()
       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, true);
       	findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
           
           // JDK proxy 动态代理的标准用法
       	return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
       }

    4. invoke()方法法

        //使用了JDK动态代理模式,真正的方法执行在invoke()方法里,看到这里在想一下上面动态代理的例子,是不是就完全明白Spring源码实现动态代理的原理了。
    			public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    		MethodInvocation invocation;
    		Object oldProxy = null;
    		boolean setProxyContext = false;
     
    		TargetSource targetSource = this.advised.targetSource;
    		Class<?> targetClass = null;
    		Object target = null;
     
    		try {
                // 1.以下的几个判断,主要是为了判断method是否为equals、hashCode等Object的方法
    			if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
    				// The target does not implement the equals(Object) method itself.
    				return equals(args[0]);
    			}
    			else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
    				// The target does not implement the hashCode() method itself.
    				return hashCode();
    			}
    			else if (method.getDeclaringClass() == DecoratingProxy.class) {
    				// There is only getDecoratedClass() declared -> dispatch to proxy config.
    				return AopProxyUtils.ultimateTargetClass(this.advised);
    			}
    			else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
    					method.getDeclaringClass().isAssignableFrom(Advised.class)) {
    				// Service invocations on ProxyConfig with the proxy config...
    				return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
    			}
     
    			Object retVal;
     
    			if (this.advised.exposeProxy) {
    				// Make invocation available if necessary.
    				oldProxy = AopContext.setCurrentProxy(proxy);
    				setProxyContext = true;
    			}
     
    			// May be null. Get as late as possible to minimize the time we "own" the target,
    			// in case it comes from a pool.
    			target = targetSource.getTarget();
    			if (target != null) {
    				targetClass = target.getClass();
    			}
    			// 2.获取当前bean被拦截方法链表
    			List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
     
    			// 3.如果为空,则直接调用target的method
    			if (chain.isEmpty()) {
    				Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
    				retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
    			}
                // 4.不为空,则逐一调用chain中的每一个拦截方法的proceed,这里的一系列执行的原因以及proceed执行的内容,我 在这里就不详细讲了,大家感兴趣可以自己去研读哈
    			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();
    			}
     
    			...
    			return retVal;
    		}
    	}
    	}

    那么到了这里,我要讲的内容就差不多结束了,如果有什么不对的,或者有什么疑惑,欢迎大家指点!

    我有一个微信公众号,经常会分享一些Java技术相关的干货;如果你喜欢我的分享,可以用微信搜索“Java团长”或者“javatuanzhang”关注。

    展开全文
  • AOP如何实现及其原理

    2019-03-18 11:39:26
    AOP如何实现及其原理 文章链接:https://juejin.im/post/5bfcb146e51d451f6e52b3c8 概述: 最近在开发中遇到了一个刚好可以用AOP实现的例子,就顺便研究了AOP的实现原理,把学习到的东西进行一个总结。文章...

    AOP如何实现及其原理

     

    文章链接:https://juejin.im/post/5bfcb146e51d451f6e52b3c8

    概述:

     

    最近在开发中遇到了一个刚好可以用AOP实现的例子,就顺便研究了AOP的实现原理,把学习到的东西进行一个总结。文章中用到的编程语言为kotlin,需要的可以在IDEA中直接转为java。 这篇文章将会按照如下目录展开:

    • AOP简介

    • 代码中实现举例

    • AOP实现原理

    • 部分源码解析

     

    1. AOP简介

    相信大家或多或少的了解过AOP,都知道它是面向切面编程,在网上搜索可以找到很多的解释。这里我用一句话来总结:AOP是能够让我们在不影响原有功能的前提下,为软件横向扩展功能。 那么横向扩展怎么理解呢,我们在WEB项目开发中,通常都遵守三层原则,包括控制层(Controller)->业务层(Service)->数据层(dao),那么从这个结构下来的为纵向,它具体的某一层就是我们所说的横向。我们的AOP就是可以作用于这某一个横向模块当中的所有方法。

     

    我们在来看一下AOP和OOP的区别:AOP是OOP的补充,当我们需要为多个对象引入一个公共行为,比如日志,操作记录等,就需要在每个对象中引用公共行为,这样程序就产生了大量的重复代码,使用AOP可以完美解决这个问题。

     

    接下来介绍一下提到AOP就必须要了解的知识点:

     

    • 切面:拦截器类,其中会定义切点以及通知

    • 切点:具体拦截的某个业务点。

    • 通知:切面当中的方法,声明通知方法在目标业务层的执行位置,通知类型如下:

      1. 前置通知:@Before 在目标业务方法执行之前执行

      2. 后置通知:@After 在目标业务方法执行之后执行

      3. 返回通知:@AfterReturning 在目标业务方法返回结果之后执行

      4. 异常通知:@AfterThrowing 在目标业务方法抛出异常之后

      5. 环绕通知:@Around 功能强大,可代替以上四种通知,还可以控制目标业务方法是否执行以及何时执行

    2. 代码中实现举例

    上面已经大概的介绍了AOP中需要了解的基本知识,也知道了AOP的好处,那怎么在代码中实现呢?给大家举个例子:我们现在有个学校管理系统,已经实现了对老师和学生的增删改,又新来个需求,说是对老师和学生的每次增删改做一个记录,到时候校长可以查看记录的列表。那么问题来了,怎么样处理是最好的解决办法呢?这里我罗列了三种解决办法,我们来看下他的优缺点。

     

    -最简单的就是第一种方法,我们直接在每次的增删改的函数当中直接实现这个记录的方法,这样代码的重复度太高,耦合性太强,不建议使用。

     

    -其次就是我们最长使用的,将记录这个方法抽离出来,其他的增删改调用这个记录函数即可,显然代码重复度降低,但是这样的调用还是没有降低耦合性。

     

    -这个时候我们想一下AOP的定义,再想想我们的场景,其实我们就是要在不改变原来增删改的方法,给这个系统增加记录的方法,而且作用的也是一个层面的方法。这个时候我们就可以采用AOP来实现了。

     

    我们来看下代码的具体实现:

    1. 首先我定义了一个自定义注解作为切点

    @Target(AnnotationTarget.FUNCTION)  //注解作用的范围,这里声明为函数
    @Order(Ordered.HIGHEST_PRECEDENCE)  //声明注解的优先级为最高,假设有多个注解,先执行这个
    annotation class Hanler(val handler: HandlerType)  //自定义注解类,HandlerType是一个枚举类型,里面定义的就是学生和老师的增删改操作,在这里就不展示具体内容了
    
    1. 接下来就是要定义切面类了

    @Aspect   //该注解声明这个类为一个切面类
    @Component
    class HandlerAspect{
    
     @Autowired
     private lateinit var handlerService: HandlerService
    
    @AfterReturning("@annotation(handler)")   //当有函数注释了注解,将会在函数正常返回后在执行我们定义的方法
    fun hanler(hanler: Hanler) {
        handlerService.add(handler.operate.value)   //这里是真正执行记录的方法
    }
    }
    
    1. 最后就是我们本来的业务方法了

    /**
    * 删除学生方法
    */
    @Handler(operate= Handler.STUDENT_DELETE)   //当执行到删除学生方法时,切面类就会起作用了,当学生正常删除后就会执行记录方法,我们就可以看到记录方法生成的数据
    fun delete(id:String) {
       studentService.delete(id)
    }
    

     

    3. AOP实现原理

    我们现在了解了代码中如何实现,那么AOP实现的原理是什么呢?之前看了一个博客说到,提到AOP大家都知道他的实现原理是动态代理,显然我之前就是不知道的,哈哈,但是相信阅读文章的你们一定是知道的。

    讲到动态代理就不得不说代理模式了, 代理模式的定义:给某一个对象提供一个代理,并由代理对象控制对原对象的引用。代理模式包含如下角色:subject:抽象主题角色,是一个接口。该接口是对象和它的代理共用的接口; RealSubject:真实主题角色,是实现抽象主题接口的类; Proxy:代理角色,内部含有对真实对象RealSubject的引用,从而可以操作真实对象。代理对象提供与真实对象相同的接口,以便代替真实对象。同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装。如下图所示:

     

    那么代理又分为静态代理和动态代理,这里写两个小的demo,动态代理采用的就是JDK代理。举个例子就是现在一个班上的学生需要交作业,现在由班长代理交作业,那么班长就是代理,学生就是被代理的对象。

     

    3.1 静态代理

    首先,我们创建一个Person接口。这个接口就是学生(被代理类),和班长(代理类)的公共接口,他们都有交作业的行为。这样,学生交作业就可以让班长来代理执行。

    /**
     * Created by Mapei on 2018/11/7
     * 创建person接口
     */
    public interface Person {
        //交作业
        void giveTask();
    }
    

    Student类实现Person接口,Student可以具体实施交作业这个行为。

    /**
     * Created by Mapei on 2018/11/7
     */
    public class Student implements Person {
        private String name;
        public Student(String name) {
            this.name = name;
        }
    
        public void giveTask() {
            System.out.println(name + "交语文作业");
        }
    }
    

    StudentsProxy类,这个类也实现了Person接口,但是还另外持有一个学生类对象,那么他可以代理学生类对象执行交作业的行为。

     

    /**
     * Created by Mapei on 2018/11/7
     * 学生代理类,也实现了Person接口,保存一个学生实体,这样就可以代理学生产生行为
     */
    public class StudentsProxy implements Person{
        //被代理的学生
        Student stu;
    
        public StudentsProxy(Person stu) {
            // 只代理学生对象
            if(stu.getClass() == Student.class) {
                this.stu = (Student)stu;
            }
        }
    
        //代理交作业,调用被代理学生的交作业的行为
        public void giveTask() {
            stu.giveTask();
        }
    }
    

    下面测试一下,看代理模式如何使用:

    /**
     * Created by Mapei on 2018/11/7
     */
    public class StaticProxyTest {
        public static void main(String[] args) {
            //被代理的学生林浅,他的作业上交有代理对象monitor完成
            Person linqian = new Student("林浅");
    
            //生成代理对象,并将林浅传给代理对象
            Person monitor = new StudentsProxy(linqian);
    
            //班长代理交作业
            monitor.giveTask();
        }
    }
    

    运行结果:

     

     

    这里并没有直接通过林浅(被代理对象)来执行交作业的行为,而是通过班长(代理对象)来代理执行了。这就是代理模式。代理模式就是在访问实际对象时引入一定程度的间接性,这里的间接性就是指不直接调用实际对象的方法,那么我们在代理过程中就可以加上一些其他用途。比如班长在帮林浅交作业的时候想告诉老师最近林浅的进步很大,就可以轻松的通过代理模式办到。在代理类的交作业之前加入方法即可。这个优点就可以运用在spring中的AOP,我们能在一个切点之前执行一些操作,在一个切点之后执行一些操作,这个切点就是一个个方法。这些方法所在类肯定就是被代理了,在代理过程中切入了一些其他操作。

    3.2 动态代理

    动态代理和静态代理的区别是,静态代理的的代理类是我们自己定义好的,在程序运行之前就已经变异完成,但是动态代理的代理类是在程序运行时创建的。相比于静态代理,动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类中的方法。比如我们想在每个代理方法之前都加一个处理方法,我们上面的例子中只有一个代理方法,如果还有很多的代理方法,就太麻烦了,我们来看下动态代理是怎么去实现的。

    首先还是定义一个Person接口:

    /**
     * Created by Mapei on 2018/11/7
     * 创建person接口
     */
    public interface Person {
        //交作业
        void giveTask();
    }
    

    接下来是创建需要被代理的实际类,也就是学生类:

     

    /**
     * Created by Mapei on 2018/11/7
     */
    public class Student implements Person {
        private String name;
        public Student(String name) {
            this.name = name;
        }
    
        public void giveTask() {
            System.out.println(name + "交语文作业");
        }
    }
    

    创建StuInvocationHandler类,实现InvocationHandler接口,这个类中持有一个被代理对象的实例target。InvocationHandler中有一个invoke方法,所有执行代理对象的方法都会被替换成执行invoke方法。

    /**
     * Created by Mapei on 2018/11/7
     */
    public class StuInvocationHandler<T> implements InvocationHandler {
        //invocationHandler持有的被代理对象
        T target;
    
        public StuInvocationHandler(T target) {
            this.target = target;
        }
    
        /**
         * proxy:代表动态代理对象
         * method:代表正在执行的方法
         * args:代表调用目标方法时传入的实参
         */
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("代理执行" +method.getName() + "方法");
            Object result = method.invoke(target, args);
            return result;
        }
    }
    

    那么接下来我们就可以具体的创建代理对象了。

    /**
     * Created by Mapei on 2018/11/7
     * 代理类
     */
    public class ProxyTest {
        public static void main(String[] args) {
    
            //创建一个实例对象,这个对象是被代理的对象
            Person linqian = new Student("林浅");
    
            //创建一个与代理对象相关联的InvocationHandler
            InvocationHandler stuHandler = new StuInvocationHandler<Person>(linqian);
    
            //创建一个代理对象stuProxy来代理linqian,代理对象的每个执行方法都会替换执行Invocation中的invoke方法
            Person stuProxy = (Person) Proxy.newProxyInstance(Person.class.getClassLoader(), new Class<?>[]{Person.class}, stuHandler);
    
            //代理执行交作业的方法
            stuProxy.giveTask();
        }
    }
    

    我们执行代理测试类,首先我们创建了一个需要被代理的学生林浅,将林浅传入stuHandler中,我们在创建代理对象stuProxy时,将stuHandler作为参数,那么所有执行代理对象的方法都会被替换成执行invoke方法,也就是说,最后执行的是StuInvocationHandler中的invoke方法。所以在看到下面的运行结果也就理所当然了。

     

     

    那么到这里问题就来了,为什么代理对象执行的方法都会通过InvocationHandler中的invoke方法来执行,带着这个问题,我们需要看一下动态代理的源码,对他进行简单的分析。

    上面我们使用Proxy类的newProxyInstance方法创建了一个动态代理对象,看一下他的源码:

    public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
            throws IllegalArgumentException
        {
            Objects.requireNonNull(h);
    
            final Class<?>[] intfs = interfaces.clone();
            final SecurityManager sm = System.getSecurityManager();
            if (sm != null) {
                checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
            }
    
            /*
             * Look up or generate the designated proxy class.
             */
            Class<?> cl = getProxyClass0(loader, intfs);
    
            /*
             * Invoke its constructor with the designated invocation handler.
             */
            try {
                if (sm != null) {
                    checkNewProxyPermission(Reflection.getCallerClass(), cl);
                }
    
                final Constructor<?> cons = cl.getConstructor(constructorParams);
                final InvocationHandler ih = h;
                if (!Modifier.isPublic(cl.getModifiers())) {
                    AccessController.doPrivileged(new PrivilegedAction<Void>() {
                        public Void run() {
                            cons.setAccessible(true);
                            return null;
                        }
                    });
                }
                return cons.newInstance(new Object[]{h});
            } catch (IllegalAccessException|InstantiationException e) {
                throw new InternalError(e.toString(), e);
            } catch (InvocationTargetException e) {
                Throwable t = e.getCause();
                if (t instanceof RuntimeException) {
                    throw (RuntimeException) t;
                } else {
                    throw new InternalError(t.toString(), t);
                }
            } catch (NoSuchMethodException e) {
                throw new InternalError(e.toString(), e);
            }
      }
    

    然后,我们需要重点关注Class<?> cl = getProxyClass0(loader, intfs)这句代码,这里产生了代理类,这个类就是动态代理的关键,由于是动态生成的类文件,我们将这个类文件打印到文件中。

            byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0", Student.class.getInterfaces());
            String path = "/Users/mapei/Desktop/okay/65707.class";
    
            try{
                FileOutputStream fos = new FileOutputStream(path);
                fos.write(classFile);
                fos.flush();
                System.out.println("代理类class文件写入成功");
            }catch (Exception e) {
                System.out.println("写文件错误");
            }
    

    对这个class文件进行反编译,我们看看jdk为我们生成了什么样的内容:

    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    import java.lang.reflect.UndeclaredThrowableException;
    import proxy.Person;
    
    public final class $Proxy0 extends Proxy implements Person
    {
      private static Method m1;
      private static Method m2;
      private static Method m3;
      private static Method m0;
    
      /**
      *注意这里是生成代理类的构造方法,方法参数为InvocationHandler类型,看到这,是不是就有点明白
      *为何代理对象调用方法都是执行InvocationHandler中的invoke方法,而InvocationHandler又持有一个
      *被代理对象的实例,就可以去调用真正的对象实例。
      */
      public $Proxy0(InvocationHandler paramInvocationHandler)
        throws 
      {
        super(paramInvocationHandler);
      }
    
      //这个静态块本来是在最后的,我把它拿到前面来,方便描述
       static
      {
        try
        {
          //看看这儿静态块儿里面的住giveTask通过反射得到的名字m3,其他的先不管
          m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
          m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
          m3 = Class.forName("proxy.Person").getMethod("giveTask", new Class[0]);
          m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
          return;
        }
        catch (NoSuchMethodException localNoSuchMethodException)
        {
          throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
        }
        catch (ClassNotFoundException localClassNotFoundException)
        {
          throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
        }
      }
    
      /**
      * 
      *这里调用代理对象的giveMoney方法,直接就调用了InvocationHandler中的invoke方法,并把m3传了进去。
      *this.h.invoke(this, m3, null);我们可以对将InvocationHandler看做一个中介类,中介类持有一个被代理对象,在invoke方法中调用了被代理对象的相应方法。通过聚合方式持有被代理对象的引用,把外部对invoke的调用最终都转为对被代理对象的调用。
      */
      public final void giveTask()
        throws 
      {
        try
        {
          this.h.invoke(this, m3, null);
          return;
        }
        catch (Error|RuntimeException localError)
        {
          throw localError;
        }
        catch (Throwable localThrowable)
        {
          throw new UndeclaredThrowableException(localThrowable);
        }
      }
    
    }
    

    看完了动态代理的源码,我们接下来就要看一下Spring中AOP实现的源码是怎样的?

    4. 部分源码解析

    aop创建代理的源码分析

    1. 看一下bean如何被包装为proxy

               protected Object createProxy(
               Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {
    
           if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
               AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
           }
    
           // 1.创建proxyFactory,proxy的生产主要就是在proxyFactory做的
           ProxyFactory proxyFactory = new ProxyFactory();
           proxyFactory.copyFrom(this);
    
           if (!proxyFactory.isProxyTargetClass()) {
               if (shouldProxyTargetClass(beanClass, beanName)) {
                   proxyFactory.setProxyTargetClass(true);
               }
               else {
                   evaluateProxyInterfaces(beanClass, proxyFactory);
               }
           }
    
           // 2.将当前bean适合的advice,重新封装下,封装为Advisor类,然后添加到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);
           }
    
           // 3.调用getProxy获取bean对应的proxy
           return proxyFactory.getProxy(getProxyClassLoader());
       }
    
    1. 创建何种类型的Proxy?JDKProxy还是CGLIBProxy?

        public Object getProxy(ClassLoader classLoader) {
            return createAopProxy().getProxy(classLoader);
        }
        // createAopProxy()方法就是决定究竟创建何种类型的proxy
        protected final synchronized AopProxy createAopProxy() {
            if (!this.active) {
                activate();
            }
            // 关键方法createAopProxy()
            return getAopProxyFactory().createAopProxy(this);
        }
    
        // createAopProxy()
        public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
            // 1.config.isOptimize()是否使用优化的代理策略,目前使用与CGLIB
            // config.isProxyTargetClass() 是否目标类本身被代理而不是目标类的接口
            // hasNoUserSuppliedProxyInterfaces()是否存在代理接口
            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.");
                }
    
                // 2.如果目标类是接口类(目标对象实现了接口),则直接使用JDKproxy
                if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
                    return new JdkDynamicAopProxy(config);
                }
    
                // 3.其他情况则使用CGLIBproxy
                return new ObjenesisCglibAopProxy(config);
            }
            else {
                return new JdkDynamicAopProxy(config);
            }
        }
    
    1. getProxy()方法

       final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable// JdkDynamicAopProxy类结构,由此可知,其实现了InvocationHandler,则必定有invoke方法,来被调用,也就是用户调用bean相关方法时,此invoke()被真正调用
       // getProxy()
       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, true);
           findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
    
           // JDK proxy 动态代理的标准用法
           return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
       }
    
    1. invoke()方法法

        //使用了JDK动态代理模式,真正的方法执行在invoke()方法里,看到这里在想一下上面动态代理的例子,是不是就完全明白Spring源码实现动态代理的原理了。
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            MethodInvocation invocation;
            Object oldProxy = null;
            boolean setProxyContext = false;
    
            TargetSource targetSource = this.advised.targetSource;
            Class<?> targetClass = null;
            Object target = null;
    
            try {
                // 1.以下的几个判断,主要是为了判断method是否为equals、hashCode等Object的方法
                if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
                    // The target does not implement the equals(Object) method itself.
                    return equals(args[0]);
                }
                else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
                    // The target does not implement the hashCode() method itself.
                    return hashCode();
                }
                else if (method.getDeclaringClass() == DecoratingProxy.class) {
                    // There is only getDecoratedClass() declared -> dispatch to proxy config.
                    return AopProxyUtils.ultimateTargetClass(this.advised);
                }
                else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
                        method.getDeclaringClass().isAssignableFrom(Advised.class)) {
                    // Service invocations on ProxyConfig with the proxy config...
                    return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
                }
    
                Object retVal;
    
                if (this.advised.exposeProxy) {
                    // Make invocation available if necessary.
                    oldProxy = AopContext.setCurrentProxy(proxy);
                    setProxyContext = true;
                }
    
                // May be null. Get as late as possible to minimize the time we "own" the target,
                // in case it comes from a pool.
                target = targetSource.getTarget();
                if (target != null) {
                    targetClass = target.getClass();
                }
                // 2.获取当前bean被拦截方法链表
                List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
    
                // 3.如果为空,则直接调用target的method
                if (chain.isEmpty()) {
                    Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
                    retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
                }
                // 4.不为空,则逐一调用chain中的每一个拦截方法的proceed,这里的一系列执行的原因以及proceed执行的内容,我 在这里就不详细讲了,大家感兴趣可以自己去研读哈
                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();
                }
    
                ...
                return retVal;
            }
        }
        }
    

     

    展开全文
  • Aop如何实现代理模式

    千次阅读 2020-04-22 22:21:38
    Aop实现方法 上篇文章讲解了aop的概念,就是从类中抽取出方法,加强后再放回去,分为两种代理,JDK动态代理,Cglib代理。第一种当有接口的时候,第二种为没有接口的时候。 JDK动态代理 这里还是举那个例子,假设要...

    Aop实现方法

    上篇文章讲解了aop的概念,就是从类中抽取出方法,加强后再放回去,分为两种代理,JDK动态代理,Cglib代理。第一种当有接口的时候,第二种为没有接口的时候。

    JDK动态代理

    这里还是举那个例子,假设要完成客户类的项目,首先定义一个接口

    public interface UserDao
    { 
       public void save();       //定义一个保存方法
       public void delete();     //定义一个删除方法
    }

    然后让你给实现该类方法

    public class UserDaoImpl implements UserDao
    {
      public void safe()
      {
        System.out.println("保存方法执行");
      }
      public void delete()
      {
        System.out.println("删除方法执行");
      }
    }

    然后让你将每个方法加上用户校验的步骤,上篇中说,如果实现多个接口,好几十种方法,你不可能每个方法种都加上用户校验,虽然很简单,复制粘贴 ,但是很冗余。所以这里用代理模式来实现该方法。

    首先定义代理类,就是代理UserDaoImpl的类。

    public calss UserImplProxy implements InvocationHandler   //实现回调接口,它会实现一个invoke函数,就是你要在哪个类里面实现加强,哪个就继承该接口
    {
       private UserDao userDao;           //定义一个接口
       public jdkProxy(UserDao userDao)
    {
       this.userDao=userDao;              //向上转型实例接口,它们用的都是java运行时多态技术,或者叫运行期绑定技术,在测试类里向上转型
    }
    public Object createProxy()
    {
        Object proxy= Proxy.newProxyInstance(userDao.getClass().getClassLoader(),userDao.getClass().getInterfaces(),this);//返回一个object类,就是你要代理的对象。
        return proxy;   //返回代理对象,这里其实已经完成增强封装。
    }
    
    public Object invoke(Object proxy, Method method, Object[] args)   throws Throwable {     //invoke表示回调的意思,这里实现增强函数的操作在这里,当在上面newProxyInstance的第三个参数将该invoke函数封装在了里面调用的时候就会自动调用invoke,每个函数都会自动调用invoke
        if("safe".equals(method.getName()))    //实现拦截,当函数名称为save时候进行操作,实现校验。
        {
            System.out.println("用户校验执行");
        }
          return method.invoke(userDao,args);    //回调函数,反射调用这个method执行save方法
     }
    }

    newProxyInstance函数参数里第一个参数为类加载器,就是你要代理的对象的类,第二个参数为接口加载器,就是代理对象实现的接口,第三个参数为你要实现加强函数的类对象,比如就在该类里面实现了加强函数invoke,所以就用this。这里就是产生了一个代理对象。

    对于第三个参数不理解的可以阅读下面文章

    Java动态代理InvocationHandler和Proxy学习笔记

    在测试类里:

    public class test {
    
        @Test
        public void test()
        {
            UserDao tt=new UserDaoImple();   //此处为向上转型实现接口,实例化接口
            UserDao proxy;  //定义一个接口对象
            proxy=(UserDao)new jdkProxy(tt).createProxy();//用代理类来实例化接口,实例化后的对象就是增强后的
            proxy.safe();  
        }
    }

    Test测试后会出现下面结果

    用户校验执行
    保存方法执行

    这里实现了增强操作,但是还有一点就是你要求增强方法在原函数方法之前还是之后,后面讲解用配置方法来实现代理模式。

    总结

    jdk动态代理就是再创建一个类,传入该类一个向上转型后的接口对象,根据该对象用newProxyInstance去创建代理类,返回一个代理对象,同时调用定义好的增强函数(第三个参数)。当创建对象时候就是一个增强后的对象了。
    在这里插入图片描述

    Cglib动态代理

    若没有实现任何接口,就是一个光秃秃的类,那么就简单了。就使用Cglib代理,其实cglib原理就是创建一个子类,然后实现加强但不像纵向继承那么麻烦。

    首先定义一个用户类

    public class UserDao {
        
        public void save()
        {
            System.out.println("这是Sava方法");
        }
        public void delete()
        {
            System.out.println("这是delete方法");
        }
    }
    

    还是上面那个业务场景,要对该类里面方法实现增强。实现用户校验功能

    首先定义代理类

    public class UserDaoProxy implements MethodInterceptor {   //MethodInterceptor和InvocationHandler道理一样,你要将实现加强的函数放在哪个类,哪个类就实现该接口,同时也会让你实现增强函数
        private UserDao userDao;
        public UserDaoProxy(UserDao userDao)
        {
            this.userDao=userDao;       //根据构造函数来获取需要代理对象的属性,来创造代理类
        }
        public Object CreateProxy ()
        {
            //创建核心类 它是一个字节码增强器,可以用来为无接口的类创建代理,它的功能与java自带的Proxy类挺相似的。它会根据某个给定的类创建子类
            Enhancer enhancer=new Enhancer();
            //获得需要代理的类。因为之前说了,cglib原理是创建一个子类
            enhancer.setSuperclass(userDao.getClass());  //由传过来的对象可以获得它的属性即它是哪个类的
            //设置回调函数,和jdk动态代理newProxyInstance一样,从这里调用增强函数,因为该类实现了MethodInterceptor接口,所以就在这个类里写增强函数,所以这里用this表示增强函数就在该类里
            enhancer.setCallback(this);
            //创建代理对象,很简单,将增强后的方法封装到Object类里,就可以根据这个创建对象
            Object proxy=enhancer.create();
            return  proxy;
        }
         //这里就是增强函数,和JDK里的invoke()函数一样,可以对照一下
    
        public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
            if("save".equals(method.getName()))          //当调用方法名称为save实现增强
            {
                System.out.println("用户校验");
            }
            return methodProxy.invokeSuper(proxy,args); //调用原来的save方法
        }
    }

    然后用Test来测试一下

    public class test {
    @Test
        public void demo1()
        {
            UserDao userDao=new UserDao();    //定义一个对象
            UserDao userDao1=(UserDao) newUserDaoProxy(userDao).CreateProxy();   //这里是向上转型,将子类对象赋值给父类对象
            userDao1.save();
        }
    }
    

    输出结果如下所示

    用户校验
    这是Sava方法

    由此可以看出实现了增强

    总结

    当你需要实现接口后的类需要增强的时候,用JDK动态代理,当没有实现接口时候,就为Cglib动态代理,这两种方法类似,都是将你要代理的类进行封装,也就是函数增强,但并不是重写函数,而是在函数里增加了方法。非常简单并且减少了代码量。这上面两个例子只是笨方法来帮助理解代理模式。下一篇文章为通过配置文件来实现代理模式。

    展开全文
  • Spring AOP实现原理 1、请问如下代码在配置完全正常的情况下控制台会输出哪些内容? 输出的顺序是什么? 简单业务代码 public interface UserService { void save(); void saveContacts(); } @Service ...

     

    如下代码在配置完全正常的情况下控制台会有几条输出?

    @SpringBootTest
    class SpringaopApplicationTests {
        @Autowired
        private UserService userService;
        @Test
        void userTest() {
            userService.save();
        }
    
    }

    简单业务代码

    public interface UserService {
        void save();
        void saveContacts();
    }
    
    
    @Service
    public class UserServiceImpl implements UserService {
        @Override
        public void save() {
            System.out.println("save user logic");
            saveContacts();
        }
    
        @Override
        public void saveContacts() {
            System.out.println("save user contacts logic");
        }
    }
    
    

     AOP代码

    @Aspect
    @Component
    public class UserAOP {
        @Pointcut("execution(public * com.learn.springaop.service..*(..))")
        public void servicePointcut() {
        }
    
        @After("servicePointcut()")
        public void after(JoinPoint joinPoint) throws Throwable {
            String name=joinPoint.getSignature().toString();
            System.out.println(String.format("日志记录 %s",name));
        }
    }

    对Spring比较了解的同学,答案应该不难得出。答案还是需要卖下关子,等分享完Spring AOP后再揭晓。

    我这里不讲高大上的理论,也不讲什么是AOP,AOP与OOP的思想。只讲我们写了一个Spring AOP后为什么能实现这样的结果,spring是如何帮忙我们实现的。本篇是属于AOP系列的第一篇。

    这里分享的是基于Spring Boot 2.4.0。

    首先看@EnableAspectJAutoProxy 这个注解,注解上的@Import AspectJAutoProxyRegistrar.class

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Import(AspectJAutoProxyRegistrar.class)
    public @interface EnableAspectJAutoProxy {
    }

     org.springframework.context.annotation.AspectJAutoProxyRegistrar

    @Override
    	public void registerBeanDefinitions(
    			AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    
    		AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
    
    		AnnotationAttributes enableAspectJAutoProxy =
    				AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
    		if (enableAspectJAutoProxy != null) {
    			if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
    				AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
    			}
    			if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
    				AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
    			}
    		}
    	}

     AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);

    @Nullable
    	public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry) {
    		return registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry, null);
    	}
    
    public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(
    			BeanDefinitionRegistry registry, @Nullable Object source) {
    
    		return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
    	}

    下面是AnnotationAwareAspectJAutoProxyCreator类的UML图,从UML图我们发现AnnotationAwareAspectJAutoProxyCreator实现了BeanPostProcessor的接口。大家都知道BeanPostProcessor接口在Spring的容器里是Bean实例生命周期里非常重要的一步。BeanPostProcessor的postProcessBeforeInitialization(Object bean, String beanName),postProcessAfterInitialization(Object bean, String beanName)方法可以给开发者对bean实例初始化前和初始化后可以做些自定义的处理。那AnnotationAwareAspectJAutoProxyCreator又对bean做了什么特殊的处理呢?

    通过阅读源码我们可以在org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator类实现了BeanPostProcessor的postProcessAfterInitialization方法。从该方法的注释可以看出这个bean的后置处理器是要针对满足条件的bean创建一个代理。其中getCacheKey是获取一个可以缓存的key。主要看下wrapIfNecessary(bean, beanName, cacheKey)这个方法干了啥

    /**
    	 * Create a proxy with the configured interceptors if the bean is
    	 * identified as one to proxy by the subclass.
    	 * @see #getAdvicesAndAdvisorsForBean
    	 */
    public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
    		if (bean != null) {
    			Object cacheKey = getCacheKey(bean.getClass(), beanName);
    			if (this.earlyProxyReferences.remove(cacheKey) != bean) {
    				return wrapIfNecessary(bean, beanName, cacheKey);
    			}
    		}
    		return bean;
    	}

    从wrapIfNecessary里代码可以看到首先从当前bean的class上获取切面和通知,如果获取到则表示当前实例需要执行AOP切面。需要执行AOP后,Spring就针对当前实例生成了一个代理,并且把这个代理实例返回给Spring容器。我们通过getBean操作获取到的就是这代理类的实例。

    protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
    		if (StringUtils.hasLength(beanName) && 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;
    		}
    
    		// Create proxy if we have advice.
            //从当前bean实例上获取通知和切面
    		Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
           //如果获取的结果不等余null,则为当前bean创建一个代理类
    		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;
    	}
    

    org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator#getAdvicesAndAdvisorsForBean解释了如何获取一个类是否满足AOP的条件。

    protected Object[] getAdvicesAndAdvisorsForBean(
    			Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {
    
    		List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
           //如果没找到满足的切面则返回null
    		if (advisors.isEmpty()) {
    			return DO_NOT_PROXY;
    		}
    		return advisors.toArray();
    	}
    
    
    	/**
    	 * Find all eligible Advisors for auto-proxying this class.
    	 * @param beanClass the clazz to find advisors for
    	 * @param beanName the name of the currently proxied bean
    	 * @return the empty List, not {@code null},
    	 * if there are no pointcuts or interceptors
    	 * @see #findCandidateAdvisors
    	 * @see #sortAdvisors
    	 * @see #extendAdvisors
    	 */
    	protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
           //找出所有候选的切面,即在BeanFactory里所有类型是Advisor的实例
           //具体@Aspectj如何解析成Advisor待分析
    		List<Advisor> candidateAdvisors = findCandidateAdvisors();
           //从所有候选的切面里找满足当前实例的切面
    		List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
    		extendAdvisors(eligibleAdvisors);
    		if (!eligibleAdvisors.isEmpty()) {
    			eligibleAdvisors = sortAdvisors(eligibleAdvisors);
    		}
    		return eligibleAdvisors;
    	}
    org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator#findAdvisorsThatCanApply
    	protected List<Advisor> findAdvisorsThatCanApply(
    			List<Advisor> candidateAdvisors, Class<?> beanClass, String beanName) {
    
    		ProxyCreationContext.setCurrentProxiedBeanName(beanName);
    		try {
    			return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass);
    		}
    		finally {
    			ProxyCreationContext.setCurrentProxiedBeanName(null);
    		}
    	}

    org.springframework.aop.support.AopUtils#findAdvisorsThatCanApply,Pointcut表达式配规则参考org.aspectj.weaver.patterns.SignaturePattern

    public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
    		if (candidateAdvisors.isEmpty()) {
    			return candidateAdvisors;
    		}
    		List<Advisor> eligibleAdvisors = new ArrayList<>();
            //如果 切面是Introduction类型的切面判断当前实例是否满足条件,Introduction类型一般用得比较少
    		for (Advisor candidate : candidateAdvisors) {
    			if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {
    				eligibleAdvisors.add(candidate);
    			}
    		}
    		boolean hasIntroductions = !eligibleAdvisors.isEmpty();
    		for (Advisor candidate : candidateAdvisors) {
    			if (candidate instanceof IntroductionAdvisor) {
    				// already processed
    				continue;
    			}
             //剩余的其他普通切面是否满足当前实例
    			if (canApply(candidate, clazz, hasIntroductions)) {
    				eligibleAdvisors.add(candidate);
    			}
    		}
        //返回所有匹配的切面
    		return eligibleAdvisors;
    	}
    
    	public static boolean canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions) {
    		if (advisor instanceof IntroductionAdvisor) {
    			return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass);
    		}
          //如果是PointcutAdvisor类型的切面,则获取切面的Pointcut配置,判断当前实例是否满足Pointcut配置的条件
    		else if (advisor instanceof PointcutAdvisor) {
    			PointcutAdvisor pca = (PointcutAdvisor) advisor;
    			return canApply(pca.getPointcut(), targetClass, hasIntroductions);
    		}
    		else {
    			// It doesn't have a pointcut so we assume it applies.
    			return true;
    		}
    	}
    
    public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
    		Assert.notNull(pc, "Pointcut must not be null");
        //这里的pc类型是AspectJExpressionPointcut类型是切点,AspectJExpressionPointcut会根据Pointcut表达式生成匹配器,判断当前实例的class是否满足表达式的类匹配
    		if (!pc.getClassFilter().matches(targetClass)) {
    			return false;
    		}
    
    		MethodMatcher methodMatcher = pc.getMethodMatcher();
    		if (methodMatcher == MethodMatcher.TRUE) {
    			// No need to iterate the methods if we're matching any method anyway...
    			return true;
    		}
    
    		IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;
    		if (methodMatcher instanceof IntroductionAwareMethodMatcher) {
    			introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;
    		}
    
    		Set<Class<?>> classes = new LinkedHashSet<>();
    		if (!Proxy.isProxyClass(targetClass)) {
    			classes.add(ClassUtils.getUserClass(targetClass));
    		}
    		classes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetClass));
           //判断当前实例的方法是否满足切面的条件,只要有一个方法满足Pointcut表达式则返回true
    		for (Class<?> clazz : classes) {
    			Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
    			for (Method method : methods) {
    				if (introductionAwareMethodMatcher != null ?
    						introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) :
    						methodMatcher.matches(method, targetClass)) {
    					return true;
    				}
    			}
    		}
    
    		return false;
    	}
    

    找到合适的切面后org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#createProxy就为当前实例生成一个Proxy

    protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
    			@Nullable Object[] specificInterceptors, TargetSource targetSource) {
    
    		if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
    			AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
    		}
    
    		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);
    		proxyFactory.addAdvisors(advisors);
    		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(@Nullable ClassLoader classLoader) {
    		return createAopProxy().getProxy(classLoader);
    	}
    

    org.springframework.aop.framework.DefaultAopProxyFactory#createAopProxy

    	public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
    		if (!IN_NATIVE_IMAGE &&
    				(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.");
    			}
             //如果当前实例实现了接口则用JdkDynamicAopProxy,否则Cglib的方式
    			if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
    				return new JdkDynamicAopProxy(config);
    			}
    			return new ObjenesisCglibAopProxy(config);
    		}
    		else {
    			return new JdkDynamicAopProxy(config);
    		}
    	}
    

    这里主要看下JdkDynamicAopProxy方法是如何实现的,重点代码是 Proxy.newProxyInstance,大家都知道Spring的AOP的实现是通过代理实现的,原来是这样代理的。从 Proxy.newProxyInstance传的参数看JdkDynamicAopProxy实现了InvocationHandler接口,代理在执行的时候会调用InvocationHandler的invoke方法,下面看看invoke方法的内容

    	@Override
    	public Object getProxy() {
    		return getProxy(ClassUtils.getDefaultClassLoader());
    	}
    
    	@Override
    	public Object getProxy(@Nullable ClassLoader classLoader) {
    		if (logger.isTraceEnabled()) {
    			logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
    		}
         //这里不就是JDK的动态代理的实现吗
    		return Proxy.newProxyInstance(classLoader, this.proxiedInterfaces, this);
    	}

     可以看到invoke方法里执行的都是切面的通知,具体这些通知如何调用,还是比较复杂的,下一篇分析AOP通知的具体执行过程。

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    		Object oldProxy = null;
    		boolean setProxyContext = false;
    
    		TargetSource targetSource = this.advised.targetSource;
    		Object target = null;
    
    		try {
    			if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
    				// The target does not implement the equals(Object) method itself.
    				return equals(args[0]);
    			}
    			else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
    				// The target does not implement the hashCode() method itself.
    				return hashCode();
    			}
    			else if (method.getDeclaringClass() == DecoratingProxy.class) {
    				// There is only getDecoratedClass() declared -> dispatch to proxy config.
    				return AopProxyUtils.ultimateTargetClass(this.advised);
    			}
    			else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
    					method.getDeclaringClass().isAssignableFrom(Advised.class)) {
    				// Service invocations on ProxyConfig with the proxy config...
    				return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
    			}
    
    			Object retVal;
    
    			if (this.advised.exposeProxy) {
    				// Make invocation available if necessary.
    				oldProxy = AopContext.setCurrentProxy(proxy);
    				setProxyContext = true;
    			}
    
    			// Get as late as possible to minimize the time we "own" the target,
    			// in case it comes from a pool.
    			target = targetSource.getTarget();
    			Class<?> targetClass = (target != null ? target.getClass() : null);
    
    			// 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.
    				Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
    				retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
    			}
    			else {
    				// We need to create a method invocation...
    				MethodInvocation invocation =
    						new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
    				// Proceed to the joinpoint through the interceptor chain.
    				retVal = invocation.proceed();
    			}
    
    			// Massage return value if necessary.
    			Class<?> returnType = method.getReturnType();
    			if (retVal != null && retVal == target &&
    					returnType != Object.class && returnType.isInstance(proxy) &&
    					!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
    				// Special case: it returned "this" and the return type of the method
    				// is type-compatible. Note that we can't help if the target sets
    				// a reference to itself in another returned object.
    				retVal = proxy;
    			}
    			else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
    				throw new AopInvocationException(
    						"Null return value from advice does not match primitive return type for: " + method);
    			}
    			return retVal;
    		}
    		finally {
    			if (target != null && !targetSource.isStatic()) {
    				// Must have come from TargetSource.
    				targetSource.releaseTarget(target);
    			}
    			if (setProxyContext) {
    				// Restore old proxy.
    				AopContext.setCurrentProxy(oldProxy);
    			}
    		}
    	}

    总结: Spring boot引入spring-boot-starter-aop stater后,开启自动配置会在容器里注册一个实现了BeanPostProcessor的AnnotationAwareAspectJAutoProxyCreator实例。在容器创建每个bean后且实例化后,spring 容器会遍历所有的BeanPostProcessor并执行对应processor的postProcessAfterInitialization方法。AnnotationAwareAspectJAutoProxyCreator的postProcessAfterInitialization会获取所有定义的切面增强(advisors)作为候选,然后循环判断每一个pointcut表达式是否满足当前的实例,如果有满足的advisor,说明该实例需要被AOP增强,则为该实例生成代理实例,并加入Spring容器里,如果没有任何advisor满足则返回原实例。

    开篇问题的答案是3条输出:

    • save user logic
    • save user contacts logic
    • 日志记录 void com.learn.springaop.service.impl.UserServiceImpl.save()

    结论:既然AOP是通过代理来实现的,且容器里存的实例也是代理后的实例,那么要想增强的部分代码被执行,只要是调用代理的实例肯定都会被执行。那么开篇的问题的结果为什么saveContacts()方法的增强部分没有被执行呢。我们知道调用代理类实例的执行顺序是 Caller->Proxy执行增强的逻辑->真实的实例调用。而在同一个类里面方法间的调用等同与this.saveContacts();而这里的this是指真实的实例而不是被代理的实例。所以他的增强部分代码不会被执行。

    破局:如果真实场景遇到这种情况要怎么解决呢。

    1. 尽量规避这种情况(Spring官方推荐)
    2. 在对应class里注入实例自己,在方法内部调用时通过注入的实例调用。或者实现ApplicationContextAware通过Object getBean(String name)方式获取实例调用,所有能实现从容器里获取实例的方式都可以。
    3. 强制转换AopContext.currentProxy()为对应实例的类型后进行调用。Spring官方不推荐使用,因为这样就把代码与AOP耦合了。

    延伸:下面是AOP能实现和不能实现的情况

    被代理类 AOP实现方式 private 方法 finall方法 protected方法 default方法 不在接口里的方法 prototype类型
    实现了接口 JDK动态代理 不存在这种情况 不存在这种情况 Interface必须public Interface必须public 是(每次生成不同实例的代理)
    未实现接口 Cglib(生成子类) 否(方法不能被重写) 否(finall方法不能被重写) 是(满足java的可见性规则的调用即可,且pointcut表达式允许) 是(满足java的可见性规则的调用即可,且pointcut表达式允许) 不存在这种情况 是(每次生成不同实例的代理)

             

    展开全文
  • 前面我们讲过了经典的基于代理的AOP和基于自动代理的AOP如何实现的,有了前面的基础,学习基于AOP标签的AOP的实现原理,就很简单了。 经典的基于代理的AOP: ...基于自动代理的AOP: ...
  • 纯 Java 实现,无编译时特殊处理、不修改和控制 ClassLoader 仅支持方法级别的 Joint Points 非完整的 AOP 框架 与 IoC 进行了整合 与 AspectJ 的注解进行了整合 使用层面,有三种编程模型...
  • 从前面代理的原理我们知道,代理的目的是调用目标方法时我们可以转而执行InvocationHandler类的invoke方法,所以如何在InvocationHandler上做文章就是Spring实现Aop的关键所在。 Spring的Aop实现是遵守Aop联盟的...
  • AOP源码实现及分析 1 主要的接口 1.1 Advice 通知 本接口定义了切面的增强方式,如:前置增强 BeforeAdvice,后置增强 AfterAdvice,异常增强 ThrowsAdvice 等。下面看两个主要的子接口的源码。 public interface ...
  • spring aop如何实现的? 要分析这个议题,我们以spring cache为例 spring cache是如何与spring整合的 在spring项目 spring-context下的META-INF\spring.handlers定义了 org.springframework.cache.config....
  • Spring实现AOP有4种方式,感兴趣的可以查看...首先,我们来看看基于经典的AOP如何实现的。 先写一个接口叫Sleepable,所有具有睡觉能力的东西都可以实现该接口。 package com.ghs.aop; public interface Sleep...
  • AOP如何实现的

    2018-09-23 23:04:37
    Spring中的AOP代理还是离不开Spring的IOC容器,代理的生成,管理及其依赖关系都是由IOC容器负责;Spring默认使用JDK动态代理,在需要代理类而不是代理接口的时候,Spring会自动切换为使用CGLIB代理,不过现在的项目...
  • <aop:after-returning method="after" pointcut-ref="afterMethod"/> </aop:aspect> </aop:config> 切面拦截处理程序 package com.tree.common; import org.aspectj.lang.JoinPoint; import org....
  • println("使用TestService2查询数据源2中的所有书籍信息") } } 二、如何实现 接下来就详细讲述如何在Spring MVC和Mybatis的单套数据源支持上扩展多数据源切换能力。以下为双数据源,三数据源的实现方式相同。 1.首先...
  • 点击上方[全栈开发者社区]→右上角[...]→[设为星标⭐]本文将通过AOP的方式实现一个相对更加简易灵活的API安全认证服务。我们先看实现,然后介绍和分析AOP基本原理和常用术语。一、...
  • AOP实现代码

    2013-10-20 19:48:01
    详细介绍了AOP的核心功能(拦截功能)在底层是如何实现的;介绍了两种实现AOP的动态代理:jdk动态代理和cglib动态代理,并详细描述了它们在代码层面的实现。附有必须的cglib.jar和asm.jar
  • 在上一篇(https://blog.csdn.net/u011983531/article/details/80359304)我们介绍了基于经典代理的AOP实现方案,在这一篇中,我们将看看基于自动代理的AOP如何实现的。 //基于自动代理AOP &amp;lt;bean id=&...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 3,219
精华内容 1,287
关键字:

aop如何实现的