目录
概念
手写SpringIOC XML版本
手写SpringIOC 注解版本
传统应用程序
IOC容器
1、编写一个service样例
public class UserServiceImpl{
public void add() {
System.out.println("解读XML文件,并通过反射机制运行的方法");
}
}
2、在xml里将该service配置进去
3、实现XML版本,在IOC容器中初始化对象
//通过XML方式注入bean
public class ClassPathXmlApplicationContext {
private String xmlPath;
ConcurrentHashMap IOCMap = null;
public ClassPathXmlApplicationContext(String xmlPath) throws Exception{
this.xmlPath = xmlPath;
//1.初始化IOC容器
initIOCMap();
}
//2.根据beanId查找对应的对象
public Object getBean(String beanId){
return IOCMap.get(beanId);
}
/**
* 初始化IOC容器
* 1.初始化IOC容器
* 2.读取XML配置,获取节点
* 3.遍历节点,获取bean信息(beanId,classPath)
* 4.通过反射机制,在IOC容器中初始化对象
*/
private void initIOCMap() throws Exception {
//1.初始化IOC容器
IOCMap = new ConcurrentHashMap();
//2.读取XML配置,获取节点
List elements = readXML();
//3.遍历节点,获取bean信息(beanId,classPath)
List
4、测试
public class Test {
public static void main(String[] args) throws Exception{
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
UserServiceImpl userServiceImpl = (UserServiceImpl) applicationContext.getBean("userServiceImpl");
userServiceImpl.add();
}
}
1、自定义@service和@Resource注解
// 自定义service注解 注入到Spring容器
@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface ServiceDemo {
}
//自定义resource注解 从Spring容器获取bean
@Target({ ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface ResourceDemo {
}
2、编写两个service样例,加上自定义注解
//将该类注入到spring容器里面
@ServiceDemo
public class OrderServiceImpl{
public void addOrder() {
System.out.println("addOrder");
}
}
//将该类注入到spring容器里面
@ServiceDemo
public class UserServiceImpl{
// 从Spring容器中读取bean
@ResourceDemo
private OrderServiceImpl orderServiceImpl;
public void add() {
orderServiceImpl.addOrder();
System.out.println("我是使用反射机制运行的方法");
}
}
3、实现注解版本
//通过注解方式注入bean
public class ClassPathXmlApplicationContext {
//扫包范围
private String packageName;
ConcurrentHashMap IOCMap = null;
public ClassPathXmlApplicationContext(String packageName) throws Exception{
this.packageName = packageName;
//1.初始化IOC容器
initIOCMap();
}
/**
* 根据beanId查找对应的对象
* 1.通过beanID查找查找对应bean对象
* 2.使用反射将加有自定义注解Resource的类作为属性注入到该对象中
* @param beanId
* @return
*/
public Object getBean(String beanId) throws Exception {
//1.通过beanID查找查找对应bean对象
Object obj = IOCMap.get(beanId);
//2.使用反射将加有自定义注解Resource的类作为属性注入到该对象中
attriAssign(obj);
return obj;
}
/**
* 使用反射将加有自定义注解Resource的类作为属性注入到该对象中
* @param obj
* @return
*/
private void attriAssign(Object obj) throws Exception {
Class> classInfo = obj.getClass();
Field[] declaredFields = classInfo.getDeclaredFields();
for(Field field:declaredFields){
//如果存在自定义Resource注解
if(field.getDeclaredAnnotation(ResourceDemo.class) == null){
continue;
}
//属性名称
String beanId = field.getName();
//使用属性名称查找bean容器
Object bean = getBean(beanId);
if(bean == null){
continue;
}
//私有访问允许访问
field.setAccessible(true);
//给属性赋值
//将加有自定义注解Resource的类作为属性注入到该对象中
field.set(obj,bean);
}
}
/**
* 初始化IOC容器
* 1.初始化IOC容器
* 2.使用反射机制获取该包下所有已存在自定义Service注解的类
* 3.使用Java反射机制初始化对象
* 4.使用反射将加有自定义注解Resource的类作为属性注入到该对象中
*/
private void initIOCMap() throws Exception {
//1.初始化IOC容器
IOCMap = new ConcurrentHashMap();
//2.使用反射机制获取该包下所有已存在自定义Service注解的类
List classSericeDemo = findClassSericeDemo();
//3.使用Java反射机制初始化对象
InitBean(classSericeDemo);
}
/**
* 1.利用反射机制,获取包下所有的类
* 2.遍历获取到的类的集合,判断该类上属否存在自定义service注解
* 3.获得存在自定义service注解的类的集合
*/
private List findClassSericeDemo(){
List exisClassesAnnotation = new ArrayList();
if(packageName == null && "".equals(packageName)){
return null;
}
//1.利用反射机制,获取包下所有的类
List> classesByPackageName = ClassUtil.getClasses(packageName);
for(Class classInfo:classesByPackageName){
//2.判断该类上属否存在注解
ServiceDemo serviceDemo = (ServiceDemo) classInfo.getDeclaredAnnotation(ServiceDemo.class);
if(serviceDemo == null){
continue;
}
//3.获得存在自定义service注解的类的集合
exisClassesAnnotation.add(classInfo);
}
return exisClassesAnnotation;
}
/**
* 初始化bean
* 1.初始化对象
* 2.获得类的简写名称
* 3.加载到IOC容器中
* @param classSericeDemo
* @throws Exception
*/
private void InitBean(List classSericeDemo) throws Exception{
for(Class classInfo:classSericeDemo){
//1.初始化对象
Object obj = classInfo.newInstance();
//2.获得类的简写名称
String beanId = toLowerCaseFirstOne(classInfo.getSimpleName());
//3.加载到IOC容器中
IOCMap.put(beanId,obj);
}
}
}
4、测试
public class Test {
public static void main(String[] args) throws Exception{
ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("service");
UserServiceImpl userService = (UserServiceImpl) app.getBean("userServiceImpl");
userService.add();
}
}
其中在网上找了一个ClassUtil工具类,用于得到包下所有的class
public class ClassUtil {
/**
* 从包package中获取所有的Class
*
* @param packageName
* @return
*/
public static List> getClasses(String packageName) {
// 第一个class类的集合
List> classes = new ArrayList>();
// 是否循环迭代
boolean recursive = true;
// 获取包的名字 并进行替换
String packageDirName = packageName.replace('.', '/');
// 定义一个枚举的集合 并进行循环来处理这个目录下的things
Enumeration dirs;
try {
dirs = Thread.currentThread().getContextClassLoader().getResources(packageDirName);
// 循环迭代下去
while (dirs.hasMoreElements()) {
// 获取下一个元素
URL url = dirs.nextElement();
// 得到协议的名称
String protocol = url.getProtocol();
// 如果是以文件的形式保存在服务器上
if ("file".equals(protocol)) {
// 获取包的物理路径
String filePath = URLDecoder.decode(url.getFile(), "UTF-8");
// 以文件的方式扫描整个包下的文件 并添加到集合中
findAndAddClassesInPackageByFile(packageName, filePath, recursive, classes);
} else if ("jar".equals(protocol)) {
// 如果是jar包文件
// 定义一个JarFile
JarFile jar;
try {
// 获取jar
jar = ((JarURLConnection) url.openConnection()).getJarFile();
// 从此jar包 得到一个枚举类
Enumeration entries = jar.entries();
// 同样的进行循环迭代
while (entries.hasMoreElements()) {
// 获取jar里的一个实体 可以是目录 和一些jar包里的其他文件 如META-INF等文件
JarEntry entry = entries.nextElement();
String name = entry.getName();
// 如果是以/开头的
if (name.charAt(0) == '/') {
// 获取后面的字符串
name = name.substring(1);
}
// 如果前半部分和定义的包名相同
if (name.startsWith(packageDirName)) {
int idx = name.lastIndexOf('/');
// 如果以"/"结尾 是一个包
if (idx != -1) {
// 获取包名 把"/"替换成"."
packageName = name.substring(0, idx).replace('/', '.');
}
// 如果可以迭代下去 并且是一个包
if ((idx != -1) || recursive) {
// 如果是一个.class文件 而且不是目录
if (name.endsWith(".class") && !entry.isDirectory()) {
// 去掉后面的".class" 获取真正的类名
String className = name.substring(packageName.length() + 1, name.length() - 6);
try {
// 添加到classes
classes.add(Class.forName(packageName + '.' + className));
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
return classes;
}
/**
* 以文件的形式来获取包下的所有Class
*
* @param packageName
* @param packagePath
* @param recursive
* @param classes
*/
public static void findAndAddClassesInPackageByFile(String packageName, String packagePath, final boolean recursive,
List> classes) {
// 获取此包的目录 建立一个File
File dir = new File(packagePath);
// 如果不存在或者 也不是目录就直接返回
if (!dir.exists() || !dir.isDirectory()) {
return;
}
// 如果存在 就获取包下的所有文件 包括目录
File[] dirfiles = dir.listFiles(new FileFilter() {
// 自定义过滤规则 如果可以循环(包含子目录) 或则是以.class结尾的文件(编译好的java类文件)
public boolean accept(File file) {
return (recursive && file.isDirectory()) || (file.getName().endsWith(".class"));
}
});
// 循环所有文件
for (File file : dirfiles) {
// 如果是目录 则继续扫描
if (file.isDirectory()) {
findAndAddClassesInPackageByFile(packageName + "." + file.getName(), file.getAbsolutePath(), recursive,
classes);
} else {
// 如果是java类文件 去掉后面的.class 只留下类名
String className = file.getName().substring(0, file.getName().length() - 6);
try {
// 添加到集合中去
classes.add(Class.forName(packageName + '.' + className));
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
}
// 首字母转小写
public static String toLowerCaseFirstOne(String s) {
if (Character.isLowerCase(s.charAt(0)))
return s;
else
return (new StringBuilder()).append(Character.toLowerCase(s.charAt(0))).append(s.substring(1)).toString();
}
}