一、使用场景
根据方法或者类上是否有指定的注解,进行业务增强。例如:手写transactional注解,实现声明式事务
1、自定义transaction注解
/** * 描述 自定义事务注解 * * @author lwb * @since 2020/7/8 */ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE, ElementType.METHOD}) @Documented @Inherited public @interface Transactional { }
2、业务代码:定义用户服务接口,实现用户服务接口,在实现类的方法上标注@transaction注解
/** * 描述 用户服务接口类 * * @author lwb * @since 2020/7/8 */ public interface UserService { void doSomething(String thing); } public class UserServiceImpl implements UserService { @Override @Transactional public void doSomething(String thing) { System.out.println("do something : " + thing); } }
3、动态代理工厂:生成对象的jdk动态代理
public static Object getJdkProxy(Object object){ return Proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass().getInterfaces(), (proxy, method, args) -> { Object result; if(method.isAnnotationPresent(Transactional.class) || method.getDeclaringClass().isAnnotationPresent(Transactional.class)){ try { System.out.println("开启事务"); result = method.invoke(object, args); System.out.println("提交事务"); }catch (Exception e){ System.out.println("回滚事务"); throw e; } }else{ result = method.invoke(object, args); } return result; }); }
4、测试
/** * 描述 * * @author lwb * @since 2020/7/8 */ public class ProxyTest { @Test public void testJdkProxy(){ UserService userService = new UserServiceImpl(); UserService jdkProxy = (UserService) ProxyFactory.getJdkProxy(userService); jdkProxy.doSomething("eat"); } }
5、校验测试结果
发现并未打印事务信息。不符合预期结果。
二、问题分析
明明在实现类的方法上标注了Transaction注解,为什么在增强中没有获取到Transaction注解?分析jdk动态代理的原理可知:该method是UserService接口的doSomething方法,method.getDeclaringClass()获取的是UserSevice接口,在doSomething方法与UserService接口上并未使用Transaction注解,所以没打印事务信息。
三、解决方案
根据问题分析,可以得出以下两种解决方案:
1、在UserService接口上或者UserService接口中的doSomething上标注Transaction注解
/** * 描述 用户服务接口类 * * @author lwb * @since 2020/7/8 */ public interface UserService { @Transactional void doSomething(String thing); } /** * 描述 用户服务实现类 * * @author lwb * @since 2020/7/8 */ public class UserServiceImpl implements UserService { @Override public void doSomething(String thing) { System.out.println("do something : " + thing); } }
测试结果
测试通过,符合预期。这种将注解标注在接口中,则所有的实现类,都会进行增强
2、修改代理工厂方法,获取实现类与实现类的方法,判断是否标注注解
业务代码还原:去除接口方法上的注解,将注解添加到实现类的方法上
/** * 描述 用户服务接口类 * * @author lwb * @since 2020/7/8 */ public interface UserService { void doSomething(String thing); } /** * 描述 用户服务实现类 * * @author lwb * @since 2020/7/8 */ public class UserServiceImpl implements UserService { @Override @Transactional public void doSomething(String thing) { System.out.println("do something : " + thing); } }
修改代理工厂的方法
public static Object getJdkProxy(Object object){ return Proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass().getInterfaces(), (proxy, method, args) -> { Object result; Method targetMethod = object.getClass().getMethod(method.getName(), method.getParameterTypes()); if(method.isAnnotationPresent(Transactional.class) || method.getDeclaringClass().isAnnotationPresent(Transactional.class) || object.getClass().isAnnotationPresent(Transactional.class) || targetMethod.isAnnotationPresent(Transactional.class)){ try { System.out.println("开启事务"); result = method.invoke(object, args); System.out.println("提交事务"); }catch (Exception e){ System.out.println("回滚事务"); throw e; } }else{ result = method.invoke(object, args); } return result; }); }
测试结果
测试通过,符合预期。标注在实现类上的注解,只在实现类生效