現實需求中,有一些相似沒法加入掃描組件註解的。如jdk裏面的類。java
那麼框架必需要支持將沒有組件註解標識的類也能夠有一個方式注入到容器裏面。框架
那麼,咱們經過相似Spring的@Bean的方案,來實現這個需求。測試
經過在配置類的方法的上面,使用@Bean註解,將返回的對象加到容器中。this
得到有@Configuration註解標識的類。檢索它的方法,若是有@Bean,執行這個方法並將返回的對象放在容器中。spa
1.定義一個Bean註解code
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 @Documented 14 public @interface Bean { 15 16 /** 17 * 用於設置對象名 18 * @return 19 */ 20 String name() default ""; 21 22 }
2.修改AbstractApplicationContext代碼component
(1)建立一個bean的方法處理從bean註解的方法中得到對象對象
(2)將從組件註解的類掃描的方式建立對象放在scan方法內blog
根據這個修改方法,首先將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的框架就寫完了。後續會根據需求,再不斷更新衆多功能。