筆記3:自定義註解的實現

Lossdate發表於2020-12-21
  1. 首先定義註解類(@Component等),參考Spring
//@Target 表示該註解可以用於什麼地方 TYPE:類、介面(包括註解型別)或enum宣告
@Target({ElementType.TYPE})
//@Retention表示需要在什麼級別儲存該註解資訊 RUNTIME:VM將在執行期間保留註解,因此可以通過反射機制讀取註解的資訊
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Component {
    String value() default "";
}
//@Target 表示該註解可以用於什麼地方 TYPE:類、介面(包括註解型別)或enum宣告
@Target({ElementType.TYPE})
//@Retention表示需要在什麼級別儲存該註解資訊 RUNTIME:VM將在執行期間保留註解,因此可以通過反射機制讀取註解的資訊
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Controller {
    String value() default "";
}
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Service {
    String value() default "";
}
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Repository {
    String value() default "";
}
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
    boolean requred() default true;
}
//@Target 表示該註解可以用於什麼地方 TYPE:類、介面(包括註解型別)或enum宣告  METHOD: 方法
@Target({ElementType.TYPE, ElementType.METHOD})
//@Retention表示需要在什麼級別儲存該註解資訊 RUNTIME:VM將在執行期間保留註解,因此可以通過反射機制讀取註解的資訊
@Retention(RetentionPolicy.RUNTIME)
//當@InheritedAnno註解加在某個類A上時,假如類B繼承了A,則B也會帶上該註解
@Inherited
@Documented
public @interface Transactional {
    String value() default "";

    Class<? extends Throwable>[] rollbackFor() default {};
}
  1. 一些基本工具類和實現類,並新增上註解
@Component
public class ConnectionUtils {

    /**
     * 儲存當前執行緒的連線
     */
    private final ThreadLocal<Connection> threadLocal = new ThreadLocal<>(); 

    /**
     * 從當前執行緒獲取連線
     */
    public Connection getCurrentThreadConn() throws SQLException {
        //判斷當前執行緒中是否已經繫結連線,如果沒有繫結,需要從連線池獲取一個連線繫結到當前執行緒
        Connection connection = threadLocal.get();
        if(connection == null) {
            // 從連線池拿連線並繫結到執行緒
            connection = DruidUtils.getInstance().getConnection();
            // 繫結到當前執行緒
            threadLocal.set(connection);
        }
        return connection;

    }
}
@Component
public class TransactionManager {

    @Autowired
    private ConnectionUtils connectionUtils;

    public void setConnectionUtils(ConnectionUtils connectionUtils) {
        this.connectionUtils = connectionUtils;
    }

    // 開啟手動事務控制
    public void beginTransaction() throws SQLException {
        connectionUtils.getCurrentThreadConn().setAutoCommit(false);
    }


    // 提交事務
    public void commit() throws SQLException {
        connectionUtils.getCurrentThreadConn().commit();
    }


    // 回滾事務
    public void rollback() throws SQLException {
        connectionUtils.getCurrentThreadConn().rollback();
    }
}
@Component
public class ProxyFactory {

    @Autowired
    private TransactionManager transactionManager;

    public void setTransactionManager(TransactionManager transactionManager) {
        this.transactionManager = transactionManager;
    }

    /**
     * Jdk動態代理
     * @param obj  委託物件
     * @return   代理物件
     */
    public Object getJdkProxy(Object obj) {

        // 獲取代理物件
        return  Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(),
                (proxy, method, args) -> {
                    Object result = null;
                    try{
                        // 開啟事務(關閉事務的自動提交)
                        transactionManager.beginTransaction();

                        result = method.invoke(obj,args);

                        // 提交事務
                        transactionManager.commit();
                    }catch (Exception e) {
                        e.printStackTrace();
                        // 回滾事務
                        transactionManager.rollback();

                        // 丟擲異常便於上層servlet捕獲
                        throw e;
                    }

                    return result;
                });
    }


    /**
     * 使用cglib動態代理生成代理物件
     * @param obj 委託物件
     */
    public Object getCglibProxy(Object obj) {
        return  Enhancer.create(obj.getClass(), (MethodInterceptor) (o, method, objects, methodProxy) -> {
            Object result = null;
            try{
                // 開啟事務(關閉事務的自動提交)
                transactionManager.beginTransaction();

                result = method.invoke(obj,objects);

                // 提交事務
                transactionManager.commit();
            }catch (Exception e) {
                e.printStackTrace();
                // 回滾事務
                transactionManager.rollback();

                // 丟擲異常便於上層servlet捕獲
                throw e;

            }
            return result;
        });
    }
}
@Service("transferService")
@Transactional(rollbackFor = Exception.class)
public class TransferServiceImpl implements TransferService {

