功能说明

该步骤实现的功能包括:

1. 启动程序时,将@ComponentScan加载的类,创建对象并放在容器里面。

2. 通过ApplicatoinContext的getBean()方法获得容器里面的对象。 (放在下一篇文实现)

实现步骤

1.定义一个扫描注解@ComponentScan

 package ioc.core.annotation;

 import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
//表示用于运行时的注解
@Retention(RetentionPolicy.RUNTIME)
//表示只能在类或者接口的上面使用
@Target(value=ElementType.TYPE)
@Documented
public @interface ComponentScan { /**
* 声明一个注解属性用于接收扫描的包路径
* @return
*/
String[] basePackages() default {} ; }

2.定义一个@Configuration标识配置类

package ioc.core.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 标识配置类注解的定义
* @author ranger
*
*/
//表示用于运行时的注解
@Retention(RetentionPolicy.RUNTIME)
//表示只能在类或者接口的上面使用
@Target(value=ElementType.TYPE)
@Documented
public @interface Configuration { }

2.定义容器 Context接口

 package ioc.core;
import java.util.Map; /**
* Ioc框架的容器接口
* @author ranger
*
*/
public interface Context { /**
* 用于获得容器中的所有对象
* @return
*/
Map<String,Object> getObjects(); /**
* 用于增加容器中的对象
* @param key
* @param value
*/
void addObject(String key, Object value); }

3.定义容器操作接口ApplicationContext

 package ioc.core;
/**
* Ioc框架的容器操作接口
* @author ranger
*
*/
public interface ApplicationContext { /**
* 通过容器里面的对象名,返回容器中的对象
* @param objectName
* @return
*/
Object getBean(String objectName);
}

4.实现容器

 package ioc.core.impl;

 import java.util.HashMap;
import java.util.Map; import ioc.core.Context; /**
* 实现框架容器,用于存储扫描注解创建的所有对象。
* @author ranger
*
*/
public class ContextImpl implements Context { //使用Map来存储对象,为什么使用Map对象呢?因为预留对象名可以设置的需要。
Map<String,Object> objects=new HashMap<String,Object>(); @Override
public Map<String,Object> getObjects() { return this.objects;
} @Override
public void addObject(String key,Object value) {
objects.put(key, value);
}
}

5.实现扫描包。获得包以及该包子包的所有类的类全名。

--使用到一个工具类从包中读取包和其子包类名--

 package ioc.core.utils;

 import java.io.File;
import java.io.IOException;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile; /**
* 本类用于读取包下面的类名
* 来自博客
* http://blog.csdn.net/aust_glj/article/details/53385651
*
*/
public class PackageUtils {
public static void main(String[] args) throws Exception {
String packageName = "ioc.core.annotation";
Set<String> classNames = getClassName(packageName, true);
if (classNames != null) {
for (String className : classNames) {
System.out.println(className);
}
}
} /**
* 获取某包下所有类
* @param packageName 包名
* @param isRecursion 是否遍历子包
* @return 类的完整名称
*/
public static Set<String> getClassName(String packageName, boolean isRecursion) {
Set<String> classNames = null;
ClassLoader loader = Thread.currentThread().getContextClassLoader();
String packagePath = packageName.replace(".", "/"); URL url = loader.getResource(packagePath);
if (url != null) {
String protocol = url.getProtocol();
if (protocol.equals("file")) {
classNames = getClassNameFromDir(url.getPath(), packageName, isRecursion);
} else if (protocol.equals("jar")) {
JarFile jarFile = null;
try{
jarFile = ((JarURLConnection) url.openConnection()).getJarFile();
} catch(Exception e){
e.printStackTrace();
} if(jarFile != null){
getClassNameFromJar(jarFile.entries(), packageName, isRecursion);
}
}
} else {
/*从所有的jar包中查找包名*/
classNames = getClassNameFromJars(((URLClassLoader)loader).getURLs(), packageName, isRecursion);
} return classNames;
} /**
* 从项目文件获取某包下所有类
* @param filePath 文件路径
* @param className 类名集合
* @param isRecursion 是否遍历子包
* @return 类的完整名称
*/
private static Set<String> getClassNameFromDir(String filePath, String packageName, boolean isRecursion) {
Set<String> className = new HashSet<String>();
File file = new File(filePath);
File[] files = file.listFiles();
for (File childFile : files) {
if (childFile.isDirectory()) {
if (isRecursion) {
className.addAll(getClassNameFromDir(childFile.getPath(), packageName+"."+childFile.getName(), isRecursion));
}
} else {
String fileName = childFile.getName();
if (fileName.endsWith(".class") && !fileName.contains("$")) {
className.add(packageName+ "." + fileName.replace(".class", ""));
}
}
} return className;
} /**
* @param jarEntries
* @param packageName
* @param isRecursion
* @return
*/
private static Set<String> getClassNameFromJar(Enumeration<JarEntry> jarEntries, String packageName, boolean isRecursion){
Set<String> classNames = new HashSet<String>(); while (jarEntries.hasMoreElements()) {
JarEntry jarEntry = jarEntries.nextElement();
if(!jarEntry.isDirectory()){
/*
* 这里是为了方便,先把"/" 转成 "." 再判断 ".class" 的做法可能会有bug
* (FIXME: 先把"/" 转成 "." 再判断 ".class" 的做法可能会有bug)
*/
String entryName = jarEntry.getName().replace("/", ".");
if (entryName.endsWith(".class") && !entryName.contains("$") && entryName.startsWith(packageName)) {
entryName = entryName.replace(".class", "");
if(isRecursion){
classNames.add(entryName);
} else if(!entryName.replace(packageName+".", "").contains(".")){
classNames.add(entryName);
}
}
}
} return classNames;
} /**
* 从所有jar中搜索该包,并获取该包下所有类
* @param urls URL集合
* @param packageName 包路径
* @param isRecursion 是否遍历子包
* @return 类的完整名称
*/
private static Set<String> getClassNameFromJars(URL[] urls, String packageName, boolean isRecursion) {
Set<String> classNames = new HashSet<String>(); for (int i = 0; i < urls.length; i++) {
String classPath = urls[i].getPath(); //不必搜索classes文件夹
if (classPath.endsWith("classes/")) {continue;} JarFile jarFile = null;
try {
jarFile = new JarFile(classPath.substring(classPath.indexOf("/")));
} catch (IOException e) {
e.printStackTrace();
} if (jarFile != null) {
classNames.addAll(getClassNameFromJar(jarFile.entries(), packageName, isRecursion));
}
} return classNames;
}
}

