介绍
Spring 的两大特性 IoC和AOP
IoC
在 Java 软件开发过程中,系统中的各个对象之间、各个模块之间、软件系统和硬件系统之间,或多或少都存在一定的耦合关系。
若一个系统的耦合度过高,那么就会造成难以维护的问题,但完全没有耦合的代码几乎无法完成任何工作,这是由于几乎所有的功能都需要代码之间相互协作、相互依赖才能完成。因此我们在程序设计时,所秉承的思想一般都是在不影响系统功能的前提下,最大限度的降低耦合度。
IoC 底层通过工厂模式、Java 的反射机制、XML 解析等技术,将代码的耦合度降低到最低限度,其主要步骤如下。
- 在配置文件(例如 Bean.xml)中,对各个对象以及它们之间的依赖关系进行配置;
- 我们可以把 IoC 容器当做一个工厂,这个工厂的产品就是 Spring Bean;
- 容器启动时会加载并解析这些配置文件,得到对象的基本信息以及它们之间的依赖关系;
- IoC 利用 Java 的反射机制,根据类名生成相应的对象(即 Spring Bean),并根据依赖关系将这个对象注入到依赖它的对象中。
由于对象的基本信息、对象之间的依赖关系都是在配置文件中定义的,并没有在代码中紧密耦合,因此即使对象发生改变,我们也只需要在配置文件中进行修改即可,而无须对 Java 代码进行修改,这就是 Spring IoC 实现解耦的原理。
根据上面的原理,来简单实现一个IoC容器
首先我们把思路理一下 :
-
首先需要两个注解(两个注解配合使用)
@MyBean:把对象交给容器管理
@MyAutowired:自动注入,把容器里的实例赋值给加了注解的属性
-
需要ApplicationContext来操作容器
-
需要一个HashMap来存储Bean对象
-
需要开启注解启用范围,包扫描
代码逻辑
- 第一步实例化一个操作容器,并且把要扫描的包当形参
new ApplicationContextImpl("com.zn.ioc");
- 第二步加载容器,扫描包下的所有文件,发现类上有@MyBean注解就把此类放到HashMap中(key:类的类型,value:类的实例化)。
- 第三步自动注入,逻辑 自动注入@MyAutowired和MyBean一起连用,判断用了MyBean的类中属性有没有加@MyAutowired注解,有的话就设置属性,根据属性名的类型去HashMap中找对用的实例赋值给此属性,保证单例。
具体实现
目录:
依赖:
<dependencies>
<!--spring context依赖-->
<!--当你引入Spring Context依赖之后,表示将Spring的基础依赖引入了-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>6.0.2</version>
</dependency>
<!--junit5测试-->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.3.1</version>
</dependency>
两个注解:@MyAutoWired,@MyBean
@Target(ElementType.FIELD) // 在属性上使用注解
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAutowired {
}
@Target(ElementType.TYPE) // 在类上使用注解
@Retention(RetentionPolicy.RUNTIME)
public @interface MyBean {
}
LoginController
@MyBean
public class LoginController {
@MyAutowired
private LoginServiceImpl loginServiceImpl;
public void login() {
System.out.println("LoginController..........login");
loginServiceImpl.login();
}
}
ApplicationContext
public interface ApplicationContext {
// 获取bean对象
public Object getBean(Class clazz);
}
LoginDao
@MyBean
public class LoginDao {
public void login() {
System.out.println("LoginDao..........login.......");
}
}
LoginService
public interface LoginService {
abstract void login();
}
LoginServiceImpl
@MyBean
public class LoginServiceImpl implements LoginService {
@MyAutowired
private LoginDao loginDao;
@Override
public void login() {
System.out.println("LoginServiceImpl......login.........");
loginDao.login();
}
}
ApplicationContextImpl (核心实现)
public class ApplicationContextImpl implements ApplicationContext{
// 使用hashMap来存储bean实列的容器
private HashMap beanFactory = new HashMap<Class,Object>();
// 根路径
private static String rootPath;
// 获取bean容器中的对象
@Override
public Object getBean(Class clazz) {
return beanFactory.get(clazz);
}
// 加载带@MyBean实例到容器中
public ApplicationContextImpl(String basePackage) {
// 逻辑 :首先扫描所有文件,发现类上有@MyBean注解就把此类放到beanFactory中
// 1、把包的路径中的 .转换成 /
String pathReplace = basePackage.replace(".", File.separator);
try {
// 获取class路径
Enumeration<URL> dirs = Thread.currentThread().getContextClassLoader().getResources(pathReplace);
while (dirs.hasMoreElements()){
URL url = dirs.nextElement();
String filePath = URLDecoder.decode(url.getFile(), "utf-8");
rootPath = filePath.substring(0, filePath.length()-pathReplace.length());
// /E:/workspace-java/learn/learn_spring/spirng-ioc/target/test-classes/
///E:/workspace-java/learn/learn_spring/spirng-ioc/target/classes/
// System.out.println(rootPath);
// 加载bean
loadBean(new File(rootPath));
}
} catch (IOException e) {
e.printStackTrace();
}
// 自动注入 逻辑 自动注入@MyAutowired和MyBean一起连用,判断用了MyBean的类中属性有没有加@MyAutowired注解的有的话就设置属性
// 遍历beanFactory
beanFactory.forEach((key,value)->{
// 使用反射 获取类中的属性 包括私有属性
Field[] fields = value.getClass().getDeclaredFields();
for (Field field : fields) {
// 判断属性上是否有@MyAutowired注解
if(field.getAnnotation(MyAutowired.class)!=null){
// 如果有此注解就把beanFactory中的对应的对象实例设置给此属性
// 设置此属性可更改
field.setAccessible(true);
try {
// 赋值
field.set(value,beanFactory.get(field.getType()));
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
});
}
// 加载Bean对象
private void loadBean(File file) {
// 如果是一个文件
if(file.isDirectory()){
File[] files = file.listFiles();
for (File childFile : files) {
// 如果是子文件还是一个目录 递归
if(childFile.isDirectory()) {
loadBean(childFile);
} else {
putBean(childFile);
}
}
}else {
putBean(file);
}
}
// 添加实例到容器中
private void putBean(File childFile) {
// 获取字节码的绝对路径
String absolutePath = childFile.getAbsolutePath();
// 把根路径删除 获得类的全路径
String classPath = absolutePath.substring(rootPath.length() -1);
// 判断后缀是否是 .class
if(classPath.endsWith(".class")){
// 替换成全类名
String replace = classPath.replace(".class", "");
String fullClassName = replace.replace(File.separator, ".");
System.out.println(fullClassName);
try {
// 获取反射
Class<?> clas = Class.forName(fullClassName);
// 判断此类是否有注解MyBean
if(clas.getAnnotation(MyBean.class)!=null){
// 实例化对象
Object instance = clas.newInstance();
// 判断此类有没有接口
if(clas.isInterface()){
// 有接口的
beanFactory.put(clas.getInterfaces()[0],
instance);
}else {
beanFactory.put(clas,instance);
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
ApplicationContextImplTest
@Test
public void myAutoWiredTest() {
ApplicationContextImpl applicationContext = new ApplicationContextImpl("com.zn.ioc");
LoginController loginController =(LoginController)applicationContext.getBean(LoginController.class);
loginController.login();
}
ApplicationContextImplTest
```java
@Test
public void myAutoWiredTest() {
ApplicationContextImpl applicationContext = new ApplicationContextImpl("com.zn.ioc");
LoginController loginController =(LoginController)applicationContext.getBean(LoginController.class);
loginController.login();
}