To realize his spring core function Two

Foreword

Previous We talked about some of the features spring and analysis of what functions need to achieve, the preparatory work has been done, we began to realize that a specific function.

Container loading process

 We know that in the spring refesh () method to do a lot of initialization work, it covers almost spring core processes

public  void Refresh () throws BeansException, {IllegalStateException
     the synchronized ( the this .startupShutdownMonitor) {
         // previous refresh preparations, including setting start time flag is activated, the source initialization properties (property source) configuration 
        prepareRefresh ();
         // a refreshing the subclass BeanFactory (if not create created), and returns BeanFactory 
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory ();
         // prepare for BeanFactory ApplicationContext use 
        prepareBeanFactory (beanFactory);
         the try {
             // subclass format by this method may be BeanFactory to modify 
            postProcessBeanFactory (beanFactory);
            // instantiate and call all registered BeanFactoryPostProcessor objects 
            invokeBeanFactoryPostProcessors (beanFactory);
             // instantiate and call all registered BeanPostProcessor objects 
            registerBeanPostProcessors (beanFactory);
             // initialize the MessageSource 
            initMessageSource ();
             // initialize event broadcaster 
            initApplicationEventMulticaster ();
             // subclasses override this method to do extra work during the refresh 
            OnRefresh ();
             // Register the application listener ApplicationListener 
            registerListeners ();
             // instantiate all-lazy-the init bean non 
            finishBeanFactoryInitialization (beanFactory);
             //Refresh completion, including the initialization LifecycleProcessor, publishing refresh completion event like 
            finishRefresh (); 
        } 
        the catch (BeansException EX) {
             // the Destroy already after the dangling Created singletons to Avoid Resources. 
            DestroyBeans ();
             // the Reset 'Active' In Flag. 
            CancelRefresh (EX );
             // the Propagate Exception to Caller. 
            the throw EX; 
        } 
    } 
}

 Do something more complicated, and we do realize the basic fine.

 We CJDispatcherServlet class init method, to achieve the following business logic, will be able to initialize the spring function, you can use a dependency injection

    @Override
     public  void the init (the ServletConfig config) {
         // load the configuration 

        // Get packet address to be scanned 

        // scan to load class 
    
        // instantiate the class to load 

        // load dependency injection, assigns them 
 
        // load map address 
     
    }

 

Load configuration

 String contextConfigLocation = config.getInitParameter("contextConfigLocation");

        loadConfig(contextConfigLocation);

Here will be the value obtained in web.xml init-param node

Specific points to application.properties profile file under the spring, there is only one line configuration

 

It can be configured to know the key name, which is designated to scan the package path

It represents the red box to define the scan all the classes

The second line is to create a method loadConfig, will pass into the package path

    void loadConfig(String contextConfigLocation) {
        InputStream is = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation);
        try {
            properties.load(is);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (null != is) {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace (); 
                } 
            } 
        } 
    }

Note that the yellow part of the code, a member variable is used here

private Properties properties = new Properties () ; 
like in the upper half part of the definition of the class, the role here is the content acquisition application.properties configuration file is loaded into the variable properties, for later use.

Gets packet address to be scanned

  

 // get the packet address to scan 
 String dirpath = properties.getProperty ( "scanner.package" );

Used herein key configuration directory address read

Scan to load the class

   // scan to load class 
  doScanner (dirpath);

We define a class doScanner scanning method, pass in the package directory address

 1     void doScanner(String dirpath) {
 2         URL url = this.getClass().getClassLoader().getResource("/" + dirpath.replaceAll("\\.", "/"));
 3         File dir = new File(url.getFile());
 4         File[] files = dir.listFiles();
 5         for (File file : files) {
 6             if (file.isDirectory()) {
 7                 doScanner(dirpath + "." + file.getName());
 8                 continue;
 9             }
10 
11             //取文件名
12             String beanName = dirpath + "." + file.getName().replaceAll(".class", "");
13             beanNames.add(beanName);
14         }
15     }

The second line was replaced escaped

The code is present within the job file in the specified path is read, if the folder, the recursive call, if the file name and path of the file into memory within the collection vessel

 Note that the yellow part of the variable is defined outside a member variable

private List<String> beanNames = new ArrayList<>();

 We add it in the top half of the class.

 BeanName obtained as follows

From here we see that it has been defined to the notes we find out.

 

 

Instantiate the class to load

 // instantiate the class to be loaded 
   doInstance ();

Just now we've got the list of names defined class, now we need to instantiate one, and save them in ioc container.

Define a container loading classes using HashMap can do, it is set member variables, defined in the top half of the class

private Map <String, Object> ioc = new HashMap <> (); 
then create a method 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();
}
View Code

 

实现类:
@JCService
public class StudentService  implements IStudentService{
    @Override
    public String sayHi(){
        return "Hello world!";
    }
}
View Code
@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;
    }
}
View Code

依赖实体:

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;

}
View Code

 



加载依赖注入,给属性赋值

//加载依赖注入,给属性赋值
        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处理请求的时候会用到它们。

下一篇,将会继续完善它,通过请求来验证是否可以达到预期效果。另外会实现参数绑定,能处理各类请求并响应。

完整代码地址

Guess you like

Origin www.cnblogs.com/jingch/p/11369599.html