序文
前の私たちは、機能を実現するために必要なものの特徴をバネと分析のいくつかについて話しました、準備作業が行われている、我々は、特定の機能を実現することを始めました。
コンテナロードプロセス
私たちは、春のrefesh()メソッドで初期化作業の多くを行うためにことを知って、それはほとんど春のコアプロセスをカバー
公共 ボイドリフレッシュ()はスローBeansException、{IllegalStateExceptionが 同期(この.startupShutdownMonitor){ // 開始時刻フラグを設定するなど、前のリフレッシュ調製物は、活性化され、ソース初期化プロパティ(プロパティソース)構成 ; prepareRefresh() // Aを(作成作成しない場合)サブクラスたBeanFactoryを更新し、リターンたBeanFactory ConfigurableListableBeanFactoryたBeanFactory = obtainFreshBeanFactory(); // たBeanFactory ApplicationContextの使用準備 prepareBeanFactory(たBeanFactory); 試み{ // この方法によってサブクラス形式であってもよいです修正するたBeanFactory postProcessBeanFactory(たBeanFactoryを)。 // インスタンス化し、呼び出すすべての登録BeanFactoryPostProcessorオブジェクト invokeBeanFactoryPostProcessors(たBeanFactoryを); // すべての登録BeanPostProcessorオブジェクトインスタンスおよびコール ; registerBeanPostProcessors(たBeanFactoryの) //はMessageSource初期化 initMessageSourceを(); //は、イベント放送局初期化 initApplicationEventMulticasterを(); // サブクラスは、リフレッシュ時に余分な作業を行うために、このメソッドをオーバーライド )OnRefresh(; // アプリケーションリスナーApplicationListener登録 registerListenersを(); //はすべて、怠惰なインスタンス化のinit豆非 finishBeanFactoryInitialization(たBeanFactoryを); //以下のような初期化LifecycleProcessor、公開の更新完了イベントを含め完了、リフレッシュ finishRefreshを(); } キャッチ(BeansException EX){ // リソースを避けるためにぶら下がって作成したシングルトンの後、すでに破壊する。 DestroyBeans(); // フラグにリセット「アクティブ」。 CancelRefresh(EX ); // 呼び出し元に伝播例外。 スローEX; } } }
より複雑な何かを、私たちは基本的な罰金を実現しません。
私たちは、あなたが依存性注入を使用することができ、バネ機能を初期化することができるようになります、次のビジネス・ロジックを実現するために、クラスのinitメソッドをCJDispatcherServlet
@Override 公共 空のinit(のServletConfigの設定){ // 設定をロード //がパケットアドレスをスキャンする取得 //は、クラスをロードするためにスキャン // ロードするようにクラスをインスタンス化 // 負荷依存性の注入を、それらを割り当て、 // ロードマップアドレス }
負荷の設定
ストリングcontextConfigLocation = config.getInitParameter( "contextConfigLocation" )。
loadConfig(contextConfigLocation)。
ここでのweb.xmlのinit-のparamノードで得られた値になります
具体的なポイントは、バネ下のプロファイルファイルをapplication.propertiesする、唯一のライン構成があります
パッケージパスをスキャンするように指定されたキーの名前を、知っているように構成することができます
これは、スキャンすべてのクラスを定義するために赤いボックスを表し
2行目はloadConfigは、パッケージのパスへと通過するメソッドを作成することです
ボイドloadConfig(文字列contextConfigLocation){ 入力ストリームは = この.getClass()のgetClassLoader()getResourceAsStream(。。contextConfigLocation)。 試す{ 特性 .LOAD(です)。 } キャッチ(IOExceptionを電子){ e.printStackTrace(); } 最後に{ 場合(ヌル!= される){ しようと{ )(is.closeを。 } キャッチ(IOExceptionを電子){ )(e.printStackTraceします。 } } } }
コードの黄色部分は、メンバ変数がここで使用されることに注意してください
プライベートプロパティプロパティは=新しいプロパティ() ;
クラスの定義の上半部のように、ここでの役割は、コンテンツ取得application.properties設定ファイルでは、後で使用するために、変数のプロパティにロードされます。
パケットアドレスをスキャンする取得します。
// スキャンするパケットのアドレスを取得する 文字列DIRPATHを= properties.getProperty( "scanner.package" );
本明細書において使用されるキー設定ディレクトリアドレスをリード
クラスをロードするためにスキャン
// クラスをロードするためにスキャン doScanner(DIRPATHを)。
私たちは、クラスdoScanner走査方式を定義し、パッケージディレクトリのアドレスを渡します
1 空隙doScanner(文字列DIRPATH){ 2 URLのURL = この .getClass()のgetClassLoader()のgetResource( "/" + dirpath.replaceAll( "\\。"、 "/"。 )); 3 ファイルDIR = 新しいファイル(url.getFile()); 4 ファイルは、[]ファイル= dir.listFiles()。 5 用(ファイルのファイル:ファイル){ 6 の場合(file.isDirectory()){ 7 doScanner( "" DIRPATH + + file.getName()); 8 続けます。 9 } 10 11 // 取文件名 12 文字列のbeanName = DIRPATH + "" 。+ file.getName()でReplaceAll( "クラス"、 "" ); 13 beanNames .add(のbeanName)。 14 } 15 }
二行目は、エスケープ置き換えられました
コードが読み込まれ、指定されたパスにあるジョブファイル内に存在する、フォルダの場合、収集容器内のメモリに再帰呼び出し、もしファイル名とファイルのパス
変数の黄色部分がメンバ変数の外に定義されていることに注意してください
プライベートリスト<文字列> beanNames =新しいArrayListを<>();
私たちは、クラスの上半分にそれを追加します。
次のようにBean名を取得しました
ここから我々はそれが我々が知るノートに定義されていることがわかります。
ロードするようにクラスをインスタンス化
// ロードするクラスのインスタンス )(doInstanceを。
ちょうど今、私たちは今、私たちは1をインスタンス化し、IOCの容器に保存する必要があり、クラス定義された名前のリストを持っています。
HashMapを使用してロードするクラスが行うことができますコンテナを定義し、それをクラスの上半分に定義されたメンバ変数を、設定されています
プライベート地図<文字列、オブジェクト> IOC =新しいHashMapのは、<>();
その後、メソッドのdoInstanceを作成します
1 void doInstance() { 2 if (beanNames.isEmpty()) { 3 return; 4 } 5 for (String beanName : beanNames) { 6 try { 7 Class cls = Class.forName(beanName); 8 if (cls.isAnnotationPresent(JCController.class)) { 9 //使用反射实例化对象 10 Object instance = cls.newInstance(); 11 //默认类名首字母小写 12 beanName = firstLowerCase(cls.getSimpleName()); 13 //写入ioc容器 14 ioc.put(beanName, instance); 15 16 17 } else if (cls.isAnnotationPresent(JCService.class)) { 18 Object instance = cls.newInstance(); 19 JCService jcService = (JCService) cls.getAnnotation(JCService.class); 20 21 String alisName = jcService.value(); 22 if (null == alisName || alisName.trim().length() == 0) { 23 beanName = cls.getSimpleName(); 24 } else { 25 beanName = alisName; 26 } 27 beanName = firstLowerCase(beanName); 28 ioc.put(beanName, instance); 29 //如果是接口,自动注入它的实现类 30 Class<?>[] interfaces = cls.getInterfaces(); 31 for (Class<?> c : 32 interfaces) { 33 ioc.put(firstLowerCase(c.getSimpleName()), instance); 34 } 35 } else { 36 continue; 37 } 38 } catch (ClassNotFoundException e) { 39 e.printStackTrace(); 40 } catch (IllegalAccessException e) { 41 e.printStackTrace(); 42 } catch (InstantiationException e) { 43 e.printStackTrace(); 44 } 45 } 46 }
只要提供类的完全限定名,通过Class.forName静态方法,我们就能将类信息加载到内存中并且返回Class 对象,通过反射来实例化,见第10行代码,
我们通过循环beanNames集合,来实例化每个类,并将实例化后的对象装入HashMap中
注意:第12行将类名的首字母小写后存入map,该方法定义如下
1 String firstLowerCase(String str) { 2 char[] chars = str.toCharArray(); 3 chars[0] += 32; 4 return String.valueOf(chars); 5 }
这行代码会将字符串转成char数组,然后将数组中第一个字符转为大写,这里采用了一种比较巧妙的方式实现,tom老师采用了一种比较骚的操作
实例化完成后,ioc容器中的数据如下:
说明:
图片中可以看出,hashMap的key 都是小写,value已经是对象了 ,见红框。
这里为什么要把蓝框标记出来,是因为这是类中的字段属性,此时可以看到,虽然类已经被实例化了,可是属性还是null呢
我这里为了测试依赖注入,所以加了2个接口和2个实现类
接口定义如下:
public interface IHomeService { String sayHi(); String getName(Integer id,String no); String getRequestBody(Integer id, String no, GetUserInfo userInfo); } public interface IStudentService { String sayHi(); }
实现类:
@JCService public class StudentService implements IStudentService{ @Override public String sayHi(){ return "Hello world!"; } }
@JCService public class HomeService implements IHomeService{ @JCAutoWrited StudentService studentService; @Override public String sayHi() { return studentService.sayHi(); } @Override public String getName(Integer id,String no) { return "SB0000"+id; } @Override public String getRequestBody(Integer id, String no, GetUserInfo userInfo) { return "userName="+userInfo.getName()+" no="+no; } }
依赖实体:
public class GetUserInfo { public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public BigDecimal getGrowthValue() { return growthValue; } public void setGrowthValue(BigDecimal growthValue) { this.growthValue = growthValue; } private String name; private Integer age; private BigDecimal growthValue; }
加载依赖注入,给属性赋值
//加载依赖注入,给属性赋值 doAutoWrited();
现在我们实现依赖注入,需要定义一个无参的方法doAutoWrite
1 void doAutoWrited() { 2 for (Map.Entry<String, Object> obj : ioc.entrySet()) { 3 try { 4 for (Field field : obj.getValue().getClass().getDeclaredFields()) { 5 if (!field.isAnnotationPresent(JCAutoWrited.class)) { 6 continue; 7 } 8 JCAutoWrited autoWrited = field.getAnnotation(JCAutoWrited.class); 9 String beanName = autoWrited.value(); 10 if ("".equals(beanName)) { 11 beanName = field.getType().getSimpleName(); 12 } 13 14 field.setAccessible(true); 15 16 field.set(obj.getValue(), ioc.get(firstLowerCase(beanName))); 17 } 18 } catch (IllegalAccessException e) { 19 e.printStackTrace(); 20 } 21 22 } 23 24 25 }
这个方法是通过循环ioc里面的实体,反射找出字段,看看是否有需要注入的标记JCAutoWrited,如果加了标记,就反射给字段赋值,类型从ioc容器中获取
加载映射地址
//加载映射地址 doRequestMapping();
映射地址的作用是根据请求的url匹配method方法
1 void doRequestMapping() { 2 if (ioc.isEmpty()) { 3 return; 4 } 5 for (Map.Entry<String, Object> obj : ioc.entrySet()) { 6 if (!obj.getValue().getClass().isAnnotationPresent(JCController.class)) { 7 continue; 8 } 9 Method[] methods = obj.getValue().getClass().getMethods(); 10 for (Method method : methods) { 11 if (!method.isAnnotationPresent(JCRequestMapping.class)) { 12 continue; 13 } 14 String baseUrl = ""; 15 if (obj.getValue().getClass().isAnnotationPresent(JCRequestMapping.class)) { 16 baseUrl = obj.getValue().getClass().getAnnotation(JCRequestMapping.class).value(); 17 } 18 JCRequestMapping jcRequestMapping = method.getAnnotation(JCRequestMapping.class); 19 if ("".equals(jcRequestMapping.value())) { 20 continue; 21 } 22 String url = (baseUrl + "/" + jcRequestMapping.value()).replaceAll("/+", "/"); 23 urlMapping.put(url, method); 24 System.out.println(url); 25 } 26 } 27 }
这里其实就是根据对象反射获取到JCRequestMapping上面的value值
@JCRequestMapping("/sayHi")
取到的就是/sayHi
另外注意的是:黄色部分使用的变量是一个hashMap,在类上半部分定义的
private Map<String, Method> urlMapping = new HashMap<>();
这里面存的是 url 和对应的method对象。后面处理请求的时候要使用到的。
结尾
容器的初始化到这里就结束了,一共使用了4个容器来存放相关对象,后续servlet处理请求的时候会用到它们。
下一篇,将会继续完善它,通过请求来验证是否可以达到预期效果。另外会实现参数绑定,能处理各类请求并响应。