一起写框架-Ioc内核容器的实现-基础功能-ComponentScan(四)
功能说明
该步骤实现的功能包括:
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(四)的更多相关文章
- 一起写框架-Ioc内核容器的实现-基础功能-ComponentScan支持组件注解限制(七)
实现功能 以上的代码我们发现.我们都是将@ComponentScan扫描的路径下的所有类都加载到容器中的. 而实际需求,我们并不希望所有的类都创建对象,而是加了组件注解@Controller,@Ser ...
- 一起写框架-Ioc内核容器的实现-基础功能-ComponentScan支持多包扫描(六)
实现功能 1.我们看到@ComponentScan注解一个开始定义就是需要支持,扫描多个包,将多个包的类名获取到.现在就实现这个功能. 实现思路 根据传入的字符串数组,获得多个包下的类全限制名. 实现 ...
- 一起写框架-Ioc内核容器的实现-基础功能-容器对象名默认首字母小写(八)
实现功能 --前面实现的代码-- 默认的对象名就类名.不符合Java的命名规范.我们希望默认的对象名首字母小写. 实现思路 创建一个命名规则的帮助类.实现将对大写开头的对象名修改为小写开头. 实现步骤 ...
- 一起写框架-Ioc内核容器的实现-基础功能-getBean(五)
实现的功能 1. 启动程序时,将@ComponentScan加载的类,创建对象并放在容器里面.(查看上一篇文) 2. 通过ApplicatoinContext的getBean()方法获得容器里面的对象 ...
- 一起写框架-Ioc内核容器的实现-基础API的定义(三)
Ioc内核要解决的问题 1.被调用方,在程序启动时就要创建好对象,放在一个容器里面. 2.调用方使用一个接口或类的引用(不用使用new),就可以创建获得对象. 解决这个两个问题的思路 1.定义一个对象 ...
- 自己动手写框架——IoC的实现
先看看 IoC百度百科 优化过程 namespace Test { class Program { static void Main(string[] args) { //场景 某公司客服要回访一些客 ...
- 用C写一个web服务器(一) 基础功能
.container { margin-right: auto; margin-left: auto; padding-left: 15px; padding-right: 15px } .conta ...
- 一起写框架-控制反转(Ioc)概述(二)
控制反转概述 控制反转(Inversion of Control,英文缩写为IoC),就是将代码的调用的控制权,由调用方转移给被调用方. 如图:修改代码A类的代码,才能将B类的对象换成C类.代码的控制 ...
- Spring框架IOC容器和AOP解析 非常 有用
Spring框架IOC容器和AOP解析 主要分析点: 一.Spring开源框架的简介 二.Spring下IOC容器和DI(依赖注入Dependency injection) 三.Spring下面 ...
随机推荐
- 【充分利用你的Azure】将Azure用作云计算平台(1)
本文将围绕几个步骤来讲. 因为本人是MSP,微软送了150刀的额度给我随便使用.这篇文章是要讲将Azure用作云计算平台,对于我来说,我是做机器学习的,那么Azure就要有机器学习的平台. 本文的目的 ...
- Power Strings poj2406(神代码)
Power Strings Time Limit: 3000MS Memory Limit: 65536K Total Submissions: 29402 Accepted: 12296 D ...
- Day2 python基础学习
http://www.pythondoc.com/ Python中文学习大本营 本节内容: 一.字符串操作 二.列表操作 三.元组操作 四.字典操作 五.集合操作 六.字符编码操作 一.字符串操作 1 ...
- Weave Scope 容器地图 - 每天5分钟玩转 Docker 容器技术(80)
Weave Scope 的最大特点是会自动生成一张 Docker 容器地图,让我们能够直观地理解.监控和控制容器.千言万语不及一张图,先感受一下. 下面开始实践 Weave Scope. 安装 执行如 ...
- git仓库管理笔录
Git是目前世界上最先进的分布式版本控制系统(没有之一). 小明做了个个人博客,放到了Git 仓库里面.第二天换了台电脑,只需要 git clone 克隆一下git 远程仓库的代码到本地即可.然后他 ...
- 【记录一次windows技术学习】使用笔记本DOS命令搭建WLAN热点
[记录一次windows技术学习]使用笔记本DOS命令搭建WLAN热点 时间:2017-10-14 22:36:13 撰写者:AK末影人 [转发请注明出处] --------------------- ...
- ajax和jsonp使用总结
前言:ajax和jsonp可以与后台通信,获取数据和信息,但是又不用刷新整个页面,实现页面的局部刷新. 一.ajax 定义:一种发送http请求与后台进行异步通讯的技术. 原理:实例化xmlhttp对 ...
- DevOps之服务手册
唠叨话 关于德语噢屁事的知识点,仅提供精华汇总,具体知识点细节,参考教程网址,如需帮助,请留言. <DevOps服务手册(Manual)> <IT资源目标化>1.设施和设备(I ...
- UWP xaml 圆形头像
圆形头像 去掉黑边 拖动打开图形 圆形头像 现在很多软件都喜欢使用圆形头像 win10 uwp使用圆形头像很简单 <Ellipse Width="200" Height=&q ...
- win10 uwp 异步进度条
本文主要讲我设计的几个进度条,还有如何使用异步控制进度条,如何使用动画做进度. 进度条可以参见:http://edi.wang/post/2016/2/25/windows-10-uwp-modal- ...