主要是为了学习Java反射的知识,以及对操作系统的一些概念进行回顾,搭建了一个小型的操作系统,包括基本的一些命令,如:clear、help、cd、mkdir、ls等;同时支持用户及角色创建,密码登录等,相关文件采用加密存储在本地;同时采用资源分配的形式设计了磁盘资源和内存资源,采用首次适应、循环首次适应、最佳适应、最坏适应等策略;采用Java的Socket通信设计了简单的socket通信模式
采用配置文件的形式指定命令的名称、描述与对应类的全路径,采用反射的形式创建对象并装载到容器中;策略模式结合HashMap的形式,可以较为方便的获取到命令对象,并执行对应的策略方法。
策略模式的体现,采用Map进行命令策略的管理
package os.strategy;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* @author bbyh
* @date 2023-07-27 21:19
* @description
*/
public abstract class BaseStrategy {
private static final Map<String, BaseStrategy> STRATEGY_FACTORY = new HashMap<>(4);
private static final Map<String, String> STRATEGY_DOC = new HashMap<>(4);
private final String name;
private final String doc;
public BaseStrategy(String name, String doc) {
this.name = name;
this.doc = doc;
}
protected final void register() {
STRATEGY_FACTORY.put(name, this);
STRATEGY_DOC.put(name, doc);
}
public abstract void execute();
public static BaseStrategy getStrategy(String name) {
if (!STRATEGY_FACTORY.containsKey(name)) {
throw new UnsupportedOperationException("该策略还不支持");
}
return STRATEGY_FACTORY.get(name);
}
public static Set<String> getCommandNameSet() {
return new HashSet<>(STRATEGY_FACTORY.keySet());
}
public static Map<String, String> getCommandDoc() {
return new HashMap<>(STRATEGY_DOC);
}
}
采用配置文件结合反射装载对象;分为只装载指定目录,或装载指定目录及其子目录,要求都是基础策略类的子类
package os.util;
import os.strategy.BaseStrategy;
import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashSet;
import java.util.Properties;
/**
* @author bbyh
* @date 2023/7/28 12:19
*/
public class OsApplication {
private static final String PACKAGE_PREFIX = "src/";
private static final String DEFAULT_STRATEGY_PACKAGE = "strategy.impl";
private static final String OS_COMMAND_PROPERTIES = "os-command-config.properties";
private static final HashSet<Class<?>> CLASS_SET = new HashSet<>(4);
private static void load() {
for (Class<?> instance : CLASS_SET) {
if (instance.getSuperclass() == BaseStrategy.class) {
try (InputStream inputStream = Files.newInputStream(Paths.get(OS_COMMAND_PROPERTIES))) {
Properties properties = new Properties();
properties.load(inputStream);
if (properties.containsKey(instance.getName())) {
String[] split = properties.getProperty(instance.getName()).split(",");
try {
Constructor<?> constructor = instance.getConstructor(String.class, String.class);
Object newInstance = constructor.newInstance(split[0], split[1]);
Method register = newInstance.getClass().getSuperclass().getDeclaredMethod("register");
register.setAccessible(true);
register.invoke(newInstance);
} catch (NoSuchMethodException | InvocationTargetException |
IllegalAccessException | InstantiationException e) {
throw new RuntimeException(e);
}
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
public static void runWithSpecifiedDirectory(Class<?> application) {
initSpecifiedDirectory(application.getPackage().getName() + "." + DEFAULT_STRATEGY_PACKAGE);
load();
}
private static void initSpecifiedDirectory(String scanPackage) {
File basePackage = new File(PACKAGE_PREFIX + scanPackage.replace(".", "/"));
File[] files = basePackage.listFiles(file -> file.getName().endsWith(".java"));
loadClasses(scanPackage, files);
}
private static void loadClasses(String scanPackage, File[] files) {
if (files != null) {
for (File file : files) {
try {
Class<?> instance = Class.forName(scanPackage + "." + file.getName().replace(".java", ""));
CLASS_SET.add(instance);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
}
}
public static void runWithRecursiveDirectory(Class<?> application) {
initRecursiveDirectory(application.getPackage().getName());
load();
}
private static void initRecursiveDirectory(String scanPackage) {
File basePackage = new File(PACKAGE_PREFIX + scanPackage.replace(".", "/"));
File[] files = basePackage.listFiles(file -> {
if (file.isDirectory()) {
initRecursiveDirectory(scanPackage + "." + file.getName());
}
return file.getName().endsWith(".java");
});
loadClasses(scanPackage, files);
}
}
配置文件
os.strategy.impl.ClearStrategy=clear,清屏命令,清空屏幕
os.strategy.impl.HelpStrategy=help,帮助命令,查看当前系统支持的命令
采用类似SpringBoot的启动方式,自动装载指定目录下的命令类,采用注解的形式设置命令的名称与描述
采用注解设置启动装载包目录名,以及各个策略类的目录名称与相关说明
package os.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author bbyh
* @date 2023/7/2 15:29
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface StrategyScan {
String value() default "strategy.impl";
}
help命令,简单输出系统支持的命令和说明
package os.strategy.impl;
import os.annotation.StrategyAnnotation;
import os.annotation.StrategyDocAnnotation;
import os.strategy.BaseStrategy;
import java.util.Map;
import java.util.Set;
/**
* @author bbyh
* @date 2023-07-27 21:26
* @description
*/
@StrategyAnnotation(commandName = "help")
@StrategyDocAnnotation(commandDoc = "帮助命令,查看当前系统支持的命令")
public class HelpStrategy extends BaseStrategy {
public HelpStrategy(String name, String doc) {
super(name, doc);
}
@Override
public void execute() {
System.out.println("系统支持的命令");
Set<String> commandNameSet = BaseStrategy.getCommandNameSet();
Map<String, String> commandDoc = BaseStrategy.getCommandDoc();
for (String commandName : commandNameSet) {
System.out.println(commandName + ":" + commandDoc.get(commandName));
}
}
}
同样加载指定目录下携带注解的策略子类,或者加载目录及其子目录的所有符合条件的类
package os.util;
import os.annotation.StrategyAnnotation;
import os.annotation.StrategyDocAnnotation;
import os.annotation.StrategyScan;
import os.strategy.BaseStrategy;
import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashSet;
/**
* @author bbyh
* @date 2023/7/28 12:19
*/
public class OsApplication {
private static final String PACKAGE_PREFIX = "src/";
private static final HashSet<Class<?>> CLASS_SET = new HashSet<>(4);
private static void load() {
for (Class<?> instance : CLASS_SET) {
if (instance.getSuperclass() == BaseStrategy.class) {
try {
StrategyAnnotation nameAnnotation = instance.getAnnotation(StrategyAnnotation.class);
StrategyDocAnnotation docAnnotation = instance.getAnnotation(StrategyDocAnnotation.class);
if (nameAnnotation != null && docAnnotation != null) {
Constructor<?> constructor = instance.getConstructor(String.class, String.class);
Object newInstance = constructor.newInstance(nameAnnotation.commandName(), docAnnotation.commandDoc());
Method register = newInstance.getClass().getSuperclass().getDeclaredMethod("register");
register.setAccessible(true);
register.invoke(newInstance);
}
} catch (NoSuchMethodException | InvocationTargetException |
IllegalAccessException | InstantiationException e) {
throw new RuntimeException(e);
}
}
}
}
/**
* 加载配置目录下的添加了StrategyAnnotation注解的类
*
* @param application 启动类
*/
public static void runWithSpecifiedDirectory(Class<?> application) {
StrategyScan strategyScan = application.getAnnotation(StrategyScan.class);
initSpecifiedDirectory(application.getPackage().getName() + "." + strategyScan.value());
load();
}
private static void initSpecifiedDirectory(String scanPackage) {
File basePackage = new File(PACKAGE_PREFIX + scanPackage.replace(".", "/"));
File[] files = basePackage.listFiles(file -> file.getName().endsWith(".java"));
loadClasses(scanPackage, files);
}
private static void loadClasses(String scanPackage, File[] files) {
if (files != null) {
for (File file : files) {
try {
Class<?> instance = Class.forName(scanPackage + "." + file.getName().replace(".java", ""));
CLASS_SET.add(instance);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
}
}
/**
* 加载配置目录及其子目录下的添加了StrategyAnnotation注解的类
*
* @param application 启动类
*/
public static void runWithRecursiveDirectory(Class<?> application) {
initRecursiveDirectory(application.getPackage().getName());
load();
}
private static void initRecursiveDirectory(String scanPackage) {
File basePackage = new File(PACKAGE_PREFIX + scanPackage.replace(".", "/"));
File[] files = basePackage.listFiles(file -> {
if (file.isDirectory()) {
initRecursiveDirectory(scanPackage + "." + file.getName());
}
return file.getName().endsWith(".java");
});
loadClasses(scanPackage, files);
}
}
经过测试发现,上面两个系统在初始化加载类时有一些小问题,因为在实际使用时都是执行class文件,而在IDEA里面的执行是以Java文件为路径的,需要进行一些简单的小修改,当前这个阶段主要以IDEA里面的运行为主,后面再对路径和初始化逻辑进行一些调整,来保证兼容性
主窗体
package os.util;
import os.strategy.BaseStrategy;
import javax.swing.*;
import java.awt.*;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.event.*;
import java.io.IOException;
/**
* @author bbyh
* @date 2023-07-29 15:51
* @description
*/
public class MainFrame extends JFrame {
public static final JLabel DISPLAY = new JLabel();
private static final String DEFAULT_WORD_DIR = "C:\\root>";
public MainFrame() {
setTitle("BBYH OS System");
setSize(1000, 600);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLocationRelativeTo(null);
JScrollPane scrollPane = new JScrollPane(DISPLAY);
DISPLAY.setOpaque(true);
DISPLAY.setBackground(Color.WHITE);
setLayout(new GridLayout(1, 1));
add(scrollPane);
setVisible(true);
Font font = new Font("Consolos", Font.PLAIN, 24);
DISPLAY.setFont(font);
DISPLAY.setVerticalAlignment(SwingConstants.TOP);
DISPLAY.setFocusable(true);
init();
// 右键菜单
JPopupMenu rightMenu = new JPopupMenu();
JMenuItem backgroundItem = new JMenuItem();
JMenuItem fontItem = new JMenuItem();
JMenuItem copyItem = new JMenuItem();
backgroundItem.setFont(font);
fontItem.setFont(font);
copyItem.setFont(font);
backgroundItem.setText("设置背景颜色");
fontItem.setText("设置字体颜色");
copyItem.setText("复制剪切版内容");
rightMenu.add(backgroundItem);
rightMenu.add(fontItem);
rightMenu.add(copyItem);
backgroundItem.addActionListener(evt -> {
Color color = JColorChooser.showDialog(null, "设置背景颜色", Color.WHITE);
if (color != null) {
DISPLAY.setBackground(color);
}
});
fontItem.addActionListener(evt -> {
Color color = JColorChooser.showDialog(null, "设置字体颜色", Color.BLACK);
if (color != null) {
DISPLAY.setForeground(color);
}
});
copyItem.addActionListener(evt -> {
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
Transferable transferable = clipboard.getContents(null);
if (transferable != null) {
if (transferable.isDataFlavorSupported(DataFlavor.stringFlavor)) {
try {
String data = (String) clipboard.getData(DataFlavor.stringFlavor);
appendDisplay(data);
} catch (UnsupportedFlavorException | IOException e) {
throw new RuntimeException(e);
}
}
}
});
DISPLAY.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
char keyChar = e.getKeyChar();
if (keyChar == KeyEvent.VK_BACK_SPACE && !"".equals(getContent())) {
DISPLAY.setText(getContent().substring(0, getContent().length() - 1));
appendDisplay("");
} else if (keyChar == KeyEvent.VK_ENTER) {
appendDisplay("
");
try {
BaseStrategy.getStrategy(Context.getNextCommand()).execute();
} catch (Exception ex) {
appendDisplay(ex.getMessage() + "
");
}
appendDisplay(Context.getWorkDir());
} else if (Character.isLetterOrDigit(keyChar)) {
appendDisplay(String.valueOf(keyChar));
}
}
});
DISPLAY.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if (e.getButton() == MouseEvent.BUTTON3) {
rightMenu.show(DISPLAY, e.getX(), e.getY() - 10);
}
}
});
}
private static void init() {
Context.setWorkDir(DEFAULT_WORD_DIR);
appendDisplay(DEFAULT_WORD_DIR);
}
public static void appendDisplay(String content) {
DISPLAY.setText("" + getContent() + content + "");
}
private static String getContent() {
return DISPLAY.getText().replace("", "").replace("", "");
}
}
Context上下文操作类
package os.util;
/**
* @author bbyh
* @date 2023-07-27 22:23
* @description
*/
public class Context {
private static String workDir;
public static String getWorkDir() {
return workDir;
}
public static void setWorkDir(String workDir) {
Context.workDir = workDir;
}
public static String getNextCommand() {
String text = MainFrame.DISPLAY.getText();
return text.substring(text.lastIndexOf(getWorkDir()) + getWorkDir().length(), text.lastIndexOf("