AOP如何实现及实现原理,2021Java面试心得

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 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(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() {

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

Java.png
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()方法法

[外链图片转存中…(img-Al5uARzU-1635264217588)]

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值