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


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);
	}
	
}





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



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);
	}
	
}

自定义@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();
	}
}




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

说明

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

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

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


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

  • 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 

代码实现

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

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 ,<mark>扫描类路径下的类,并自动创建实例</mark>
  • Step2 ,<mark>对象中的变量自动注入,完成对象的自动装配</mark>

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


扫描目录:

扫描结果:

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:<mark>为什么用三个注解?</mark>
答:<mark>只是方便阅读,其实功能是一样的</mark>

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

整理了一下目录

控制台结果

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

现在实现依赖注入:

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

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


添加@AutoWired注解

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

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类中的修改:


完成自动装配、注入实现

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
**********************************




<mstyle mathcolor="&#35;ff0011"> </mstyle> \color{#ff0011}{**完整代码**}

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