--容器操作类的公用代码写在AbstractApplicationContext抽象类的构造函数里面,方便以后扩展其他的加载情况--

 package ioc.core.impl;

 import java.util.Iterator;
import java.util.Set; import ioc.core.ApplicationContext;
import ioc.core.Context;
import ioc.core.annotation.ComponentScan;
import ioc.core.annotation.Configuration;
import ioc.core.utils.PackageUtils; public abstract class AbstractApplicationContext implements ApplicationContext{
//声明一个线程变量,存储容器对象,表示同一条线程,一个ApplicationContext只操作一个容器对象。
private ThreadLocal<Context> contexts=new ThreadLocal<Context>(); protected String[] basePackage=null;
/**
* 将容器操作加载创建对象的代码写抽象类里面,这样可以方便以后扩展多种实现。
* @param classType
*/
public AbstractApplicationContext(Class<?> classType) {
//判断配置类是否有Configuration注解
Configuration annotation = classType.getAnnotation(Configuration.class);
if(annotation!=null){
//获得组件扫描注解
ComponentScan componentScan = classType.getDeclaredAnnotation(ComponentScan.class);
//获得包名
this.basePackage = componentScan.basePackages();
//根据包名获得类全限制名
Set<String> classNames = PackageUtils.getClassName(this.basePackage[0], true);
//通过类名创建对象
Iterator<String> iteratorClassName = classNames.iterator();
while(iteratorClassName.hasNext()){
String className = iteratorClassName.next();
try {
//通过类全名创建对象
Object instance = Class.forName(className).newInstance();
//将对象加到容器中,对象名就类全名
this.getContext().addObject(instance.getClass().getSimpleName(),instance);
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
} public String[] getBasePackage() {
return basePackage;
} public Context getContext(){
if(contexts.get()==null){
//调用容器
Context context=new ContextImpl();
contexts.set(context);
}
return contexts.get();
} }

----实现AnnotationApplicationContext注解操作类,继承bstractApplicationContext---

注意:这里还没有实现getBean方法。先实现启动程序时,包下面的所有类有没有加入到容器里了--

 package ioc.core.impl;

 public class AnntationApplicationContext extends AbstractApplicationContext {

     public AnntationApplicationContext(Class<?> classType) {
super(classType);
} @Override
public Object getBean(String objectName) { return null;
}
}

测试代码

测试是否可以获得指定扫描包下的类的对象

1.创建一个测试源码包存放测试代码

2. Config类,是一个配置类。里面定义扫描包的路径

 package ioc.core.test.config;

 import ioc.core.annotation.ComponentScan;
import ioc.core.annotation.Configuration; //使用定义@Configuration定义该类是一个配置类
@Configuration
//使用ComponentScan设置扫描包的路径
@ComponentScan(basePackages="ioc.core.test")
public class Config { }

3. 编写一个普通的UserService类测试

package ioc.core.test.service;

/**
* 一个普通的类,用于测试是否可以创建对象
* @author ranger
*
*/
public class UserService { public void login(){
System.out.println("-登录-");
}
}

4. 创建一个AnntationApplicationContextTest测试类

package ioc.core.test;

import org.junit.Test;

import ioc.core.impl.AnntationApplicationContext;
import ioc.core.test.config.Config; public class AnntationApplicationContextTest { @Test
public void constructor(){
try {
AnntationApplicationContext context=new AnntationApplicationContext(Config.class);
//如果可以打印出容器里面的对象,说明成功
System.out.println(context.getContext().getObjects());
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} }

5.测试结果,获得UserService的对象。成功!

一起写框架-Ioc内核容器的实现-基础功能-ComponentScan(四)的更多相关文章

  1. 一起写框架-Ioc内核容器的实现-基础功能-ComponentScan支持组件注解限制(七)

    实现功能 以上的代码我们发现.我们都是将@ComponentScan扫描的路径下的所有类都加载到容器中的. 而实际需求,我们并不希望所有的类都创建对象,而是加了组件注解@Controller,@Ser ...

  2. 一起写框架-Ioc内核容器的实现-基础功能-ComponentScan支持多包扫描(六)

    实现功能 1.我们看到@ComponentScan注解一个开始定义就是需要支持,扫描多个包,将多个包的类名获取到.现在就实现这个功能. 实现思路 根据传入的字符串数组,获得多个包下的类全限制名. 实现 ...

  3. 一起写框架-Ioc内核容器的实现-基础功能-容器对象名默认首字母小写(八)

    实现功能 --前面实现的代码-- 默认的对象名就类名.不符合Java的命名规范.我们希望默认的对象名首字母小写. 实现思路 创建一个命名规则的帮助类.实现将对大写开头的对象名修改为小写开头. 实现步骤 ...

  4. 一起写框架-Ioc内核容器的实现-基础功能-getBean(五)

    实现的功能 1. 启动程序时,将@ComponentScan加载的类,创建对象并放在容器里面.(查看上一篇文) 2. 通过ApplicatoinContext的getBean()方法获得容器里面的对象 ...

  5. 一起写框架-Ioc内核容器的实现-基础API的定义(三)

    Ioc内核要解决的问题 1.被调用方,在程序启动时就要创建好对象,放在一个容器里面. 2.调用方使用一个接口或类的引用(不用使用new),就可以创建获得对象. 解决这个两个问题的思路 1.定义一个对象 ...

  6. 自己动手写框架——IoC的实现

    先看看 IoC百度百科 优化过程 namespace Test { class Program { static void Main(string[] args) { //场景 某公司客服要回访一些客 ...

  7. 用C写一个web服务器(一) 基础功能

    .container { margin-right: auto; margin-left: auto; padding-left: 15px; padding-right: 15px } .conta ...

  8. 一起写框架-控制反转(Ioc)概述(二)

    控制反转概述 控制反转(Inversion of Control,英文缩写为IoC),就是将代码的调用的控制权,由调用方转移给被调用方. 如图:修改代码A类的代码,才能将B类的对象换成C类.代码的控制 ...

  9. Spring框架IOC容器和AOP解析 非常 有用

    Spring框架IOC容器和AOP解析   主要分析点: 一.Spring开源框架的简介  二.Spring下IOC容器和DI(依赖注入Dependency injection) 三.Spring下面 ...

随机推荐

  1. http://codeforces.com/contest/845

    A. Chess Tourney time limit per test 1 second memory limit per test 256 megabytes input standard inp ...

  2. PE文件格式详解,第三讲,可选头文件格式,以及节表

    PE文件格式详解,第三讲,可选头文件格式,以及节表 作者:IBinary出处:http://www.cnblogs.com/iBinary/版权所有,欢迎保留原文链接进行转载:) 一丶可选头结构以及作 ...

  3. java远程备份mysql数据库关键问题(限windows环境,亲测解决)

    其它环境同理也可解决. 条件:为了使用mysql命令,本机要安装mysql ,我本机安装的是mysql 5.5. 错误1:使用命令 mysqldump -h192.168.1.50 -u root - ...

  4. Java web JavaScript DOM 编程

     JavaScript DOM 编程 (1).DOM概述及分类 (2).DOM结构模型:XML DOM 和 HTML DOM 关系? (3).结点,结点树,结点属性与方法? 1.DOM是什么? d ...

  5. 在SQLSERVER中创建DBLINK,操作远程服务器数据库

    --配置SQLSERVER数据库的DBLINK exec sp_addlinkedserver @server='WAS_SMS',@srvproduct='',@provider='SQLOLEDB ...

  6. Jquery实现按钮点击遮罩加载,处理完后恢复

    <%@ Page Language="C#" AutoEventWireup="true" CodeFile="EasyUiLoad.aspx. ...

  7. mongodb生产环境(副本集模式)集群搭建配置

    mongodb副本集模式由如下几部分组成: 1.路由实例mongos 2.配置实例configsvr 3.副本集集群replset(一主多从) tips: 1.以上实例都是mongod守护进程 2.以 ...

  8. 学习笔记之CSS样式(选择器背景字体边框绝/相对、固定位置and分层流等)

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  9. GooglePlay - 文件上传限制的扩展

    前言 Google Play应用商店在上传限制100MB大小,超过该大小的应用必须将超过部分以扩展文件的形式进行上传处理. 总共可上传2个扩展文件,每个最大文件可为2GB,同时obb文件格式可自选. ...

  10. vue.js实例对象+组件树

    vue的实例对象 首先用js的new关键字实例化一个vue el: vue组件或对象装载在页面的位置,可通过id或class或标签名 template: 装载的内容.HTML代码/包含指令或者其他组件 ...