springIOC简单模拟实现-----XML方式和注解方式
为了看懂spring底层代码结构,模拟完成了一个依赖注入的小例子,仅供自己更好的理解springIoc。
XML方式
1、创建一个dao接口和对应的两个实现类。
public interface UserService {
public void find();
}
public class UserDaoImpl implements UserDao {
@Override
public void query() {
System.out.println("user");
}
}
public class UserDaoImpl1 implements UserDao {
@Override
public void query() {
System.out.println("user1");
}
}
2、创建一个service接口和一个实现类,调用dao接口。
public class UserServiceImpl implements UserService {
UserDao dao;
TestDao test;
/*public UserServiceImpl(UserDao dao){//构造方法注入时使用
this.dao = dao;
}
*/
@Override
public void find() {
System.out.println("service");
dao.query();
test.query();
}
/*public void setDao(UserDao dao) {//setter方法注入时使用
this.dao = dao;
}*/
}
3、创建一个xml配置文件,提供需要维护的对象和对象之间的依赖关系。
<?xml version="1.0" encoding="UTF-8"?>
<beans default-autowire="byType"><!-- 自动装配时使用 -->
<!--<bean id="dao" class="com.spring.dao.UserDaoImpl"></bean>-->
<bean id="dao1" class="com.spring.dao.UserDaoImpl1"></bean>
<bean id="test" class="com.spring.dao.TestDaoImpl"></bean>
<bean id="service" class="com.spring.service.UserServiceImpl">
<!--setter方法注入-->
<!--<property name="dao" ref="dao"></property>-->
<!--构造方法注入-->
<!--<constructor-arg name="dao" ref="dao"></constructor-arg>-->
</bean>
</beans>
4、解析xml,创建对象并维护对象的依赖。(根据配置文件中的要求实现)
public class BeanFactory {
//对象名称和类的对应关系
Map<String,Object> map = new HashMap<String,Object>();
public BeanFactory(String xml){
parseXml(xml);
}
//解析xml中的映射关系
private void parseXml(String xml){
//读取XML文件
File xmlFile = new File(this.getClass().getResource("/").getPath()+"//"+xml);
SAXReader reader = new SAXReader();
try {
//获得XML文档
Document document = reader.read(xmlFile);
Element documentRoot = document.getRootElement();
//是否自动装配
Attribute autowire = documentRoot.attribute("default-autowire");
Boolean flag = false;
if(autowire != null){
flag = true;
}
//bean列表
for (Iterator<Element> itFirst = documentRoot.elementIterator(); itFirst.hasNext();) {
//创建对象
Element elementFirst = itFirst.next();
//对象名
Attribute attributeId = elementFirst.attribute("id");
String attributeIdName = attributeId.getValue();
//对象
Attribute attributeClass = elementFirst.attribute("class");
String attributeClassName = attributeClass.getValue();
Class objectClazz = Class.forName(attributeClassName);
Object object = null;
//维护对象的依赖关系
for (Iterator<Element> itSecond = elementFirst.elementIterator(); itSecond.hasNext();) {
Element elementSecond = itSecond.next();
//用setter方法注入
if(elementSecond.getName().equals("property")){
//使用默认构造方法
object = objectClazz.newInstance();
//获取ref属性值
Attribute attributeRef = elementSecond.attribute("ref");
String refValue = attributeRef.getValue();
Object refObject = map.get(refValue);//根据ref值找到对应对象
//获取name属性值--对应对象中的set方法后面的值
Attribute attributeName = elementSecond.attribute("name");
String nameValue = attributeName.getValue();
Field field = objectClazz.getDeclaredField(nameValue);
//找不到对应方法
if(field == null){
throw new NyExecption("依赖在类中未被注入!");
}
field.setAccessible(true);//开启属性注入
field.set(object,refObject);
}else if(elementSecond.getName().equals("constructor-arg")){
//获得依赖对象
String refValue = elementSecond.attribute("ref").getValue();
Object refObject = map.get(refValue);
//获得构造参数需要的class
String nameValue = elementSecond.attribute("name").getValue();
Class fieldClazz = objectClazz.getDeclaredField(nameValue).getType();
//获得构造对象
Constructor constructor = objectClazz.getConstructor(fieldClazz);
//使用特殊的构造方法
object = constructor.newInstance(refObject);
}else {
throw new NyExecption("配置文件标签错误,请修改。");
}
}
//没有配置注入时使用自动装配
if(flag && object == null){
String autoType = autowire.getValue();
if(autoType.equals("byType")){//按类型装配
//获得对象
object = objectClazz.newInstance();
//判斷是否有依賴
Field[] fields = objectClazz.getDeclaredFields();
for (Field field : fields) {
//得到屬性的類型
Class injectClazz = field.getType();
/**
* 由於是bytype 所以需要遍历map当中的所有对象
* 判断对象的类型是不是和这个injectObjectClazz相同
*/
int count = 0;
Object injectObject = null;
for (String key : map.keySet()) {
Class injectObjectClazz = map.get(key).getClass().getInterfaces()[0];
if(injectObjectClazz.getName().equals(injectClazz.getName())){
injectObject = map.get(key);
//记录找到一个,因为可能找到多个count
count++;
}
}
if(count > 1){
throw new NyExecption("需要一个对象,但是找到了"+count+"个对象");
}else {//注入对象依赖
field.setAccessible(true);
field.set(object,injectObject);
}
}
}else if(autoType.equals("byName")){//按名称装配
//判斷是否有依賴
Field[] fields = objectClazz.getDeclaredFields();
object = objectClazz.newInstance();//获得对象
for (Field field : fields) {
String injectName = field.getName();
Object injectObject = map.get(injectName);
//找不到注入的对象
if(injectObject == null){
throw new NyExecption("依赖的对象不存在!");
}
//对象注入属性--依赖对象
field.setAccessible(true);
field.set(object,injectObject);
}
}
}
//没有子标签
if(object == null){
object = objectClazz.newInstance();
}
map.put(attributeIdName,object);
}
} catch (Exception e) {
e.printStackTrace();
}
}
//获取对应对象
public Object getBean(String beanName){
return map.get(beanName);
}
}
5、测试类(根据不同情况进行修改测试)
public class Test {
public static void main(String[] args) {
BeanFactory beanFactory = new BeanFactory("spring.xml");
UserService userService = (UserService) beanFactory.getBean("service");
userService.find();
}
}
注解方式
前两步与第一种方法一样。
3、自定义一个注解,用于标记扫描的类
@Retention(RetentionPolicy.RUNTIME)
public @interface MyService {
public String value();
}
4、解析注解
public class MyAnnotationConfigApplicationContext {
//对象名称和类的对应关系
Map<String,Object> map = new HashMap<String,Object>();
public MyAnnotationConfigApplicationContext(String basePackage){
Scan(basePackage);
}
public Object Scan(String basePackage){
Object object = null;
String rootPath = this.getClass().getResource("/").getPath();
//将包路径替换为/格式
String basePackagePath = basePackage.replaceAll("\\.","\\\\");
File file = new File(rootPath+"\\"+basePackagePath);
String[] fileList = file.list();//获取路径下所有类
for (String className : fileList) {
try {
className = className.substring(0,className.indexOf("."));
//获取class对象
Class clazz = Class.forName(basePackage + "." + className);
//只扫描有MyService注解的类
if (clazz.isAnnotationPresent(MyService.class)) {
//获取注解中的value
MyService service = (MyService) clazz.getDeclaredAnnotation(MyService.class);
System.out.println(service.value());
//对象注入
object = clazz.newInstance();
System.out.println(object);
map.put(service.value(),object);
}
} catch (Exception e) {
e.printStackTrace();
}
}
//检查类中是否有需要自动注入的属性
//思路:1、建一个map存入上方注入的类名称和class对象
//2、检查注入的对象中属性是否有@Autowired注解或@Resource注解
//3、如果有@Autowired注解按照属性类型的class到map中找是否有对应类型进行注入,注意只能有一个
//4、如果有@Resource注解,取里面的name属性,到map中的key中找是否有对应的key,进行注入。
if (!map.isEmpty()){
//遍历对象进行依赖注入
for (String name : map.keySet()) {
Object injectObject = map.get(name);
Class injectObjectClazz = injectObject.getClass();
Object innerObject = null;
//获取所有属性
Field[] fields = injectObjectClazz.getDeclaredFields();
if(fields.length > 0){
for (Field field : fields) {
//存在Autowired注解
if(field.isAnnotationPresent(Autowired.class)){
Class fieldClazz = field.getType();
int count = 0;
//遍历map找到对应class对象
for (String objName : map.keySet()) {
Class objClazz = map.get(objName).getClass();
if(fieldClazz.getName().equals(objClazz.getInterfaces()[0].getName())){
innerObject = map.get(objName);
count++;
}
}
//判断是否有匹配的注入对象
if(count == 0){
throw new NyExecption("没有找到属性值匹配的类型对象。");
}else if(count == 1){
try {
field.setAccessible(true);
field.set(injectObject,innerObject);
} catch (Exception e) {
e.printStackTrace();
}
}else {
throw new NyExecption("需要一个对象,但是找到了"+count+"个对象");
}
}
//存在Resource注解
if(field.isAnnotationPresent(Resource.class)){
//获取注解中的name
Resource resource = (Resource) field.getDeclaredAnnotation(Resource.class);
String daoName = resource.name();
//在map中找到对应class对象
Object fieldObject = map.get(daoName);
if(fieldObject == null){
throw new NyExecption("没有找到属性值匹配的类型对象。");
}
try {
field.setAccessible(true);
field.set(injectObject,fieldObject);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
}
return object;
}
//获取对应对象
public Object getBean(String beanName){
return map.get(beanName);
}
}
5、测试类,根据不同情况调整代码进行测试
public class Test {
public static void main(String[] args) {
MyAnnotationConfigApplicationContext context = new MyAnnotationConfigApplicationContext("com.spring.dao");
TestDao testDao = (TestDao) context.getBean("testDaoImpl");
testDao.query();
}
}
以上就是简易的SpringIoc实现小例子,需要根据不同情况就行调整测试,是新手理解springIoc的一个好方式。