Java培优班 - 第十八天 - JavaSE#Part2 - 模仿Spring -【自动扫描目录、自动创建对象(并赋值)、自动把对象放进map集合、自动注入装配】 - IoC控制权翻转、DI依赖注入

文章目录

  • Step1: 热身 - 扫描 /bin目录下的全部.class文件
  • Step 2:热身 - 将配置信息注入到对象中
    • 创建Configure工具类
    • 自定义@value注解
    • UserDao - 要赋值/创建的测试对象
    • Test2 - 测试类
  • Step 3: 自动扫描、自动创建对象
    • 说明
    • 代码实现
      • UserController类
      • UserServer类
      • SpringContext类
        • Step 3.1. 扫描路径、创建实例
        • Step 3.2. 选择性创建创建对象、注入变量值、自动装配
          • **3.2.1 添加注解 @Component、@Service、@controller** - 有选择的创建对象
          • **3.2.2 自动赋值、自动装配**
            • **添加@AutoWired注解**
            • **完成自动装配、注入实现**
  • ∗ ∗ 完 整 代 码 ∗ ∗ \color{#ff0011}{**完整代码**}
    • SpringContext
    • UserController
    • UserService
    • UserDao
    • Configure
    • 注解
      • AutoWired
      • Component
      • Controller
      • Service
      • Value


Step3 才是真正的功能实现,前面两步分别测试两个功能(扫描目录、给对象赋值),有助于理解Step3。


Step1: 热身 - 扫描 /bin目录下的全部.class文件

扫描功能在Step3中,写SptringContext类的功能时候会用到,这里热身。

Note:

  1. 这里记录了bin目录,其实不记录bin目录更能方便后面创建对象。(修改代码也很简单,有兴趣试下。不修改代码也就改下后面的路径就ok,所以这里就不改了。)
package cn.edut.com.tarena;

import java.io.File;
import java.net.URLDecoder;

public class Test1 {
     
	public static void main(String[] args) throws Exception {
     
		// 获取bin目录的完整路径
		String path = Test1.class.getResource("/").getPath();
		path= URLDecoder.decode(path,"UTF-8");
		System.out.println("path="+path);
		
		// 扫描文件
		File file = new File(path);
		scan(file);//扫描目录,找到所有的class文件
	}

	private static void scan(File file) {
     
		scan(file,0);
	}

	private static void scan(File file, int deph) {
     
		//对文件夹列表
		if(file.isDirectory()) {
     
			showFile(file.getName() , deph);
			File[] files = file.listFiles();
			//路径不存在,文件夹没有权限进入,会得到null值
			if (files == null) {
     
				return ;
			}
			//对所有的子文件和子目录
			for (File f : files) {
     
				scan(f,deph+1);
			}
		}else if(file.isFile()) {
     
			showFile(file.getName(), deph);
			
		}else {
     
			throw new RuntimeException() ; 
		}
	}

	private static void showFile(String name, int deph) {
     
		for(int i=0 ; i<deph ; i++) {
     
			System.out.print("\\-- ");
		}
		System.out.println(name);
	}
	
}

Java培优班 - 第十八天 - JavaSE#Part2 - 模仿Spring -【自动扫描目录、自动创建对象(并赋值)、自动把对象放进map集合、自动注入装配】 - IoC控制权翻转、DI依赖注入_第1张图片
Java培优班 - 第十八天 - JavaSE#Part2 - 模仿Spring -【自动扫描目录、自动创建对象(并赋值)、自动把对象放进map集合、自动注入装配】 - IoC控制权翻转、DI依赖注入_第2张图片




Step 2:热身 - 将配置信息注入到对象中

创建Configure工具类

SpringBoot中的配置文件application.yml
我们也模仿使用 :

新建项目: day16
右键点击项目–build path–add external …
application.yml
yaml - yet another markup language - 夜猫
不能使用tab制表符,都要使用空格
同一层次,必须对齐,至少两个空格2
冒号后面需要加空格
在src下新建文件 application.yml

spring:
  datasource:
    driver: com.mysql.jdbc.Driver
    username: root
    password: "123456"
    url: jdbc:mysql://127.0.0.1:3306/jt?allowMultiQueries=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai

处理:

处理.yml也是使用jackson,但是要加插件snakeyaml-1.23

加载yml文件的工具类
使用 jackson 提供了 yaml 数据处理工具,来读取解析yml配置文件
加载后的数据结构:三层map

工具下载:https://download.csdn.net/download/LawssssCat/11996025

Java培优班 - 第十八天 - JavaSE#Part2 - 模仿Spring -【自动扫描目录、自动创建对象(并赋值)、自动把对象放进map集合、自动注入装配】 - IoC控制权翻转、DI依赖注入_第3张图片
Java培优班 - 第十八天 - JavaSE#Part2 - 模仿Spring -【自动扫描目录、自动创建对象(并赋值)、自动把对象放进map集合、自动注入装配】 - IoC控制权翻转、DI依赖注入_第4张图片

Java培优班 - 第十八天 - JavaSE#Part2 - 模仿Spring -【自动扫描目录、自动创建对象(并赋值)、自动把对象放进map集合、自动注入装配】 - IoC控制权翻转、DI依赖注入_第5张图片
Java培优班 - 第十八天 - JavaSE#Part2 - 模仿Spring -【自动扫描目录、自动创建对象(并赋值)、自动把对象放进map集合、自动注入装配】 - IoC控制权翻转、DI依赖注入_第6张图片

package cn.edut.com.tarena.spring;

import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.Map;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;

import ognl.Ognl;
import ognl.OgnlException;

public class Configure {
     
	//step1 : map/load()/get(String exp)
	//step2 : TODO load()
	//step3 : TODO get(String exp)
	
	
	private static Map<String,Map> map ;
	
	/**
	 * 用来加载配置,把yml配置数据解析,生成一个map集合
	 */
	@SuppressWarnings("unchecked")
	public static void load() throws IOException {
     
		String path = Configure.class.getResource("/application.yml").getPath();
		path = URLDecoder.decode(path,"UTF-8");
		
		//使用 jackson + yaml 插件,来解析处理yaml格式数据
		ObjectMapper m = new ObjectMapper(new YAMLFactory());
		//读取配置文件,把数据处理成一个map对象
		map = m.readValue(new File(path), Map.class ) ; 
	}
	
	/**
	 * "${spring.datasource.driver}" --- OGNL
	 * step1 : 处理${} replaceAll
	 * step2 : Ognl.getValue()
	 * @throws OgnlException 
	 */
	public static String get(String exp) throws OgnlException {
     
		//空格替换成空
		exp = exp.replaceAll("\\s+", "") ;
		//把$()去掉
		exp = exp.substring(2, exp.length()-1) ; 
		
		//用ognal工具,使用实行表达式从map提取数据
		String value = (String) Ognl.getValue(exp, map);
		return value ;
	}
	public static void main(String[] args) throws Exception {
     
		
		System.out.println("----load()-------");
		load();
		System.out.println(map.toString());
		
		System.out.println("---get()-------");
		//${spring.datasource.driver}
		String testGet = get("$  {   sp ring . datas  ou rce.dri  v er   } ");
		System.out.println(testGet);
	}
	
}

Java培优班 - 第十八天 - JavaSE#Part2 - 模仿Spring -【自动扫描目录、自动创建对象(并赋值)、自动把对象放进map集合、自动注入装配】 - IoC控制权翻转、DI依赖注入_第7张图片

自定义@value注解

自定义的@value注解:

package cn.edut.com.tarena.spring;

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 value {
     
	
	String value() ;
	
}




UserDao - 要赋值/创建的测试对象

定义一个要用value注解赋值的

UserDao类:

package cn.edut.com.tarena.spring;

public class UserDao {
     
	@value("${spring.datasource.url}")
	private String url ; 
	@value("${spring.datasource.username}")
	private String username ; 
	@value("${spring.datasource.password}")
	private String password ; 
	@value("${spring.datasource.driver}")
	private String driver; 
	
	private void test() {
     
		System.out.println("\n----自动填充的数据----------");
		System.out.println("url="+url);
		System.out.println("username="+username);
		System.out.println("password="+password);
		System.out.println("driver="+driver);
	}
}



Test2 - 测试类

package cn.edut.com.tarena;

import java.lang.reflect.Field;

import cn.edut.com.tarena.spring.Configure;
import cn.edut.com.tarena.spring.UserDao;
import cn.edut.com.tarena.spring.Value;

public class Test2 {
     
	public static void main(String[] args) throws Exception {
     
		Configure.load();
		
		//新建UserDao实例,并自动填充它的属性
		UserDao userDao = new UserDao();
		
		//获取所有的成员变量
		Field[] fields = userDao.getClass().getDeclaredFields();
		//遍历变量
		for (Field field : fields) {
     
			//变量上是哦否存在@Value注解
			if(field.isAnnotationPresent(Value.class)) {
     
				//获取@Value注解
				Value annotation = field.getAnnotation(Value.class);
				String exp = annotation.value();//从注解获取ognl表达式
				
				//如果没有ognl(这里忽略)
				//用value当ognl(这里忽略)
				
				//用get方法解析ognl表达式,获得value
				String value = Configure.get(exp) ;
				//私有变量可访问
				field.setAccessible(true);
				//把输出的配置数据,保存到变量实例userDao
				field.set(userDao, value);
			}
		}
		
		userDao.test();
	}
}

Java培优班 - 第十八天 - JavaSE#Part2 - 模仿Spring -【自动扫描目录、自动创建对象(并赋值)、自动把对象放进map集合、自动注入装配】 - IoC控制权翻转、DI依赖注入_第8张图片




Step 3: 自动扫描、自动创建对象

说明

自动扫描、自动创建对象、自动赋值,本质上就实现下面的两种模式

  • IoC - Inverse of Control 控制权翻转
    控制翻转,自己不控制对象的创建,而是权利翻转,交给工具来创建对象需要对象,从工具来取用

  • DI - Dependency Injection 依赖注入
    对象需要的数据,使用的其他对象,进行自动装配


自动在类路径中扫描,找到所有添加了以下注解的类,并自动创建实例

  • Component
  • Service
  • Controller

SpringContext - Spring环境对象,上下文对象

包含一个map集合:

Key Value
“day16.UserDao” UserDao实例
“day16.UserService” UserService实例
“day16.UserController” UserController实例
/
 |- day16
      |- UserDao
      |- UserService
      |- UserControler
      |- pojo
           |- A
           |- B
           |- xxx
                |- ...
 |- day15
      |- X
      |- Y 

代码实现

UserDao上面准备好了,现在准备两个类的模型 (UserController类、UserServer类)

UserController类

package cn.edut.com.tarena.spring;

public class UserController {
     
	private UserService userService ; 
	public void test() {
     
		System.out.println("\n--控制器对象--------------");
		System.out.println("调用业务对象 - "+userService);
		userService.test();
	}
}

UserServer类

package cn.edut.com.tarena.spring;

public class UserService {
     
	private UserDao userDao ; 
	public void test() {
     
		System.out.println("\n--业务对象-----------------");
		System.out.println("调用数据对象 - "+userDao);
		userDao.test();
	}
}

SpringContext类

  • Step1 ,扫描类路径下的类,并自动创建实例
  • Step2 ,对象中的变量自动注入,完成对象的自动装配

Step 3.1. 扫描路径、创建实例

扫描实现:

package cn.edut.com.tarena.spring;

import java.io.File;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

/*
 * 自动在bin目录下,扫描所有的class文件,并创建实例,放入map集合
 * [bin]
 * 		|- [aa]
 * 			|- A.class 
 * 			|- B.class 
 * 			|- [bb]
 * 				|- C.class
 * 				|- D.class
 * 				|- [cc]
 *	 				|- E.class
 * 			|- F.class 
 *
 * 包名"aa.bb.cc"
 */
public class SpringContext {
     
	private Map<String , Object> map = new HashMap<>();
	
	/**
	 * 自动在bin目录下,扫描所有的class文件,并创建实例,放入map集合
	 */
	public void autoScan() throws Exception {
     
		String path = SpringContext.class.getResource("/").getPath();
		path = URLDecoder.decode(path,"UTF-8");
		
		File file = new File(path+"/cn");
		
		//扫描目录, pkg 用来处理包民
		StringBuilder pkg = new StringBuilder();
		scan(file , pkg);
	}

	private void scan(File dir, StringBuilder pkg) {
     
		if(dir == null || pkg==null) {
     
			throw new RuntimeException();
		}
		String name = dir.getName();		
		if(dir.isDirectory()) 
		{
      //dir是目录
			File[] list = dir.listFiles();
			if(list==null) {
     
				return ;
			}
			pkg.append(name);
			System.out.println(pkg);
			for (File file : list) {
      
				pkg.append(".");
				scan(file, pkg);
				pkg.delete(pkg.length()-1, pkg.length());
			}
			pkg.delete(pkg.length()-name.length(), pkg.length());
		}else if(dir.isFile()) 
		{
      //dir 是文件
			pkg.append(name) ; 
			String suffix = ".class" ; 
			if(pkg.toString().endsWith(suffix)) {
     
				String pkgName = pkg.subSequence(0 , pkg.length()-suffix.length()).toString();
				try {
     
					Object newInstance = Class.forName(pkgName).newInstance();
					map.put(pkgName, newInstance);
				} catch (Exception e) {
     
					System.out.println(pkgName + "无法创建实例");
				}
			}
			System.out.println(pkg);
			pkg.delete(pkg.length()-name.length(), pkg.length());
		}
	}

	
	public static void main(String[] args) throws Exception {
     
		SpringContext c = new SpringContext();
		c.autoScan();
		System.out.println("--创建的实例-----------");
		Set<Entry<String, Object>> entrySet = c.map.entrySet();
		for (Entry<String, Object> entry : entrySet) {
     
			System.out.println(entry.getKey()+" - "+entry.getValue());
		}
	}
	
}


扫描目录:
Java培优班 - 第十八天 - JavaSE#Part2 - 模仿Spring -【自动扫描目录、自动创建对象(并赋值)、自动把对象放进map集合、自动注入装配】 - IoC控制权翻转、DI依赖注入_第9张图片

扫描结果:

cn
cn.edut
cn.edut.com
cn.edut.com.tarena
cn.edut.com.tarena.spring
cn.edut.com.tarena.spring.Configure.class
cn.edut.com.tarena.spring.SpringContext.class
cn.edut.com.tarena.spring.Test003.class
cn.edut.com.tarena.spring.UserController.class
cn.edut.com.tarena.spring.UserDao.class
cn.edut.com.tarena.spring.UserService.class
cn.edut.com.tarena.spring.Value无法创建实例
cn.edut.com.tarena.spring.Value.class
cn.edut.com.tarena.Test1.class
cn.edut.com.tarena.Test2.class
–创建的实例-----------
cn.edut.com.tarena.spring.UserService - cn.edut.com.tarena.spring.UserService@1d35f92f
cn.edut.com.tarena.Test2 - cn.edut.com.tarena.Test2@427a8ba4
cn.edut.com.tarena.spring.Test003 - cn.edut.com.tarena.spring.Test003@7b65de14
cn.edut.com.tarena.Test1 - cn.edut.com.tarena.Test1@5cd73256
cn.edut.com.tarena.spring.UserController - cn.edut.com.tarena.spring.UserController@3429dbb8
cn.edut.com.tarena.spring.Configure - cn.edut.com.tarena.spring.Configure@281827c9
cn.edut.com.tarena.spring.UserDao - cn.edut.com.tarena.spring.UserDao@49dc11af
cn.edut.com.tarena.spring.SpringContext - cn.edut.com.tarena.spring.SpringContext@f0d1e0b




Step 3.2. 选择性创建创建对象、注入变量值、自动装配


3.2.1 添加注解 @Component、@Service、@controller - 有选择的创建对象

问题1:为什么创建这三个注解?
答:有选择的创建对象,节省空间

问题2:为什么用三个注解?
答:只是方便阅读,其实功能是一样的

Component 组件
Service
Controller 控制器

@Component注解(三个注解名字外一样,直接复制)

package cn.edut.com.tarena.spring.anno;

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 Component {
     
}

@Controller 注解

package cn.edut.com.tarena.spring.anno;

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 Controller {
     
}

@Service 注解

package cn.edut.com.tarena.spring.anno;

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 Service {
     
}

更新后的SpringContext类

package cn.edut.com.tarena.spring;

import java.io.File;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

import cn.edut.com.tarena.spring.anno.Component;
import cn.edut.com.tarena.spring.anno.Controller;
import cn.edut.com.tarena.spring.anno.Service;

import java.util.Set;


/*
 * 自动在bin目录下,扫描所有的class文件,并创建实例,放入map集合
 * [bin]
 * 		|- [aa]
 * 			|- A.class 
 * 			|- B.class 
 * 			|- [bb]
 * 				|- C.class
 * 				|- D.class
 * 				|- [cc]
 *	 				|- E.class
 * 			|- F.class 
 *
 * 包名"aa.bb.cc"
 */
public class SpringContext {
     
	private Map<String , Object> map = new HashMap<>();
	
	/**
	 * 自动在bin目录下,扫描所有的class文件,并创建实例,放入map集合
	 */
	public void autoScan() throws Exception {
     
		String path = SpringContext.class.getResource("/").getPath();
		path = URLDecoder.decode(path,"UTF-8");
		
		File file = new File(path+"/cn");
		
		//扫描目录, pkg 用来处理包民
		StringBuilder pkg = new StringBuilder();
		scan(file , pkg);
	}

	private void scan(File dir, StringBuilder pkg) {
     
		if(dir == null || pkg==null) {
     
			throw new RuntimeException();
		}
		String name = dir.getName();		
		if(dir.isDirectory()) 
		{
      //dir是目录
			File[] list = dir.listFiles();
			if(list==null) {
     
				return ;
			}
			pkg.append(name);
			System.out.println(pkg);
			for (File file : list) {
      
				pkg.append(".");
				scan(file, pkg);
				pkg.delete(pkg.length()-1, pkg.length());
			}
			pkg.delete(pkg.length()-name.length(), pkg.length());
		}else if(dir.isFile()) 
		{
      //dir 是文件
			pkg.append(name) ; 
			String suffix = ".class" ; 
			if(pkg.toString().endsWith(suffix)) {
     
				String pkgName = pkg.subSequence(0 , pkg.length()-suffix.length()).toString();
				try {
     
					Class<?> clazz = Class.forName(pkgName);
					if(clazz.isAnnotationPresent(Component.class) ||
						clazz.isAnnotationPresent(Controller.class) ||
						clazz.isAnnotationPresent(Service.class)) {
     
						Object newInstance = clazz.newInstance();
						map.put(pkgName, newInstance);
					}
				} catch (Exception e) {
     
					System.out.println(pkgName + "无法创建实例");
				}
			}
			System.out.println(pkg);
			pkg.delete(pkg.length()-name.length(), pkg.length());
		}
	}

	public <T> T getObject(Class<T> c) {
     
		String name = c.getName();
		return (T) map.get(name); 
	}
	
	public static void main(String[] args) throws Exception {
     
		SpringContext c = new SpringContext();
		c.autoScan();
		
		System.out.println("--创建的实例-----------");
		Set<Entry<String, Object>> set1 = c.map.entrySet();
		for (Entry<String, Object> entry : set1) {
     
			System.out.println(entry.getKey()+" - "+entry.getValue());
		}
		
		System.out.println("--自动填充数据-----------");
		
		
		UserDao userDao = c.getObject(UserDao.class);
		userDao.test();
		
		UserService userService = c.getObject(UserService.class);
		//userService.test();
	}
	
}

整理了一下目录

Java培优班 - 第十八天 - JavaSE#Part2 - 模仿Spring -【自动扫描目录、自动创建对象(并赋值)、自动把对象放进map集合、自动注入装配】 - IoC控制权翻转、DI依赖注入_第10张图片

控制台结果

cn
cn.edut
cn.edut.com
cn.edut.com.tarena
cn.edut.com.tarena.spring
cn.edut.com.tarena.spring.anno
cn.edut.com.tarena.spring.anno.Component.class
cn.edut.com.tarena.spring.anno.Controller.class
cn.edut.com.tarena.spring.anno.Service.class
cn.edut.com.tarena.spring.anno.Value.class
cn.edut.com.tarena.spring.Configure.class
cn.edut.com.tarena.spring.SpringContext.class
cn.edut.com.tarena.spring.Test003.class
cn.edut.com.tarena.spring.UserController.class
cn.edut.com.tarena.spring.UserDao.class
cn.edut.com.tarena.spring.UserService.class
cn.edut.com.tarena.Test1.class
cn.edut.com.tarena.Test2.class
--创建的实例-----------
cn.edut.com.tarena.spring.UserService - cn.edut.com.tarena.spring.UserService@3f9432e0
cn.edut.com.tarena.spring.UserController - cn.edut.com.tarena.spring.UserController@1a9830bc
cn.edut.com.tarena.spring.UserDao - cn.edut.com.tarena.spring.UserDao@7da79447
--自动填充数据-----------

----自动填充的数据----------
url=null
username=null
password=null
driver=null



3.2.2 自动赋值、自动装配

自动装配 - autowired

现在实现依赖注入:

  • DI - Dependency Injection 依赖注入
    对象需要的数据,使用的其他对象,进行自动装配

先给需要注入数据的属性添加注解


添加@AutoWired注解

给UserController、UserService类的属性添加 @AutoWired 注解,自动填充属性数据 - 自动装配 - 依赖注入

package cn.edut.com.tarena.spring.anno;

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 AutoWired {
     
}

在UserController、UserService类中的修改:
Java培优班 - 第十八天 - JavaSE#Part2 - 模仿Spring -【自动扫描目录、自动创建对象(并赋值)、自动把对象放进map集合、自动注入装配】 - IoC控制权翻转、DI依赖注入_第11张图片
Java培优班 - 第十八天 - JavaSE#Part2 - 模仿Spring -【自动扫描目录、自动创建对象(并赋值)、自动把对象放进map集合、自动注入装配】 - IoC控制权翻转、DI依赖注入_第12张图片


完成自动装配、注入实现

autoWired() - 方法

/**
	 * 自动装配
	 * 
	 * 分析:
	 * 现在map里面的数据:
	 * map
	 * 	
	 * 		key											value
	 * ------------------------------------------------------------------
	 * cn.edut.com.tarena.spring.UserService			UserDao实例
	 * cn.edut.com.tarena.spring.UserController			UserService实例
	 * cn.edut.com.tarena.spring.UserDao				UserController实例
	 * @throws Exception 
	 */
	private void autoWired() throws Exception {
     
		Collection<Object> values = map.values();
		for (Object object : values) {
     
			Class<? extends Object> clazz = object.getClass();
			Field[] fields = clazz.getDeclaredFields();
			for (Field field : fields) {
     
				if(field.isAnnotationPresent(AutoWired.class)) {
     //如果是需要装配的
					//注入对象
					injectObject(object,field);
				}else if(field.isAnnotationPresent(Value.class)) {
     //如果是数据
					//注入配置数据
					injectValue(object, field);
				}
			}
		}
	}




	private void injectObject(Object object, Field field) throws Exception {
     
		Class<?> type = field.getType();
		Object injectObject = this.getObject(type);
		field.setAccessible(true);
		field.set(object, injectObject);
	}
	private void injectValue(Object object, Field field) throws Exception {
     
		//从配置文件获取要注入的数据
		Value annotation = field.getAnnotation(Value.class); //获得变量上的@Value注解
		String value = annotation.value();//获取ognl表达式
		
		String v = Configure.get(value); //从配置文件中获取配置数据
		//注入数据
		field.setAccessible(true);//修改访问权限
		field.set(object, v);//把配置数据v,保存到变量f
	}

测试结果:

cn
cn.edut
cn.edut.com
cn.edut.com.tarena
cn.edut.com.tarena.spring
cn.edut.com.tarena.spring.anno
cn.edut.com.tarena.spring.anno.AutoWired.class
cn.edut.com.tarena.spring.anno.Component.class
cn.edut.com.tarena.spring.anno.Controller.class
cn.edut.com.tarena.spring.anno.Service.class
cn.edut.com.tarena.spring.anno.Value.class
cn.edut.com.tarena.spring.Configure.class
cn.edut.com.tarena.spring.SpringContext.class
cn.edut.com.tarena.spring.Test003.class
cn.edut.com.tarena.spring.UserController.class
cn.edut.com.tarena.spring.UserDao.class
cn.edut.com.tarena.spring.UserService.class
cn.edut.com.tarena.Test1.class
cn.edut.com.tarena.Test2.class
--创建的实例-----------
cn.edut.com.tarena.spring.UserService - cn.edut.com.tarena.spring.UserService@793e6657
cn.edut.com.tarena.spring.UserController - cn.edut.com.tarena.spring.UserController@6fb117f0
cn.edut.com.tarena.spring.UserDao - cn.edut.com.tarena.spring.UserDao@539ac6d9
--自动填充数据-----------
**********************************

----UserDao数据----------
url=jdbc:mysql://127.0.0.1:3306/jt?allowMultiQueries=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
username=root
password=123456
driver=com.mysql.jdbc.Driver
**********************************

--业务对象-----------------
调用数据对象 - cn.edut.com.tarena.spring.UserDao@539ac6d9

----UserDao数据----------
url=jdbc:mysql://127.0.0.1:3306/jt?allowMultiQueries=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
username=root
password=123456
driver=com.mysql.jdbc.Driver
**********************************

--控制器对象--------------
调用业务对象 - cn.edut.com.tarena.spring.UserService@793e6657

--业务对象-----------------
调用数据对象 - cn.edut.com.tarena.spring.UserDao@539ac6d9

----UserDao数据----------
url=jdbc:mysql://127.0.0.1:3306/jt?allowMultiQueries=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
username=root
password=123456
driver=com.mysql.jdbc.Driver
**********************************




∗ ∗ 完 整 代 码 ∗ ∗ \color{#ff0011}{**完整代码**}

Java培优班 - 第十八天 - JavaSE#Part2 - 模仿Spring -【自动扫描目录、自动创建对象(并赋值)、自动把对象放进map集合、自动注入装配】 - IoC控制权翻转、DI依赖注入_第13张图片

SpringContext

package cn.edut.com.tarena.spring;

import java.io.File;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.net.URLDecoder;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

import cn.edut.com.tarena.spring.anno.AutoWired;
import cn.edut.com.tarena.spring.anno.Component;
import cn.edut.com.tarena.spring.anno.Controller;
import cn.edut.com.tarena.spring.anno.Service;
import cn.edut.com.tarena.spring.anno.Value;
import ognl.Ognl;
import ognl.OgnlException;
import sun.security.krb5.Config;

import java.util.Set;


/*
 * 自动在bin目录下,扫描所有的class文件,并创建实例,放入map集合
 * [bin]
 * 		|- [aa]
 * 			|- A.class 
 * 			|- B.class 
 * 			|- [bb]
 * 				|- C.class
 * 				|- D.class
 * 				|- [cc]
 *	 				|- E.class
 * 			|- F.class 
 *
 * 包名"aa.bb.cc"
 */
public class SpringContext {
     
	private Map<String , Object> map = new HashMap<>();
	
	/**
	 * 自动在bin目录下,扫描所有的class文件,并创建实例,放入map集合
	 */
	public void autoScan() throws Exception {
     
		String path = SpringContext.class.getResource("/").getPath();
		path = URLDecoder.decode(path,"UTF-8");
		
		File file = new File(path+"/cn");
		
		//扫描目录, pkg 用来处理包民
		StringBuilder pkg = new StringBuilder();
		scan(file , pkg);
		
		autoWired();
	}
	/**
	 * 自动装配
	 * 
	 * 分析:
	 * 现在map里面的数据:
	 * map
	 * 	
	 * 		key											value
	 * ------------------------------------------------------------------
	 * cn.edut.com.tarena.spring.UserService			UserDao实例
	 * cn.edut.com.tarena.spring.UserController			UserService实例
	 * cn.edut.com.tarena.spring.UserDao				UserController实例
	 * @throws Exception 
	 */
	private void autoWired() throws Exception {
     
		Collection<Object> values = map.values();
		for (Object object : values) {
     
			Class<? extends Object> clazz = object.getClass();
			Field[] fields = clazz.getDeclaredFields();
			for (Field field : fields) {
     
				if(field.isAnnotationPresent(AutoWired.class)) {
     //如果是需要装配的
					//注入对象
					injectObject(object,field);
				}else if(field.isAnnotationPresent(Value.class)) {
     //如果是数据
					//注入配置数据
					injectValue(object, field);
				}
			}
		}
	}




	private void injectObject(Object object, Field field) throws Exception {
     
		Class<?> type = field.getType();
		Object injectObject = this.getObject(type);
		field.setAccessible(true);
		field.set(object, injectObject);
	}
	private void injectValue(Object object, Field field) throws Exception {
     
		//从配置文件获取要注入的数据
		Value annotation = field.getAnnotation(Value.class); //获得变量上的@Value注解
		String value = annotation.value();//获取ognl表达式
		
		String v = Configure.get(value); //从配置文件中获取配置数据
		//注入数据
		field.setAccessible(true);//修改访问权限
		field.set(object, v);//把配置数据v,保存到变量f
	}
	private void scan(File dir, StringBuilder pkg) {
     
		if(dir == null || pkg==null) {
     
			throw new RuntimeException();
		}
		String name = dir.getName();		
		if(dir.isDirectory()) 
		{
      //dir是目录
			File[] list = dir.listFiles();
			if(list==null) {
     
				return ;
			}
			pkg.append(name);
			System.out.println(pkg);
			for (File file : list) {
      
				pkg.append(".");
				scan(file, pkg);
				pkg.delete(pkg.length()-1, pkg.length());
			}
			pkg.delete(pkg.length()-name.length(), pkg.length());
		}else if(dir.isFile()) 
		{
      //dir 是文件
			pkg.append(name) ; 
			String suffix = ".class" ; 
			if(pkg.toString().endsWith(suffix)) {
     
				String pkgName = pkg.subSequence(0 , pkg.length()-suffix.length()).toString();
				try {
     
					Class<?> clazz = Class.forName(pkgName);
					if(clazz.isAnnotationPresent(Component.class) ||
						clazz.isAnnotationPresent(Controller.class) ||
						clazz.isAnnotationPresent(Service.class)) {
     
						Object newInstance = clazz.newInstance();
						map.put(pkgName, newInstance);
					}
				} catch (Exception e) {
     
					System.out.println(pkgName + "无法创建实例");
				}
			}
			System.out.println(pkg);
			pkg.delete(pkg.length()-name.length(), pkg.length());
		}
	}

	public <T> T getObject(Class<T> c) {
     
		String name = c.getName();
		return (T) map.get(name); 
	}
	
	public static void main(String[] args) throws Exception {
     
		//在自动扫描之前,先加载
		Configure.load();
		
		//自动扫描
		SpringContext c = new SpringContext();
		c.autoScan();
		
		System.out.println("--创建的实例-----------");
		Set<Entry<String, Object>> set1 = c.map.entrySet();
		for (Entry<String, Object> entry : set1) {
     
			System.out.println(entry.getKey()+" - "+entry.getValue());
		}
		
		System.out.println("--自动填充数据-----------");
		System.out.println("**********************************");
		
		UserDao userDao = c.getObject(UserDao.class);
		userDao.test();
		
		System.out.println("**********************************");
		UserService userService = c.getObject(UserService.class);
		userService.test();
		
		System.out.println("**********************************");
		UserController userController = c.getObject(UserController.class);
		userController.test();
		System.out.println("**********************************");
	}
	
}

UserController

package cn.edut.com.tarena.spring;

import cn.edut.com.tarena.spring.anno.AutoWired;
import cn.edut.com.tarena.spring.anno.Controller;

@Controller
public class UserController {
     
	//自动装配
	//自动从SpringContext中,获取UserService实例,保存到这变量
	@AutoWired
	private UserService userService ; 
	public void test() {
     
		System.out.println("\n--控制器对象--------------");
		System.out.println("调用业务对象 - "+userService);
		userService.test();
	}
}

UserService

package cn.edut.com.tarena.spring;

import cn.edut.com.tarena.spring.anno.AutoWired;
import cn.edut.com.tarena.spring.anno.Service;

@Service
public class UserService {
     
	@AutoWired
	private UserDao userDao ; 
	public void test() {
     
		System.out.println("\n--业务对象-----------------");
		System.out.println("调用数据对象 - "+userDao);
		userDao.test();
	}
}

UserDao

package cn.edut.com.tarena.spring;

import cn.edut.com.tarena.spring.anno.Component;
import cn.edut.com.tarena.spring.anno.Value;

@Component
public class UserDao {
     
	@Value("${spring.datasource.url}")
	private String url ; 
	@Value("${spring.datasource.username}")
	private String username ; 
	@Value("${spring.datasource.password}")
	private String password ; 
	@Value("${spring.datasource.driver}")
	private String driver; 
	
	public void test() {
     
		System.out.println("\n----UserDao数据----------");
		System.out.println("url="+url);
		System.out.println("username="+username);
		System.out.println("password="+password);
		System.out.println("driver="+driver);
	}
}

Configure

package cn.edut.com.tarena.spring;

import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.Map;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;

import ognl.Ognl;
import ognl.OgnlException;

public class Configure {
     
	//step1 : map/load()/get(String exp)
	//step2 : TODO load()
	//step3 : TODO get(String exp)
	
	
	private static Map<String,Map> map ;
	
	/**
	 * 用来加载配置,把yml配置数据解析,生成一个map集合
	 */
	@SuppressWarnings("unchecked")
	public static void load() throws IOException {
     
		String path = Configure.class.getResource("/application.yml").getPath();
		path = URLDecoder.decode(path,"UTF-8");
		
		//使用 jackson + yaml 插件,来解析处理yaml格式数据
		ObjectMapper m = new ObjectMapper(new YAMLFactory());
		//读取配置文件,把数据处理成一个map对象
		map = m.readValue(new File(path), Map.class ) ; 
	}
	
	/**
	 * "${spring.datasource.driver}" --- OGNL
	 * step1 : 处理${} replaceAll
	 * step2 : Ognl.getValue()
	 * @throws OgnlException 
	 */
	public static String get(String exp) throws Exception {
     
		//空格替换成空
		exp = exp.replaceAll("\\s+", "") ;
		//把$()去掉
		exp = exp.substring(2, exp.length()-1) ; 
		
		//用ognal工具,使用实行表达式从map提取数据
		String value = (String) Ognl.getValue(exp, map);
		return value ;
	}
	public static void main(String[] args) throws Exception {
     
		
		System.out.println("----load()-------");
		load();
		System.out.println(map.toString());
		
		System.out.println("---get()-------");
		//${spring.datasource.driver}
		String testGet = get("$  {   sp ring . datas  ou rce.dri  v er   } ");
		System.out.println(testGet);
	}
	
}

注解

AutoWired

package cn.edut.com.tarena.spring.anno;

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 AutoWired {
     
}

Component

package cn.edut.com.tarena.spring.anno;

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 Component {
     
}

Controller

package cn.edut.com.tarena.spring.anno;

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 Controller {
     
}

Service

package cn.edut.com.tarena.spring.anno;

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 Service {
     
}

Value

package cn.edut.com.tarena.spring.anno;

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 Value {
     
	
	String value() ;
	
}

你可能感兴趣的:(java,培优班,#,spring,java,中级,-,JavaEE)