DI即依赖注入,其实是springIOC中的一种模式,spring的注入方式有三种,构造方法注入、setter方法注入以及注解注入。下面主要讨论一下注解注入,首先,项目启动时,扫描所有的包,然后读取包下的.class文件,将对象实例化之后放入ioc容器。
注解
package annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAutowired {
}
package annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyComponent {
//此注解定义为scope属性
public String scope() default "";
}
package annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyValue {
public String value();
}
实体
package entity;
import annotation.MyComponent;
import annotation.MyValue;
@MyComponent
public class User {
@MyValue("1")
private Integer id;
@MyValue("xiaoming")
private String name;
@MyValue("123456")
private String password;
public User() {
System.out.println("无参构造方法执行");
}
public void login(){
System.out.println("用户登录:id=" + id + ", name=" + name + ", password=" + password);
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
applicationContext文件,主要逻辑的处理
package applicationContext;
import java.io.File;
import java.io.FileFilter;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.Collection;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import annotation.MyAutowired;
import annotation.MyComponent;
import annotation.MyValue;
public class AnnotationConfigApplicationContext {
private Map<String,Class<?>> beanDefinationFactory = new ConcurrentHashMap<>();//用于存储类定义对象
private Map<String,Object> singletonBeanFactory = new ConcurrentHashMap<>();//用于存储单例对象
//有参构造方法,参数类型为指定要加载的包名,此工厂可以接受多个包路径
public AnnotationConfigApplicationContext(String... packageNames) {
for(String packageName: packageNames){
System.out.println("开始扫描包:" + packageName);
//扫描指定的包路径
scanPkg(packageName);
}
//进行依赖注入
dependencyInjection();
}
//扫描指定包,找到包中的类文件,对于标准类文件(类上有注解定义的),反射加载创建类,定义对象放到容器中
private void scanPkg(final String packageName) {
String pkgName = packageName.replaceAll("\\.", "/");
//获取目录结构在类文件中的位置
URL url = getClass().getClassLoader().getResource(pkgName);
//基于这个路径资源,创建一个文件对象
File file = new File(url.getFile());
//获取此目录中指定的标准类文件(以.class结尾的文件)
File[] files = file.listFiles(new FileFilter() {
@Override
public boolean accept(File file) {
String name = file.getName();
//判断该文件是否为目录,若为目录,则继续扫描该文件
if(file.isDirectory()){
scanPkg(packageName + "." + name);
}else{
//判断文件名是否为.class文件
if(name.endsWith(".class")){
return true;
}
}
return false;
}
});
for(File fs: files){
String name = fs.getName();
//获取去除.class之后的文件名
name = name.substring(0, name.indexOf("."));
//将类名的第一个字母转换为小写(用它作为key存储在工厂中)
String beanId = String.valueOf(name.charAt(0)).toLowerCase() + name.substring(1);
//构建一个全类名
String claPkg = packageName+ "." + name;
try {
//通过反射构建类对象
Class<?> cls = Class.forName(claPkg);
//判断这个类上是否有mycomponent注解
if(cls.isAnnotationPresent(MyComponent.class)){
//将类对象存储到容器中
beanDefinationFactory.put(beanId, cls);
}
} catch (Exception e) {
throw new RuntimeException();
}
}
}
/*此方法用于对属性进行依赖注入,从工厂中获取所有的类对象,
*如果对象的属性有MyAutowired注解,首先根据属性名获
*取对象,或者根据对象类型获取对象,最后用该对象对属性进
*行注入*/
private void dependencyInjection() {
//获取容器中所有类定义的对象
Collection<Class<?>> classes = beanDefinationFactory.values();
for(Class cls: classes){
String clsName = cls.getName();
//获得类名
clsName = clsName.substring(clsName.indexOf(".") + 1);
//将类名的第一个字母转换为小写
String beanId = clsName.valueOf(clsName.charAt(0)).toLowerCase() + clsName.substring(1);
//获取类中所有的属性
Field[] fields = cls.getDeclaredFields();
for(Field filed: fields){
try {
// 如果这个属性上有MyAutowired,进行注入操作
if (filed.isAnnotationPresent(MyAutowired.class)) {
// 获取属性名
String fName = filed.getName();
System.out.println("属性名:" + fName);
// 定义为属性注入的Bean对象(从容器中取得)
Object filedBean = null;
// 首先根据属性名从容器中去除对象,如果不为空,则赋值给filedBean对象
if (beanDefinationFactory.get(fName) != null) {
filedBean = getBean(fName, filed.getType());
} else {
// 按照属性的类型从容器中取出对象进行注入
String type = filed.getType().getName();
// 截取最后的类名
type = type.substring(type.indexOf(".") + 1);
// 将类名的第一个字母转换为小写
String filedBeanId = type.valueOf(type.charAt(0)).toLowerCase() + type.substring(1);
System.out.println("属性类型ID:" + filedBeanId);
// 根据转换后的beanId从容器中获取bean对象,赋值给filedBean
filedBean = getBean(filedBeanId, filed.getType());
}
System.out.println("要注入的属性值:" + filedBean);
// 如果filedBean对象不为空,则为该属性进行注入
if (filedBean != null) {
// 获取此类定义对象的实例
Object clsBean = getBean(beanId, cls);
// 设置该属性为可访问
filed.setAccessible(true);
// 为该属性注入值
filed.set(clsBean, filedBean);
System.out.println("注入成功!");
}else{
System.out.println("注入失败!");
}
}
} catch (IllegalArgumentException | IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
//此方法为重载方法
public <T>T getBean(String beanId, Class<T> c) {
return (T)getBean(beanId);
}
//根据传入的beanId获取容器中的对象,类型为Object
public Object getBean(String beanId) {
//根据传入的BeanId获取类对象
Class<?> cls = beanDefinationFactory.get(beanId);
//根据类对象获取其定义的注解
MyComponent annotation = cls.getAnnotation(MyComponent.class);
String scope = annotation.scope();
try {
if ("singleton".equals(scope) || "".equals(scope)) {
// 判断容器中是否已有该对象的实例,如果没有,就创建一个放到容器中
if (singletonBeanFactory.get(beanId) == null) {
Object instance = cls.newInstance();
setFiledValue(cls,instance);
singletonBeanFactory.put(beanId, instance);
}
return singletonBeanFactory.get(beanId);
}
//如果scope为prototype,则创建并返回多例
if("prototype".equals(scope)){
Object instance = cls.newInstance();
setFiledValue(cls,instance);
return instance;
}
} catch (InstantiationException | IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
/*此方法用于为对象的属性赋值,内部时通过获取成员属性上注解的值,在转换类型之后,通过反射为对象赋值*/
private void setFiledValue(Class<?> cls, Object obj) {
//获取类中所有的成员属性
Field[] fields = cls.getDeclaredFields();
for(Field field: fields){
//如果此属性有MyValue注解,对其进行操作
if(field.isAnnotationPresent(MyValue.class)){
String name = field.getName();
String value = field.getAnnotation(MyValue.class).value();
//获取属性所定义的类型
String type = field.getType().getName();
//将属性名该为以大写字母开头
name = String.valueOf(name.charAt(0)).toUpperCase() + name.substring(1);
String setterName = "set" + name;
try {
//根据方法名和参数类型获取对应的set方法对象
Method method = cls.getMethod(setterName, field.getType());
//判断属性类型,如果类型不一致,则转换类型后调用set方法为其属性赋值
if("java.lang.Integer".equals(type) || "int".equals(type)){
int intValue = Integer.valueOf(value);
method.invoke(obj, intValue);
}
if("String".equals(type)){
method.invoke(obj, value);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
//销毁方法,用于释放资源
public void close(){
beanDefinationFactory.clear();
beanDefinationFactory = null;
singletonBeanFactory.clear();
singletonBeanFactory = null;
}
service
package service;
import annotation.MyAutowired;
import annotation.MyComponent;
import entity.User;
@MyComponent
public class UserService {
@MyAutowired
User user1;
@MyAutowired
User user2;
public void userLogin(){
System.out.println("用户1:"+user1);
user1.login();
System.out.println("用户2:"+user2);
user2.login();
}
}
demo
package test;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import annotation.MyComponent;
import applicationContext.AnnotationConfigApplicationContext;
import service.UserService;
@MyComponent
public class TestSpringDI {
AnnotationConfigApplicationContext ctx;
UserService userService;
@Before
public void init(){
ctx = new AnnotationConfigApplicationContext("entity","service","test");
userService = ctx.getBean("userService", UserService.class);
}
@Test
public void testSpringDI(){
userService.userLogin();
}
@After
public void close(){
ctx.close();
}
}