<mark>Step3 才是真正的功能实现,前面两步分别测试两个功能(扫描目录、给对象赋值),有助于理解Step3。</mark>
Step1: 热身 - 扫描 /bin目录下的全部.class文件
扫描功能在Step3中,写SptringContext类的功能时候会用到,这里热身。
Note:
- 这里记录了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
**********************************
∗∗完整代码∗∗
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() ;
}