1.所需jar包:
2.在src目录下件匹配文件:
application.yml如下:
spring:
datasource:
driver: com.mysql.jdbc.Driver
username: root
password: 123456
url: jdbc:mysql://127.0.0.1:3306/xxoo?allowMultiQueries=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
3.创建spring常用的几个注解:Value,Autowried,Component,Service,Controller,Service ;
package com;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @Authro: QYF
* @Time:2020/10/21 0:18
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Value {
//配置文件中的属性名,ognl表达式格式
String name() default "";
//name别名
String value() default "";
}
package com;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @Authro: QYF
* @Time:2020/10/21 0:43
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Autowired {
}
package com;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @Authro: QYF
* @Time:2020/10/21 0:22
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Component {
}
package com;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @Authro: QYF
* @Time:2020/10/21 0:32
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Controller {
}
package com;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @Authro: QYF
* @Time:2020/10/21 0:32
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Service {
}
4.用来读取解析yml配置文件的工具类
package com;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import ognl.Ognl;
import ognl.OgnlException;
import java.io.File;
import java.net.URLDecoder;
import java.util.HashMap;
import java.util.Map;
/**
* @Authro: QYF
* @Time:2020/10/20 23:34
*/
public class Configure {
private static Map<String, String> cfg = new HashMap<String, String>();
/**
* 反射获取配置文件内容
* @throws Exception
*/
public static void load() throws Exception {
//获取文件路径
String path = Configure.class.getResource("/application.yml").getPath();
//防止路径有中文
path = URLDecoder.decode(path, "UTF-8");
//定义解析yml文件
ObjectMapper mapper = new ObjectMapper(new YAMLFactory());
//解析yml文件,用map接收结果
cfg = mapper.readValue(new File(path), Map.class);
// System.out.println(cfg);
}
/**
* 根据表达式快速获取对应的值
* @param expr
* @return
*/
public static String get(String expr) {
/**
* ognl - Object Graph Navigation Language
* 对象导航图
* {a={b={c="111", d="222"}}}
* ${a.b.c} ---- "111"
* ${a.b.d} ---- "222"
*/
// ${a.b.c} ---> a.b.c
expr = expr.trim();
expr = expr.substring(2, expr.length()-1);
String r;
try {
r = String.valueOf(Ognl.getValue(expr, cfg));
return r;
} catch (OgnlException e) {
e.printStackTrace();
return null;
}
}
public static void main(String[] args) throws Exception {
Configure.load();
System.out.println(cfg);
String driver = get("${spring.datasource.driver}");
String username = get("${spring.datasource.username}");
String password = get("${spring.datasource.password}");
String url = get("${spring.datasource.url}");
System.out.println(driver);
System.out.println(username);
System.out.println(password);
System.out.println(url);
}
}
测试结果输出:
{spring={datasource={driver=com.mysql.jdbc.Driver, username=root, password=123456, url=jdbc:mysql://127.0.0.1:3306/xxoo?allowMultiQueries=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai}}}
com.mysql.jdbc.Driver
root
123456
jdbc:mysql://127.0.0.1:3306/xxoo?allowMultiQueries=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
5.最主要的SpringContext
package com;
import java.io.File;
import java.lang.reflect.Field;
import java.net.URLDecoder;
import java.util.HashMap;
import java.util.Map;
/**
* @Authro: QYF
* @Time:2020/10/21 0:41
* Spring 环境对象, 上下文对象
*/
public class SpringContext {
private static Map<String, Object> ctx = new HashMap<String, Object>();
static {
try {
Configure.load();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 自动扫描,在类文件夹下,自动找到所有的类,
* 并判断是否存在 @Component,@Service,@Controller 注解,
* 如果存在,就自动新建实例并放入map集合
*/
public static void autoScan() throws Exception {
String path = SpringContext.class.getResource("/").getPath();
path = URLDecoder.decode(path, "UTF-8");
File dir = new File(path);
/*
* 需要一个StringBuilder对象,拼接包名类名
* "day16" --- 遇到目录拼包名
* "day16.UserDao" --- 这里要处理这个类
* "day16" --- 处理完一个类,要把类名截掉
* "" --- 处理完包,要把包截掉
*/
//在类目录中扫描
scan(dir, new StringBuilder());
//完成所有实例的自动装配
autowire();
}
/**
* 自动注入
* @throws Exception
*/
private static void autowire() throws Exception {
/*
* ctx中所有的实例,一个一个的进行装配
*
* 在实例中寻找添加了@Value和@Autowired注解的变量,
* 自动为变量注入数据
*/
for (Object obj : ctx.values()) {//对map中的value这一列遍历
//获得"类对象"
Class<? extends Object> c = obj.getClass();
//获取所有成员变量
Field[] a = c.getDeclaredFields();
//遍历所有的成员变量
for (Field f : a) {
if (f.isAnnotationPresent(Value.class)) {
//注入配置信息
injectValue(c, obj, f);
}
if (f.isAnnotationPresent(Autowired.class)) {
//注入对象
injectObject(c, obj, f);
}
}
}
}
/**
* 注入Value
* @param c
* @param obj
* @param f
* @throws Exception
*/
private static void injectValue(Class<? extends Object> c, Object obj, Field f) throws Exception {
//从变量获得Value注解,并获得ognl表达式
Value v = f.getAnnotation(Value.class);
String name = v.name();
if (name == null || name.equals("")) {
name = v.value();
}
//用ognl获取配置数据
String r = Configure.get(name);
f.setAccessible(true);
//把配置数据,存到变量
f.set(obj, r);
}
/**
* 注入对象
* @param c
* @param obj
* @param f
* @throws Exception
*/
private static void injectObject(Class<? extends Object> c, Object obj, Field f) throws Exception {
/*
* @Autowired
* private UserDao userDao
*
* 获得 UserDao 的完整类名,用类名从ctx提取它的实例
* 取出实例后,保存到这个变量
*/
String name = f.getType().getName();
Object r = ctx.get(name);
f.setAccessible(true);
f.set(obj, r);
}
/**
* 递归获取所以类的全路径
*
* @param dir
* @param sb
* @throws Exception
*/
private static void scan(File dir, StringBuilder sb) throws Exception {
File[] files = dir.listFiles();
//不能获得列表,结束
if (files == null) {
return;
}
//遍历目录列表
for (File f : files) {
String name = f.getName();
//f是文件
if (f.isFile()) {
//不是class文件,跳过
if (!name.endsWith(".class")) {
continue;
}
// Abc.class
// 去掉.class,把".Abc"拼到末尾,例如"day16.Abc"
sb.append(".").append(name.substring(0, name.length() - 6));
// System.out.println(sb);
//处理这个类
handle(sb.toString());
//f是文件夹
} else {
//第一层包名不加点,后面每层包名都先加一个点
if (sb.length() != 0) {
sb.append(".");
}
//拼包名
sb.append(name);
//递归处理f文件夹
scan(f, sb);
}
//处理完后,把末尾一段删除,例如"aa.bb.cc.Abc",把".Abc"删掉
//找最后一个点字符
int start = sb.lastIndexOf(".");
//没有点的情况
if (start == -1) {
//例如"aa",直接全部删掉
sb.delete(0, sb.length());
} else {
//有点,从点位置删
sb.delete(start, sb.length());
}
}
}
/**
* 根据全路径名,创建实例
*
* @param className
* @throws Exception
*/
private static void handle(String className) throws Exception {
//得到"类对象"
Class<?> c = Class.forName(className);
//是否存在 @Component,@Service,@Controller注解
if (c.isAnnotationPresent(Component.class) ||
c.isAnnotationPresent(Service.class) ||
c.isAnnotationPresent(Controller.class)) {
//创建实例
Object obj = c.newInstance();
//放入map集合,完整类名--->实例
ctx.put(className, obj);
}
}
/**
* 获取实例
* 从"类对象"先获取完整类名,再用类名从map获取实例
*
* @param c
* @param <V>
* @return
*/
public static <V> V getObject(Class<V> c) {
// "day16.UserDao" -----> UserDao实例
return (V) ctx.get(c.getName());
}
public static void main(String[] args) throws Exception {
autoScan();
// System.out.println(ctx);
UserDao dao = getObject(UserDao.class);
// System.out.println(dao);
dao.test();
}
}
6.dao对象
package com;
/**
*@Authro: QYF
*@Time:2020/10/21 0:21
*/
@Component
public class UserDao {
//通过注解,为driver变量自动注入数据
@Value("${spring.datasource.driver}")
private String driver;
@Value("${spring.datasource.username}")
private String username;
@Value("${spring.datasource.password}")
private String password;
@Value("${spring.datasource.url}")
private String url;
public void test() {
System.out.println("\n---UserDao注入的数据:--------------");
System.out.println(driver);
System.out.println(username);
System.out.println(password);
System.out.println(url);
}
}
7.模拟Controller调Service再调Dao
package com;
/**
* @Authro: QYF
* @Time:2020/10/21 0:36
*/
@Controller
public class UserController {
@Autowired
private UserService userService;
public void test() {
System.out.println("\n---UserController调用service----------------");
userService.test();
}
}
package com;
/**
* @Authro: QYF
* @Time:2020/10/21 0:36
*/
@Service
public class UserService {
//自动装配,自动注入UserDao的实例
@Autowired
private UserDao userDao;
public void test() {
System.out.println("\n---UserService调用dao-------------");
userDao.test();
}
}
最后测试下:
package com;
/**
* @Authro: QYF
* @Time:2020/10/21 1:42
*/
public class _Test2 {
public static void main(String[] args) throws Exception {
SpringContext.autoScan();
UserController c = SpringContext.getObject(UserController.class);
c.test();
}
}
输出结果:
---UserController调用service----------------
---UserService调用dao-------------
---UserDao注入的数据:--------------
com.mysql.jdbc.Driver
root
123456
jdbc:mysql://127.0.0.1:3306/xxoo?allowMultiQueries=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
这样就可以通过@Value注解把配置文件的信息注入给对象属性.
需要对象也直接从SpringContext获取和通过@Autowried注入