模仿 spring IOC Annotation版自动装配

        spring 有两大核心 IOC和AOP。  IOC (inversion of control) 译为 控制反转,也可以称为 依赖注入 ; AOP(Aspect Oriented Programming)即面向切面编程。
        我们此次所模仿的是 spring IOC 中的Annotation版的自动装配;Spring 在2.5版本后 引入了 @Autowired以及一系列的 Annotation,它可以对类成员变量、方法及构造函数进行注解,完成自动装配的工作。相比于我们繁琐的传统xml配置注入来说,Annotation 的自动装配会更加简便,给需要注入的属性或方法加上 @Autowired 后即可对该属性或方法进行自动注入,如果我们的属性是接口,
 或者是一个父类的话,我们可以再加一个 @Qualifier 并为其设置一个value 即可指定该接口或该父类所指定的实现类或子类(对应于实现类或父类中 @Component 中的value)。 
       
一、实现功能:Annotation版的spring自动装配
二、实现思路:

       spring ioc 底层也是基于java反射技术实现的,本次模仿牵扯到很多关于java反射方面的知识,如果各位小伙伴对java反射还不是太了解的的话可能这篇博文你会听的晕乎乎的噢!
              1、创建我们需要的Annotation @Component @Autowired @Qualifier
              2、创建ApplicationContext接口,里面一个getBean()方法,创建AnnotationConfigApplicationContext类实现ApplicationContext接口
              3、AnnotationConfigApplicationContext的构造方法会接收一个包名,然后负责把这个包下面的所有java类的路径拿出来,再将路径处理一下即可得到类全名,并放入集合,
    再通Class.forName(类全名)得到所有Java类的Class 并放入集合
              4、遍历集合所有的class判断该类上是否加了@Component, 再把加了@Component 的Class放置一个集合,然后再判断Class 的@Component 是否存在value,如果存在,则把value
    value作为key 该Class作为value 放置一个Map集合

              5、AnnotationConfigApplicationContext重写的 getBean()  接收一个类的Class, 得到接收Class的实例对象,进行相应属性的依赖注入,解决完依赖后return实例对象。得到Class所有的Field,遍历Field是否加了 @Autowired ,如果加了 @Autowired再次判断是否加了 @Qualifier,如果加了 @Qualifier ,用 @Qualifier的value去Map集合 得到对应的Class,然后使用Field.set为实例对象的该Field赋值如(field.set(object,value))value为回调本方法后的返回值(本方法会返回Class的实例), 如果没有加 @Qualifier 则得到该Field的类型的Class ,使用Field.set为实例对象的该Field赋值,值为回调本方法后的返回值。待处理完所有的依赖注入后返回实例对象。

