公司自研一套服務治理框架,只需實現特定接口便可在服務中心上註冊服務,但該框架有一套自動重發機制,在網絡抖動狀況下可能會重複交易(例如重複放款),並不是全部交易系統都有完善的交易流水控制機制。爲了解決重複交易問題,儘量的下降代碼侵入性,因此使用aop+aspectj+redis解決這一問題。git
一、maven 項目。github
二、JDK 1.7redis
整體介紹,spring aop分爲jdk和cglib兩種動態代理,一般咱們使用cglib的方式,cglib會在啓動初始化時加載稍慢,在後期運行時效率更高而且能夠支持無接口的代理。經過 aop+aspectj方式是我的認爲自動化程度最高,開發工做最少的實現方法,可是spring aop的實現方法遠不止於此。spring
aop+aspectj,主要任務爲編寫aspectj攔截類,bean上添加@Aspect註解,bean中能夠添加@Around、@Before()、@After()等註解。在spring配置文件中開啓aspectj便可,<aop:aspectj-autoproxy proxy-target-class="true"/>,true時使用cglib代碼。如下爲詳細說明。apache
一、服務接口、接口實現、實體對象。編程
package com.aop.api; import com.aop.request.RequestData; import com.aop.response.ResponseData; public interface ApiInterface { ResponseData helloWorld(RequestData requestData); }
package com.aop.service; import com.aop.api.ApiInterface; import com.aop.request.RequestData; import com.aop.response.ResponseData; import org.springframework.stereotype.Service; @Service public class ApiImpl implements ApiInterface { @Override public ResponseData helloWorld(RequestData requestData) { ResponseData responseData = new ResponseData("SUCCESS","請求成功!"); System.out.println("impl 請求參數:" + requestData.getData()); return responseData; } }
package com.aop.request; public class RequestData { private String data; public RequestData(String data) { this.data = data; } public String getData() { return data; } public void setData(String data) { this.data = data; } @Override public String toString() { return "RequestData{" + "data='" + data + '\'' + '}'; } }
package com.aop.response; public class ResponseData { private String respCode; private String respDesc; public ResponseData(String respCode, String respDesc) { this.respCode = respCode; this.respDesc = respDesc; } @Override public String toString() { return "ResponseData{" + "respCode='" + respCode + '\'' + ", respDesc='" + respDesc + '\'' + '}'; } }
二、攔截AOPapi
package com.aop.aop; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.springframework.stereotype.Component; @Component @Aspect public class AopIntercept { /** * 只有around攔截能夠使用joinPoint。咱們就練習這個相對複雜的 * * @param joinPoint * @return */ //execution 攔截點介紹:第一個* 表示全部返回值,第二個*表示全部方法,後面的(..)表示這個方法中全部入參。即攔截ApiInterface下全部方法 @Around("execution(* com.aop.api.ApiInterface.*(..))") public Object around(ProceedingJoinPoint joinPoint) { before(); //得到全部請求參數 Object[] args = joinPoint.getArgs(); for (Object obj : args) { System.out.println("攔截中請求參數:" + obj.toString()); } Object obj = null; try { obj = joinPoint.proceed(); System.out.println("攔截中方法返回值:" + obj.toString()); } catch (Throwable throwable) { throwable.printStackTrace(); } after(); return obj; } private void before() { //流水號重複校驗 System.out.println("攔截方法前執行!"); } private void after() { System.out.println("攔截方法後執行!"); } }
三、配置文件、pom文件網絡
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd " xmlns:aop="http://www.springframework.org/schema/aop"> <context:component-scan base-package="com.aop"/> <!-- 動態代理默認jdk使用cglib時設置成true--> <aop:aspectj-autoproxy proxy-target-class="true"/> </beans>
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.aop.test</groupId> <artifactId>aop</artifactId> <version>0.0.1-SNAPSHOT</version> <dependencies> <!-- https://mvnrepository.com/artifact/org.springframework/spring-core --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>4.3.7.RELEASE</version> </dependency> <!-- https://mvnrepository.com/artifact/org.springframework/spring-context --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>4.3.7.RELEASE</version> </dependency> <!-- https://mvnrepository.com/artifact/aspectj/aspectjweaver --> <dependency> <groupId>aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.5.4</version> </dependency> </dependencies> <build> <finalName>aop</finalName> <resources> <resource> <directory>src/main/resources</directory> <includes> <include>**/**</include> </includes> <filtering>true</filtering> </resource> </resources> <plugins> <plugin> <artifactId>maven-resources-plugin</artifactId> <!--<version>2.5</version>--> <configuration> <encoding>UTF-8</encoding> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>1.7</source> <target>1.7</target> <encoding>UTF-8</encoding> </configuration> </plugin> </plugins> </build> </project>
四、測試代碼。app
package com.aop; import com.aop.api.ApiInterface; import com.aop.request.RequestData; import com.aop.response.ResponseData; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Test { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); ApiInterface apiInterface = context.getBean(ApiInterface.class); ResponseData responseData = apiInterface.helloWorld(new RequestData("請求參數:name=張三")); System.out.println("main end response:" + responseData); } }
五、輸出結果。框架
咱們經過aop方式,在before()方法中加入redis,經過在redis中檢查流水號的key是否已存在來斷定是否爲重複交易。
下面在介紹一些經常使用的aop方式
一、基於XML配置的Spring AOP。
二、編程式aop等。
https://github.com/binary-vi/binary.github.io/tree/master/aop