目录
手动开发--简单的 Spring 基于注解配置的程序
需求说明
思路分析+程序结构
2) 程序框架图
● 应用实例
创建ComponentScan.java注解
创建WyxSpringConfig
创建WyxSpringApplicationContext
作用
注意
获取全类名的步骤
Class.forName和Class.loadClass的区别
手动开发--简单的 Spring 基于注解配置的程序
需求说明
自己写一个简单的Spring 容器 , 通过读取类的解(@Component @Controller @Service @Reponsitory),将对象注入到 IOC 容器
也就是说,不使用 Spring 原生框架,我们自己使用 IO+Annotaion+反射+集合 技术实现
思路分析+程序结构
1) 我们使用注解方式完成, 这里不用 xml 来配置
2) 程序框架图
● 应用实例
1. 手动实现注解的方式来配置 Controller / Service / Respository / Component
2. 我们使用自定义注解来完成.
创建ComponentScan.java注解
作用
可以指定要扫描包
类似于component--scan 扫描
1. @Target(ElementType.TYPE)指定我们的ComponentScan注解可以修饰 Type程序元素
2. @Retention(RetentionPolicy.RUNTIME) 指定ComponentScan注解 保留范围
3. String value() default ""; 表示ComponentScan 可以传入 value
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ComponentScan {
String value() default "";
}
创建WyxSpringConfig
这是一个配置类, 作用类似我们原生spring的 beans.xml 容器配置文件
@ComponentScan(value = "com.spring.component")
public class WyxSpringConfig {
}
创建WyxSpringApplicationContext
作用
Spring容器
通过WyxSpringConfig 进行初始化
在这个容器中会创建ComponentScan注解指定的包并生成对象
public class WyxSpringApplicationContext {
private Class configClass;
//ioc我存放的就是通过反射创建的对象(基于注解方式)
private final ConcurrentHashMap<String, Object> ioc =
new ConcurrentHashMap<>();
//构造器
public WyxSpringApplicationContext(Class configClass) {
this.configClass = configClass;
//System.out.println("this.configClass=" + this.configClass);
//获取要扫描的包
//1. 先得到WyxpringConfig配置的的@ComponentScan(value = "com.spring.component")
ComponentScan componentScan =
(ComponentScan) this.configClass.getDeclaredAnnotation(ComponentScan.class);
//2. 通过componentScan的value=> 即要扫描的包
String path = componentScan.value();
System.out.println("要扫描的包= " + path);
//得到要扫描的包下的所有资源(类 .class)
//1.得到类的加载器
ClassLoader classLoader =
WyxApplicationContext.class.getClassLoader();
//2. 通过类的加载器获取到要扫描的包的资源 url=》类似一个路径
path = path.replace(".", "/");//一定要把. 替换成 /
URL resource =
classLoader.getResource(path);
System.out.println("resource=" + resource);
//3. 将要加载的资源(.class) 路径下的文件进行遍历=>io
File file = new File(resource.getFile());
if (file.isDirectory()) {
File[] files = file.listFiles();
for (File f : files) {
System.out.println("=====================");
System.out.println("=" + f.getAbsolutePath());
//D:\xxx_spring\spring\out\production\spring\com\spring\component\UserService.class
//获取到 com.spring.component.UserService
String fileAbsolutePath = f.getAbsolutePath();
//这里我们只处理.class文件
if (fileAbsolutePath.endsWith(".class")) {
//1. 获取到类名
String className =
fileAbsolutePath.substring(fileAbsolutePath.lastIndexOf("\\") + 1, fileAbsolutePath.indexOf(".class"));
//System.out.println("className=" + className);
//2. 获取类的完整的路径(全类名)
//解读 path.replace("/",".") => com.spring.component.
String classFullName = path.replace("/", ".") + "." + className;
//System.out.println("classFullName=" + classFullName);
//3. 判断该类是不是需要注入容器, 就看该类是不是有注解 @Component @Service..
try {
//这时,我们就得到该类的Class对象
//Class clazz = Class.forName(classFullName)
//说一下
//1. Class clazz = Class.forName(classFullName) 可以反射加载类
//2. classLoader.loadClass(classFullName); 可以反射类的Class
//3. 区别是 : 上面方式后调用来类的静态方法, 下面方法不会
//4. aClass.isAnnotationPresent(Component.class) 判断该类是否有 @Component
Class<?> aClass = classLoader.loadClass(classFullName);
if (aClass.isAnnotationPresent(Component.class) ||
aClass.isAnnotationPresent(Controller.class) ||
aClass.isAnnotationPresent(Service.class) ||
aClass.isAnnotationPresent(Repository.class)) {
//这里演示一个Component注解指定value,分配id
//就是演示了一下机制.
if(aClass.isAnnotationPresent(Component.class)) {
//获取到该注解
Component component = aClass.getDeclaredAnnotation(Component.class);
String id = component.value();
if(!"".endsWith(id)) {
className = id;//替换
}
}
//这时就可以反射对象,并放入到容器中
Class<?> clazz = Class.forName(classFullName);
Object instance = clazz.newInstance();
//放入到容器中, 将类名的首字母小写作为id
//StringUtils
ioc.put(StringUtils.uncapitalize(className) , instance);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
//编写方法返回对容器中对象
public Object getBean(String name) {
return ioc.get(name);
}
}
注意
获取全类名的步骤
1. 获取到类名
StringclassName=
fileAbsolutePath.substring
(fileAbsolutePath.lastIndexOf("\\")+1.fileAbsolutePath.indexOf(".class"));
2. 获取类的完整的路径(全类名)
解读 path.replace("/",".") => com.spring.component.
String classFullName = path.replace("/", ".") + "." + className;
3. 判断该类是不是需要注入容器, 就看该类是不是有注解 @Component @Service..
Class.forName和Class.loadClass的区别
1. Class clazz = Class.forName(classFullName) 可以反射加载类
2. classLoader.loadClass(classFullName); 可以反射类的Class
3. 区别是 : 上面方式后会调用 类的静态方法, 下面方法不会调用