Spring中我们用到的功能实现:基于注解的Ioc自动装配

简介:

  我们要完成自动装配,那么就要有一个存放bean对象的容器,然后要有装配的注解,那么哪些类该被存到容器呢,在spring中我们使用过@Service、@Resource等,看下面的代码,你也可以做到。

    来看看这是一个简单的容器接口

/**
 * 容器接口
 * @author:rex
 * @create_time:2014-6-26
 * @version:V1.0
 */
public interface Container {

	Object getBean(String name, BeanType beanType);
	
	Object getBean(Class<?> type, BeanType beanType);
	
	Set<?> getBeanNames();
	
	Collection<?> getBeans();
	
	boolean hasBean(Class<?> clazz);
	
	boolean hasBean(String name);
	
	void registBean(Class<?> clazz);

	void initWired();
	
}

    这个容器提供了基础的存取方法,分别是获取bean对象和注册、是否包含bean,还有一个初始化的方法。

    接下来我们来为容器做一个基本的实现。    

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import com.biezhi.ioc.BeanType;
import com.biezhi.ioc.Container;
import com.biezhi.ioc.anntation.Autowired;

/**
 * 默认的bean容器实现
 * @author:rex
 * @create_time:2014-6-26
 * @version:V1.0
 */
public class DefaultContainerImpl implements Container {

	//存放bean的容器
	private final Map<StringObject> beansMap = new HashMap<StringObject>();
	
	public DefaultContainerImpl() {
		//初始化加载bean
		ContainerLoader c = new ContainerLoader(this);
		c.init();
	}
	
	@Override
	public Object getBean(String name, BeanType beanType) {
		try {
			if(beanType == BeanType.NEW)
				return Class.forName(name).newInstance();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return beansMap.get(name);
	}
	
	@Override
	public Object getBean(Class<?> type, BeanType beanType) {
		try {
			if(beanType == BeanType.NEW)
				return type.newInstance();
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		}
		Iterator<Object> it = this.beansMap.values().iterator();
		while(it.hasNext()){
			Object obj = it.next();
			if(type.isAssignableFrom(obj.getClass())){
				return obj;
			}
		}
		return null;
	}

	@Override
	public Set<?> getBeanNames(){
		return beansMap.keySet();
	}
	
	@Override
	public Collection<?> getBeans(){
		return beansMap.values();
	}

	@Override
	public boolean hasBean(Class<?> clz) {
		if(null != this.getBean(clz, null)){
			return true;
		}
		return false;
	}
	
	@Override
	public boolean hasBean(String name){
		if(null != this.getBean(name, null)){
			return true;
		}
		return false;
	}

	/**
	 * 注册一个bean对象到容器里
	 */
	@Override
	public void registBean(Class<?> clazz){
		String name = clazz.getCanonicalName();
		try {
			if(!Modifier.isAbstract(clazz.getModifiers()) && 
			    !Modifier.isInterface(clazz.getModifiers())){
				Object obj = clazz.newInstance();
				beansMap.put(name, obj);
			}
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} 
	}
	
	/**
	 * 初始化注入
	 */
	@Override
	public void initWired(){
		Iterator<Object> it = this.beansMap.values().iterator();
		try {
			while(it.hasNext()){
				Object obj = it.next();
				Field[] fields = obj.getClass().getDeclaredFields();
				for(Field field : fields){
					Autowired autowired = 
					    field.getAnnotation(Autowired.class);
					if(null != autowired){
						//要注入的字段
						Object wiredField = 
						    this.getBean(field.getType(), null);
						if(null == wiredField){
    						       throw new RuntimeException("Unable to load "+field.getType().getCanonicalName()+"!");
						}
						boolean accessible = field.isAccessible();
						field.setAccessible(true);
						field.set(obj, wiredField);
						field.setAccessible(accessible);
					}
				}
			}
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} 
	}
}

    在构造器里将扫描到的类加载到容器里,然后提供注册bean和获取bean的方法。

import java.io.File;
import java.io.FileFilter;
import java.util.HashSet;
import java.util.Set;

import com.biezhi.ioc.Container;
import com.biezhi.ioc.anntation.Service;
import com.biezhi.ioc.util.ClassHelper;

/**
 * 加载容器bean
 * @author:rex
 * @create_time:2014-6-26
 * @version:V1.0
 */
public class ContainerLoader {
	
	private Container container;
	
	public ContainerLoader(Container container) {
		this.container = container;
	}
	
	public void init(){
		//加载要扫描的包,这里可以使用配置文件,我们就默认扫描所有类
		Set<String> packages = getPackages();
		for(String pack : packages){
			scanPack(pack);
		}
		//初始化注入
		container.initWired();
	}
	
	private void scanPack(String pack){
		Set<Class<?>> classes = ClassHelper.scanPackage(pack);
		for(Class<?> clazz : classes){
			// 这里我只把带有@Service注解的存进去了,你也可以存其他的或者全部
			Service service = clazz.getAnnotation(Service.class);
			if(null != service){
				//将扫描到的对象保存到容器中
				container.registBean(clazz);
			}
		}
	}
	
