面向对象编程oop,只能尽可能的减少重复的代码,但是无法避免重复代码的出现。面向对象是在类的基础上封装,对于多个类不同方法中重复的代码,无法优雅的解决复用问题。—AOP就是为了解决这个问题
AOP
图中的红框我们说它称为横切面,英文表示为 Aspect ,,它表示的是分布在一个 / 多个类的多个方法中的相同逻辑。利用动态代理,将这部分相同的逻辑抽取为一个独立的 Advisor 增强器,并在原始对象的初始化过程中,动态组合原始对象并产生代理对象,同样能完成一样的功能增强。在此基础上,通过指定增强的类名、方法名(甚至方法参数列表类型等),可以更细粒度的对方法增强。使用这种方式,可以在不修改原始代码的前提下,对已有任意代码的功能增强。而这种**针对相同逻辑的扩展和抽取,就是所谓的面向切面编程**
(Aspect Oriented Programming,AOP)。
jdk原生动态代理
被代理的类必须实现接口。
场景:
类A实现接口B。
代码中生成代理对象:
A a = new A();
Proxy.newProxyInstance(a.getClass().getClassLoader,a.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if ("自定义逻辑") {
return method.invoke(a, args);
}
return null;
}
});
jdk动态代理的核心API
jdk 的动态代理,要求被代理的对象所属类必须实现一个以上的接口,代理对象的创建使用 Proxy.newProxyInstance
方法,该方法中有三个参数:
1.ClassLoader loader
:被代理的对象所属类的类加载器
2.Class<?>[] interfaces
:被代理的对象所属类实现的接口
3.InvocationHandler h
:代理的具体代码实现
最后一个 InvocationHandler
是一个接口,它的核心方法 invoke 中也有三个参数:
1.Object proxy
:代理对象的引用(代理后的)
2.Method method
:代理对象执行的方法
3.Object[] args
:代理对象执行方法的参数列表
可以看到Proxy类中有个InvocationHandler的成员变量。要执行实现接口中的相关方法,都会调用InvocationHandler接口方法invoke的方法。
JDK动态代理有两大核心类,它们都在Java的反射包下(java.lang.reflect),分别为InvocationHandler
接口和Proxy
类。
Proxy
public interface TestInt {
String ping(String name);
}
public class test implements TestInt {
@Override
public String ping(String name) {
System.out.println("ping");
return "pong";
}
}
public class MyInvocationHandler implements InvocationHandler {
// 目标对象
private final Object target;
public MyInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("proxy - " + proxy.getClass());
System.out.println("method - " + method);
System.out.println("args - " + Arrays.toString(args));
return method.invoke(target, args);
}
}
proxy有两个静态方法:
1.getProxyClass
@Test
public void test1() throws Exception {
Test test = new Test();
// 根据类加载器和接口数组获取代理类的Class对象
Class<?> proxyClass = Proxy.getProxyClass(Test.class.getClassLoader(), Test.class);
// 通过Class对象的构造器创建一个实例(代理类的实例)
Test testProxy = (Test) proxyClass.getConstructor(InvocationHandler.class)
.newInstance(new MyInvocationHandler(test));
// 调用 ping 方法,并输出返回值
String value = testProxy.ping("杨过");
System.out.println(value);
}
2.newProxyInstance
Test testProxy = (Test) Proxy.newProxyInstance(Test.class.getClassLoader(),
Test.class.getInterfaces(),
(proxy, method, args) -> method.invoke(test, args));
代理类如何创建的
getProxyClass 和 newProxyInstance方法
两个方法最终都会调用getProxyClass0
方法来生成代理类的Class
对象。只不过newProxyInstance
方法为我们创建好了代理实例,而getProxyClass
方法需要我们自己创建代理实例。
getProxyClass0 方法
/**
* Generate a proxy class. Must call the checkProxyAccess method
* to perform permission checks before calling this.
*/
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}
// If the proxy class defined by the given loader implementing
// the given interfaces exists, this will simply return the cached copy;
// otherwise, it will create the proxy class via the ProxyClassFactory
return proxyClassCache.get(loader, interfaces);
}
从源码和注解可以看出:
1.代理接口的最多不能超过65535个
2.会先从缓存中获取代理类,则没有再通过ProxyClassFactory创建代理类。(代理类会被缓存一段时间。)
WeakCache类
该类主要是为代理类进行缓存的。获取代理类时,会首先从缓存中获取,若没有会调用ProxyClassFactory
类进行创建,创建好后会进行缓存。
@Override
public synchronized V get() { // serialize access
// re-check
Supplier<V> supplier = valuesMap.get(subKey);
if (supplier != this) {
// something changed while we were waiting:
// might be that we were replaced by a CacheValue
// or were removed because of failure ->
// return null to signal WeakCache.get() to retry
// the loop
return null;
}
// else still us (supplier == this)
// create new value
V value = null;
try {
//调用代理工厂类创建代理类
value = Objects.requireNonNull(valueFactory.apply(key, parameter));
} finally {
if (value == null) { // remove us on failure
valuesMap.remove(subKey, this);
}
}
// the only path to reach here is with non-null value
assert value != null;
// wrap value with CacheValue (WeakReference)
CacheValue<V> cacheValue = new CacheValue<>(value);
// put into reverseMap
reverseMap.put(cacheValue, Boolean.TRUE);
// try replacing us with CacheValue (this should always succeed)
if (!valuesMap.replace(subKey, this, cacheValue)) {
throw new AssertionError("Should not reach here");
}
// successfully replaced us with new CacheValue -> return the value
// wrapped by it
return value;
}
}
ProxyClassFactory类
ProxyClassFactory
类是Proxy
类的一个静态内部类,这个类用于生成代理对象。
/**
* A factory function that generates, defines and returns the proxy class given
* the ClassLoader and array of interfaces.
*/
private static final class ProxyClassFactory
implements BiFunction<ClassLoader, Class<?>[], Class<?>>
{
// prefix for all proxy class names
private static final String proxyClassNamePrefix = "$Proxy";
// next number to use for generation of unique proxy class names
private static final AtomicLong nextUniqueNumber = new AtomicLong();
@Override
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
for (Class<?> intf : interfaces) {
/*
* Verify that the class loader resolves the name of this
* interface to the same Class object.
*/
Class<?> interfaceClass = null;
try {
interfaceClass = Class.forName(intf.getName(), false, loader);
} catch (ClassNotFoundException e) {
}
if (interfaceClass != intf) {
throw new IllegalArgumentException(
intf + " is not visible from class loader");
}
/*
* Verify that the Class object actually represents an
* interface.
*/
if (!interfaceClass.isInterface()) {
throw new IllegalArgumentException(
interfaceClass.getName() + " is not an interface");
}
/*
* Verify that this interface is not a duplicate.
*/
//验证这个接口是否是重复的
if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
throw new IllegalArgumentException(
"repeated interface: " + interfaceClass.getName());
}
}
String proxyPkg = null; // package to define proxy class in
int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
/*
* Record the package of a non-public proxy interface so that the
* proxy class will be defined in the same package. Verify that
* all non-public proxy interfaces are in the same package.
*/
for (Class<?> intf : interfaces) {
int flags = intf.getModifiers();
if (!Modifier.isPublic(flags)) {
accessFlags = Modifier.FINAL;
String name = intf.getName();
int n = name.lastIndexOf('.');
String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
if (proxyPkg == null) {
proxyPkg = pkg;
} else if (!pkg.equals(proxyPkg)) {
throw new IllegalArgumentException(
"non-public interfaces from different packages");
}
}
}
if (proxyPkg == null) {
// if no non-public proxy interfaces, use com.sun.proxy package
proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
}
/*
* Choose a name for the proxy class to generate.
*/
long num = nextUniqueNumber.getAndIncrement();
String proxyName = proxyPkg + proxyClassNamePrefix + num;
/*
* Generate the specified proxy class.
*/
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
try {
return defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
/*
* A ClassFormatError here means that (barring bugs in the
* proxy class generation code) there was some other
* invalid aspect of the arguments supplied to the proxy
* class creation (such as virtual machine limitations
* exceeded).
*/
throw new IllegalArgumentException(e.toString());
}
}
}
核心逻辑:
1.代理类的名称就是在这里定义的,其前缀是$Proxy,后缀是一个数字。
2.调用ProxyGenerator.generateProxyClass来生成指定的代理类。
3.defineClass0方法是一个native方法,负责字节码加载的实现,并返回对应的Class对象。
Cglib动态代理
引入Cglib
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.1</version>
</dependency>
使用 Cglib 时有几个前提:
1.被代理的类不能是 final
的( Cglib 动态代理会创建子类,final 类型的 Class 无法继承)
2.被代理的类必须有默认的 / 无参构造方法(底层反射创建对象时拿不到构造方法参数)。
Cglib动态代理的核心API
Enhancer.create(Class type, new MethodInterceptor() {
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy)
throws Throwable {
if ("自定义逻辑判断") {
return method.invoke(partner, args);
}
return null;
}
});
}
1.Class type
:被代理的对象所属类的类型
2.Callback callback
:增强的代码实现
一般情况下我们都是对类中的方法增强,所以在传入 Callback 时通常选择这个接口的子接口 MethodInterceptor
。
MethodInterceptor
的 intercept
方法中参数列表与 InvocationHandler
的 invoke
方法类似,唯独多了一个 MethodProxy
,它是对参数列表中的 Method 又做了一层封装,利用它可以直接执行被代理对象的方法,就像这样:
// 执行代理对象的方法
method.invoke(proxy, args);
// 执行原始对象(被代理对象)的方法
methodProxy.invokeSuper(proxy, args);
AOP术语
AOP的核心工作:解耦。通过解耦,业务逻辑只需要关注业务逻辑,扩展逻辑只需要关心扩展逻辑,以及切入业务逻辑的位置即可。
AOP面向切面编程,关注的核心是切面(Aspect)。AOP可以在不修改源码的前提下,使用动态代理技术对已有的代码进行逻辑增强。AOP可以实现组件化,可插拔的功能扩展,通过简单的配置即可将功能增强到指定的切入点。
1.Target:目标对象
目标对象就是被代理的对象。
2.JoinPoint:连接点
简单的理解为目标对象的所属类中,定义的所有方法
。
切入点与连接点的关系应该是包含关系:切入点可以是 0 个或多个(甚至全部)连接点的组合。
切入点一定是连接点,连接点不一定是切入点。
3. Pointcut:切入点
那些被拦截 / 被增强的连接点
。
4.Advice:通知
增强的逻辑,也就是增强的代码。
切入点和通知是要配合在一起使用的
,有了切入点之后,需要搭配上增强的逻辑,才能算是给目标对象进行了代理、增强。
5.Proxy:代理对象
代理对象 = 目标对象 + advice通知。
6.Aspect:切面
Aspect 切面 = PointCut 切入点 + Advice 通知。
7. Weaving:织入
它是一个动作。目的就是将Advice增强的逻辑应用到目标对象,生成代理对象的过程。
8.通知的类型
Spring框架中支持的通知类型包含5种,这些通知是基于AspectJ的。
1.Before 前置通知
:目标对象的方法调用之前触发。
2.After 后置通知
:目标对象的方法调用之后触发。
3.AfterReturning 返回通知
:目标对象的方法调用完成,在返回结果值之后触发。
4.AfterThrowing 异常通知
:目标对象的方法运行中抛出 / 触发异常后触发。
注意一点,AfterReturning 与 AfterThrowing 两者是互斥的!如果方法调用成功无异常,则会有返回值;如果方法抛出了异常,则不会有返回值。
5.Around 环绕通知
:编程式控制目标对象的方法调用。环绕通知是所有通知类型中可操作范围最大的一种,因为它可以直接拿到目标对象,以及要执行的方法,所以环绕通知可以任意的在目标对象的方法调用前后搞事,甚至不调用目标对象的方法。