现在已经知道各部分是如何工作的了, 把他们组合在一起做点有意义的事!
业务服务的执行在并发时可能会失败(死锁).如果重试该操作,则很可能在下一轮成功,适用于重试的商业服务(满足幂等操作,无需返回用户进行冲突解决,),我们显示的重试操作,避免客户端看到`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