	/**
	 * 获取当前classes的包名称
	 * @author:rex  
	 * @return
	 */
	private Set<String> getPackages(){
		Set<String> packages = new HashSet<String>();
		String appPath = ContainerLoader.class.getResource("/").getPath();
		File classDir = new File(appPath);
		// 如果存在 就获取包下的所有文件 包括目录
		File[] dirfiles = classDir.listFiles(new FileFilter() {
			public boolean accept(File file) {
				return file.isDirectory();
			}
		});
		for(File f : dirfiles){
			packages.add(f.getName());
		}
		return packages;
	}
}

    这个类是加载需要的类文件。还有几个代码文件没有贴出来,想看代码的等会打包自己看。

    接下来我们看看这个测试,

    

@Service
public class A {

	String name = "菊花";
	
	public void say(){
		System.out.println("hello, I,m rex !");
	}
}

@Service
public class B {

	@Autowired
	private A a;
	
	private String qq = "3838438";
	
	public void hehe(){
		a.say();
		System.out.println("请问您是" + a.name + "吗?");
	}
	
	public String getQq(){
		return this.qq;
	}
}

public class Test {

	public static void main(String[] args) {
		Container c = new DefaultContainerImpl();
		c.initWired();
		//System.out.println(c.getBeanNames());
		B b = (Bc.getBean(B.classBeanType.SINGLE);
		b.hehe();
		System.out.println(b.getQq());
		System.out.println("==================");
		B b2 = (Bc.getBean(B.classBeanType.NEW);
		b2.hehe();
	}
}

    运行结果:

hello, I,m rex !
请问您是菊花吗?
3838438
==================
Exception in thread "main" java.lang.NullPointerException
	at com.biezhi.ioc.test.B.hehe(B.java:15)
	at com.biezhi.ioc.test.Test.main(Test.java:18)

好了,这样就基本完成了一个简单的ioc自动装配。有喜欢的朋友可以参考代码。点击下载

目录
相关文章
|
14天前
|
XML Java 数据格式
SpringBoot入门(8) - 开发中还有哪些常用注解
SpringBoot入门(8) - 开发中还有哪些常用注解
35 0
|
11天前
|
消息中间件 缓存 Java
手写模拟Spring Boot启动过程功能
【11月更文挑战第19天】Spring Boot自推出以来,因其简化了Spring应用的初始搭建和开发过程,迅速成为Java企业级应用开发的首选框架之一。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,帮助读者深入理解其工作机制。
24 3
|
1月前
|
Java Spring
在使用Spring的`@Value`注解注入属性值时,有一些特殊字符需要注意
【10月更文挑战第9天】在使用Spring的`@Value`注解注入属性值时,需注意一些特殊字符的正确处理方法,包括空格、引号、反斜杠、新行、制表符、逗号、大括号、$、百分号及其他特殊字符。通过适当包裹或转义,确保这些字符能被正确解析和注入。
|
11天前
|
Java 开发者 微服务
手写模拟Spring Boot自动配置功能
【11月更文挑战第19天】随着微服务架构的兴起,Spring Boot作为一种快速开发框架,因其简化了Spring应用的初始搭建和开发过程,受到了广大开发者的青睐。自动配置作为Spring Boot的核心特性之一,大大减少了手动配置的工作量,提高了开发效率。
30 0
|
1月前
|
Java API 数据库
Spring Boot框架因其简洁的配置、快速的启动特性及丰富的功能集而备受开发者青睐
本文通过在线图书管理系统案例,详细介绍如何使用Spring Boot构建RESTful API。从项目基础环境搭建、实体类与数据访问层定义,到业务逻辑实现和控制器编写,逐步展示了Spring Boot的简洁配置和强大功能。最后,通过Postman测试API,并介绍了如何添加安全性和异常处理,确保API的稳定性和安全性。
38 0
|
24天前
|
JavaScript 安全 Java
如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个具有动态路由和菜单功能的前后端分离应用。
本文介绍了如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个具有动态路由和菜单功能的前后端分离应用。首先,创建并配置 Spring Boot 项目,实现后端 API;然后,使用 Ant Design Pro Vue 创建前端项目,配置动态路由和菜单。通过具体案例,展示了如何快速搭建高效、易维护的项目框架。
97 62
|
21天前
|
XML JSON Java
SpringBoot必须掌握的常用注解!
SpringBoot必须掌握的常用注解!
43 4
SpringBoot必须掌握的常用注解!
|
22天前
|
JavaScript 安全 Java
如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个前后端分离的应用框架,实现动态路由和菜单功能
本文介绍了如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个前后端分离的应用框架,实现动态路由和菜单功能。首先,确保开发环境已安装必要的工具,然后创建并配置 Spring Boot 项目,包括添加依赖和配置 Spring Security。接着,创建后端 API 和前端项目,配置动态路由和菜单。最后,运行项目并分享实践心得,帮助开发者提高开发效率和应用的可维护性。
40 2
|
23天前
|
存储 缓存 Java
Spring缓存注解【@Cacheable、@CachePut、@CacheEvict、@Caching、@CacheConfig】使用及注意事项
Spring缓存注解【@Cacheable、@CachePut、@CacheEvict、@Caching、@CacheConfig】使用及注意事项
75 2
|
23天前
|
JSON Java 数据库
SpringBoot项目使用AOP及自定义注解保存操作日志
SpringBoot项目使用AOP及自定义注解保存操作日志
34 1
下一篇
无影云桌面