現在已經知道各部分是如何工作的了, 把他們組合在一起做點有意義的事!
業務服務的執行在并發時可能會失敗(死鎖).如果重試該操作,則很可能在下一輪成功,適用于重試的商業服務(滿足冪等操作,無需返回用戶進行沖突解決,),我們顯示的重試操作,避免客戶端看到`PessimisticLockingFailureException`,這是多個切面建議執行在同一個服務上的.
因為要重試方法,只能采用包圍建議,如下:
~~~java
@Aspect
public class ConcurrentOperationExecutor implements Ordered {
private static final int DEFAULT_MAX_RETRIES = 2;
private int maxRetries = DEFAULT_MAX_RETRIES;
private int order = 1;
public void setMaxRetries(int maxRetries) {
this.maxRetries = maxRetries;
}
public int getOrder() {
return this.order;
}
public void setOrder(int order) {
this.order = order;
}
@Around("com.xyz.myapp.SystemArchitecture.businessService()")
public Object doConcurrentOperation(ProceedingJoinPoint pjp) throws Throwable {
int numAttempts = 0;
PessimisticLockingFailureException lockFailureException;
do {
numAttempts++;
try {
return pjp.proceed();
}
catch(PessimisticLockingFailureException ex) {
lockFailureException = ex;
}
} while(numAttempts <= this.maxRetries);
throw lockFailureException;
}
}
~~~
注意,切面實現了`Ordered`接口,我們可以設置優先級高于事務建議(每一次的重試都是一次新的事務).通過spring設置參數`maxRetries`和` order`.主要的操作在包圍建議方法`doConcurrentOperation `內.這里是對所有的方法`businessService()`嘗試重試邏輯.每次遇到`PessimisticLockingFailureException `異常會重試執行,知道耗光重試次數.
對應的spring配置:
~~~xml
<aop:aspectj-autoproxy/>
<bean id="concurrentOperationExecutor" class="com.xyz.myapp.service.impl.ConcurrentOperationExecutor">
<property name="maxRetries" value="3"/>
<property name="order" value="100"/>
</bean>
~~~
為了明確切面只是重試冪等操作,可以定義注解`Idempotent `:
~~~java
@Retention(RetentionPolicy.RUNTIME)
public @interface Idempotent {
// marker annotation
}
~~~
實現業務方法需要添加注解`@Idempotent`,切面表達式也需要匹配注解
~~~java
@Around("com.xyz.myapp.SystemArchitecture.businessService() && " +
"@annotation(com.xyz.myapp.service.Idempotent)")
public Object doConcurrentOperation(ProceedingJoinPoint pjp) throws Throwable {
...
}
~~~
自己動手寫一個例子:
首先要有一個已存在的業務方法作為連接點,這里是簡單的打印日志
~~~java
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
@Component
public class JoinPoint {
Logger log = LoggerFactory.getLogger(this.getClass());
public String say(String name) {
log.info("hello:{}",name);
return "hello" + name;
}
}
~~~
然后,定義切面,指定切點表達式和建議執行的位置,這里切點和建議合并寫在一起.
另外一定要注意,要讓切面起作用,切面上的三個注解是必須的
~~~java
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Aspect
@Configuration
@EnableAspectJAutoProxy
public class BeforeExample {
Logger log = LoggerFactory.getLogger(this.getClass());
@Before("execution(* com.ixinnuo.financial.knowledge.aop.JoinPoint.say(..))")
public void doAccessCheck(JoinPoint jp) {
log.info("BeforeExample... ");
Object[] args = jp.getArgs();
for (Object object : args) {
log.info("參數{}",args);
}
}
}
~~~
最后,原來調用業務邏輯的地方,不需要任何改動,正常執行即可
~~~java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@RequestMapping("run")
public class RunController {
@Autowired
private JoinPoint joinPoint;
@GetMapping("runAop")
@ResponseBody
public String runAop(){
joinPoint.say("hanmeimei");
return "ok";
}
}
~~~
下面是輸出的日志信息,對應程序的結構,看出定義的切面已經執行了 ;
注意BeforeExample的代理實例只會創建一次,不會每次調用都創建
~~~
[2018-07-23 14:15:05.692][INFO][9124-[http-nio-80-exec-1] BeforeExample$$EnhancerBySpringCGLIB$$69278439:20] - BeforeExample...
[2018-07-23 14:15:05.693][INFO][9124-[http-nio-80-exec-1] BeforeExample$$EnhancerBySpringCGLIB$$69278439:23] - 參數hanmeimei
[2018-07-23 14:15:05.702][INFO][9124-[http-nio-80-exec-1] JoinPoint:13] - hello:hanmeimei
~~~
- 正確打開本書的姿勢
- 第一部分 Core
- 1. Ioc container
- 1.1. Introduction to the Spring IoC container and beans
- 1.2. Container overview
- 1.2.1. Configuration metadata
- 1.2.2. Instantiating a container
- 1.2.3. Using the container
- 1.3. Bean overview
- 1.3.1. Naming beans
- 1.3.2. Instantiating beans
- 1.4. Dependencies
- 1.4.1. Dependency Injection
- 1.4.2. Dependencies and configuration in detail
- 1.4.3. Using depends-on
- 1.4.4. Lazy-initialized beans
- 1.4.5. Autowiring collaborators
- 1.4.6. Method injection
- 1.5 Bean Scopes
- 1.6. Customizing the nature of a bean TODO
- 1.7. Bean definition inheritance TODO
- 1.8. Container Extension Points TODO
- 1.9. Annotation-based container configuration
- 1.9.1. @Required
- 1.9.2. @Autowired
- 1.9.3. Fine-tuning annotation-based autowiring with @Primary
- 1.9.4. Fine-tuning annotation-based autowiring with qualifiers TODO
- 1.9.5. Using generics as autowiring qualifiers TODO
- 1.9.6. CustomAutowireConfigurer TODO
- 1.10. Classpath scanning and managed components
- 1.10.1. @Component and further stereotype annotations
- 1.11. Using JSR 330 Standard Annotations TODO
- 1.12. Java-based container configuration
- 1.12.1. Basic concepts: @Bean and @Configuration
- 1.12.2. Instantiating the Spring container using AnnotationConfigApplicationContext
- 2. Resources
- 2.1. Introduction
- 2.2. The Resource interface
- 2.3. Built-in Resource implementations
- 2.3.1. UrlResource
- 2.3.2. ClassPathResource
- 2.3.3. FileSystemResource
- 2.3.4. ServletContextResource
- 2.3.5. InputStreamResource
- 2.3.6. ByteArrayResource
- 2.4. The ResourceLoader
- 2.5. The ResourceLoaderAware interface
- 2.6. Resources as dependencies
- 2.7. Application contexts and Resource paths
- 2.7.1. Constructing application contexts
- 2.7.2. Wildcards in application context constructor resource paths
- 2.7.3. FileSystemResource caveats
- 3. Validation, Data Binding, and Type Conversion
- 4. Spring Expression Language (SpEL)
- 5. Aspect Oriented Programming with Spring
- 5.1. Introduction
- 5.1.1. AOP concepts
- 5.1.2. Spring AOP capabilities and goals
- 5.1.3. AOP Proxies
- 5.2. @AspectJ support
- 5.2.1. Enabling @AspectJ Support
- 5.2.2. Declaring an aspect
- 5.2.3. Declaring a pointcut
- 5.2.4. Declaring advice
- 5.2.5. Introductions TODO
- 5.2.6. Aspect instantiation models TODO
- 5.2.7. Example
- 5.3. Schema-based AOP support TODO
- 5.4. Choosing which AOP declaration style to use TODO
- 5.5. Mixing aspect types TODO
- 5.6. Proxying mechanisms
- 5.6.1. Understanding AOP proxies
- 5.7. Programmatic creation of @AspectJ Proxies
- 5.8. Using AspectJ with Spring applications
- 5.8.1. Using AspectJ to dependency inject domain objects with Spring
- 5.8.2. Other Spring aspects for AspectJ
- 第二部分 Testing
- 第三部分 Data Access
- 1. Transaction Management
- 1.1. Introduction to Spring Framework transaction management
- 1.2 Advantages of the Spring Framework’s transaction support model
- 1.2.1. Global transactions
- 1.2.2. Local transactions
- 1.2.3. Spring Framework’s consistent programming model
- 1.3. Understanding the Spring Framework transaction abstraction
- 1.4. Synchronizing resources with transactions
- 1.4.1. High-level synchronization approach
- 1.4.2. Low-level synchronization approach
- 1.4.3. TransactionAwareDataSourceProxy
- 1.5. Declarative transaction management
- 1.5.1. Understanding the Spring Framework’s declarative transaction implementation
- 1.5.2. Example of declarative transaction implementation
- 1.5.3. Rolling back a declarative transaction
- 1.5.4. Configuring different transactional semantics for different beans
- 1.5.5. tx:advice元素的 settings
- 1.5.6. Using @Transactional
- 1.5.7. Transaction propagation
- 1.5.8. Advising transactional operations
- 1.5.9. Using @Transactional with AspectJ TODO
- 第四部分 web servlet
- 第五部分 Web Reactive
- 第六部分 Integration
- 第七部分 Languages