一 概述
面向切面编程的织入时机
1. 编译期织入(AspectJ)
2. 类加载加载织入(AspectJ 5+)
3. 运行时(Spring AOP)
二 运行时织入的实现逻辑
代理对象的交互图
如图,客户端是通过代理对象间接的同目标对象进行交互,换言之,目标对象委托了代理对象同客户端进行交互。
根据类图可知,客户端通过接口来对目标对象或代理对象进行引用,这里就涉及了一个很重要的原则,面向接口编程,这时候就涉及到两个重要的结论:
1. 因为客户端调用的真正对象的方法,代理类都要有。所以代理对象和调用对象都要作为该接口的实现类。
2. 代理对象会将方法委托给真正的对象进行执行,而代理类就执行一些额外的逻辑,即AOP织入的逻辑。
运行时织入是通过代理对象,代理可以分为了两种:静态代理和动态代理。
其中动态代理又分为基于接口代理和基于继承代理实现,这两类的代表为JDK代理与CGlib代理。
三 运行时织入之静态代理的实现
接口类
public interface IA {
void getName();
}
接口实际实现类
public class RealIA implements IA{
@Override
public void getName() {
System.out.println("真正对象实现请求!");
}
}
接口代理类
public class ProxyIA implements IA{
//代理对象需要委托真正对象去完成一些逻辑
private RealIA realIA;
//强制
public ProxyIA(RealIA realIA) {
this.realIA = realIA;
}
//代理对象不会实现真是的逻辑
@Override
public void getName() {
//类似于AOP的before,after,around
//代理对象完成其他逻辑
System.out.println("before代理逻辑");
//代理对象委托真正的对象来完成客户端逻辑
try {
realIA.getName();
} catch (Exception e) {
System.out.println("ex:" + e.getMessage());
throw e;
} finally {
System.out.println("after代理逻辑");
}
}
}
由上述静态代理的实现可知,当接口中方法越多,需要实现的代理方法越多时,重复的逻辑就越多。
四 运行时织入之JDK动态代理
1. JDK代理需要通过java.lang.reflect.Proxy类来动态生成代理类
2. 代理类需要实现织入逻辑需要实现接口InvocationHandler
3. JDK动态代理只能基于接口进行动态代理
JDK动态代理代码实现
JDK动态代理类
public class JdkProxySubject implements InvocationHandler {
private RealIA realIA;
public JdkProxySubject(RealIA realIA) {
this.realIA = realIA;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//织入的逻辑
System.out.println("before代理");
Object result = null;
try{
//通过invoke方法执行动态代理的逻辑
//Method通过应用反射机制,来动态反射方法
result = method.invoke(realIA,args);
}catch (Exception e){
}finally {
//织入的逻辑
System.out.println("after代理");
}
return result;
}
}
JDK动态代理客户端
public class JdkClient {
public static void main(String[] args) {
IA ia = (IA) Proxy.newProxyInstance(JdkClient.class.getClassLoader(),
new Class[]{IA.class},new JdkProxySubject(new RealIA()));
ia.getName();
}
}
结合上面的IA接口和RealIA实现类,就完成了JDK动态代理的实例。
五 运行时织入之JDK动态代理底层逻辑
根据实例代码可知JDK动态代理的代码生成,先通过Proxy.newProxyInstance方法,接着调用一次通过方法getProxyClass0,ProxyClassFactory,ProxyGenerator方法实现动态代理的代码的生成 ,这个过程生成的代码时字节码,最后通过Java的反射机制new出实例代码。
newProxyInstance方法源码
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
//寻找或者生成指定代理类
/*
* Look up or generate the designated proxy class.
*/
Class<?> cl = getProxyClass0(loader, intfs);
}
getProxyClass0方法源码
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}
//如果缓存中存在代码,则getProxyClass0方法先从缓存中获取代码,如果没有就会通过ProxyClassFactory进行创建并放入缓存
// 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);
}
缓存源码
proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
ProxyClassFactory方法源码
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");
}
}
代理类名称格式
/*
* Choose a name for the proxy class to generate.
*/
long num = nextUniqueNumber.getAndIncrement();
String proxyName = proxyPkg + proxyClassNamePrefix + num;
根据源码可知,代理类的最后名称中包含long类型的数字字符。
六 运行时织入之CGlib动态代理
Cglib动态代理类
public class IAInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("before in cglib");
Object result = null;
try{
result = methodProxy.invokeSuper(o,objects);
}catch (Exception e){
System.out.println(e.getMessage());
}finally {
System.out.println("after in cglib");
}
return result;
}
}
Cglib动态代理客户端类
public class CglibClient {
public static void main(String[] args) {
//Cglib的增强对象
Enhancer enhancer = new Enhancer();
//设置增强类型,增强真是对象
enhancer.setSuperclass(RealIA.class);
enhancer.setCallback(new IAInterceptor());
IA ia = (IA) enhancer.create();
ia.getName();
}
}
基于静态代理的接口IA真正实现类RealIA来实现的代码实例 。
七 JDK动态代理和Cglib动态代理之间的区别
1. JDK动态代理时基于接口实现的动态代理,对接口中的方法进行代理织入代码。
2. Cglib动态代理是基于继承实现的动态代理,所以实现Static和final类进行代理。
3. 同因为Cglib动态代理是基于继承实现的动态代理,所以无法完成Static和private方法的代理。
4. 因为接口中方法不可以是private方法,所以JDK动态代理也无法完成private方法的代理。
八 Spring创建代理bean的方法
对于动态代理而言,同时存在JDK动态代理和Cglib动态代理两种。
根据Spring创建代理类的时序图可得,先由AbstractAutoProxyCreator类调用createProxy方法返回代理类。
public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware {
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());
}
}
而createProxy方法又是调用ProxyFactory来生成的,然而createProxy方法又是委托给ProxyCreatorSupport类来生成。
public class ProxyCreatorSupport extends AdvisedSupport {
protected final synchronized AopProxy createAopProxy() {
if (!this.active) {
activate();
}
return getAopProxyFactory().createAopProxy(this);
}
}
最后ProxyCreatorSupport又是通过DefaultAopProxyFactory来实现的。
public class ProxyCreatorSupport extends AdvisedSupport {
public ProxyCreatorSupport() {
this.aopProxyFactory = new DefaultAopProxyFactory();
}
}
DefaultAopProxyFactory实现createAopProxy生成代理类的方法。
public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {
/**
* Whether this environment lives within a native image.
* Exposed as a private static field rather than in a {@code NativeImageDetector.inNativeImage()} static method due to https://github.com/oracle/graal/issues/2594.
* @see <a href="https://github.com/oracle/graal/blob/master/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/ImageInfo.java">ImageInfo.java</a>
*/
private static final boolean IN_NATIVE_IMAGE = (System.getProperty("org.graalvm.nativeimage.imagecode") != null);
@Override
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.");
}
//使用JDK动态代理
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
//Cglib动态代理
return new ObjenesisCglibAopProxy(config);
}
else {
//使用JDK动态代理
return new JdkDynamicAopProxy(config);
}
}
}
根据源码可知,Spring中只有两种情况使用JDK动态代理,其他的情况都是使用Cglib动态代理。
根据上述源码可知,当目标对象实现了接口的情况下,默认使用的JDK动态代理,而没有实现接口的情况下,则使用的是Cglib动态代理,当然在目标对象实现了接口的情况下,但是强制使用Cglib动态代理的情况下,还是会通过Cglib动态代理。
九 SpringBoot中强制使用CGlib动态代理的方法
@SpringBootApplication
//强制使用Cglib动态代理
public class SpringaopApplication {
public static void main(String[] args) { SpringApplication.run(SpringaopApplication.class, args); }
}
根据实例代码可知通过注解@EnableAspectJAutoProxy(proxyTargetClass = true)可以强制使用Cglib动态代理。
这样做的原理的使得if (!IN_NATIVE_IMAGE && (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)))判断条件中的config.isProxyTargetClass()为true。