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自动装配。有喜欢的朋友可以参考代码。点击下载

目录
相关文章
|
28天前
|
Java Spring
【Spring】方法注解@Bean,配置类扫描路径
@Bean方法注解,如何在同一个类下面定义多个Bean对象,配置扫描路径
170 73
|
25天前
|
XML Java 数据格式
【SpringFramework】Spring IoC-基于XML的实现
本文主要讲解SpringFramework中IoC和DI相关概念,及基于XML的实现方式。
108 69
|
23天前
|
Java Spring 容器
【SpringFramework】Spring IoC-基于注解的实现
本文主要记录基于Spring注解实现IoC容器和DI相关知识。
51 21
|
29天前
|
设计模式 XML Java
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
本文详细介绍了Spring框架的核心功能,并通过手写自定义Spring框架的方式,深入理解了Spring的IOC(控制反转)和DI(依赖注入)功能,并且学会实际运用设计模式到真实开发中。
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
|
28天前
|
存储 Java Spring
【Spring】获取Bean对象需要哪些注解
@Conntroller,@Service,@Repository,@Component,@Configuration,关于Bean对象的五个常用注解
|
28天前
|
存储 Java 应用服务中间件
【Spring】IoC和DI,控制反转,Bean对象的获取方式
IoC,DI,控制反转容器,Bean的基本常识,类注解@Controller,获取Bean对象的常用三种方式
|
28天前
|
Java Spring
【Spring配置】idea编码格式导致注解汉字无法保存
问题一:对于同一个项目,我们在使用idea的过程中,使用汉字注解完后,再打开该项目,汉字变成乱码问题二:本来a项目中,汉字注解调试好了,没有乱码了,但是创建出来的新的项目,写的注解又成乱码了。
|
1月前
|
存储 缓存 Java
Spring面试必问:手写Spring IoC 循环依赖底层源码剖析
在Spring框架中,IoC(Inversion of Control,控制反转)是一个核心概念,它允许容器管理对象的生命周期和依赖关系。然而,在实际应用中,我们可能会遇到对象间的循环依赖问题。本文将深入探讨Spring如何解决IoC中的循环依赖问题,并通过手写源码的方式,让你对其底层原理有一个全新的认识。
65 2
|
Java 数据库 数据安全/隐私保护
《Spring 3.0就这么简单》——1.2 实例功能概述
Spring拥有持久层、业务层和展现层的“原生技术”,分别是Spring JDBC、声明式事务和Spring MVC。为了充分展现Spring本身的魅力,在本章中仅使用Spring的这些原生技术,在以后的章节中,我们将学习其他的持久层和展现层技术,只要用户愿意,就可以平滑地将其过渡到其他技术实现中。
2103 0
|
2天前
|
XML Java 应用服务中间件
Spring Boot 两种部署到服务器的方式
本文介绍了Spring Boot项目的两种部署方式:jar包和war包。Jar包方式使用内置Tomcat,只需配置JDK 1.8及以上环境,通过`nohup java -jar`命令后台运行,并开放服务器端口即可访问。War包则需将项目打包后放入外部Tomcat的webapps目录,修改启动类继承`SpringBootServletInitializer`并调整pom.xml中的打包类型为war,最后启动Tomcat访问应用。两者各有优劣,jar包更简单便捷,而war包适合传统部署场景。需要注意的是,war包部署时,内置Tomcat的端口配置不会生效。
72 17
Spring Boot 两种部署到服务器的方式