jdk動態代理 注解的獲取


一、使用場景

  根據方法或者類上是否有指定的注解,進行業務增強。例如:手寫transactional注解,實現聲明式事務

  1、自定義transaction注解

/**
 * 描述 自定義事務注解
 *
 * @author lwb
 * @since 2020/7/8
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@Documented
@Inherited
public @interface Transactional {
}
View Code

  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);
    }
}
View Code

  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;
                });
    }
View Code

  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");
    }
}
View Code

  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);
    }
}
View Code

     測試結果

     

  測試通過,符合預期。這種將注解標注在接口中,則所有的實現類,都會進行增強

  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);
    }
}
View Code

  修改代理工廠的方法

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;
                });
    }
View Code

 測試結果

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM