前言
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