三、Java代码:

       一、创建需要的Annotation

               1、创建@Component
				package com.custom.annotation;
				import java.lang.annotation.ElementType;
				import java.lang.annotation.Retention;
				import java.lang.annotation.RetentionPolicy;
				import java.lang.annotation.Target; 
				@Target(value={ElementType.TYPE})
				@Retention(RetentionPolicy.RUNTIME)
				public @interface Component {
				   public String value() default "";
				}	
                2、创建@Autowired
				package com.custom.annotation;
				import java.lang.annotation.ElementType;
				import java.lang.annotation.Retention;
				import java.lang.annotation.RetentionPolicy;
				import java.lang.annotation.Target;
				@Target(value={ElementType.FIELD,ElementType.METHOD})
				@Retention(RetentionPolicy.RUNTIME)
				public @interface Autowired {
				}
                3、创建@Qualifier
				package com.custom.annotation;
				import java.lang.annotation.Retention;
				import java.lang.annotation.RetentionPolicy;
				@Retention(RetentionPolicy.RUNTIME)
				public @interface Qualifier {
					public String value() default "";
				}


        二、主要功能实现
                1、创建ApplicationContext接口
				package com.custom.controller;
				public interface ApplicationContext {
					public Object getBean(Class clazz);
				}

                 2.、AnnotationConfigApplicationContext类

					 package com.custom.controller;
					import java.io.File;
					import java.lang.reflect.Field;
					import java.lang.reflect.Method;
					import java.util.ArrayList;
					import java.util.HashMap;
					import java.util.List;
					import java.util.Map;
					import com.custom.annotation.Autowired;
					import com.custom.annotation.Component;
					import com.custom.annotation.Qualifier;
					import com.custom.exception.CustomException;
					import com.example.Hello;
					import com.example.World; 


					public class AnnotationConfigApplicationContext implements ApplicationContext {
						 
						private String projectPath=this.getClass().getResource("/").getPath();//项目路径
						private List<Class> clazzList;//包下所有类
						private List<String> filePaths;//包下所有文件名
						private Map<String,Class> existComponentClassMap;//含有@Component注解的类
						private Map<String,Class> existComponentValueClassMap;//含有@Qualifier值的类
						
						public AnnotationConfigApplicationContext(String... strings){ 
							clazzList=new ArrayList<Class>(); 
							existComponentClassMap=new HashMap<>();
							existComponentValueClassMap=new HashMap<>();
							for (String tempPackageName : strings) {//遍历传进来的包
								filePaths=getFileName(projectPath+(tempPackageName.replaceAll("[.]","/")));
							try { 
									//把扫描到包下的类都放入clazzList集合
									clazzList.addAll(getFileClass(filePaths)) ;
									//遍历所有类,看是否加了@Component		
									isComponent();	
								} catch (Exception e) {
								// TODO Auto-generated catch block 
								e.printStackTrace();
								} 
							}
						}
						
						@Override
						public Object getBean(Class clazz) {
							try {
								//判断传入的类是否加了@Component
								if(existComponentClassMap.get(clazz.getName())!=null){
									//解决clazz依赖并返回clazz的实例
									return isAutowired(clazz);
								}else{
									//抛出异常
									throw new CustomException("not found "+clazz.getName()+" mapping class"); 
								}
							} catch (Exception e) {
								// TODO Auto-generated catch block
								e.printStackTrace();
							}	
							return null;
						}


						 
						//循环判断是否加了@Component
						private void isComponent(){		
							for (Class tempClass : clazzList) {
								//判断该类是否加有@Component
								if(tempClass.isAnnotationPresent(Component.class)){
									//把加了@Component注解的类放入集合
									existComponentClassMap.put(tempClass.getName(),tempClass); 
									Component component=(Component)tempClass.getAnnotation(Component.class);
									//得出@Component中的value
									String componentValue=component.value();
									//判断@Component中的value是否有值
									if(componentValue.length()>0){
										//把@Component中的value和当前类的class放入Map集合中
										existComponentValueClassMap.put(componentValue, tempClass); 
									}
								}
							} 
						}
						
						//循环判断加了Component注解类里面是否有加了@Autowired注解,和@Qualifier的属性或方法
						private Object isAutowired(Class clazz) throws Exception{ 
								//得到传入clazz的实例
								Object object=clazz.newInstance();
								//得到clazz的所有的属性
								Field fields[]=clazz.getDeclaredFields();
								//遍历所有属性
								for (Field field : fields) {
									//判断该属性是否加了@Autowired
									if(field.isAnnotationPresent(Autowired.class)){ 
											//判断该属性是否加了@Qualifier
										    if(field.isAnnotationPresent(Qualifier.class)){
											Qualifier qualifier=field.getAnnotation(Qualifier.class);
											//使用@Qualifier的值从Map集合中拿出对该值对应的class
											Class Tempclazz=existComponentValueClassMap.get(qualifier.value());
											if(Tempclazz!=null){
												//为属性设置赋值权限
												field.setAccessible(true);
												//为实例出来的clazz对象的该属性赋值,赋值之前再次递归调用本方法,传入该属性类型的class
												field.set(object,isAutowired(Tempclazz));
											}else{
												throw new CustomException("not found "+qualifier.value()+" mapping class"); 
											}
										    }else{
											//得到该属性的类型
												Class fieldType=field.getType(); 
											Class Tempclazz=existComponentClassMap.get(fieldType.getName());
											if(Tempclazz!=null){
												field.setAccessible(true);
												field.set(object,isAutowired(Tempclazz)); 
											}else{
												throw new CustomException("not found "+fieldType.getName()+" mapping class");
											} 
										    }
										} 
									} 
								return object;
						}
		
						//得到指定包下面的所有java文件路径
						public List<String> getFileName(String packgePath){	
							List<String> filePaths=new ArrayList<>(); 
							String filePath=packgePath;
							File file=new File(filePath); 
							//判断是否为目录
							if(file.isDirectory()){
								//得到包下所有文件
								File files[]=file.listFiles();
								 for (File file2 : files) {
									 //判断是否为目录
									if(file2.isDirectory()){ 
										//递归调用
										filePaths.addAll(getFileName(file2.getPath()));
									}else{
										//如果后缀为class则把该文件路径放入集合
										if(file2.getName().substring(file2.getName().lastIndexOf(".")+1).equals("class")){
											filePaths.add(file2.getPath()); 
										}						
									}
								}
							}	
								return filePaths;
						}
						
						//返回所有java文件的class
						public List<Class> getFileClass(List<String> filePath) throws ClassNotFoundException{
							List<Class> list=new ArrayList<Class>();
							for (String tempFileName : filePath) {
								//从项目路径之后开始截取java文件名
								String tempClassName=tempFileName.substring(projectPath.length()-1);
								//把路径中的“\”替换成“.”例如“com\test\test.java”替换后“com.test.test.java”
								tempClassName=tempClassName.replaceAll("\\\\",".");  
								//再把后面的“.java”截取掉 然后使用Class.forName得到该类的class,并放入集合
								list.add(Class.forName(tempClassName.substring(0,tempClassName.lastIndexOf("."))));
							}
							return list;
						}
					}


四、测试

        好了,代码贴完了 那么我们来看一下实现的效果吧大笑,为了自动装配效果更加突出,这里我准备了六个类一个接口,每个类都有依赖。我们的最终目的是,需要通过getBean()方法得到World类的实例,并且 World类里面所有的依赖都被自动注入经来了。

       World类有如下依赖

                   

各个类之间的依赖



最终结果我们得到了World类的实例,并且里面的各种依赖都自动注入经来了得意

        这里我只给属性实现了自动装配的功能,方法的自动装配由于时间的原因  我就没有写啦,其实原理都是一样的,只要各位小伙伴能够领悟到java反射的精髓的话  都是分分钟的事啦。

        当然Annotation 自动装配也不是特别完美的,就比如我们要自动装配一个类的话  必须为其设置 @Component ,比如我们要实例一些第三方jar包中的类,我们总不可能让第三方为我们加上

@Component吧,当然 这是不现实的,不过如果你面子够大的话还是可以试一下的哈!所以在我们的实际开发中结合Annotation和xml使用才是最佳王道


                                                      



        
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值