    @Autowired
    private AccountDao accountDao;
    
    public void setAccountDao(AccountDao accountDao) {
        this.accountDao = accountDao;
    }



    @Override
    public void transfer(String fromCardNo, String toCardNo, int money) throws Exception {
        Account from = accountDao.queryAccountByCardNo(fromCardNo);
        Account to = accountDao.queryAccountByCardNo(toCardNo);

        from.setMoney(from.getMoney()-money);
        to.setMoney(to.getMoney()+money);

        accountDao.updateAccountByCardNo(to);
        int c = 1/0; //測試rollback
        accountDao.updateAccountByCardNo(from);
    }
}
@Repository("accountDao")
public class JdbcAccountDaoImpl implements AccountDao {

    @Autowired
    private ConnectionUtils connectionUtils;

    public void setConnectionUtils(ConnectionUtils connectionUtils) {
        this.connectionUtils = connectionUtils;
    }

    @Override
    public int update(Money money) throws Exception {
        Connection con = connectionUtils.getCurrentThreadConn();
        String sql = "xxxx";
        PreparedStatement preparedStatement = con.prepareStatement(sql);
        preparedStatement.setInt(1,money.getMoney());
        preparedStatement.setString(2,money.getId());
        int i = preparedStatement.executeUpdate();

        preparedStatement.close();
        return i;
    }
}
  1. 實現註解及事務,其中事務用到了動態代理,分為jdk和cglib兩種,其中jdk動態代理需要有介面類
public class BeanFactory {

    /**
     * 儲存物件
     */
    private static final Map<String,Object> MAP = new HashMap<>();

    private static final int COMPONENT = 1;
    private static final int CONTROLLER = 2;
    private static final int SERVICE = 3;
    private static final int REPOSITORY = 4;


