Appearance
定义四个注解
四个注解名称不同,其他的包括包名都相同
java
package org.simpleframework.core.annotation;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Component/Controller/Repository/Service {
}
创建Bean工作类实现Bean的加载
- 构造方法借助枚举实现单例,这样相比传统的饿汉式和懒汉式可以直接防止反射/序列化攻击,核心原理的枚举类不支持无参构造,反射拿不到构造出来的对象,攻击也就无从谈起。
loadBeans
:用于加载指定包下的所有被注解修饰的类,将其加入bean的map集合isLoaded()
:由于loadBeans()
方法可能会被多次调用,为防止重复调用设置一个变量,如果开始加载则标记已进行过bean的初始化。
java
package org.simpleframework.core;
@Slf4j
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class BeanContainer {
// 存放所有bean
private final Map<Class<?>, Object> beanMap = new ConcurrentHashMap<>();
// 加载bean的注解列表
private static final List<Class<? extends Annotation>> BEAN_ANNOTATION = Arrays.asList(Component.class, Controller.class, Service.class, Repository.class);
/**
* 加载Bean,synchronized防止被多线程同时执行
* @param packageName 包名
*/
public synchronized void loadBeans(String packageName) {
if (isLoaded()) {
log.warn("Bean容器已被加载,请勿重复加载");
return;
}
Set<Class<?>> classSet = ClassUtil.extractPackageClass(packageName);
if (ValidationUtil.isEmpty(classSet)) {
log.warn("该包下没有加载到任何类:{}", packageName);
return;
}
for (Class<?> clazz : classSet) {
for (Class<? extends Annotation> annotation : BEAN_ANNOTATION) {
// 如果类上有该注解,则加入容器beanMap集合
if (clazz.isAnnotationPresent(annotation)) {
beanMap.put(clazz, ClassUtil.newInstance(clazz, true));
}
}
}
loaded = true;
}
/**
* 容器是否已经加载过bean
*/
private boolean loaded = false;
/**
* 是否已加载过Bean
* @return 是否已加载
*/
public boolean isLoaded() {
return loaded;
}
private enum ContainerHolder {
HOLDER;
private final BeanContainer instance;
ContainerHolder() {
instance = new BeanContainer();
}
}
public static BeanContainer getInstance() {
return ContainerHolder.HOLDER.instance;
}
/**
* 获取bean的数量
*/
public int size() {
return beanMap.size();
}
}
测试
自行创建测试类,给类加上注解,输出加上注解类的数量,数量一致代表测试成功;
java
public class BeanContainerTest {
@Test
public void loadBeansTest() {
BeanContainer.getInstance().loadBeans("com.xk857");
System.out.println(BeanContainer.getInstance().size());
Assertions.assertEquals(6, BeanContainer.getInstance().size());
}
}
实现对容器的操作
上文实现了对容器的初始化加载,加下来将完善BeanContainer
类实现对容器的操作,即增加和删除等操作,这里重点介绍两个方法:
getClassesByAnnotation( annotation )
:查找集合内使用例如@Controller
修饰的类getClassesBySuper( Class )
:通过接口或者父类获取实现类或者子类的Class集合,不包括其本身class1.isAssignableFrom(class2)
:判断class1是否是class2的父类或父接口
java
@Slf4j
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class BeanContainer {
// 存放所有bean
private final Map<Class<?>, Object> beanMap = new ConcurrentHashMap<>();
/**
* 添加一个class对象及其Bean实例
* @param clazz Class对象
* @param bean Bean实例
* @return 原有的Bean实例, 没有则返回null
*/
public Object addBean(Class<?> clazz, Object bean) {
return beanMap.put(clazz, bean);
}
/**
* 移除一个IOC容器管理的对象
* @param clazz Class对象
* @return 删除的Bean实例, 没有则返回null
*/
public Object removeBean(Class<?> clazz) {
return beanMap.remove(clazz);
}
/**
* 根据Class对象获取Bean实例
* @param clazz Class对象
* @return Bean实例
*/
public Object getBean(Class<?> clazz) {
return beanMap.get(clazz);
}
/**
* 获取容器管理的所有Class对象的集合,即返回bean的所有key
*/
public Set<Class<?>> getClasses(){
return beanMap.keySet();
}
/**
* 获取所有Bean集合,即获取所有bean的value
*/
public Set<Object> getBeans(){
return new HashSet<>(beanMap.values());
}
/**
* 筛选出指定注解Bean的Class集合
* @param annotation 注解
* @return Class集合
*/
public Set<Class<?>> getClassesByAnnotation(Class<? extends Annotation> annotation) {
// 1.获取beanMap的所有class对象
Set<Class<?>> keySet = getClasses();
if (ValidationUtil.isEmpty(keySet)) {
log.warn("bean为空");
return null;
}
// 2.通过注解筛选被注解标记的class对象,并添加到classSet里
Set<Class<?>> classSet = new HashSet<>();
for (Class<?> clazz : keySet) {
// 类是否有相关的注解标记
if (clazz.isAnnotationPresent(annotation)) {
classSet.add(clazz);
}
}
return classSet.isEmpty() ? null : classSet;
}
/**
* 通过接口或者父类获取实现类或者子类的Class集合,不包括其本身
* @param interfaceOrClass 接口Class或者父类Class
* @return Class集合
*/
public Set<Class<?>> getClassesBySuper(Class<?> interfaceOrClass) {
// 1.获取beanMap的所有class对象
Set<Class<?>> keySet = getClasses();
if (ValidationUtil.isEmpty(keySet)) {
log.warn("bean为空");
return null;
}
// 2.判断keySet里的元素是否是传入的接口或者类的子类,如果是,就将其添加到classSet里
Set<Class<?>> classSet = new HashSet<>();
for (Class<?> clazz : keySet) {
// 判断keySet里的元素是否是传入的接口或者类的子类
if (interfaceOrClass.isAssignableFrom(clazz) && !clazz.equals(interfaceOrClass)) {
classSet.add(clazz);
}
}
return classSet.isEmpty() ? null : classSet;
}
}
点击展开完整测试代码
java
public class BeanContainerTest {
@Test
public void loadBeansTest() {
BeanContainer.getInstance().loadBeans("com.xk857");
System.out.println(BeanContainer.getInstance().size());
Assertions.assertEquals(6, BeanContainer.getInstance().size());
}
@Test
@DisplayName("根据类获取实例")
public void getBeanTest() {
BeanContainer.getInstance().loadBeans("com.xk857");
Assertions.assertNotNull(BeanContainer.getInstance().getBean(MainPageController.class));
Assertions.assertNull(BeanContainer.getInstance().getBean(HeadLine.class));
}
@Test
@DisplayName("根据注解获取对应实例")
public void getClassesByAnnotationTest() {
BeanContainer.getInstance().loadBeans("com.xk857");
Set<Class<?>> classesBySuper = BeanContainer.getInstance().getClassesByAnnotation(Controller.class);
System.out.println(classesBySuper.size());
}
@Test
@DisplayName("根据接口获取实现类")
public void getClassesBySuperTest() {
BeanContainer.getInstance().loadBeans("com.xk857");
Set<Class<?>> classesBySuper = BeanContainer.getInstance().getClassesBySuper(HeadLineService.class);
for (Class<?> aClass : classesBySuper) {
System.out.println(aClass.getName());
}
}
}