bean id= MyComponent bean 对象=
com.hj.spring.component.MyComponent@13221655bean id= UserDao bean 对象= com.hj.spring.component.UserDao@2f2c9b19
bean id= UserService bean 对象=
com.hj.spring.component.UserService@31befd9fbean id= UserController bean 对象=
com.hj.spring.component.UserController@1c20c684 i am user da
● 应用实例
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 解读
* 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 "";
}
/**
* @author hj
* @version 1.0
* 这是一个配置类, 作用类似我们原生spring的 beans.xml 容器配置文件
*/
@ComponentScan(value = "com.hj.springboot_06_ssmp.component")
public class HJSpringConfig {
}
import java.net.URL;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author hj
* SpringApplicationContext 类的作用类似Spring原生ioc容器
*/
public class SpringApplicationContext {
private Class configClass;
//ioc我存放的就是通过反射创建的对象(基于注解方式)
private final ConcurrentHashMap<String, Object> ioc = new ConcurrentHashMap<>();
public SpringApplicationContext(Class configClass) {
this.configClass = configClass;
//获取要扫描的包
//1. 先得到HJSpringConfig配置的的@ComponentScan(value = "com.hj.springboot_06_ssmp.component")
ComponentScan componentScan =(ComponentScan) this.configClass.getDeclaredAnnotation(ComponentScan.class);//注解
System.out.println("注解内容= " + componentScan);
//2. 通过componentScan的value=> 即要扫描的包
String path = componentScan.value();
System.out.println("要扫描的包= " + path);
}
public static void main(String[] args) {
SpringApplicationContext springApplicationContext = new SpringApplicationContext(HJSpringConfig.class);
}
}
第一步:获取要扫描的包
//1. 先得到HJSpringConfig配置的的@ComponentScan(value = "com.hj.springboot_06_ssmp.component")
ComponentScan componentScan =(ComponentScan) this.configClass.getDeclaredAnnotation(ComponentScan.class);//注解
System.out.println("注解内容= " + componentScan);
//2. 通过componentScan的value=> 即要扫描的包
String path = componentScan.value();
System.out.println("要扫描的包= " + path);
运行结果:
注解内容= @com.hj.springboot_06_ssmp.annotation.ComponentScan(value=com.hj.springboot_06_ssmp.component)
要扫描的包= com.hj.springboot_06_ssmp.component
第二步:得到要扫描的包下的所有资源(类.class)
//1.得到类的加载器
ClassLoader classLoader = SpringApplicationContext.class.getClassLoader();
//2. 通过类的加载器获取到要扫描的包的资源 url=》类似一个路径
path = path.replace(".", "/");//一定要把. 替换成 /
URL resource = classLoader.getResource(path);
System.out.println("resource=" + resource);
运行结果:
resource=file:/F:/hspJava/SpringBoot/springboot/springboot_06_ssmp/target/classes/com/hj/springboot_06_ssmp/component
//3. 将要加载的资源(.class) 路径下的文件进行遍历=>io
File file = new File(resource.getFile());//目录也是一个文件
System.out.println(file.isDirectory());
if(file.isDirectory()) {
File[] files = file.listFiles();
for (File f : files) {
System.out.println("=======================");
System.out.println(f.getAbsolutePath());
//F:\hspJava\SpringBoot\springboot\springboot_06_ssmp\target\classes\com\hj\springboot_06_ssmp\component\UserService.class
//获取到 com.hj.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.hspedu.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();
}
}
/**
* @author hj
* SpringApplicationContext 类的作用类似Spring原生ioc容器
*/
public class SpringApplicationContext {
private Class configClass;
//ioc我存放的就是通过反射创建的对象(基于注解方式)
private final ConcurrentHashMap<String, Object> ioc = new ConcurrentHashMap<>();
public SpringApplicationContext(Class configClass) {
this.configClass = configClass;
//获取要扫描的包
//1. 先得到HJSpringConfig配置的的@ComponentScan(value = "com.hj.springboot_06_ssmp.component")
ComponentScan componentScan =(ComponentScan) this.configClass.getDeclaredAnnotation(ComponentScan.class);//注解
System.out.println("注解内容= " + componentScan);
//2. 通过componentScan的value=> 即要扫描的包
String path = componentScan.value();
System.out.println("要扫描的包= " + path);
//得到要扫描的包下的所有资源(类.class)
//1.得到类的加载器
ClassLoader classLoader = SpringApplicationContext.class.getClassLoader();
//2. 通过类的加载器获取到要扫描的包的资源 url=》类似一个路径
path = path.replace(".", "/");//一定要把. 替换成 /
URL resource = classLoader.getResource(path);//得到全路径
System.out.println("resource=" + resource);
System.out.println(resource.getFile());
//3. 将要加载的资源(.class) 路径下的文件进行遍历=>io
File file = new File(resource.getFile());//目录也是一个文件
System.out.println(file.isDirectory());
if(file.isDirectory()) {
File[] files = file.listFiles();
for (File f : files) {
System.out.println("=======================");
System.out.println(f.getAbsolutePath());
//F:\hspJava\SpringBoot\springboot\springboot_06_ssmp\target\classes\com\hj\springboot_06_ssmp\component\UserService.class
//获取到 com.hj.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.hspedu.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);
}
public static void main(String[] args) {
SpringApplicationContext springApplicationContext = new SpringApplicationContext(HJSpringConfig.class);
}
}
最后如果你觉得这篇文章对你有帮助的话,请转发、收藏和点赞哦!谢谢支持!
纸上得来终觉浅,绝知此事要躬行。希望大家不要光看,还要多练,自己去实现比你想明白更加的有趣!