- 1. 框架具备的基本功能
- 2. IOC 容器的实现
- 1. 自定义注解
- 1. 自定义注解 @Controller
- 2. 自定义注解 @Service
- 3. 自定义注解 @Repository
- 4. 自定义注解 @Component
- 2. ClassUtil 工具类封装
- 1. 获取类加载器
- 2. 获取扫描包下的类集合
- 3. 实例化class
- 4. 设置类的属性值
- 3. 单例模式
- 1. 恶汉模式
- 2. 懒汉模式
- 3. 枚举方式实现单例
- 4. 容器 BeanContainer
- 1. 获取Bean容器实例
- 2. 扫描加载所有Bean
- 3. 容器的增删改查操作
- 3. 容器的依赖注入
- 1. 自定义注解 @Autowired
- 2. 实现依赖注入
- 3. 测试
- 解析配置
- 定位和注册对象
- 注入对象
- 提供通用的工具类
- 创建注解
- 提取标记对象
- 实现容器
- 依赖注入
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Controller {
}
2. 自定义注解 @Service
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Service {
}
3. 自定义注解 @Repository
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Repository {
}
4. 自定义注解 @Component
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Component {
}
2. ClassUtil 工具类封装
1. 获取类加载器
@Slf4j
public class ClassUtil {
public static ClassLoader getClassLoader(){
return Thread.currentThread().getContextClassLoader();
}
}
2. 获取扫描包下的类集合
@Slf4j
public class ClassUtil {
public static final String FILE_PROTOCOL = "file";
public static Set> extractPackageClass(String packageName){
//1.获取到类的加载器。
ClassLoader classLoader = getClassLoader();
//2.通过类加载器获取到加载的资源
URL url = classLoader.getResource(packageName.replace(".", "/"));
// url:file:/D:/source_study/spring_source/simpleframework-master/simpleframework/target/classes/com/imooc/entity
if (url == null){
log.warn("unable to retrieve anything from package: " + packageName);
return null;
}
//3.依据不同的资源类型,采用不同的方式获取资源的集合
Set> classSet = null;
//过滤出文件类型的资源
if (url.getProtocol().equalsIgnoreCase(FILE_PROTOCOL)){
classSet = new HashSet>();
// /D:/source_study/spring_source/simpleframework-master/simpleframework/target/classes/com/imooc/entity
File packageDirectory = new File(url.getPath());
extractClassFile(classSet, packageDirectory, packageName);
}
return classSet;
}
private static void extractClassFile(Set> emptyClassSet, File fileSource, String packageName) {
// 如果不是目录(是文件),停止递归
if(!fileSource.isDirectory()){
return;
}
// 如果是一个文件夹,则调用其listFiles方法获取文件夹下的文件或文件夹
File[] files = fileSource.listFiles(new FileFilter() {
@Override
public boolean accept(File file) {
if(file.isDirectory()){
return true;
} else{
//获取文件的绝对值路径
String absoluteFilePath = file.getAbsolutePath();
if(absoluteFilePath.endsWith(".class")){
//若是class文件,则直接加载
addToClassSet(absoluteFilePath);
}
}
return false;
}
//根据class文件的绝对值路径,获取并生成class对象,并放入classSet中
private void addToClassSet(String absoluteFilePath) {
//1.从class文件的绝对值路径里提取出包含了package的类名
// D:source_studyspring_sourcesimpleframework-mastersimpleframeworktargetclassescomimoocentityboHeadLine.class
// D:.source_study.spring_source.simpleframework-master.simpleframework.target.classes.com.imooc.entity.bo.ShopCategory.class
absoluteFilePath = absoluteFilePath.replace(File.separator, ".");
// com.imooc.entity.bo.HeadLine.class
String className = absoluteFilePath.substring(absoluteFilePath.indexOf(packageName));
// com.imooc.entity.bo.ShopCategory
className = className.substring(0, className.lastIndexOf("."));
//2.通过反射机制获取对应的Class对象并加入到classSet里
Class targetClass = loadClass(className);
emptyClassSet.add(targetClass);
}
});
if(files != null){
for(File f : files){
//递归调用
extractClassFile(emptyClassSet, f, packageName);
}
}
}
public static Class> loadClass(String className){
try {
return Class.forName(className);
} catch (ClassNotFoundException e) {
log.error("load class error:", e);
throw new RuntimeException(e);
}
}
}
测试:
public class ClassUtilTest {
@DisplayName("提取目标类方法:extractPackageClassTest")
@Test
public void extractPackageClassTest(){
Set> classSet = ClassUtil.extractPackageClass("com.imooc.entity");
System.out.println(classSet);
Assertions.assertEquals(4, classSet.size());
}
}
输出:
3. 实例化class[class com.imooc.entity.dto.MainPageInfoDTO, class com.imooc.entity.bo.ShopCategory, class com.imooc.entity.dto.Result, class com.imooc.entity.bo.HeadLine]
public static4. 设置类的属性值T newInstance(Class> clazz, boolean accessible){ try { Constructor constructor = clazz.getDeclaredConstructor(); constructor.setAccessible(accessible); return (T)constructor.newInstance(); } catch (Exception e) { log.error("newInstance error", e); throw new RuntimeException(e); } }
public static void setField(Field field, Object target, Object value, boolean accessible){
field.setAccessible(accessible);
try {
field.set(target, value);
} catch (IllegalAccessException e) {
log.error("setField error", e);
throw new RuntimeException(e);
}
}
3. 单例模式
1. 恶汉模式
public class StarvingSingleton {
private static final StarvingSingleton starvingSingleton = new StarvingSingleton();
private StarvingSingleton(){ }
public static StarvingSingleton getInstance(){
return starvingSingleton;
}
}
2. 懒汉模式
public class LazyDoubleCheckSingleton {
private volatile static LazyDoubleCheckSingleton instance;
private LazyDoubleCheckSingleton(){}
public static LazyDoubleCheckSingleton getInstance(){
//第一次检测
if (instance==null){
//同步
synchronized (LazyDoubleCheckSingleton.class){
if (instance == null){
//memory = allocate(); //1.分配对象内存空间
//instance(memory); //2.初始化对象
//instance = memory; //3.设置instance指向刚分配的内存地址,此时instance!=null
instance = new LazyDoubleCheckSingleton();
}
}
}
return instance;
}
public static void main(String[] args) {
System.out.println(LazyDoubleCheckSingleton.getInstance());
System.out.println(LazyDoubleCheckSingleton.getInstance());
}
}
3. 枚举方式实现单例
public class EnumStarvingSingleton {
private EnumStarvingSingleton(){}
public static EnumStarvingSingleton getInstance(){
return ContainerHolder.HOLDER.instance;
}
private enum ContainerHolder{
HOLDER;
private EnumStarvingSingleton instance;
ContainerHolder(){
instance = new EnumStarvingSingleton();
}
}
}
4. 容器 BeanContainer
1. 获取Bean容器实例
@Slf4j
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class BeanContainer {
public static BeanContainer getInstance() {
return ContainerHolder.HOLDER.instance;
}
private enum ContainerHolder {
// 容器
HOLDER;
private BeanContainer instance;
ContainerHolder() {
instance = new BeanContainer();
}
}
}
2. 扫描加载所有Bean
@Slf4j
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class BeanContainer {
private final Map, Object> beanMap = new ConcurrentHashMap<>();
public static BeanContainer getInstance() {
return ContainerHolder.HOLDER.instance;
}
private enum ContainerHolder {
// 容器
HOLDER;
private BeanContainer instance;
ContainerHolder() {
instance = new BeanContainer();
}
}
public int size() {
return beanMap.size();
}
private boolean loaded = false;
public boolean isLoaded() {
return loaded;
}
private static final List> BEAN_ANNOTATION
= Arrays.asList(Component.class, Controller.class, Service.class, Repository.class, Aspect.class);
public synchronized void loadBeans(String packageName) {
//判断bean容器是否被加载过
if (isLoaded()) {
log.warn("BeanContainer has been loaded.");
return;
}
Set> classSet = ClassUtil.extractPackageClass(packageName);
if (ValidationUtil.isEmpty(classSet)) {
log.warn("extract nothing from packageName" + packageName);
return;
}
for (Class> clazz : classSet) {
for (Class extends Annotation> annotation : BEAN_ANNOTATION) {
//如果类上面标记了定义的注解
if (clazz.isAnnotationPresent(annotation)) {
//将目标类本身作为键,目标类的实例作为值,放入到beanMap中
beanMap.put(clazz, ClassUtil.newInstance(clazz, true));
}
}
}
loaded = true;
}
}
测试:
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class BeanContainerTest {
private static BeanContainer beanContainer;
@BeforeAll
static void init(){
beanContainer = BeanContainer.getInstance();
}
@DisplayName("加载目标类及其实例到BeanContainer:loadBeansTest")
@Order(1)
@Test
public void loadBeansTest(){
Assertions.assertEquals(false, beanContainer.isLoaded());
beanContainer.loadBeans("com.imooc");
Assertions.assertEquals(6, beanContainer.size());
Assertions.assertEquals(true, beanContainer.isLoaded());
}
}
3. 容器的增删改查操作
@Slf4j
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class BeanContainer {
private final Map, Object> beanMap = new ConcurrentHashMap<>();
public static BeanContainer getInstance() {
return ContainerHolder.HOLDER.instance;
}
private enum ContainerHolder {
// 容器
HOLDER;
private BeanContainer instance;
ContainerHolder() {
instance = new BeanContainer();
}
}
public int size() {
return beanMap.size();
}
private boolean loaded = false;
public boolean isLoaded() {
return loaded;
}
private static final List> BEAN_ANNOTATION
= Arrays.asList(Component.class, Controller.class, Service.class, Repository.class, Aspect.class);
public synchronized void loadBeans(String packageName) {
//判断bean容器是否被加载过
if (isLoaded()) {
log.warn("BeanContainer has been loaded.");
return;
}
Set> classSet = ClassUtil.extractPackageClass(packageName);
if (ValidationUtil.isEmpty(classSet)) {
log.warn("extract nothing from packageName" + packageName);
return;
}
for (Class> clazz : classSet) {
for (Class extends Annotation> annotation : BEAN_ANNOTATION) {
//如果类上面标记了定义的注解
if (clazz.isAnnotationPresent(annotation)) {
//将目标类本身作为键,目标类的实例作为值,放入到beanMap中
beanMap.put(clazz, ClassUtil.newInstance(clazz, true));
}
}
}
loaded = true;
}
public Object addBean(Class> clazz, Object bean) {
return beanMap.put(clazz, bean);
}
public Object removeBean(Class> clazz) {
return beanMap.remove(clazz);
}
public Object getBean(Class> clazz) {
return beanMap.get(clazz);
}
public Set> getClasses(){
return beanMap.keySet();
}
public Set
测试:
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class BeanContainerTest {
private static BeanContainer beanContainer;
@BeforeAll
static void init(){
beanContainer = BeanContainer.getInstance();
}
@DisplayName("根据类获取其实例:getBeanTest")
@Order(2)
@Test
public void getBeanTest(){
MainPageController controller = (MainPageController)beanContainer.getBean(MainPageController.class);
Assertions.assertEquals(true, controller instanceof MainPageController);
DispatcherServlet dispatcherServlet = (DispatcherServlet) beanContainer.getBean(DispatcherServlet.class);
Assertions.assertEquals(null, dispatcherServlet);
}
@DisplayName("根据注解获取对应的实例:getClassesByAnnotationTest")
@Order(3)
@Test
public void getClassesByAnnotationTest(){
Assertions.assertEquals(true, beanContainer.isLoaded());
Assertions.assertEquals(3, beanContainer.getClassesByAnnotation(Controller.class).size());
}
@DisplayName("根据接口获取实现类:getClassesBySuperTest")
@Order(4)
@Test
public void getClassesBySuperTest(){
Assertions.assertEquals(true, beanContainer.isLoaded());
Assertions.assertEquals(true, beanContainer.getClassesBySuper(HeadLineService.class).contains(HeadLineServiceImpl.class));
}
}
3. 容器的依赖注入
-
定义依赖注入的注解标签
-
实现被创建注解标记的成员变量实例,并将其注入到成员变量里
-
依赖注入的使用
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Autowired {
String value() default "";
}
2. 实现依赖注入
@Slf4j
public class DependencyInjector {
private BeanContainer beanContainer;
public DependencyInjector(){
beanContainer = BeanContainer.getInstance();
}
public void doIoc(){
if(ValidationUtil.isEmpty(beanContainer.getClasses())){
log.warn("empty classset in BeanContainer");
return;
}
//1.遍历Bean容器中所有的Class对象
for(Class> clazz : beanContainer.getClasses()){
//2.遍历Class对象的所有成员变量
Field[] fields = clazz.getDeclaredFields();
if (ValidationUtil.isEmpty(fields)){
continue;
}
for(Field field : fields){
//3.找出被Autowired标记的成员变量
if(field.isAnnotationPresent(Autowired.class)){
Autowired autowired = field.getAnnotation(Autowired.class);
String autowiredValue = autowired.value();
//4.获取这些成员变量的类型
Class> fieldClass = field.getType();
//5.获取这些成员变量的类型在容器里对应的实例
Object fieldValue = getFieldInstance(fieldClass, autowiredValue);
if(fieldValue == null){
throw new RuntimeException("unable to inject relevant type,target fieldClass is:" + fieldClass.getName() + " autowiredValue is : " + autowiredValue);
} else {
//6.通过反射将对应的成员变量实例注入到成员变量所在类的实例里
Object targetBean = beanContainer.getBean(clazz);
ClassUtil.setField(field, targetBean, fieldValue, true);
}
}
}
}
}
private Object getFieldInstance(Class> fieldClass, String autowiredValue) {
// 如果容器中存在该类的实例,直接返回
Object fieldValue = beanContainer.getBean(fieldClass);
if (fieldValue != null){
return fieldValue;
} else {
// 获取该类的子类,并返回子类的实例
Class> implementedClass = getImplementedClass(fieldClass, autowiredValue);
if(implementedClass != null){
return beanContainer.getBean(implementedClass);
} else {
return null;
}
}
}
private Class> getImplementedClass(Class> fieldClass, String autowiredValue) {
Set> classSet = beanContainer.getClassesBySuper(fieldClass);
if(!ValidationUtil.isEmpty(classSet)){
// 如果@Autowired注解中没有指定名称
if(ValidationUtil.isEmpty(autowiredValue)){
if(classSet.size() == 1){
return classSet.iterator().next();
} else {
//如果多于两个实现类且用户未指定其中一个实现类,则抛出异常
throw new RuntimeException("multiple implemented classes for " + fieldClass.getName() + " please set @Autowired's value to pick one");
}
} else {
// 如果@Autowired注解中指定了属性名称,找到对应属性名称的类
for(Class> clazz : classSet){
if(autowiredValue.equals(clazz.getSimpleName())){
return clazz;
}
}
}
}
return null;
}
}
3. 测试
① Controller
@Controller
@Getter
@RequestMapping(value = "/main")
public class MainPageController {
@Autowired(value = "HeadLineShopCategoryCombineServiceImpl")
private HeadLineShopCategoryCombineService headLineShopCategoryCombineService;
}
② Service
public interface HeadLineShopCategoryCombineService {
Result getMainPageInfo();
}
③ ServiceImpl
@Service
public class HeadLineShopCategoryCombineServiceImpl implements HeadLineShopCategoryCombineService {
@Autowired
private HeadLineService headLineService;
@Autowired
private ShopCategoryService shopCategoryService;
}
④ 测试方法:
public class DependencyInjectorTest {
@DisplayName("依赖注入doIoc")
@Test
public void doIocTest(){
BeanContainer beanContainer = BeanContainer.getInstance();
beanContainer.loadBeans("com.imooc");
Assertions.assertEquals(true, beanContainer.isLoaded());
MainPageController mainPageController = (MainPageController)beanContainer.getBean(MainPageController.class);
Assertions.assertEquals(true, mainPageController instanceof MainPageController);
Assertions.assertEquals(null, mainPageController.getHeadLineShopCategoryCombineService());
new DependencyInjector().doIoc();
Assertions.assertNotEquals(null, mainPageController.getHeadLineShopCategoryCombineService());
Assertions.assertEquals(true, mainPageController.getHeadLineShopCategoryCombineService() instanceof HeadLineShopCategoryCombineServiceImpl);
Assertions.assertEquals(false, mainPageController.getHeadLineShopCategoryCombineService() instanceof HeadLineShopCategoryCombineServiceImpl2);
}
}



