AutoWired屬性上使用的簡單實現

前言

本文在建立類的實例時,是直接操做的屬性,而沒有用setter或者constructor方法,這是不合理的(違背了JavaBean封裝的思想)。故建議在閱讀本文時,僅關注實現的思路,不要參考具體實現過程,而應該使用spring的setter/constructor兩種方法java

IOC控制反轉

本文繼java的springMvc框架簡單實現後,就Service-ServiceImplDao--DaoImpl之間的依賴關係進行解耦,簡單實現@AutoWired有關此部分的依賴注入(DI)。spring

思路

  1. 以Dao爲例,在聲明某個Dao時,咱們再也不賦初值:
@XxgAutoWired
private UserDao userDao;
  1. UserDaoImpl類上面加入@XxgComponent註解
@XxgComponent
public class UserDaoImpl extends BaseDaoImpl<User> implements UserDao {
 ...
}
  1. 服務器啓動,

    3.1 掃描包過程當中,加入:判斷類中是否有@XxgComponent註解,若是有,則加入咱們指定的容器segmentfault

    3.2 掃描結束以後,給MethodDefinition建立所在類的實例服務器

  2. 利用反射執行method時,傳遞上一步建立的所在類的實例

所在類的實例說明:框架

主要將該實例中有@AutoWired註解的屬性,賦予初值,每次調用方法都使用該實例,就達到了預想的效果。spa

賦初值的方式:code

  1. Map<Class, ComponentDefinition> componentMap容器的key存放依賴類實現的接口的class,value存放該類的相關屬性。
  2. 獲取到有@AutoWired註解的屬性時,獲取該屬性的類型class做爲key,在容器中查找該key
  3. 容器中查到的數據,即爲其實現類的相關屬性
  4. 將其賦值給該實現類

問題

這種實現模式,有一個最大的問題:component

一個Dao的接口,只能有一個對應的實現類。xml

其次:對象

依賴注入僅在Controller類中使用了纔有效

能夠使用xml文件來存儲接口與其實現類的依賴關係。

一個接口有多個實現類時,只在須要的實現類上添加@XxgComponent註解

代碼實現

1.XxgAutoWired註解

/**
 * @Auther dbc
 * @Date 2020/10/15 14:40
 * @Description
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface XxgAutoWired {
}

2. XxgComponent註解

/**
 * @Auther dbc
 * @Date 2020/10/16 15:48
 * @Description
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface XxgComponent {
}

3.MethodDefinition中添加一個類的實例

private Object parentInstance; // 所屬類的實例

4.掃描類的部分修改

添加Component容器

private static Map<Class, ComponentDefinition> componentMap = new ConcurrentHashMap<>(); // component類容器

ComponentDefinition

/**
 * @Auther dbc
 * @Date 2020/10/16 16:03
 * @Description 對有@XxgComponent註解的類 進行包裝
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ComponentDefinition {
 private Class typeClazz; // 類對象
 private String typeName; // 類名
 private XxgComponent component; // component註解
 private Class implClazz; // 實現的父類
}

修改dealClass方法

private void dealClass(Class clazz) throws Exception {
 // 一開始就添加
 if (clazz.isAnnotationPresent(XxgComponent.class)) {
 // 將實現類添加到容器中
 addComponent(clazz);
 return ;
 }
 ...
}

將實現類添加到容器中的方法

​
 /**
 * 將Component類添加到容器中
 */
 private void addComponent(Class clazz) throws Exception {
 ComponentDefinition componentDefinition = new ComponentDefinition();
 componentDefinition.setComponent((XxgComponent) clazz.getAnnotation(XxgComponent.class));
 componentDefinition.setTypeClazz(clazz);
 componentDefinition.setImplClazz(clazz.getInterfaces()[0]);
 componentDefinition.setTypeName(clazz.getName());
 if (componentMap.get(componentDefinition.getImplClazz()) != null) {
 throw new Exception("已存在相同的實現類" + componentDefinition.getImplClazz().getName());
 }
 componentMap.put(componentDefinition.getImplClazz(), componentDefinition);
 }
 
 /**
 * 處理controller的屬性,自動注入值, 返回該類的一個實例
 */
 private Object dealClassField(Class clazz) {
 Field[] fields = clazz.getDeclaredFields();
 Object instance = null;
 try {
 instance = clazz.newInstance();
 } catch (InstantiationException | IllegalAccessException e) {
 e.printStackTrace();
 }
 for(Field field : fields) {
 if (field.isAnnotationPresent(XxgAutoWired.class)) {
 // 給有XxgAutoWired註解的屬性,自動注入值
 field.setAccessible(true);
 try {
 ComponentDefinition componentDefinition = componentMap.get(field.getType());
 if (componentDefinition != null) {
 // 處理@XxgComponent 的自動注入
 field.set(instance, dealClassField(componentDefinition.getTypeClazz()));
 }
 } catch (Exception e) {
 e.printStackTrace();
 }
 }
 }
 return instance;
 }

5.調用方法時,傳遞掃描時存入的實例

DispatcherServlet中,當獲取到請求執行的方法,以及裝配好參數以後,調用該方法時,傳遞建立好的實例

try {
  Object result = methodDefinition.getMethod().invoke(
  methodDefinition.getParentInstance(), // 所屬類的實例
  params.toArray());
  sendResponse(result, req, resp);
} catch (IllegalAccessException | InvocationTargetException e) {
  e.printStackTrace();
  sendResponse(e.getMessage(), req, resp);
}

後續

真正的@AutoWired太強大了,能夠用在屬性上,還能夠用在方法上等,博主就再也不深究了。

下文博主將使用XML配置文件的方式實現依賴注入,並對單例模式、依賴的值類型等進行處理。

相關文章
相關標籤/搜索