AOP如何实现及实现原理,Java开发面试题及答案

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


/**

 * 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()方法

Kafka实战笔记

关于这份笔记,为了不影响大家的阅读体验,我只能在文章中展示部分的章节内容和核心截图,如果你需要完整的pdf版本,戳这里即可免费领取

image.png

  • Kafka入门
  • 为什么选择Kafka
  • Karka的安装、管理和配置

image.png

  • Kafka的集群
  • 第一个Kafka程序
  • image.png

afka的生产者

image.png

  • Kafka的消费者
  • 深入理解Kafka
  • 可靠的数据传递

image.png

image.png

  • Spring和Kalka的整合
  • Sprinboot和Kafka的整合
  • Kafka实战之削峰填谷
  • 数据管道和流式处理(了解即可)

image.png

  • Kafka实战之削峰填谷

image.png

只能在文章中展示部分的章节内容和核心截图,如果你需要完整的pdf版本,戳这里即可免费领取。**

[外链图片转存中…(img-y4GSzFdd-1630121649511)]

  • Kafka入门
  • 为什么选择Kafka
  • Karka的安装、管理和配置

[外链图片转存中…(img-Or0UizI5-1630121649512)]

  • Kafka的集群
  • 第一个Kafka程序
  • [外链图片转存中…(img-9vVGYhwV-1630121649514)]

afka的生产者

[外链图片转存中…(img-9KyDV0FX-1630121649515)]

  • Kafka的消费者
  • 深入理解Kafka
  • 可靠的数据传递

[外链图片转存中…(img-W6bIU7gs-1630121649515)]

[外链图片转存中…(img-Bqm6FJDS-1630121649516)]

  • Spring和Kalka的整合
  • Sprinboot和Kafka的整合
  • Kafka实战之削峰填谷
  • 数据管道和流式处理(了解即可)

[外链图片转存中…(img-ioqcU0uT-1630121649517)]

  • Kafka实战之削峰填谷

[外链图片转存中…(img-gHYxjJ6z-1630121649517)]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值