spring 依赖注入注解配置原理解析

来源:互联网 发布:c语言做游戏 编辑:程序博客网 时间:2024/05/16 12:18

紧接着上一篇spring依赖注入xml配置原理解析,本文实现了使用注解的配置方式注入bean,大致分为以下几步:

1)解析xml文件,获取需要扫描的包

2)递归获取这些包下及其子包下所有的java类(此时真正扫描的是.class文件)

3)将所有标识有Service,Repository注解的类进行实例化,并将这些类中需要注入的属性进行保存

4)对bean的属性进行注入

具体实现方式如下:

1.添加相关依赖:

        <dependency>  <groupId>dom4j</groupId><artifactId>dom4j</artifactId><version>1.6.1</version>  </dependency>  <dependency>  <groupId>jaxen</groupId><artifactId>jaxen</artifactId><version>1.1.1</version>  </dependency>  <dependency>  <groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.16</version>  </dependency>

2.自定义注解

/** * 定义注解的存在范围,共三种,详情请参考JDK API(1.5版本及以后)中的RetentionPolicy */@Retention(RetentionPolicy.RUNTIME)/** * 定义注解的使用范围,共有8种,详情请参考JDK API(1.5版本及以后)中的ElementType */@Target(ElementType.TYPE)/** * 指示注释类型被自动继承,详情请参考JDK API(1.5版本及以后)中的Inherited */@Inherited//自定义Service层注解public @interface Service {//用于保存Service的id,默认为空字符String value() default "";}


@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)@Inherited//自定义Dao层注解public @interface Repository {//用于保存Service的id,默认为空字符String value() default "";}

@Retention(RetentionPolicy.RUNTIME)@Target(value={ElementType.FIELD,ElementType.METHOD})public @interface Inject {String value() default "";}


3.工具类

