一起寫框架-Ioc內核容器的實現-對象的調用-@Bean註解註入容器的對象(十二)
阿新 • • 發佈:2017-11-11
擴展 lac iter component cts block 根據 spa urn
實現功能
現實需求中,有一些類似無法加入掃描組件註解的。如jdk裏面的類。
那麽框架必須要支持將沒有組件註解標識的類也可以有一個方式註入到容器裏面。
那麽,我們通過類似Spring的@Bean的方案,來實現這個需求。
通過在配置類的方法的上面,使用@Bean註解,將返回的對象加到容器中。
實現思路
獲得有@Configuration註解標識的類。檢索它的方法,如果有@Bean,執行這個方法並將返回的對象放在容器中。
實現步驟
1.定義一個Bean註解
1 package ioc.core.annotation; 2 3 import java.lang.annotation.Documented;4 import java.lang.annotation.ElementType; 5 import java.lang.annotation.Retention; 6 import java.lang.annotation.RetentionPolicy; 7 import java.lang.annotation.Target; 8 9 //表示用於運行時的註解 10 @Retention(RetentionPolicy.RUNTIME) 11 //表示只能在類或者接口的上面使用 12 @Target(value=ElementType.METHOD) 13 @Documented14 public @interface Bean { 15 16 /** 17 * 用於設置對象名 18 * @return 19 */ 20 String name() default ""; 21 22 }
2.修改AbstractApplicationContext代碼
(1)創建一個bean的方法處理從bean註解的方法中獲得對象
(2)將從組件註解的類掃描的方式創建對象放在scan方法內
根據這個修改方法,首先將AbstractApplicationContext類構造方法原來使用@ComponentScan創建對象的代碼移到scan()方法中,然後創建一個bean()方法編寫通過@Bean獲得對象
1 private void bean(Class<?> classType,Context context) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException{ 2 Map<String, Object> objects = context.getObjects(); 3 //1.獲得配置類的對象 4 Object configuration = classType.newInstance(); 5 //2.獲得配置類的所有方法 6 Method[] methods = classType.getMethods(); 7 for(Method method:methods){ 8 Bean bean = method.getDeclaredAnnotation(Bean.class); 9 if(bean!=null){ 10 Object object = method.invoke(configuration); 11 if(bean.name()!=null&&!"".equals(bean.name())){ 12 objects.put(bean.name(), object); 13 }else{ 14 objects.put(NamingUtils.firstCharToLower(object.getClass().getSimpleName()), object); 15 } 16 } 17 } 18 19 } 20 21 private void scan(Class<?> classType,Context context) throws ClassNotFoundException, InstantiationException, IllegalAccessException{ 22 23 // 獲得組件掃描註解 24 ComponentScan componentScan = classType.getDeclaredAnnotation(ComponentScan.class); 25 // 獲得包名 26 this.basePackage = componentScan.basePackages(); 27 // 根據包名獲得類全限制名 28 // Set<String> classNames = 29 // PackageUtils.getClassName(this.basePackage[0], true); 30 // 將掃描一個包,修改為多個包 31 Set<String> classNames = PackageUtils.getClassNames(this.basePackage, true); 32 // 通過類名創建對象 33 Iterator<String> iteratorClassName = classNames.iterator(); 34 while (iteratorClassName.hasNext()) { 35 36 String className = iteratorClassName.next(); 37 // System.out.println(className); 38 39 // 通過類全名創建對象 40 Class<?> objectClassType = Class.forName(className); 41 /* 42 * 判斷如果類權限名對應的不是接口,並且包含有@Component|@Controller|@Service| 43 * 44 * @Repository 才可以創建對象 45 */ 46 if (this.isComponent(objectClassType)) { 47 Object instance = objectClassType.newInstance(); 48 // 修改為,默認對象支持首字符小寫 49 String objectName = null; 50 // 獲得組件註解的name屬性值 51 String componentName = this.getComponentOfName(objectClassType); 52 53 if (componentName == null) { 54 // 如果組件註解的name屬性沒有值,使用默認命名對象 55 objectName = NamingUtils.firstCharToLower(instance.getClass().getSimpleName()); 56 } else { 57 // 如果組件註解的name屬性有值,使用自定義命名對象 58 objectName = componentName; 59 } 60 System.out.println("1."+instance); 61 //將對象加入到容器 62 context.addObject(objectName, instance); 63 } 64 65 } 66 } 67
2.修改AbstractApplicationContext的構造方法的代碼。如下:
1 /** 2 * 將容器操作加載創建對象的代碼寫抽象類裏面,這樣可以方便以後擴展多種實現。 3 * 4 * @param classType 5 */ 6 public AbstractApplicationContext(Class<?> classType) { 7 try { 8 // 判斷配置類是否有Configuration註解 9 Configuration annotation = classType.getDeclaredAnnotation(Configuration.class); 10 if(annotation!=null){ 11 bean(classType, this.getContext()); 12 scan(classType, this.getContext()); 13 14 // 註入對象到有@Autowired註解屬性和方法中。 15 autowired(); 16 } catch (InstantiationException e) { 17 e.printStackTrace(); 18 } catch (IllegalAccessException e) { 19 e.printStackTrace(); 20 } catch (ClassNotFoundException e) { 21 e.printStackTrace(); 22 } catch (IllegalArgumentException e) { 23 // TODO Auto-generated catch block 24 e.printStackTrace(); 25 } catch (InvocationTargetException e) { 26 // TODO Auto-generated catch block 27 e.printStackTrace(); 28 } catch (SecurityException e) { 29 // TODO Auto-generated catch block 30 e.printStackTrace(); 31 } 32 33 }
測試代碼
1.創建一個沒有註解的普通Java類
1 package ioc.core.test; 2 3 public class TestUtils { 4 5 public void test(){ 6 System.out.println("--測試沒有組件註解的類-"); 7 } 8 9 }
2.在配置類使用@Bean註入這個類
1 package ioc.core.test.config; 2 3 import ioc.core.annotation.Bean; 4 import ioc.core.annotation.ComponentScan; 5 import ioc.core.annotation.Configuration; 6 import ioc.core.test.TestUtils; 7 8 //使用定義@Configuration定義該類是一個配置類 9 @Configuration 10 //使用ComponentScan設置掃描包的路徑 11 @ComponentScan(basePackages={"ioc.core"}) 12 public class Config { 13 14 @Bean 15 public Object getTestUtils(){ 16 TestUtils tu=new TestUtils(); 17 return tu; 18 } 19 20 }
3.測試輸出TestUtils的test() 方法
1 package ioc.core.test; 2 3 import org.junit.Test; 4 5 import ioc.core.impl.AnntationApplicationContext; 6 import ioc.core.test.config.Config; 7 8 public class ConfigruationTest { 9 10 @Test 11 public void bean(){ 12 try { 13 AnntationApplicationContext context=new AnntationApplicationContext(Config.class); 14 TestUtils testUtils = context.getBean("testUtils", TestUtils.class); 15 System.out.println(context.getContext().getObjects().keySet()+"-----"); 16 testUtils.test(); 17 } catch (Exception e) { 18 // TODO Auto-generated catch block 19 e.printStackTrace(); 20 } 21 } 22 23 }
4.測試結果,輸出了這個方法的內容,成功!
一起寫框架-Ioc內核容器的實現-對象的調用-@Bean註解註入容器的對象(十二)