一、Spring
1.IOC概念(控制反转)
IOC,或控制反转,是一种设计原则,它将对象创建和依赖的管理从应用程序代码中移除,由容器来负责。这使得应用程序组件更加松耦合,便于维护和测试。
- IOC方式:对象的创建和管理由Spring容器来负责,依赖通过配置文件或注解来注入。
- 传统开发方式:对象在代码中直接创建和管理,依赖关系是硬编码的
2.IOC容器运行流程
2.1. 容器启动
Spring容器的启动通常从加载配置开始。配置可以是基于XML文件的配置、Java注解配置类,或者是基于Java代码的配置类。Spring会解析这些配置来构建Bean定义,并初始化容器。
- 基于XML配置的启动:
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
- 基于注解配置的启动:
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
2.2. 读取配置和扫描
容器启动后,Spring会根据配置读取Bean定义。它可以通过解析XML文件中的Bean定义、读取Java配置类中的@Bean
方法、扫描@Component
注解的类等方式来获取Bean的定义信息。
- XML配置文件:Spring会读取XML文件中的
<bean>
标签,以获取Bean的定义。 - 注解扫描:Spring通过类路径扫描,找到使用
@Component
、@Service
、@Repository
等注解标识的类,并将它们注册为Bean。 - Java配置类:Spring通过读取
@Configuration
类中的@Bean
方法,来定义和注册Bean。
2.3. Bean的定义注册
一旦Spring读取到Bean定义,便会将这些定义存储在一个内部的数据结构中(如BeanDefinition
对象),这些定义包括Bean的类名、作用域、构造参数、依赖关系等。
2.4. Bean的实例化
容器会在需要使用Bean时,根据Bean的定义实例化Bean。这一步并不涉及属性的注入,仅仅是创建Bean实例。Spring通过调用Bean的构造器(无参或有参)来创建Bean的对象。
2.5. 依赖注入(Dependency Injection)
在Bean实例化后,Spring会处理依赖注入。Spring会根据Bean的依赖配置(构造器注入或Setter注入),将其他Bean注入到当前Bean中。
- 构造器注入:Spring通过调用Bean的有参构造器,传入所需的依赖。
- Setter注入:Spring通过调用Bean的Setter方法,注入所需的依赖。
2.6. Bean的初始化
在完成依赖注入后,Spring会对Bean进行初始化操作。这包括调用Bean的@PostConstruct
注解方法(如果有),以及执行配置的初始化方法(通过init-method
属性配置)。如果Bean实现了InitializingBean
接口,则会调用其afterPropertiesSet()
方法。
2.7. Bean的使用
经过初始化的Bean现在已准备好被容器使用。应用程序可以通过容器获取Bean实例,并调用其方法来完成具体的业务逻辑。
- 获取Bean:通过
getBean()
方法从容器中获取Bean实例。
MyClass myClass = context.getBean(MyClass.class);
2.8. Bean的销毁
当容器关闭或Bean不再需要时,Spring会销毁Bean。销毁包括调用@PreDestroy
注解方法(如果有),执行配置的销毁方法(通过destroy-method
属性配置),以及如果Bean实现了DisposableBean
接口,调用其destroy()
方法。
3.AOP(待补充)
4.简单手写一个Spring(进行bean的创建和依赖注入)
- Bean 注解(Bean创建)
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Bean {
}
- DI注解(依赖注入)
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Di {
}
- 创建ApplicationContext接口定义getBean方法
public interface ApplicationContext {
Object getBean(Class clazz);
}
- 创建AnnotationApplicationContext对象实现ApplicationContext接口进行bean的创建和依赖注入
public class AnnotationApplicationContext implements ApplicationContext{
//创建map集合用于存放bean对象
Map<Class,Object> beanFactory=new HashMap<>();
private String rootPath;
@Override
public Object getBean(Class clazz) {
return beanFactory.get(clazz);
}
//设置包扫描规则
//当前包及其子包,那个类有@Bean注解,把这个类通过反射实例化出来
public AnnotationApplicationContext(String basePackage){
// public static void path1(String basePackage){
try {
//com.java
//把.换成\
String pagePath = basePackage.replaceAll("\\.", "\\\\");
//获取包的绝对路径
Enumeration<URL> resources = ApplicationContext.class.getClassLoader().getResources(pagePath);
while (resources.hasMoreElements()){
URL url = resources.nextElement();
//对url进行转码
String filePath = URLDecoder.decode(url.getFile(), "utf-8");
//获取包前面路径部分,字符串截取
rootPath= filePath.substring(0, filePath.length() - pagePath.length());
loadBean(new File(filePath));
//包扫描
// System.out.println(filePath);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
loadDi();
}
//包扫描
private void loadBean(File file) {
//判断是不是文件夹
if (file.isDirectory()) {
//获取文件夹内容
File[] childrenFiles = file.listFiles();
//判断文件夹为空直接返回
if(childrenFiles==null&&childrenFiles.length==0){
return;
}
//遍历问价夹所有内容
for (File childrenFile : childrenFiles) {
if (childrenFile.isDirectory()){
loadBean(childrenFile);
}else {
//通过文件路径转变成全类名,第一步把绝对路径部分去掉
String pathWithClass = childrenFile.getAbsolutePath().substring(rootPath.length() - 1);
//选中其中的class文件
if(pathWithClass.contains(".class")){
//去掉.class并且将\替换为.
String fullPath = pathWithClass.replaceAll( "\\\\",".").replaceAll(".class", "");
try {
Class<?> aClass = Class.forName(fullPath);
//获取非接口类
if(!aClass.isInterface()){
//获取含有bean注解的类
Bean annotation = aClass.getAnnotation(Bean.class);
if(annotation!=null){
Object instance = aClass.newInstance();
//判断instance有没有接口
if(aClass.getInterfaces().length>0){
//如果有的话创建实例对象
System.out.println("正在加载【"+ aClass.getInterfaces()[0] +"】,实例对象是:" + instance.getClass().getName());
beanFactory.put(aClass.getInterfaces()[0],instance);
}else {
//如果没有就将class对象当key
beanFactory.put(aClass,instance);
}
}
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
}
}
}
//属性注入
private void loadDi(){
//实例化对象在beanFactory的map集合里面
//1.遍历beanFactory中的集合
Set<Map.Entry<Class, Object>> entries = beanFactory.entrySet();
for (Map.Entry<Class, Object> entry : entries) {
//2.获取map集合的每个对象
Object value = entry.getValue();
//3.获取属性
Field[] declaredFields = value.getClass().getDeclaredFields();
//4.遍历属性
for (Field declaredField : declaredFields) {
//判断属性上是否有@Di注解
Di annotation = declaredField.getAnnotation(Di.class);
if(annotation!=null){
//属性赋值
try {
declaredField.setAccessible(true);
declaredField.set(value,beanFactory.get(declaredField.getType()));
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}
}
}
}
5. 手写一个复杂的Spring
4.1.流程
4.2.案例:
- Autowired注解实现自动注入
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Autowired {
}
- Component注解标准需要被Spring管理的类
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Component {
String value() default "";//自定义bean的名称
}
- ComponentScan 注解标注需要扫描的包名
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ComponentScan {
String value() default "";
}
- Scope 注解标注是否为单例bean
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Scope {
String value() default "";
}
- BeanDefinition 存放扫描到的bean
@Data
public class BeanDefinition {
private Class type;
private String scope;
}
- 执行Aware方法时调用的方法
public interface BeanNameAware {
public void setName(String name);
}
- Aspect注解标注需要解析代理的bean
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Aspect {
}
- BeanPostProcessor定义初始化前后执行的方法
public Object postProcessBeforeInitialization(String beanName,Object bean);
public Object postProcessAfterInitialization(String beanName,Object bean);
- BeanPostProcessorImpl实现BeanPostProcessor接口
@Component
public class BeanPostProcessorImpl implements BeanPostProcessor {
//初始化前调用方法
@Override
public Object postProcessBeforeInitialization(String beanName,Object bean) {
if(beanName.equals("userService")){
System.out.println(beanName+" 初始化的前置处理!");
}
return bean;
}
//初始化后调用方法
@Override
public Object postProcessAfterInitialization(String beanName,Object bean) {
// System.out.println(bean.getClass().getName()+"的后置通知");
// System.out.println(bean.getClass().isAnnotationPresent(Aspect.class));
//aop
if(bean.getClass().isAnnotationPresent(Aspect.class)){
Object instance = Proxy.newProxyInstance(BeanPostProcessorImpl.class.getClassLoader(),
bean.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("切面逻辑");
return method.invoke(bean, args);
}
});
return instance;
}
return bean;
}
}
- MyApplicationContext容器主要的功能实现
public class MyApplicationContext {
private Class configClass;
private ConcurrentHashMap<String,BeanDefinition> beanDefinitionMap=new ConcurrentHashMap<>();
private ConcurrentHashMap<String,Object> beanFactory=new ConcurrentHashMap<>();
//存放初始化前后方法对象的集合
private List<BeanPostProcessor> beanPostProcessors=new ArrayList<>();
private String rootPath;
public MyApplicationContext(Class configClass) {
this.configClass = configClass;
//扫描
if(configClass.isAnnotationPresent(ComponentScan.class)){
ComponentScan componentScan= (ComponentScan) configClass.getAnnotation(ComponentScan.class);
String path = componentScan.value();//扫描路径 com.java.service
//com/java/service
path=path.replaceAll("\\.","\\\\");
//通过类加载器获取文件路径
URL url = MyApplicationContext.class.getClassLoader().getResource(path);
//对url进行转码
String filePath = null;
try {
filePath = URLDecoder.decode(url.getFile(), "utf-8");
//获取包前面路径部分,字符串截取
rootPath= filePath.substring(0, filePath.length() - path.length());
//获取beanDefinition集合
loadBean(new File(filePath));
//通过beanDefinition集合对其中的bean进行创建
for (String beanName : beanDefinitionMap.keySet()) {
BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
//如果是单例模式就直接创建一个bean
if(beanDefinition.getScope().equals("singleton")){
Object bean = createBean(beanName, beanDefinition);
beanFactory.put(beanName,bean);
}
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
//bean的注册
private Object createBean(String beanName,BeanDefinition beanDefinition) {
Class aClass = beanDefinition.getType();
Object instance = null;
try {
instance = aClass.getConstructor().newInstance();
//依赖注入
for (Field declaredField : aClass.getDeclaredFields()) {
if(declaredField.isAnnotationPresent(Autowired.class)){
declaredField.setAccessible(true);
declaredField.set(instance,getBean(declaredField.getName()));
}
}
//Aware
//通过aware回调对其bean的name进行赋值
if(instance instanceof BeanNameAware){
((BeanNameAware)instance).setName(beanName);
}
//初始化前执行方法
for (BeanPostProcessor beanPostProcessor : beanPostProcessors) {
instance = beanPostProcessor.postProcessBeforeInitialization(beanName, instance);
}
//初始化
if(instance instanceof InitializingBean){
((InitializingBean)instance).afterPropertiesSet();
}
//初始化后执行方法
for (BeanPostProcessor beanPostProcessor : beanPostProcessors) {
instance=beanPostProcessor.postProcessAfterInitialization(beanName,instance);
}
} catch (InstantiationException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
} catch (Exception e) {
throw new RuntimeException(e);
}
return instance;
}
private void loadBean(File file) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
//判断文件是否为文件夹
if (file.isDirectory()) {
//获取所有的文件
File[] files = file.listFiles();
if(files.length==0||files==null){
return;
}
//遍历所有的文件
for (File childrenFile : files) {
if (childrenFile.isDirectory()) {
loadBean(childrenFile);
}
//通过文件路径转变成全类名,第一步把绝对路径部分去掉
String pathWithClass = childrenFile.getAbsolutePath().substring(rootPath.length() - 1);
// System.out.println(pathWithClass);
//获取.class的文件
if(pathWithClass.contains(".class")){
pathWithClass=pathWithClass.replace("\\",".").replace(".class","");
// System.out.println(pathWithClass);
//获取需要扫描的字节码文件
Class<?> aClass = Class.forName(pathWithClass);
//扫描带Component的注解,并将其存入一个beanDefinition
if(aClass.isAnnotationPresent(Component.class)){
BeanDefinition beanDefinition=new BeanDefinition();
//判断是否实现beanPostProcessor接口
if(BeanPostProcessor.class.isAssignableFrom(aClass)){
BeanPostProcessor beanPostProcessor = (BeanPostProcessor) aClass.newInstance();
beanPostProcessors.add(beanPostProcessor);
}
// //通过component注解获取bean的名字
Component component = aClass.getAnnotation(Component.class);
String beanName = component.value();
if(beanName.equals("")){
beanName= Introspector.decapitalize(aClass.getSimpleName());
}
//判断scope注解中的内容(判断单例还是多例)
if(aClass.isAnnotationPresent(Scope.class)){
//获取scope注解
Scope scopeAnno=aClass.getAnnotation(Scope.class);
//获取注解中value值
String scope = scopeAnno.value();
beanDefinition.setScope(scope);
}else {
beanDefinition.setScope("singleton");
}
beanDefinition.setType(aClass);
beanDefinitionMap.put(beanName,beanDefinition);
}
}
}
}
}
public Object getBean(String beanName){
BeanDefinition beanDefinition=beanDefinitionMap.get(beanName);
if(beanDefinition==null){
throw new NullPointerException();
}else {
String scope=beanDefinition.getScope();
//为单例bean时直接取bean工厂拿
if(scope.equals("singleton")){
Object bean = beanFactory.get(beanName);
if(bean==null){
//如果bean为空创建bean
Object bean1 = createBean(beanName, beanDefinition);
beanFactory.put(beanName,bean1);
}
return bean;
}else {
//如果是多例bean则不需要加入bean工厂集合
return createBean(beanName, beanDefinition);
}
}
}
二、SpringMVC
1.核心概念
-
DispatcherServlet(调度器)
:
DispatcherServlet
是 Spring MVC 的核心组件,相当于一个前端控制器(Front Controller)。它接收所有的 HTTP 请求,并将它们分发给合适的处理器(Controller)。- 它负责初始化 Spring MVC 环境,包括加载配置文件、创建上下文等。
-
Controller(控制器)
:
- 控制器是处理 HTTP 请求的核心组件。它接收用户的输入、处理业务逻辑,并返回相应的视图或数据。
- 控制器通过注解
@Controller
标记,并通过方法级别的注解(如@RequestMapping
)来定义如何处理不同的请求路径和请求方法。
-
Model(模型)
:
- 模型包含了应用程序的业务数据和状态。控制器将数据封装在模型中,供视图展示。
- 在 Spring MVC 中,模型通常是
Model
或ModelMap
类型的对象,用于传递数据到视图层。
-
View(视图)
:
- 视图负责将模型中的数据展示给用户。Spring MVC 支持多种视图技术,如 JSP、Thymeleaf、FreeMarker 等。
- 视图的选择通常由视图解析器(View Resolver)决定。视图解析器根据逻辑视图名(由控制器返回)找到实际的视图实现。
-
View Resolver(视图解析器)
:
- 视图解析器是 Spring MVC 中用于解析视图名并将其转换为实际视图对象的组件。通过配置视图解析器,可以指定视图技术和视图文件的路径。
-
HandlerMapping(处理器映射)
:
- 处理器映射是 Spring MVC 中用于将请求 URL 映射到具体处理器(Controller)的组件。Spring 提供了多种处理器映射策略,如基于注解的映射、基于路径的映射等。
2.工作流程
- 流程图
-
具体流程
- Tomecat服务器接收用户请求后创建两个对象HttpServletRequest和HttpServletResqponse并将对象交给DispetcherServlet(调度器)。
- DispetcherServlet(调度器)获取req请求对象中的访问路径url交给HandlerMapping(处理器映射器).
- HandlerMapping会根据我们在Controller上的注解找到对应的Handler处理器并交给DispetcherServlet(调度器).
- DispetcherServlet(调度器)会将处理器和req对象交给HandlerAdapter(处理器适配器),让处理器适配器去调用处理器中相应的处理方法并返回处理好的ModelAndView(视图模型和视图名称).(因为处理器方法会有多种对参数的要求,因此调度器只需要将req和处理器交给适配器即可,适配器可以通过req随意传入参数)
- DispetcherServlet(调度器)将视图名传给ViewResolver(视图解析器)解析出对应的视图文件返回给调度器.
作流程
-
流程图
[外链图片转存中…(img-hAiOCSPY-1728438204087)]
- 具体流程
- Tomecat服务器接收用户请求后创建两个对象HttpServletRequest和HttpServletResqponse并将对象交给DispetcherServlet(调度器)。
- DispetcherServlet(调度器)获取req请求对象中的访问路径url交给HandlerMapping(处理器映射器).
- HandlerMapping会根据我们在Controller上的注解找到对应的Handler处理器并交给DispetcherServlet(调度器).
- DispetcherServlet(调度器)会将处理器和req对象交给HandlerAdapter(处理器适配器),让处理器适配器去调用处理器中相应的处理方法并返回处理好的ModelAndView(视图模型和视图名称).(因为处理器方法会有多种对参数的要求,因此调度器只需要将req和处理器交给适配器即可,适配器可以通过req随意传入参数)
- DispetcherServlet(调度器)将视图名传给ViewResolver(视图解析器)解析出对应的视图文件返回给调度器.
- DispetcherServlet(调度器)将model(视图模型)对对应的view(视图文件)进行渲染得到一个有内容的也页面通过resp对象返回给客户端的浏览器.