public class AnnotationUtil {private static final AnnotationUtil util = new AnnotationUtil();//保存xml文件配置的所有的包名private List<String> basePackages = new ArrayList<String>();//保存需扫描的包及其子包下所有的类的全限定名private List<String> clzNames = new ArrayList<String>();//保存需扫描的包下及其子包下标识有Service和Repository的全限定类名,key为这个bean解析出的id,value为全限定类名private Map<String,String> annotationClzNames = new HashMap<String, String>();//类路径private final String classesPath = AnnotationUtil.class.getClassLoader().getResource("").getPath();//保存扫描到的类的Service和Repository注解的value值和class,id为注解的value()值,value为反射获得的实例private Map<String,Object> beans = new HashMap<String,Object>();//保存bean的id和其需要注入的字段及该字段需要注入的bean的idprivate Map<String,Map<String,String>> propertys = new HashMap<String,Map<String,String>>();private SAXReader reader;private Document document;private AnnotationUtil(){reader = new SAXReader();}public static AnnotationUtil getInstance(){return util;}public Map<String,Object> parseXml(String xmlPath) throws DocumentException{document = reader.read(new File(xmlPath));//解析xml文档this.parseComponent(document);//获取xml配置的包下的所有类this.scanClass();//遍历所有的类,并将有Service,Repository注解标识的类实例化放进beans当中this.InteratorClass();//遍历所有标识有Service和Repository注解的类进行属性注入this.InjectBean();return beans;}/** * 解析xml文档中的component-scan节点,保存所有的包名 * @param document */@SuppressWarnings("unchecked")public void parseComponent(Document document){if(document==null){return;}//获取根元素beansElement ele = document.getRootElement();//使用xpath表达式查找所有的component-scan节点List<Element> beanElements = ele.selectNodes("//beans/component-scan");if(beanElements==null||beanElements.size()==0){return;}String basePackage = null;//遍历所有的component-scan节点for(Element beanElement:beanElements){basePackage = beanElement.attributeValue("base-package");if(basePackage==null||basePackages.contains(basePackage)){continue;}basePackages.add(basePackage);}}//获得配置的包下的所有类private void scanClass(){if(basePackages.size()==0){return ;}//保存一个包下的类名List<String> everyPackageClzNames = null;for(String basePackage:basePackages){//将每一个包下的类名进行保存everyPackageClzNames = scanClassFile(basePackage);clzNames.addAll(everyPackageClzNames);}}//递归获取某个包下的所有类private List<String> scanClassFile(String path){//将包名.转换为路径/,跟类路径进行拼接String packagePath = classesPath+path.replaceAll("\\.", "/");File file = new File(packagePath);List<String> clzPaths= new ArrayList<String>();if(!file.exists()){return clzPaths;}    File[] files = file.listFiles(new FilenameFilter() {//获取该包下的子包和.class文件@Overridepublic boolean accept(File dir, String name) {return dir.isDirectory()||name.endsWith(".class");}});if(files.length==0){return clzPaths;}for(File f:files){if(f.isFile()){//保存全限定类名(包名+类名)clzPaths.add(path+"."+f.getName().substring(0, f.getName().length()-6));}else if(f.isDirectory()){//如果是目录就进行递归clzPaths.addAll(scanClassFile(path+"."+f.getName()));}}return clzPaths;}private void InteratorClass(){if(clzNames.size()==0){return;}Class<?> clz = null;Service service = null;Repository repository = null;Inject inject = null;//保存注解的value值,如果是默认值,就让idValue为类名首字母小写形式当做beans的keyString idValue = null;//用于标记这个注解是否是Service和Repository当中的一个boolean flag = false;for(String clzName:clzNames){try {clz = Class.forName(clzName);//如果这个类是接口或者抽象类就跳过if(clz.isInterface()||Modifier.isAbstract(clz.getClass().getModifiers())){continue;}Annotation[] classAnnotations = clz.getAnnotations();for(Annotation annotation:classAnnotations){//判断是Service注解还是Repository注解if(annotation instanceof Service){service =  (Service)annotation;idValue = service.value();flag = true;break;}else if(annotation instanceof Repository){repository = (Repository)annotation;idValue = repository.value();flag = true;break;}}//当这个注解是Service和Repository当中的一个的时候,才执行一下操作if(flag){//如果没有指定id值(即给注解的value赋值),则默认id为类名首字母小写形式if("".equals(idValue)){//此时获取的是类名idValue = clz.getSimpleName();//转化为首字母小写idValue = idValue.substring(0, 1).toLowerCase()+idValue.substring(1);}//判断该类是否已经被实例化if(beans.containsKey(idValue)){throw new RuntimeException("The class "+clzName+ " has already been instantiated");}//如果flag为true,说明这个类被标识了Service和Repository其中的一个注解annotationClzNames.put(idValue,clzName);beans.put(idValue, clz.newInstance());//保存那个字段需要注入String fieldName = null;//获取这个类声明的所有字段Field[] fields = clz.getDeclaredFields();//保存这个类里面有哪些字段需要注入,key为需要注入的字段名,value为需要注入的bean的id值Map<String,String> map = null;for(Field field:fields){Annotation[] fieldAnnotations = field.getDeclaredAnnotations();map = new HashMap<String,String>();for(Annotation annotation:fieldAnnotations){//判断该属性是否标识有Inject注解if(annotation instanceof Inject){inject = (Inject)annotation;fieldName = inject.value();//如果为空,默认以属性名进行注入if("".equals(fieldName)){fieldName = field.getName();}map.put(fieldName, fieldName);}}}if(map!=null&&!map.isEmpty()){propertys.put(idValue, map);}//执行完了之后再重置flagflag = false;}} catch (ClassNotFoundException e) {e.printStackTrace();} catch (InstantiationException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();}}}@SuppressWarnings("rawtypes")private void InjectBean() {if(annotationClzNames.size()==0){return;}Class<?> clz = null;Inject inject = null;String idValue = null;Set<String> keys = annotationClzNames.keySet();for(String key:keys){try {//根据全限定类名获取Classclz = Class.forName(annotationClzNames.get(key));//获取该类声明的所有字段Field[] fields = clz.getDeclaredFields();for(Field field:fields){Annotation[] annotations = field.getDeclaredAnnotations();for(Annotation annotation:annotations){//判断该属性是否标识有Inject注解if(annotation instanceof Inject){inject = (Inject)annotation;idValue = inject.value();//如果为空,默认以属性名进行注入if("".equals(idValue)){idValue = field.getName();}//如果该字段为private或者protected修饰的,则必须要设置可以访问field.setAccessible(true);//根据key获得这个类的已经实例化的对象,然后对该的字段进行注入field.set(beans.get(key),beans.get(((Map)propertys.get(key)).get(idValue)));break;}}}} catch (ClassNotFoundException e) {e.printStackTrace();} catch (IllegalArgumentException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();}}}}


4.简单的业务逻辑

public interface IUserDao {void addUser();}

@Repository("UserDao")public class UserDao implements IUserDao{@Overridepublic void addUser() {System.out.println("进行了新增操作");}}

public interface IUserService {void addUser();}


@Servicepublic class UserService implements IUserService{//属性注入@Inject("UserDao")private IUserDao userDao;@Overridepublic void addUser() {userDao.addUser();}}

5.配置文件

<?xml version="1.0" encoding="UTF-8"?><beans><component-scan base-package="cn.edu.hbut.zw.spring" /></beans>

6.测试

public class SpringTest {public static void main(String[] args) {ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");IUserService us = (IUserService) context.getBean("userService");us.addUser();}}




1 0
原创粉丝点击