目录
手动开发–简单的 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 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) 路径下的文件进行遍历=>ioFile 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.UserServiceString 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) 判断该类是否有 @ComponentClass clazz = Class.forName(classFullName);Object instance = clazz.newInstance();//放入到容器中, 将类名的首字母小写作为id//StringUtilsioc.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. 区别是 : 上面方式后会调用 类的静态方法, 下面方法不会调用