一起写框架-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下面 ...
随机推荐
- python之HTMLParser解析HTML文档
HTMLParser是Python自带的模块,使用简单,能够很容易的实现HTML文件的分析.本文主要简单讲一下HTMLParser的用法. 使用时需要定义一个从类HTMLParser继承的类,重定义函 ...
- IDL 结构体
1.创建结构体 (1) 命名结构体 创建具有两个成员变量A.B的命名为str1的结构体 IDL> struct1={str1,a:1,b:2} IDL> help,struct1,/str ...
- c# Linq操作XML,查找节点数据
/*查找XML*/ var filePath = Server.MapPath("~/xml/sample.xml"); XDocument doc = XDocument.Loa ...
- WPF中的imagesource 和内存图片的处理
[转载]ImageSource的使用心得 很多时候,我们会使用图片来装饰UI,比如作为控件背景等. 而这些图片可以分为两种形式,即存在于本地文件系统中的图片和存在于内存中的图片 对于这两种形式的图片, ...
- Echarts数据可视化series-scatter散点图,开发全解+完美注释
全栈工程师开发手册 (作者:栾鹏) Echarts数据可视化开发代码注释全解 Echarts数据可视化开发参数配置全解 6大公共组件详解(点击进入): title详解. tooltip详解.toolb ...
- plsql中文乱码问题方案解决
1.查看服务器端编码 a.select userenv('language') from dual; b.我实际查到的结果为:AMERICAN_AMERICA.ZHS16GBK 2.执行语句 ...
- Linux“体检”指标
* { color: #3e3e3e } body { font-family: "Helvetica Neue", Helvetica, "Hiragino Sans ...
- invalid types 'int[int]' for array subscript// EOF 输入多组数据//如何键盘输入EOF
数组维度搞错了 一次运行,要输入多组数据,直到读至输入文件末尾(EOF)为止 while(scanf("%d %d",&a, &b) != EOF) // 输入结束 ...
- emacs 配置
个人的Emacs配置,环境是archlinux,参考了不少网上资料,因为太多,就不一一列举了,在这里感谢那些作者的辛苦经验劳动. (custom-set-variables ;; custom-set ...
- Git相关操作三
1.显示当前分支: git branch 输入上述命令可以显示出分支,*所在的分支为当前分支. 2.新建分支: git branch new_branch new_branch为新建分支的名称,注意该 ...