前言:
最怕面试问到这种开放性的问题,毕竟仁者见仁智者见智,可能你说的面试官并不会觉得你说到他想要听的点子上。好了废话不多了,开始我们的正文:
什么是 AOP?
AOP (Aspect Orient Programming),直译过来就是 面向切面编程。AOP 是一种编程思想,是面向对象编程(OOP)的一种补充。面向对象编程将程序抽象成各个层次的对象,而面向切面编程是将程序抽象成各个切面。通俗点来说就是非业务代码对主业务的一种补充(增强),而不侵入原业务代码中实现解耦。
AOP中的一些术语
- 通知(Advice): AOP 框架中的增强处理。通知描述了切面何时执行以及如何执行增强处理。
- 连接点(join point): 连接点表示应用执行过程中能够插入切面的一个点,这个点可以是方法的调用、- - 异常的抛出。在 Spring AOP 中,连接点总是方法的调用。
- 切点(PointCut): 可以插入增强处理的连接点。
- 切面(Aspect): 切面是通知和切点的结合。
- 引入(Introduction):引入允许我们向现有的类添加新的方法或者属性。
- 织入(Weaving): 将增强处理添加到目标对象中,并创建一个被增强的对象,这个过程就是织入。
实际开发中难免会遇到添加非业务功能的代码,如统计、日志、权限验证、效率测试等,功能通用不可能在每个类中都去写一遍,这时候就需要“代理类”,最早的方法主要是两种:1-聚合织入;2-继承。看一下伪代码
传统AOP的实现
- 聚合:
class A{
private C;
function a{
C.c();
print("a");
}
}
class B{
private C;
function b{
C.c();
print("b");
}
}
// 公用方法类
class C{
// 增强方法
function c{
print("a");
}
}
- 继承
class A extends C{
function a{
super.c();
print("a");
}
}
class B extends C{
function b{
super.c();
print("b");
}
}
// 公用方法类
class C{
// 增强方法
function c{
print("a");
}
}
很明显,上述方式代码过于耦合。
Spring AOP五种通知(advice)方式:
- @Before:前置通知,在调用目标方法之前执行通知定义的任务
- @After:后置通知,在目标方法执行结束后,无论执行结果如何都执行通知定义的任务
- @After-returning:后置通知,在目标方法执行结束后,如果执行成功,则执行通知定义的任务
- @After-throwing:异常通知,如果目标方法执行过程中抛出异常,则执行通知定义的任务
- @Around:环绕通知,在目标方法执行前和执行后,都需要执行通知定义的任务。
Spring AOP代理原理的实现
这里直接用代码来说明
/**
* 我们定义的接口
* @author lix
* @Date 2020/5/30
*/
public interface MyInterface {
void sayHello();
}
/**************************华丽的分隔符******************************/
/**
* 我们的接口实现类
* @author lix
* @Date 2020/5/30
*/
public class MyInterfaceImpl implements MyInterface {
/**
* 需要被代理的方法
*/
@Override
public void sayHello() {
System.out.println("---execute sayHello---");
}
}
/**************************华丽的分隔符******************************/
/**
* 代理对象
* @author lix
* @Date 2020/5/30
*/
public class ProxyObject implements MyInterface{
/**
* 被代理的接口
*/
private MyInterface myInterface;
public ProxyObject(MyInterface myInterface) {
// 这里传进来的是MyInterface myInterface = new MyInterfaceImpl();
this.myInterface = myInterface;
}
/**
* 代理的方法
*/
@Override
public void sayHello() {
// @Before
System.out.println("---before execute do somethings---");
try {
myInterface.sayHello();
}catch (Exception e){
// @After-throwing
System.out.println("---catch exception do somethings---");
throw e;
}finally {
// @After
System.out.println("---after execute do somethings---");
}
}
}
意思是这么个意思,但是实际上的JDK代理类是通过反射执行的,后面会讲到。
看看 JDK动态代理的实现 ,这里比较重要的两个类:
java.lang.reflect.Proxy
java.lang.reflect.InvocationHandler
这里直接使用上面的代码改造:
/**
* 我们定义的接口
* @author lix
* @Date 2020/5/30
*/
public interface MyInterface {
void test();
void sayHello();
}
/**************************华丽的分隔符******************************/
/**
* 我们的接口实现类
* @author lix
* @Date 2020/5/30
*/
public class MyInterfaceImpl implements MyInterface {
/**
* 需要被代理的方法
*/
@Override
public void test() {
System.out.println("---my test---");
}
/**
* 需要被代理的方法
*/
@Override
public void sayHello() {
System.out.println("---execute sayHello---");
}
}
/**************************华丽的分隔符******************************/
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* 动态代理类的实现
* @author lix
* @Date 2020/5/30
*/
public class JdkDynamicProxy implements InvocationHandler {
private MyInterface object;
public JdkDynamicProxy(MyInterface object) {
this.object = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// @Before
System.out.println("---JdkDynamicProxy before execute do somethings---");
Object invoke = null;
try {
invoke = method.invoke(object, args);
}catch (Exception e){
// @After-throwing
System.out.println("---JdkDynamicProxy catch exception do somethings---");
throw e;
}finally {
// @After
System.out.println("---JdkDynamicProxy after execute do somethings---");
}
return invoke;
}
}
/**************************华丽的分隔符******************************/
import java.lang.reflect.Proxy;
public class SpringContainer {
/**
* 通过spring容器被调用
* @param args
*/
public static void main(String[] args) {
MyInterface myInterface = (MyInterface) Proxy.newProxyInstance(SpringContainer.class.getClassLoader(),
new Class[]{MyInterface.class}, new JdkDynamicProxy(new MyInterfaceImpl()));
myInterface.sayHello();
}
}
执行结果:
可能这么看来优势不明显,但是如果我们被代理的接口有很多方法,如果按照传统集合的的形式我们就要这么写:
出现大量的重复代码,而动态代理则不需要再对队里实现类做修改,直接调用方法就能实现代理的增强:
那么Spring在哪里做的代理?
在Spring容器启动后首先要对管理的类进行实例化,之后才会对实例化后的对象做加工处理,空口无凭,来跟一下代码:
这里我提供一个测试类:
发现该类在进入一个org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsAfterInitialization
的方法后发生了改变。
代理前:
代理后:
也就是说我们的后置处理器对原对象进行了加工,那么具体是哪个处理器呢?
通过类名我们大概锁定到这个类,于是便跟进去(这里直接写省略一些堆栈的执行图片描述):
org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#postProcessAfterInitialization
org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#wrapIfNecessary
org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#createProxy
看到这里已经确认是spring做代理的代码org.springframework.aop.framework.ProxyFactory#getProxy(java.lang.ClassLoader)
org.springframework.aop.framework.ProxyCreatorSupport#createAopProxy
org.springframework.aop.framework.DefaultAopProxyFactory#createAopProxy
见下图判断代理类型,果然这里是对的org.springframework.aop.framework.AopProxy#getProxy(java.lang.ClassLoader)
最终返回的Object就是我们的代理对象。代码分析到此结束
这里不对JDK具体的生成代理类的方式深究,感兴趣的小伙伴可以看看Proxy的相关源码,重点是getProxyClass0方法。这里贴一份JDK动态代理类生成的类。开启保留JDK代理class文件:System.getProperties().put(“sun.misc.ProxyGenerator.saveGeneratedFiles”, “true”);
public final class $Proxy0 extends Proxy implements MyInterface {
private static Method m1;
private static Method m4;
private static Method m3;
private static Method m2;
private static Method m0;
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final void sayHello() throws {
try {
super.h.invoke(this, m4, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final void test() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final int hashCode() throws {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m4 = Class.forName("MyInterface").getMethod("sayHello");
m3 = Class.forName("MyInterface").getMethod("test");
m2 = Class.forName("java.lang.Object").getMethod("toString");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
(1)JDK动态代理(基于反射调用,动态生成代理类的实现类,实现类会继承Proxy类,所以为什么必须要有接口)
(2)Cglib动态代理(基于继承,动态编译生成代理类的子类,不依赖接口)
这里如果有接口,通常走JDK的动态代理,如果没有接口则走cglib的代理,但是还有种情况,就是强制使用cglib代理,可以使用配置指定:
xml中使用:
<aop:aspectj-autoproxy proxy-target-class="true"/>
springboot在启动类上添加注解:
@EnableAspectJAutoProxy(proxyTargetClass = true)
Spring 中AOP的应用:
1.事务管理
2.缓存管理
3.权限控制
…等等…
提问:
Spring是如何知道哪些类需要被代理的?
SpringAOP是如何实现链式调用的?
到此本篇文章结束,SpringAop实战请转义至《如何自定义一个Spring Aop切面功能注解》,内容包括springaop配置方法实现,注解通配符形式实现,注解标记注解方式实现,标记类实现所有公共方法的代理。。。
只是对aop简单的做一下分析记录,欢迎大佬补充指教。
。
。
。
。
。
。
。
。
。
补充一份CGLIB Enhancer API
Object create()
必要时生成一个新类,并使用指定的回调(如果有的话)创建一个新的对象实例。
Object create(Class[] argumentTypes, Object[] arguments)
必要时生成一个新类,并使用指定的回调(如果有的话)创建一个新的对象实例。
static Object create(Class type, Callback callback)
帮助器方法来创建拦截的对象。
static Object create(Class superclass, Class[] interfaces, Callback callback)
帮助器方法来创建拦截的对象。
static Object create(Class superclass, Class[] interfaces, CallbackFilter filter, Callback[] callbacks)
帮助器方法来创建拦截的对象。
Class createClass()
如果需要,生成一个新类并返回它,而不创建一个新实例。
protected void filterConstructors(Class sc, List constructors)
从超类中筛选构造函数列表。
protected Object firstInstance(Class type)
不应该在常规流中调用此方法。
protected Class generate(AbstractClassGenerator.ClassLoaderData data)
void generateClass(org.objectweb.asm.ClassVisitor v)
protected ClassLoader getDefaultClassLoader()
static void getMethods(Class superclass, Class[] interfaces, List methods)
使用指定的超类和接口,查找将由增强器生成的类扩展的所有方法。
protected ProtectionDomain getProtectionDomain()
返回定义类时要使用的保护域。
static boolean isEnhanced(Class type)
确定是否使用增强器生成了一个类。
protected Object nextInstance(Object instance)
static void registerCallbacks(Class generatedClass, Callback[] callbacks)
在通过反射创建生成类的新实例之前,调用此方法来注册要使用的回调数组。
static void registerStaticCallbacks(Class generatedClass, Callback[] callbacks)
与registerCallbacks(java.lang.Class,net.sf.cglib.proxy.Callback [])相似,但适用于多个线程将创建所生成类的实例的情况。
void setCallback(Callback callback)
设置要使用的单个回调。
void setCallbackFilter(CallbackFilter filter)
设置用于将生成的类的方法映射到特定回调索引的CallbackFilter。
void setCallbacks(Callback[] callbacks)
设置要使用的回调数组。
void setCallbackType(Class callbackType)
设置要使用的回调类型。
void setCallbackTypes(Class[] callbackTypes)
设置要使用的回调类型数组。
void setInterceptDuringConstruction(boolean interceptDuringConstruction)
设置是否将拦截从代理的构造函数中调用的方法。
void setInterfaces(Class[] interfaces)
设置要实现的接口。
void setSerialVersionUID(Long sUID)
将静态serialVersionUID字段插入到生成的类中。
void setSuperclass(Class superclass)
设置生成的类将扩展的类。
void setUseFactory(boolean useFactory)
设置增强的对象实例是否应实现Factory接口。
protected Object unwrapCachedValue(Object cached)
protected Object wrapCachedClass(Class klass)
扩展资料:Cglib 如何实现多重代理