一、使用場景
根據方法或者類上是否有指定的注解,進行業務增強。例如:手寫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; }); }
測試結果

測試通過,符合預期。標注在實現類上的注解,只在實現類生效
