0.本文重点
- 1.spring如何织入增强,并创建代理对象
- 2.spring如何选择cglib代理还是jdk代理
- 3.jdk代理执行过程
1.例子及概念
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface LogAnnotation {
String value() default "";
}
@Aspect
@Slf4j
@Component
public class LogAspect {
@Pointcut("@annotation(logAnnotation)")
public void pointCut(LogAnnotation logAnnotation){}
@Before("pointCut(logAnnotation)")
public void before(JoinPoint joinPoint, LogAnnotation logAnnotation){
log.info("process before method, remark is :{}",logAnnotation.value());
}
@After("pointCut(logAnnotation)")
public void after(JoinPoint joinPoint, LogAnnotation logAnnotation){
log.info("process after method, remark is :{}",logAnnotation.value());
}
@Around("pointCut(logAnnotation)")
public Object around(ProceedingJoinPoint proceedingJoinPoint, LogAnnotation logAnnotation) throws Throwable {
log.info("process around-before method, remark is :{}",logAnnotation.value());
Object proceed = proceedingJoinPoint.proceed(proceedingJoinPoint.getArgs());
log.info("process around-after method, remark is :{},result is :{}",logAnnotation.value(),proceed);
return proceed;
}
@AfterReturning("pointCut(logAnnotation)")
public void afterReturning(JoinPoint joinPoint, LogAnnotation logAnnotation){
log.info("process afterReturning method, remark is :{}",logAnnotation.value());
}
}
@Override
@LogAnnotation("purchase")
public String purchase(){
orderService.place();
storeService.deduct();
return "1";
}
@Override
public void cancel() {
log.info("取消订单");
}
切点、切面、增强在上述例子中的体现:
增强/通知 (advice): before、after等声明了特定注解的方法 ,不仅仅是方法本身,方法与其携带的注解
会被解析为不同类型的Advice
切点: 声明@Pointcut 的方法
Advisor: 增强方法 + 切点
切面:@Aspect注解所在类,其携带Around, Before, After, AfterReturning, AfterThrowing注解的方法会被解析成 Advisor
除@annotation方式声明切点外,还可以
通常 "." 代表一个包名,".." 代表包及其子包,方法参数任意匹配使用两个点 ".."。
2.创建代理对象
前面文章将spring ioc机制时,有提到过 AbstractAutoProxyCreator(AnnotationAwareAspectJAutoProxyCreator)在initializeBean阶段完成代理化
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
//循环依赖时会将原始对象存放在此,如果remove != ,那么没有提前完成代理化,继续走
if (this.earlyProxyReferences.remove(cacheKey) != bean) {
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
//当产生循环依赖时,上述的remove 结果相等,直接返回原始对象,最终依赖二级缓存获取代理对象,
如果有不理解的地方 可以去看 spring ioc处理机制 文章结尾 介绍三级缓存部分
return bean;
}
2.1 wrapIfNecessary
AbstractAutoProxyCreator#wrapIfNecessary
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
...
//已经判断过不需要增强 或者 本身是 Advice/Pointcut/Advisor/AopInfrastructureBeank
及有@Aspect注解 这类特殊的bean
if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
return bean;
}
//本身是 Advice/Pointcut/Advisor/AopInfrastructureBeank 或有@Aspect注解
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
// 查找增强 内部调用 AbstractAdvisorAutoProxyCreator#findEligibleAdvisors
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
if (specificInterceptors != DO_NOT_PROXY) {
//只要不为空表示 表示需要创建代理对象
this.advisedBeans.put(cacheKey, Boolean.TRUE);
//创建代理对象
Object proxy = createProxy(
bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
}
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
2.1.1 AbstractAdvisorAutoProxyCreator#findEligibleAdvisors
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
//内部会调用 BeanFactoryAspectJAdvisorsBuilder#buildAspectJAdvisors 扫描当前容器中所有带
@Aspect注解的bean 调用 ReflectiveAspectJAdvisorFactory#getAdvisors 将bean内部方法包装为Advisor 。
除此之外还有容器内已创建的Advisor,比如 BeanFactoryTransactionAttributeSourceAdvisor
这里最终返回容器内所有Advisor
List<Advisor> candidateAdvisors = findCandidateAdvisors();
//PointcutAdvisor是Advisor的一个实现类,这里的例子只涉及PointcutAdvisor
PointcutAdvisor = 增强 + 切点,所以这里会遍历 所有Advisor,根据其内部携带的切点,遍历所有方法 只要有一个方法符合切点作用范围,则代表该bean受Advisor作用
//以@Transactional为例 Advisor是 BeanFactoryTransactionAttributeSourceAdvisor
内部的 pointCut 是TransactionAttributeSourcePointcut
基于 SpringTransactionAnnotationParser 判断公有方法 或类上是否包含 Transactional
List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
//在头部添加一个DefaultPointcutAdvisor(advice为 ExposeInvocationInterceptor)
extendAdvisors(eligibleAdvisors);
...
return eligibleAdvisors;
}
2.1.1.1 ReflectiveAspectJAdvisorFactory#getAdvisors
public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) {
...
List<Advisor> advisors = new ArrayList<>();
//获取其内部不携带 @Pointcut 注解的方法
for (Method method : getAdvisorMethods(aspectClass)) {
//根据方法上的 Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class
注解,将注解信息包装为 AspectJExpressionPointcut(切点:注解内容+class属性),继而将方法与AspectJExpressionPointcut
一同包装为 InstantiationModelAwarePointcutAdvisorImpl(Advisor)
Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, 0, aspectName);
if (advisor != null) {
advisors.add(advisor);
}
}
...
return advisors;
}
DefaultPointcutAdvisor 是Spring提供的通用的 Advisor。它可以把任意的Advice和Pointcut放在一起
BeanFactoryCacheOperationSourceAdvisor:和Cache有关 @Caching @Cacheable @CacheEvict @CachePut
InstantiationModelAwarePointcutAdvisor 解析被 @Aspect注解注释的类时生成的 Advisor,。而这个 Advisor中的 Pointcut与Advice都是由ReflectiveAspectJAdvisorFactory 来解析生成的(与之对应的 Advice 是 AspectJMethodBeforeAdvice, AspectJAfterAdvice, AspectJAfterReturningAdvice, AspectJAfterThrowingAdvice, AspectJAroundAdvice, Pointcut 则是AspectJExpressionPointcut)
IntroductionAdvisor 一个Java类,没有实现A接口,在不修改Java类的情况下,使其具备A接口的功能。仅有一个类过滤器ClassFilter 而没有 MethodMatcher,因为它的切点是类级别的,而 Pointcut 的切点是方法级别的(细粒度更细)。与之一起的是 IntroductionInterceptor:引介拦截器。
2.1.2 AbstractAutoProxyCreator#createProxy
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();
//this = AnnotationAwareAspectJAutoProxyCreator,本身是ProxyConfig对象,
proxyFactory.copyFrom(this);
//proxyTargetClass 为true时默认使用cglib 为false默认使用jdk
注:spring boot 2.0后 注解@EnableAspectJAutoProxy(proxyTargetClass = false) 声明已不再有效,需要使用配置 spring.aop.proxy-target-class 详情可见 AopAutoConfiguration
if (!proxyFactory.isProxyTargetClass()) {
//如果显著声明 期望优先走jdk代理 ,那就判断 当前bean是否支持jdk代理
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);
}
//调用 DefaultAopProxyFactory#createAopProxy 创建代理对象
return proxyFactory.getProxy(getProxyClassLoader());
}
2.1.2.1 ProxyProcessorSupport#evaluateProxyInterfaces
protected void evaluateProxyInterfaces(Class<?> beanClass, ProxyFactory proxyFactory) {
//获取所有接口
Class<?>[] targetInterfaces = ClassUtils.getAllInterfacesForClass(beanClass, getProxyClassLoader());
boolean hasReasonableProxyInterface = false;
for (Class<?> ifc : targetInterfaces) {
//只要有 非空 普通接口 就返回 ,即 可以走jdk代理
if (!isConfigurationCallbackInterface(ifc) && !isInternalLanguageInterface(ifc) &&
ifc.getMethods().length > 0) {
hasReasonableProxyInterface = true;
break;
}
}
if (hasReasonableProxyInterface) {
for (Class<?> ifc : targetInterfaces) {
proxyFactory.addInterface(ifc);
}
}
else {
//没有有效的接口 那就把配置改为true 后面还需要根据这个属性进行判断
proxyFactory.setProxyTargetClass(true);
}
}
2.1.1.2 DefaultAopProxyFactory#createAopProxy
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
//proxyTargetClass为true 或者 没有接口 或者只有一个接口且为SpringProxy
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.");
}
//如果本身是一个接口 或者 代理对象 会使用jdk代理 ,否则使用 CGLIB 实现代理
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
return new ObjenesisCglibAopProxy(config);
}
else {
return new JdkDynamicAopProxy(config);
}
}
综上我们可以看到spring创建代理对象,以及决定代理方式的过程。 代理一个servieImpl bean时,主要还是根据 proxyTargetClass属性 (受spring.aop.proxy-target-class 配置影响,如果你愿意,专门创建一个MergedBeanDefinitionPostProcessor 或InstantiationAwareBeanPostProcessor 在属性注入前修改对应值也是可以的) 来决定代理方式。一般默认走cglib代理。cglib代理从代码管理上的好处是能兼容 注入接口类/子类的场景。如果是jdk代理,注入的对象是子类的话就会报错,因为jdk代理的是接口,而不是实现类。
3.JdkDynamicAopProxy
public Object getProxy(@Nullable ClassLoader classLoader) {
Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
//是否显著声明了hashcode 和 equals方法
findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
//第三个参数 InvocationHandler 代理对象的方法调用执行 都是委托给InvocationHandler执行的
return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}
3.1 JdkDynamicAopProxy#invoke
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object oldProxy = null;
boolean setProxyContext = false;
TargetSource targetSource = this.advised.targetSource;
Object target = null;
try {
..hashcode equals 方法 ..
else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
method.getDeclaringClass().isAssignableFrom(Advised.class)) {
//如果是Advised接口方法 直接反射调用
return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
}
Object retVal;
if (this.advised.exposeProxy) {
//如果设置了exposeProxy 将代理对象放到ThreadLocal ,如EnableAspectJAutoProxy#exposeProxy
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
target = targetSource.getTarget();
Class<?> targetClass = (target != null ? target.getClass() : null);
//根据Advisor 的 pointCut判断是否作用于该方法,同时将AfterReturningAdvice转化为 AfterReturningAdviceInterceptor,
MethodBeforeAdvice转化为MethodBeforeAdviceInterceptor,ThrowsAdvice转化为 ThrowsAdviceInterceptor
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
if (chain.isEmpty()) {
//不需要增强 反射调用
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
}
else {
MethodInvocation invocation =
new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
//基于拦截器链 调用
retVal = invocation.proceed();
}
...
}
finally {
...
if (setProxyContext) {
//threadlocal还原
AopContext.setCurrentProxy(oldProxy);
}
}
}
3.1.1 ReflectiveMethodInvocation#proceed
public Object proceed() throws Throwable {
//下标从-1开始递增 全部执行完毕后 反射调用被代理对象的目标方法 。
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
return invokeJoinpoint();
}
Object interceptorOrInterceptionAdvice =
this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
InterceptorAndDynamicMethodMatcher dm =
(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
//调用interceptor(Advice)增强方法
return dm.interceptor.invoke(this);
}
else {
return proceed();
}
}
else {
//前文extendAdvisors方法中添加的 DefaultPointcutAdvisor(advice为 ExposeInvocationInterceptor) 进入这里执行,将MethodInvocation 放入线程变量Threadlocal中
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}
总结: 创建代理过程:
bean实例化 在initializeBean 阶段, 调用所有的BeanPostProcessor . postProcessAfterInitialization 方法,AnnotationAwareAspectJAutoProxyCreator 在这里尝试进行代理化 (如果有循环依赖,在populate 注入阶段会提前触发代理化)
如果bean不是类似Advisor,Advice之类的实例,获取所有Advisor, 根据pointCut切点判断是否能作用于bean,如果有适配的Advisor,则表示该bean需要代理
如果proxyTargetClass设置为false(springboot默认走cglib代理),遍历bean的接口,判断是不是有可用的接口(非空 普通接口 类似InitializingBean,DisposableBean,Aware都不算),如果没有 设置 proxyTargetClass为true
调用 DefaultAopProxyFactory .createProxy方法创建代理 ,如果 proxyTargetClass 为true 或者没有接口 , 且 bean 不是接口 也不是代理对象,走cglib代理 ,其他走jdk代理 ,返回 AopProxy对象 ,其getProxy方法返回代理对象
以JdkDynamicAopProxy为例 ,调用 getProxy方法时 内部调用 Proxy.newProxyInstance(classLoader, proxiedInterfaces, this); (最终基于JdkDynamicAopProxy.invoke 代理目标方法)
其他
web层,也就是controller处 使用@Transactional注解,刚好 路由方法声明为private 会发生什么?
前面的文章提到过 路由注册 只看@RequestMapping注解,私有方法也是可以注册为路由的。 最终会发现 方法中调用的service全部为空。
@Transactional注解使 controller 成为一个代理对象,由于controller无接口且默认实现为cglib代理,方法为私有方法,不被继承,所以不会调用到 目标对象,最终的调用形式就变成了下面所示
public class Foo {
String name;
private String getName(){
System.out.println("name :" + name);
return name;
}
}
class Bar extends Foo{
}
public static void main(String[] args) throws Exception {
Foo foo = new Foo();
foo.name = "qwe";
Bar bar = new Bar();
Method method = foo.getClass().getDeclaredMethod("getName");
method.setAccessible(true);
method.invoke(bar);
}
```