记录一下尝试编写补偿式分布式事务...

看了看大牛们的各种补偿式事务框架,也就是tcc框架.然后自己尝试写下,实现了很简单功能,还有很多需要像大牛学习的地方...

1.入口方法:

@Aspect
@Component
public class Aopconfig implements ApplicationContextAware {

    @Autowired
    MyService myService;

    private ApplicationContext applicationContext;

    @Pointcut(value = "@annotation(com.example.testaop.config.Gao)")
    public void gao() {
    }

    @Around(value = "gao()")
    public Object test(ProceedingJoinPoint joinPoint) throws Throwable {
        Object[] before = myService.before(joinPoint);
        Object proceed = null;
        String flag = null;
        Object[] args = joinPoint.getArgs();
        try {
            proceed = joinPoint.proceed();
            myService.todo(joinPoint, (String)before[0],(Class)before[2],args);
        } catch (Exception e) {
            myService.todo(joinPoint, (String)before[1],(Class)before[2],args);
            throw new Throwable(e);
        }
        return proceed;
    }


    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        ApplicationUtil.getInstance().setApplication(applicationContext);
    }
}

每个程序都要有他的入口,这里也是,我这里是使用spring的aop,然后切面是判断执行方法是否含有我的自定义注解;这里实现ApplicationContextAware 接口,获取到spring上下文环境,后面可以通过他来注入或提取bean;

 

2.bean的提取工具方法:

public class ApplicationUtil {
    private final static ApplicationUtil app = new ApplicationUtil();
    private ApplicationContext applicationContext;

    public ApplicationContext getApplicationContext() {
        return applicationContext;
    }

    public void setApplicationContext(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }

    public static ApplicationUtil getInstance() {
        return app;
    }

    public ApplicationUtil setApplication(ApplicationContext applicationContext) {
        app.setApplicationContext(applicationContext);
        return app;
    }

    public Object getBean(Class aClass) {
        return this.applicationContext.getBean(aClass);
    }

    public Object getBeanByInterface(Class aClass) {
        AutowireCapableBeanFactory autowireCapableBeanFactory = this.applicationContext.getAutowireCapableBeanFactory();
        return autowireCapableBeanFactory.getBean(aClass);
    }
}

 

3.自定义注解:(作用域是在方法上面)

@Retention(value = RetentionPolicy.RUNTIME)
@Target(value = ElementType.METHOD)
public @interface Gao {
    String confirm();

    String cancel();

    Class interface_();
}

 

4.简单定义了一个接口,两个方法一个是执行反射方法,一个是获取拦截方法的参数列表:

public interface MyService {
    public void todo(ProceedingJoinPoint joinPoint, String flag,Class a,Object[] args)throws Throwable;
    public Object[] before(ProceedingJoinPoint joinPoint);
}

5.实现类:

@Service
public class MyServiceImpl implements MyService {

    @Override
    public void todo(ProceedingJoinPoint joinPoint, String flag, Class aclass,Object[]args) throws Throwable {
        if (aclass.isInterface()) {
            Object beanByInterface = ApplicationUtil.getInstance().getBeanByInterface(aclass);
            Method[] methods = aclass.getMethods();
            for (Method m : methods) {
                if (flag.equals(m.getName())) {
                    m.invoke(beanByInterface, args);
                }
            }
        } else {
            Method[] methods = aclass.getMethods();
            for (Method m : methods) {
                if (flag.equals(m.getName())) {
                    m.invoke(ApplicationUtil.getInstance().getBean(aclass), args);
                }
            }
        }
    }

    @Override
    public Object[] before(ProceedingJoinPoint joinPoint) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        Gao gao = method.getAnnotation(Gao.class);
        String succ = gao.confirm();
        String fail = gao.cancel();
        Class aClass = gao.interface_();
        return new Object[]{succ, fail, aClass};
    }
}

方法中判断注解传入的参数是一个类还是一个接口;如果是接口的话,直接注入一个实现.这时如果程序中接口有多个实现方法的话就需要@Primary注解.否则会出错(其实可以在注解上多一个实现类名字来精确的提取bean)

在测试的时候出现过反射参数位数错误问题.原因就是注解所在的方法参数数量与实现类的参数数量不相同;所以需要参数类型和数量等完全一样;

____________________________________________________________________________________________

到这里其实就结束了-.-哈哈,只是简单的实现了先执行try方法,如果正常执行confirm程序,错误执行cancel程序.其实中间还有很复杂的各种逻辑.比如confirm和cancel中也要实现新的事务,还有记录操作状态,本地补偿等等....

 

下面说下我集成到程序中:(springcloud项目)

1.首先将上面的代码打成jar包.

2.将jar包复制到项目中,然后引入maven(这里使用本地引入jar包)

        
            com.gao.www
            gao
            system
            ${project.basedir}/src/main/resources/lib/aop-test.jar
        

3.解决扫描不到jar包中注解等问题:

因为springboot的@SpringBootApplication注解其实已经包含了@ComponentScan注解,不过它默认扫描的路径是自身项目的所有包;所以这里要将第三方的jar包路径加进来就可以了

@SpringBootApplication(scanBasePackages = {"com.example.testaop","com.example.client22"})

4.下面就是测试的方法:

@FeignClient("client1")
public interface MyFeignClient {

    @GetMapping("/try")
    public Integer tryy(@RequestParam("id") String id,@RequestParam("amount") double amount);

    @GetMapping("/confirm")
    public Integer confirm(@RequestParam("id") String id,@RequestParam("amount") double amount);

    @GetMapping("/cancel")
    public Integer cancel(@RequestParam("id") String id,@RequestParam("amount") double amount);
}
@Service
public class MysServiceImpl implements MysService {

    @Autowired
    JdbcTemplate jdbcTemplate;
    @Resource
    MyFeignClient myFeignClient;

    @Override
    public Integer confirm(String id, double amount, Integer count) {
        int update = jdbcTemplate.update("update test2 set amount= amount + ?,frozen = frozen - ? where id = ?", amount, amount, id);
        myFeignClient.confirm(id, amount);
        return update;
    }

    @Override
    public Integer cancel(String id, double amount, Integer count) {
        int update = jdbcTemplate.update("update test2 set frozen = frozen - ? where id = ?", amount, id);
        myFeignClient.cancel(id, amount);
        return update;
    }

}
@RestController
public class Web {

    @Autowired
    JdbcTemplate jdbcTemplate;
    @Resource
    MyFeignClient myFeignClient;

    @Transactional
    @Gao(cancel = "cancel", confirm = "confirm", interface_ = MysService.class)
    @GetMapping("/try")
    public Integer t(String id, double amount, @RequestParam(defaultValue = "1") Integer count) {
        int update = jdbcTemplate.update("update test2 set frozen = ? where id = ?", amount, id);
        Integer tryy = myFeignClient.tryy(id, amount);
        int i = 100 / count;
        return update + tryy;
    }
}

 

你可能感兴趣的:(记录一下尝试编写补偿式分布式事务...)