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反射还不是太了解的的话可能这篇博文你会听的晕乎乎的噢!

  • 创建我们需要的Annotation @Component @Autowired @Qualifier

  • 创建ApplicationContext接口,里面一个getBean()方法,创建AnnotationConfigApplicationContext类实现ApplicationContext接口

  • AnnotationConfigApplicationContext的构造方法会接收一个包名,然后负责把这个包下面的所有java类的路径拿出来,再将路径处理一下即可得到类全名,并放入集合,再通Class.forName(类全名)得到所有Java类的Class 并放入集合

  • 遍历集合所有的class判断该类上是否加了@Component, 再把加了@Component 的Class放置一个集合,然后再判断Class 的@Component 是否存在value,如果存在,则把valuevalue作为key 该Class作为value 放置一个Map集合

  • 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.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使用才是最佳王道、当然,前者是基于普通是ssm项目、如果是基于springboot来的话 我们要配合javaConfig配置模式

模仿 spring IOC Annotation版自动装配的更多相关文章

  1. [原创]java WEB学习笔记99:Spring学习---Spring Bean配置:自动装配,配置bean之间的关系(继承/依赖),bean的作用域(singleton,prototype,web环境作用域),使用外部属性文件

    本博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用 内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系. 本人互联网技术爱 ...

  2. Spring学习(六)-----Spring使用@Autowired注解自动装配

    Spring使用@Autowired注解自动装配 在上一篇 Spring学习(三)-----Spring自动装配Beans示例中,它会匹配当前Spring容器任何bean的属性自动装配.在大多数情况下 ...

  3. Spring基于的注解自动装配和依赖注入(***)

    #自动装配的小Demo: package com.gyf.annotation; //DAO层 public interface UserDao { public void save(); } pac ...

  4. Spring入门(八):自动装配的歧义性

    1. 什么是自动装配的歧义性? 在Spring中,装配bean有以下3种方式: 自动装配 Java配置 xml配置 在这3种方式中,自动装配为我们带来了很大的便利,大大的降低了我们需要手动装配bean ...

  5. Spring学习总结(2)-自动装配

    上面说过,IOC的注入有两个地方需要提供依赖关系,一是类的定义中,二是在spring的配置中需要去描述.自动装配则把第二个取消了,即我们仅仅需要在类中提供依赖,继而把对象交给容器管理即可完成注入.在实 ...

  6. Spring@Autowired注解与自动装配

    1   配置文件的方法 我们编写spring 框架的代码时候.一直遵循是这样一个规则:所有在spring中注入的bean 都建议定义成私有的域变量.并且要配套写上 get 和 set方法. Boss ...

  7. 【转】Spring@Autowired注解与自动装配

    1   配置文件的方法 我们编写spring 框架的代码时候.一直遵循是这样一个规则:所有在spring中注入的bean 都建议定义成私有的域变量.并且要配套写上 get 和 set方法. Boss ...

  8. Spring@Autowired注解与自动装配(转发)

    1   配置文件的方法 我们编写spring 框架的代码时候.一直遵循是这样一个规则:所有在spring中注入的bean 都建议定义成私有的域变量.并且要配套写上 get 和 set方法. Boss ...

  9. [转] Spring@Autowired注解与自动装配

    1   配置文件的方法 我们编写spring 框架的代码时候.一直遵循是这样一个规则:所有在spring中注入的bean 都建议定义成私有的域变量.并且要配套写上 get 和 set方法. Boss ...

随机推荐

  1. MyBatis基于注解----增删改查

    select sysdate from dual; --账户表 --账户编号,账户卡号,账户密码,账户余额,账户状态,创建时间 drop table account; create table acc ...

  2. CreateRemoteThread 远程dll注入

    1.dll中的内容 // dllmain.cpp : 定义 DLL 应用程序的入口点.#include "stdafx.h" BOOL APIENTRY DllMain( HMOD ...

  3. Solaris10上如何识别新增加的HDLM LUN

    先在磁盘阵列上将新加LUN映射给主机组,然后在光纤交换机上增加相关zone信息.以下是Solaris10上需要执行的操作步骤. 在Solaris10上重新扫描磁盘 -bash-3.2# cfgadm ...

  4. 部署和调优 2.4 tomcat安装

    下载tamcet 官网 http://tomcat.apache.org/ 左侧选择版本 复制下载链接 切换到下载目录 cd /usr/local/src linux wget wget http:/ ...

  5. java中常用的时间操作

    最近项目设计时间的转换和计算,长时间没用时间操作了,感觉手有点生,所以在这里记录一下: Date 常用的方法: getTime() .setTime(): SimpleDateFormate 常用的方 ...

  6. hadoop-2.6.0.tar.gz + spark-1.6.1-bin-hadoop2.6.tgz + zeppelin-0.5.6-incubating-bin-all.tgz(master、slave1和slave2)(博主推荐)(图文详解)

    不多说,直接上干货! 我这里,采取的是CentOS6.5,当然大家也可以在ubuntu 16.04系统里,这些都是小事 CentOS 6.5的安装详解 hadoop-2.6.0.tar.gz + sp ...

  7. memset,memcpy,strcpy的使用与区别

    1.memset 原型:   extern void *memset(void *buffer, int c, int count); 功能:   把buffer所指内存区域的前count个字节设置成 ...

  8. Android远程服务(AIDL)实现步骤

    AIDL是安卓接口定义语言的缩写 由于笔者使用的是android studio所以建立AIDL文件的位置也需要注意,要在APPNAME->main->aidl->packagenam ...

  9. 第5章 使用MUI与H5+构建移动端app

    H5+是JS封装的工具集合,通过H5+我们就可以使用JS的方式去调用到我们手机端上的一些原生的组件. http://dev.dcloud.net.cn/mui/ http://dev.dcloud.n ...

  10. css 层叠式样式表(2)

    一,样式表分类 (1)内联样式. --优先级最高,代码重复使用最差. (当特殊的样式需要应用到单独某个元素时,可以使用. 直接在相关的标签中使用样式属性.样式属性可以包含任何 CSS 属性.) (2) ...