关于java:极简版Autowired实现

7次阅读

共计 6180 个字符,预计需要花费 16 分钟才能阅读完成。

前言

Spring 中 @Autowired 能够主动拆卸容器组件,默认按 type 注入,有多个雷同类型的 bean 时按 name 注入。此例为 @Autowired 的最简略实现,并不齐全正当,自己也是小白,大神轻喷。

思路

实现 BeanPostProcessor 接口能够对 bean 进行后置解决。通过反射,查找 bean 中有需主动拆卸字段,从容器中获取 bean,并对其注入。

代码实现

1.pom

<dependencies>
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>4.3.12.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.1</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

2. 注解

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoInject {}

3. 测试实体

public class School {

    private String name;

    public String getName() {return name;}

    public void setName(String name) {this.name = name;}

    @Override
    public String toString() {
        return "School{" +
                "name='" + name + '\'' +
                '}';
    }
}

public class Grade {

    private String level;

    @AutoInject
    private School school;

    public String getLevel() {return level;}

    public void setLevel(String level) {this.level = level;}

    public School getSchool() {return school;}

    public void setSchool(School school) {this.school = school;}

    @Override
    public String toString() {
        return "Grade{" +
                "level='" + level + '\'' +
                ", school=" + school +
                '}';
    }
}

public class Student {

    private String name;

    @AutoInject
    private Grade grade;

    public String getName() {return name;}

    public void setName(String name) {this.name = name;}

    public Grade getGrade() {return grade;}

    public void setGrade(Grade grade) {this.grade = grade;}

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", grade=" + grade +
                '}';
    }
}

4. 后置处理器

@Component
public class AutoInjectProcessor implements BeanPostProcessor, ApplicationContextAware {
    private ApplicationContext applicationContext;

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {Field[] declaredFields = bean.getClass().getDeclaredFields();
        for (Field declaredField : declaredFields) {// 遍历字段查找须要主动注入的字段
            AutoInject autoInject = declaredField.getAnnotation(AutoInject.class);
            if(autoInject!=null){Class<?> aClass = declaredField.getType();
                Map<String, ?> beansOfType = applicationContext.getBeansOfType(aClass);// 从容器中获取 bean

                Object injectBean;
                if(beansOfType.keySet().size()==1){// 如果只有一个匹配的 bean
                    injectBean=beansOfType.get(beansOfType.keySet().iterator().next());
                }else {// 多个 bean 则依照 name 匹配
                    injectBean=beansOfType.get(declaredField.getName());
                }
                declaredField.setAccessible(true);
                try {declaredField.set(bean,injectBean);
                } catch (IllegalAccessException e) {e.printStackTrace();
                }
            }
        }
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {return bean;}

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {this.applicationContext=applicationContext;}
}

5. 配置类
请留神 @ComponentScan 中的门路

@Configuration
@ComponentScan("com.github.beta.bean.test")
public class AutoInjectConfig {

    @Bean
    public Grade grade(){Grade grade=new Grade();
        grade.setLevel("一年级");
        return grade;
    }

    @Bean
    public Student zhangsan(){Student student=new Student();
        student.setName("张三");
        return student;
    }

    @Bean
    public School school(){School school=new School();
        school.setName("宁波大学科学技术学院");
        return school;
    }

}

6. 测试类

public class AutowiredTest {AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AutoInjectConfig.class);

    @Test
    public void autoInject(){Student bean = applicationContext.getBean(Student.class);
        System.out.println(bean);
    }
}

测试后果

"C:\Program Files\Java\jdk1.8.0_181\bin\java.exe" -ea -Didea.test.cyclic.buffer.size=1048576 "-javaagent:D:\IntelliJ IDEA 2020.2.3\lib\idea_rt.jar=6122:D:\IntelliJ IDEA 2020.2.3\bin" -Dfile.encoding=UTF-8 -classpath "D:\IntelliJ IDEA 2020.2.3\lib\idea_rt.jar;D:\IntelliJ IDEA 2020.2.3\plugins\junit\lib\junit5-rt.jar;D:\IntelliJ IDEA 2020.2.3\plugins\junit\lib\junit-rt.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\rt.jar;E:\study\spring-ano\target\test-classes;E:\study\spring-ano\target\classes;E:\RepMaven\org\springframework\spring-context\4.3.12.RELEASE\spring-context-4.3.12.RELEASE.jar;E:\RepMaven\org\springframework\spring-aop\4.3.12.RELEASE\spring-aop-4.3.12.RELEASE.jar;E:\RepMaven\org\springframework\spring-beans\4.3.12.RELEASE\spring-beans-4.3.12.RELEASE.jar;E:\RepMaven\org\springframework\spring-core\4.3.12.RELEASE\spring-core-4.3.12.RELEASE.jar;E:\RepMaven\commons-logging\commons-logging\1.2\commons-logging-1.2.jar;E:\RepMaven\org\springframework\spring-expression\4.3.12.RELEASE\spring-expression-4.3.12.RELEASE.jar;E:\RepMaven\junit\junit\4.13.1\junit-4.13.1.jar;E:\RepMaven\org\hamcrest\hamcrest-core\1.3\hamcrest-core-1.3.jar" com.intellij.rt.junit.JUnitStarter -ideVersion5 -junit4 com.github.beta.AutowiredTest2,autoInject
四月 22, 2021 5:03:15 下午 org.springframework.context.annotation.AnnotationConfigApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@5cbc508c: startup date [Thu Apr 22 17:03:15 SGT 2021]; root of context hierarchy
Student{name='张三', grade=Grade{level='一年级', school=School{name='宁波大学科学技术学院'}}}
Picked up _JAVA_OPTIONS: -Xms512m -Xmx1024m

Process finished with exit code 0
正文完
 0