    static {
        try {
            System.out.println("-========================================================================================");
            //讀取檔案
            List<Class<?>> classList = getClass("com.lossdate.learning");

            //處理Component
            Set<Class<?>> componentClassSet = getClassByType(COMPONENT, classList);
            Set<Class<?>> repositoryClassSet = getClassByType(REPOSITORY, classList);
            Set<Class<?>> serviceClassSet = getClassByType(SERVICE, classList);
            Set<Class<?>> controllerClassSet = getClassByType(CONTROLLER, classList);


            String[] nameSpilt;
            for (Class<?> aClass : componentClassSet) {
                Object o = aClass.newInstance();
                Component annotation = aClass.getAnnotation(Component.class);
                if(StringUtils.isNullOrEmpty(annotation.value())) {
                    nameSpilt = aClass.getName().split("\\.");
                    MAP.put(nameSpilt[nameSpilt.length - 1], o);
                } else {
                    MAP.put(originNameWrapper(annotation.value()), o);
                }
            }
            for (Class<?> aClass : repositoryClassSet) {
                Object o = aClass.newInstance();
                Repository annotation = aClass.getAnnotation(Repository.class);
                if(StringUtils.isNullOrEmpty(annotation.value())) {
                    nameSpilt = aClass.getName().split("\\.");
                    MAP.put(nameSpilt[nameSpilt.length - 1], o);
                } else {
                    MAP.put(originNameWrapper(annotation.value()), o);
                }
            }
            for (Class<?> aClass : serviceClassSet) {
                Object o = aClass.newInstance();
                Service annotation = aClass.getAnnotation(Service.class);
                if(StringUtils.isNullOrEmpty(annotation.value())) {
                    nameSpilt = aClass.getName().split("\\.");
                    MAP.put(nameSpilt[nameSpilt.length - 1], o);
                } else {
                    MAP.put(originNameWrapper(annotation.value()), o);
                }
            }
            for (Class<?> aClass : controllerClassSet) {
                Object o = aClass.newInstance();
                Controller annotation = aClass.getAnnotation(Controller.class);
                if(StringUtils.isNullOrEmpty(annotation.value())) {
                    nameSpilt = aClass.getName().split("\\.");
                    MAP.put(nameSpilt[nameSpilt.length - 1], o);
                } else {
                    MAP.put(originNameWrapper(annotation.value()), o);
                }
            }

            //處理依賴和事務
            for (Map.Entry<String, Object> classMap : MAP.entrySet()) {
                Object aClass = classMap.getValue();
                Class<?> c = aClass.getClass();
                //處理依賴
                Field[] declaredFields = c.getDeclaredFields();
                for (Field declaredField : declaredFields) {
                    declaredField.setAccessible(true);
                    if(declaredField.isAnnotationPresent(Autowired.class) && declaredField.getAnnotation(Autowired.class).requred()) {
//                        String name = declaredField.getName(); map.get(name)會有首字母大小寫問題
                        String[] nameArr = declaredField.getType().getName().split("\\.");
                        String name = nameArr[nameArr.length - 1];
                        Method[] methods = c.getMethods();
                        for (Method method : methods) {
                            if(method.getName().equalsIgnoreCase("set" + name)) {
                                //啟用方法注入依賴
                                method.invoke(aClass, MAP.get(name));
                            }
                        }
                    }
                }

                //處理事務
                if(c.isAnnotationPresent(Transactional.class)) {
                    //獲取介面 jdk動態代理需要有介面才行
                    Class<?>[] interfaces = c.getInterfaces();
                    ProxyFactory proxyFactory = (ProxyFactory) BeanFactory.getBean("ProxyFactory");
                    if(interfaces.length > 0) {
                        //jdk動態代理
                        //注意接收返參
                        aClass = proxyFactory.getJdkProxy(aClass);
                    } else {
                        //cglib動態代理
                        aClass = proxyFactory.getCglibProxy(aClass);
                    }
                }

                MAP.put(classMap.getKey(), aClass);
            }


        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 首字母轉大寫
     */
    private static String originNameWrapper(String name) {
        return name.substring(0,1).toUpperCase() + name.substring(1);
    }

    /**
     * 根據註解整理類
     */
    private static Set<Class<?>> getClassByType(int type, List<Class<?>> classList) {
        Set<Class<?>> classSet = new HashSet<>();
        classList.forEach(aClass -> {
            Object annotation = null;
            switch (type) {
                case COMPONENT:
                    annotation = aClass.getAnnotation(Component.class);
                    break;
                case CONTROLLER:
                    annotation = aClass.getAnnotation(Controller.class);
                    break;
                case SERVICE:
                    annotation = aClass.getAnnotation(Service.class);
                    break;
                case REPOSITORY:
                    annotation = aClass.getAnnotation(Repository.class);
                    break;
                default: break;
            }
            if(annotation != null) {
                classSet.add(aClass);
            }
        });

        return classSet;
    }

    /**
     * 獲取指定目錄下的所有類檔案
     */
    private static List<Class<?>> getClass(String packageName) throws ClassNotFoundException, IOException {
        if(StringUtils.isNullOrEmpty(packageName)) {
            throw new RuntimeException("無效的初始化路徑");
        }

        List<Class<?>> classList = new ArrayList<>();
        String path = packageName.replace(".", "/");
        // 獲取絕對路徑用於載入檔案
        Enumeration<URL> dirs;
        dirs = Thread.currentThread().getContextClassLoader().getResources(path);
        while (dirs.hasMoreElements()) {
            URL url = dirs.nextElement();
            if(url.getProtocol().equals("file")) {
                // D:/IT/java/Learning/code/learning-annotation/target/classes/com/lossdate/learning
                String filePath = URLDecoder.decode(url.getFile(), "UTF-8");
                loadClassFactory(packageName, filePath, classList);
            }
        }

        return classList;
    }

    private static void loadClassFactory(String packageName, String path, List<Class<?>> classList) throws ClassNotFoundException {
        File currentFile = new File(path);
        //篩選資料夾或者字尾為.class的檔案
        File[] files = currentFile.listFiles(file -> file.isDirectory() || file.getName().endsWith(".class"));

        if(files != null) {
            String className;
            Class<?> aClass;
            for (File file : files) {
                if(file.isDirectory()) {
                    //是資料夾,遞迴
                    loadClassFactory(packageName + "." + file.getName(), file.getAbsolutePath(), classList);
                } else {
                    //例項化
                    className = file.getName().replace(".class", "");
                    aClass = Class.forName(packageName + "." + className);
                    classList.add(aClass);
                }
            }
        }

    }


    /**
     * 對外提供獲取例項物件的介面(根據id獲取)
     */
    public static  Object getBean(String id) {
        return MAP.get(originNameWrapper(id));
    }
}
  1. 最後測試類
public class MyTest {

    @Test
    public void test() throws Exception {
        TransferService transferService = (TransferService) BeanFactory.getBean("transferService");
        String fromId = "111";
        String toId = "112";
        int money = 100;
        transferService.transfer(fromId, toId, money);
        System.out.println(transferService);
    }

}

相關文章