Skip to content

Commit

Permalink
aop source code
Browse files Browse the repository at this point in the history
  • Loading branch information
rbmonster committed Nov 17, 2020
1 parent a1fcda3 commit eb38660
Show file tree
Hide file tree
Showing 5 changed files with 193 additions and 7 deletions.
141 changes: 139 additions & 2 deletions src/main/java/com/four/SOURCECODE.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ public final TransactionStatus getTransaction(@Nullable TransactionDefinition de
- Spring 事务处理 中,可以通过设计一个 TransactionProxyFactoryBean 来使用 AOP 功能,通过这个 TransactionProxyFactoryBean 可以生成 Proxy 代理对象
# @Transactional失效场景
### @Transactional失效场景
- private方法不会生效,JDK中必须是接口,接口中不可能有private方法,protect方法的话,也不生效。原因是spring内部判断方法修饰符如果不是public不生成事务拦截代理类。
- CGLib代理的时候,final方法不会生效,抛NullPointException,cglib与JDK内部机制。
Expand All @@ -171,4 +171,141 @@ public final TransactionStatus getTransaction(@Nullable TransactionDefinition de
- @Transactional 注解属性 propagation 设置错误,设置了以非事务的状态运行
- @Transactional 注解属性 rollbackFor 设置错误,实际抛出的错误跟设置不一致
- 异常被 catch 导致@Transactional失效
- 数据库引擎不支持事务
- 数据库引擎不支持事务
## Spring AOP
### HandlerAdapter与InvocableHandlerMethod
- 对于Controller的方法,请求发送的时候通过HandlerAdapter调用InvocableHandlerMethod.doInvoke方法。
- InvocableHandlerMethod.doInvoke 强制会把方法设为可见:
- ```
protected Object doInvoke(Object... args) throws Exception {
ReflectionUtils.makeAccessible(getBridgedMethod());
try {
return getBridgedMethod().invoke(getBean(), args);
}
...
}
```
### 整体的调用流程
1. 对于web的调用,首先通过InvocationHandlerMethod,设置方法可见,强制调用方法。
2. 调用方法后,判断方法是否使用代理,若没使用代理,直接调用方法。若使用了代理,进入代理方法的invoke方法。
3. 区分JDK代理、Cglib代理,获取Advisor的对应拦截链,分别进入到拦截类的proceed()方法执行中。
4.
### 判断使用JDK代理还是Cglib代理
- 对于实际的Method调用,如果是代理对象的调用会分别进入各自的代理的invoke方法中,主要分为Cglib(CglibAopProxy)和JDK代理(JdkDynamicAopProxy)
- DefaultAopProxyFactory:如何判断使用JDK代理还是Cglib代理
- ```
@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
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代理
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
return new ObjenesisCglibAopProxy(config);
}
else {
return new JdkDynamicAopProxy(config);
}
}
```
### CglibAopProxy的代理方法
- CglibAopProxy 代理方法:
- ```
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object oldProxy = null;
boolean setProxyContext = false;
Object target = null;
// 获取AopProxyAdvier
TargetSource targetSource = this.advised.getTargetSource();
try {
if (this.advised.exposeProxy) {
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
target = targetSource.getTarget();
Class<?> targetClass = (target != null ? target.getClass() : null);
// 获取对应的拦截链
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
Object retVal;
// 拦截链为空,且方法为public 直接调用
if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = methodProxy.invoke(target, argsToUse);
}
else {
// 执行拦截链中的interceptor
retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
}
retVal = processReturnType(proxy, target, method, retVal);
return retVal;
}
finally {
}
```
### JdkDynamicAopProxy的代理方法
- JdkDynamicAopProxy: 基于JDK的代理可以看出,对于基本的方法hashcode以及equals方法都是没有进行拦截的。
- ```
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 {
if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
return equals(args[0]);
}
else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
return hashCode();
}
else if (method.getDeclaringClass() == DecoratingProxy.class) {
// There is only getDecoratedClass() declared -> dispatch to proxy config.
return AopProxyUtils.ultimateTargetClass(this.advised);
}
else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
method.getDeclaringClass().isAssignableFrom(Advised.class)) {
return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
}
Object retVal;
if (this.advised.exposeProxy) {
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
target = targetSource.getTarget();
Class<?> targetClass = (target != null ? target.getClass() : null);
// Get the interception chain for this method.
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
// Check whether we have any advice. If we don't, we can fallback on direct
// reflective invocation of the target, and avoid creating a MethodInvocation.
if (chain.isEmpty()) {
// We can skip creating a MethodInvocation: just invoke the target directly
// Note that the final invoker must be an InvokerInterceptor so we know it does
// nothing but a reflective operation on the target, and no hot swapping or fancy proxying.
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
}
else {
// We need to create a method invocation...
MethodInvocation invocation =
new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
// Proceed to the joinpoint through the interceptor chain.
retVal = invocation.proceed();
}
}
```
### AspectJ的方法织入
- AspectJ的Aop 在方法调用的时候添加了AOP对应的拦截方法,根据对应的拦截类型
![avatar](https://github.com/rbmonster/learning-note/blob/master/src/main/java/com/four/picture/methodAop.jpg)
Binary file added src/main/java/com/four/picture/methodAop.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
34 changes: 34 additions & 0 deletions src/main/java/com/four/server/SingleController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.four.server;

import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

/**
* <pre>
* @Description:
* TODO
* </pre>
*
* @version v1.0
* @ClassName: SingleController
* @Author: sanwu
* @Date: 2020/11/17 15:07
*/
@RestController
@RequestMapping("/home")
public class SingleController {

@GetMapping(path = "/getCommand3")
public String getCommand3(@RequestParam("command1") String command1, @RequestParam("command2") String command2) {
return "command is " +command1 +" " + command2;
}

@Transactional
@GetMapping(path = "/getCommand4")
public String getCommand4(@RequestParam("command1") String command1, @RequestParam("command2") String command2) {
return "command is " +command1 +" " + command2;
}
}
8 changes: 8 additions & 0 deletions src/main/java/com/four/server/controller/TestController.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.four.server.controller;

import com.four.server.aop.entity.LogHome;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;

/**
Expand Down Expand Up @@ -34,7 +35,14 @@ public String forHome(@RequestParam("command") String command) {

@LogHome("logHome for command")
@GetMapping(path = "/getCommand2")
@Transactional
public String forAop(@RequestParam("command1") String command1, @RequestParam("command2") String command2) {
return "command is " +command1 +" " + command2;
}

@GetMapping(path = "/getCommand5")
public String forAop2(@RequestParam("command") String command) {
return "command is " +command;
}

}
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package com.four.transaction.controller;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
Expand All @@ -10,10 +13,7 @@
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.*;

/**
* <pre>
Expand All @@ -28,7 +28,7 @@
*/
@RestController
@RequestMapping("/transaction")
public class TestTransactionController {
public class TestTransactionController implements ApplicationContextAware {

private final JdbcTemplate jdbcTemplate;

Expand All @@ -48,6 +48,7 @@ public List getIdFromDb(@PathVariable("id") String id) {
@Transactional(propagation = Propagation.NEVER)
@GetMapping("/update")
public String update() {
Object testTransactionController = applicationContext.getBean("testTransactionController");
Thread thread = Thread.currentThread();
ThreadLocal threadLocal = new ThreadLocal();
// Object obj = threadLocal.get();
Expand All @@ -66,4 +67,10 @@ void testTransaction() {
Object[] objects = new Object[]{String.valueOf(random.nextInt(1000000)), "test"};
jdbcTemplate.update(sql, objects);
}

private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}

0 comments on commit eb38660

Please sign in to comment.