Spring(查漏补缺)

Spring48

使用 Spring 框架来完成其他框架的组件注入,使用 Spring 之后开发者不再需要手动进行各种配置,Spring 会自动完成组件的注入,开发者只需要调用实现业务逻辑即可。Spring 已经成为 Java 开发的行业标准

IOC:Spring 自动创建对象,开发者直接使用

实现IOC的三种方式如下:

  • 通过配置文件 XML 来指定要创建的对象(这种方式实际开发基本不用)

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xmlns:p="http://www.springframework.org/schema/p"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
    
        <bean class="com.southwind.entity.Account" id="account">
            <property name="id" value="1"></property>
            <property name="name" value="张三"></property>
            <property name="age" value="22"></property>
        </bean>
    
    </beans>
    
    package com.southwind.test;
    
    import com.southwind.entity.Account;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class Test {
        public static void main(String[] args) {
            //加载IoC
            ApplicationContext applicationContext =
                    new ClassPathXmlApplicationContext("spring.xml");
            Account account = (Account) applicationContext.getBean("account");// 这里取的是id
            System.out.println(account);
        }
    }
    
  • 基于配置类,用配置类的方式取代 XML 配置文件

    package com.southwind.configuration;
    
    import com.southwind.entity.Account;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    public class BeanConfiguration {
        
        // 这里用的是Bean注解,相当于XML文件中的bean标签
        @Bean
        public Account account(){
            return new Account();// 把方法的返回值装到IOC容器中
        }
    }
    
    package com.southwind.test;
    
    import com.southwind.configuration.BeanConfiguration;
    import com.southwind.entity.Account;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.annotation.AnnotationConfigApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class Test {
        public static void main(String[] args) {
            //加载IoC
            ApplicationContext applicationContext =
            new AnnotationConfigApplicationContext(BeanConfiguration.class);
            Account account = (Account) applicationContext.getBean("account");// 这里取的是方法名
            System.out.println(account);
        }
    }
    
  • 基于注解,直接在实体类上添加注解,并且保证被扫描到。这个实体类就会自动注入IOC容器(切记:加注解,扫描包。缺一不可!!!)

    package com.southwind.entity;
    
    import lombok.Data;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.context.annotation.Bean;
    import org.springframework.stereotype.Component;
    
    @Data
    @Component
    public class Account {
        @Value("1")
        private Integer id;
        @Value("张三")
        private String name;
        @Value("22")
        private Integer age;
    }
    
    package com.southwind.test;
    
    import com.southwind.entity.Account;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.annotation.AnnotationConfigApplicationContext;
    
    public class Test {
        public static void main(String[] args) {
            //加载IoC
            ApplicationContext applicationContext =
                    new AnnotationConfigApplicationContext("com.southwind");// 指定包名,能扫描到添加了Component的注解的实体类就行
            Account account = (Account) applicationContext.getBean("account");
            System.out.println(account);
        }
    }
    

AOP:面向切面编程,实现业务代码和非业务代码的解耦合。将非业务代码从业务代码中分离,同时还能实现非业务代码的执行

实现AOP的关键代码如下:

1、实现类(业务代码)

package com.southwind.aop;

import org.springframework.stereotype.Component;

@Component
public class CalImpl implements Cal {
    @Override
    public int add(int num1, int num2) {
        int result = num1 + num2;
        return result;
    }

    @Override
    public int sub(int num1, int num2) {
        int result = num1 - num2;
        return result;
    }

    @Override
    public int mul(int num1, int num2) {
        int result = num1 * num2;
        return result;
    }

    @Override
    public int div(int num1, int num2) {
        int result = num1 / num2;
        return result;
    }
}

2、创建切面类(非业务代码)

package com.southwind.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

import java.util.Arrays;

@Aspect
@Component
public class LoggerAspect {

    @Before(value = "execution(public int com.southwind.aop.CalImpl.*(..))")
    public void before(JoinPoint joinPoint){
        String name = joinPoint.getSignature().getName();// 获取方法名
        String args = Arrays.toString(joinPoint.getArgs());// 获取方法的参数
        System.out.println(name+"方法的参数是" + args);
    }

    @AfterReturning(value = "execution(public int com.southwind.aop.CalImpl.*(..))",returning = "result")
    public void afterReturn(JoinPoint joinPoint,Object result){
        String name = joinPoint.getSignature().getName();
        System.out.println(name+"方法的结果是" + result);
    }

}

3、配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
	
    <!--指定包名,能扫描到添加了Component的注解的实体类就行-->
    <context:component-scan base-package="com.southwind.aop"></context:component-scan>
	<!--切面自动代理:本质上就是生成动态代理类-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

</beans>

4、测试类

package com.southwind.aop;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-aop.xml");
        Cal cal = (Cal) applicationContext.getBean("calImpl");// 获取的其实是动态代理对象,本质是动态代理类把业务类和非业务类糅合在一起实现AOP机制
        cal.add(10, 3);
        cal.sub(10, 3);
        cal.mul(10, 3);
        cal.div(10, 3);
    }
}

Spring49

Spring 框架最核心的功能就是自动装载对象,IoC

实现IOC的三种方式如下:

  • 基于 XML 方式
  • 基于配置类(第三方组件,Swagger 接口测试、MyBatis Plus 分页)不是开发者自定义的类,第三方框架提供的,代码是开发者无法修改
  • 基于注解(业务逻辑代码,Service、Controller、Mapper)跟业务相关的代码,开发者自己写的代码

IoC 就是 Java 对象池,项目中需要用的组件对象全部在对象池中

  • 基于配置类的加载,我们自己手写一个。体会一下IOC底层原理
package com.southwind.configuration;

import com.southwind.entity.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class BeanConfiguration {
    
    @Bean(value = "user")
    public User getUser(){
        return new User(1,"张三");
    }
    
}
package com.southwind.ioc;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

public class MyAnnotationConfigApplicationContext {

    // 模拟IOC容器
    private Map<String,Object> ioc = new HashMap<>();

    public MyAnnotationConfigApplicationContext(Class clazz) {
        //判断目标类是否为一个配置类
        Annotation annotation = clazz.getAnnotation(Configuration.class);
        if(annotation == null) throw new RuntimeException(clazz.getName()+"不是一个配置类!");
        //获取配置类中,方法的注解为Bean的方法
        Method[] declaredMethods = clazz.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {
            Bean annotation1 = declaredMethod.getAnnotation(Bean.class);
            if(annotation1 != null){
                //调用目标方法
                try {
                    Object invoke = declaredMethod.invoke(clazz.getConstructor(null).newInstance(null), null);
                    //把对象注入ioc
                    String[] value = annotation1.value();
                    String key = null;
                    if(value.length == 0){// 如果目标方法的注解没有指定value值
                        // 则把目标方法的方法名当作key
                        key = declaredMethod.getName();
                    } else {
                        // 否则
                        key = value[0];
                    }
                    ioc.put(key, invoke);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                } catch (InstantiationException e) {
                    e.printStackTrace();
                } catch (NoSuchMethodException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public Object getBean(String name){
        Object o = ioc.get(name);
        if(o == null) throw new RuntimeException("NoSuchBeanException");
        return o;
    }
}
public class Test {
    public static void main(String[] args) {
        //加载IoC
        MyAnnotationConfigApplicationContext myAnnotationConfigApplicationContext
                 = new MyAnnotationConfigApplicationContext(BeanConfiguration.class);
        System.out.println(myAnnotationConfigApplicationContext.getBean("user"));
    }
}
  • 基于注解的加载,我们自己手写一个。体会一下IOC底层原理
@Data
@Component
public class Account {
    @Value("1")
    private Integer id;
    @Value("张三")
    private String name;
    @Value("22")
    private Integer age;
}
public class MyAnnotationApplicationContext {

    private Map<String,Object> ioc = new HashMap<>();

    public MyAnnotationApplicationContext(String packageName) {
        //找到包下所有的类
        Set<Class<?>> classes = MyTools.getClasses(packageName);// 这里调用了一个工具类,这个工具类可以根据包名找到包下所有的类
        Iterator<Class<?>> iterator = classes.iterator();
        while (iterator.hasNext()) {
            Class<?> aClass = iterator.next();
            //找到目标类
            Component annotation = aClass.getAnnotation(Component.class);
            if(annotation != null){
                //创建目标类对象
                try {
                    Constructor<?> constructor = aClass.getConstructor(null);
                    Object o = constructor.newInstance(null);
                    //给属性赋值
                    Field[] declaredFields = aClass.getDeclaredFields();
                    for (Field declaredField : declaredFields) {
                        Value annotation1 = declaredField.getAnnotation(Value.class);
                        if(annotation1 != null){
                            String value = annotation1.value();
                            //进行赋值操作
                            String fieldName = declaredField.getName();
                            String methodName = "set"+fieldName.substring(0, 1).toUpperCase()+fieldName.substring(1);
                            Method declaredMethod = aClass.getDeclaredMethod(methodName, declaredField.getType());
                            if(declaredField.getType().equals(Integer.class)){
                                declaredMethod.invoke(o, Integer.parseInt(value));
                            }
                            if(declaredField.getType().equals(String.class)){
                                declaredMethod.invoke(o,value);
                            }
                        }
                    }
                    //获取key
                    String value = annotation.value();
                    String key = null;
                    if(!value.equals("")){
                        key = value;
                    } else {
                        String className = aClass.getName();
                        className = className.replaceAll(packageName+".","");
                        key = className.substring(0, 1).toLowerCase()+className.substring(1);
                    }
                    ioc.put(key, o);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public Object getBean(String key){
        Object o = ioc.get(key);
        if(o == null) throw new RuntimeException("NoSuchBeanException");
        return o;
    }
}
package com.southwind.util;

import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLDecoder;
import java.util.Enumeration;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

public class MyTools {

    public static Set<Class<?>> getClasses(String pack) {

        // 第一个class类的集合
        Set<Class<?>> classes = new LinkedHashSet<Class<?>>();
        // 是否循环迭代
        boolean recursive = true;
        // 获取包的名字 并进行替换
        String packageName = pack;
        String packageDirName = packageName.replace('.', '/');
        // 定义一个枚举的集合 并进行循环来处理这个目录下的things
        Enumeration<URL> dirs;
        try {
            dirs = Thread.currentThread().getContextClassLoader().getResources(packageDirName);
            // 循环迭代下去
            while (dirs.hasMoreElements()) {
                // 获取下一个元素
                URL url = dirs.nextElement();
                // 得到协议的名称
                String protocol = url.getProtocol();
                // 如果是以文件的形式保存在服务器上
                if ("file".equals(protocol)) {
                    // 获取包的物理路径
                    String filePath = URLDecoder.decode(url.getFile(), "UTF-8");
                    // 以文件的方式扫描整个包下的文件 并添加到集合中
                    findClassesInPackageByFile(packageName, filePath, recursive, classes);
                } else if ("jar".equals(protocol)) {
                    // 如果是jar包文件
                    // 定义一个JarFile
                    System.out.println("jar类型的扫描");
                    JarFile jar;
                    try {
                        // 获取jar
                        jar = ((JarURLConnection) url.openConnection()).getJarFile();
                        // 从此jar包 得到一个枚举类
                        Enumeration<JarEntry> entries = jar.entries();
                        findClassesInPackageByJar(packageName, entries, packageDirName, recursive, classes);
                    } catch (IOException e) {
                        // log.error("在扫描用户定义视图时从jar包获取文件出错");
                        e.printStackTrace();
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return classes;
    }

    private static void findClassesInPackageByJar(String packageName, Enumeration<JarEntry> entries, String packageDirName, final boolean recursive, Set<Class<?>> classes) {
        // 同样的进行循环迭代
        while (entries.hasMoreElements()) {
            // 获取jar里的一个实体 可以是目录 和一些jar包里的其他文件 如META-INF等文件
            JarEntry entry = entries.nextElement();
            String name = entry.getName();
            // 如果是以/开头的
            if (name.charAt(0) == '/') {
                // 获取后面的字符串
                name = name.substring(1);
            }
            // 如果前半部分和定义的包名相同
            if (name.startsWith(packageDirName)) {
                int idx = name.lastIndexOf('/');
                // 如果以"/"结尾 是一个包
                if (idx != -1) {
                    // 获取包名 把"/"替换成"."
                    packageName = name.substring(0, idx).replace('/', '.');
                }
                // 如果可以迭代下去 并且是一个包
                if ((idx != -1) || recursive) {
                    // 如果是一个.class文件 而且不是目录
                    if (name.endsWith(".class") && !entry.isDirectory()) {
                        // 去掉后面的".class" 获取真正的类名
                        String className = name.substring(packageName.length() + 1, name.length() - 6);
                        try {
                            // 添加到classes
                            classes.add(Class.forName(packageName + '.' + className));
                        } catch (ClassNotFoundException e) {
                            // .error("添加用户自定义视图类错误 找不到此类的.class文件");
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }

    private static void findClassesInPackageByFile(String packageName, String packagePath, final boolean recursive, Set<Class<?>> classes) {
        // 获取此包的目录 建立一个File
        File dir = new File(packagePath);
        // 如果不存在或者 也不是目录就直接返回
        if (!dir.exists() || !dir.isDirectory()) {
            // log.warn("用户定义包名 " + packageName + " 下没有任何文件");
            return;
        }
        // 如果存在 就获取包下的所有文件 包括目录
        File[] dirfiles = dir.listFiles(new FileFilter() {
            // 自定义过滤规则 如果可以循环(包含子目录) 或则是以.class结尾的文件(编译好的java类文件)
            @Override
            public boolean accept(File file) {
                return (recursive && file.isDirectory()) || (file.getName().endsWith(".class"));
            }
        });
        // 循环所有文件
        for (File file : dirfiles) {
            // 如果是目录 则继续扫描
            if (file.isDirectory()) {
                findClassesInPackageByFile(packageName + "." + file.getName(), file.getAbsolutePath(), recursive, classes);
            } else {
                // 如果是java类文件 去掉后面的.class 只留下类名
                String className = file.getName().substring(0, file.getName().length() - 6);
                try {
                    // 添加到集合中去
                    // classes.add(Class.forName(packageName + '.' +
                    // className));
                    // 经过回复同学的提醒,这里用forName有一些不好,会触发static方法,没有使用classLoader的load干净
                    classes.add(Thread.currentThread().getContextClassLoader().loadClass(packageName + '.' + className));
                } catch (ClassNotFoundException e) {
                    // log.error("添加用户自定义视图类错误 找不到此类的.class文件");
                    e.printStackTrace();
                }
            }
        }
    }

}

public class Test {
    public static void main(String[] args) {
        //加载IoC
        MyAnnotationApplicationContext myAnnotationApplicationContext
                 = new MyAnnotationApplicationContext("com.southwind.entity");
        System.out.println(myAnnotationConfigApplicationContext.getBean("account"));
    }
}

我们会发现框架底层大部分实现都是基于反射机制的

Spring50

Spring 对 Java Web 也做了封装,直接使用 Spring 框架可以快速完成 Web 开发

DispatcherServlet:是 Spring MVC 提供的一个 Servlet

使用 Java 进行 Web 开发,一定是依赖 Servlet 的

Spring MVC 的运行机制:提供一个通用的 Servlet(即:DispatcherServlet),接收所有的请求,在这个通用的 Servlet 中对请求进行处理,把业务逻辑分发到不同的 Handler 中,Handler 就是一个 Java 类,专门用来处理各种不同的业务。

前后端一体的架构:后端代码和前端代码放在同一个工程中,则需要配置视图解析器

前后端分离的架构:前后端是两个不同的服务。后端一般不走视图解析器,直接返回数据即可!

SpringMVC的数据校验

1、引入数据校验相关依赖

<!-- 数据校验 -->
<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>5.2.4.Final</version>
</dependency>
<dependency>
    <groupId>javax.validation</groupId>
    <artifactId>validation-api</artifactId>
    <version>1.1.0.Final</version>
</dependency>
<dependency>
    <groupId>org.jboss.logging</groupId>
    <artifactId>jboss-logging</artifactId>
    <version>3.4.1.Final</version>
</dependency>

2、创建实体类

package com.southwind.entity;

import lombok.Data;
import org.hibernate.validator.constraints.NotEmpty;

import javax.validation.constraints.NotNull;

@Data
public class User {
    @NotNull(message = "ID不能为空")
    private Integer id;
    @NotEmpty(message = "姓名不能为空")
    private String name;
    @NotEmpty(message = "性别不能为空")
    private String gender;
    @NotEmpty(message = "密码不能为空")
    private String password;
}

3、在 Handler 中进行接收校验

package com.southwind.controller;

import com.southwind.entity.User;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.validation.Valid;
import java.util.List;

@Controller
@RequestMapping("/user")
public class UserHandler {

    @RequestMapping("/reg")
    // @Valid 这注解表示从前端接收到User对象后,会自动校验该对象的属性是否存在为空的情况
    // 校验后的结果用BindingResult的对象接收
    public String reg(@Valid User user, BindingResult bindingResult){
        if (bindingResult.hasErrors()) {
            List<ObjectError> allErrors = bindingResult.getAllErrors();
            StringBuffer stringBuffer = new StringBuffer();
            for (ObjectError allError : allErrors) {
                String defaultMessage = allError.getDefaultMessage();
                stringBuffer.append(defaultMessage+",");
            }
            throw new RuntimeException(stringBuffer.toString());
        } else {
            System.out.println(user);
        }
        return "index";
    }

}

4、还要在springmvc的配置文件中加上如下代码

<mvc:annotation-driven/>

Spring51

在SSM框架整合中,Spring 负责管理维护 Spring MVC、MyBatis。本质上就是 Spring 自动创建 Spring MVC 和 MyBatis 运行时所需要的各种组件和对象。让我们更加专注于业务代码的开发,而不是写了很多代码有一部分都仅仅是在创建对象而已

整合过SSM框架会发现如下问题:

  • 整合步骤非常繁琐
  • 配置很多
  • 不方便上手
  • 容易出错

SSM 已经不是主流的技术了,就是因为配置比较麻烦

Spring Boot 来解决这个问题,不是替换 SSM,本质上还是使用 Spring MVC + MyBatis,只不过将这种很繁琐的基于 XML 的配置方式替换掉了。

  • 使用 Spring 来创建 Spring MVC 和 MyBatis 需要的 bean,是可以自动创建,但是需要开发者进行各种 XML 配置,必须先在 XML 配好,然后 Spring 读取这些 XML 配置文件,才能自动创建。
  • Spring Boot 是真正意义上的自动创建,开发者不再需要进行任何的配置,Spring Boot 自动创建 MVC 和 MyBatis 所需要的所有组件。实际上就是把SSM框架中需要手动配置的 XML 文件给省略掉!

Spring Boot 是快速构建 Java 项目的框架,Spring Boot 可以快速搭建起很多主流框架,新建SpringBoot项目的时候,只需要选择导入哪些框架即可,不需要进行额外的配置。

Spring Boot 的启动类(自动生成的)内嵌了 tomcat,可以直接运行

Spring52

SpringBoot整合视图层:SpringBoot官方不推荐使用JSP,推荐使用Thymeleaf模板

Thymeleaf跟JSP类似,但效率高于JSP

Spring Data JPA 是 Spring 提供的一个持久层框架,功能和 MyBatis 一样的。Spring Data JPA的底层实现是Hibernate

SpringBoot整合Spring Data JPA的关键步骤如下:

1、pom.xml,记得还要在application.yml文件中配置数据源,MyBatis的数据源可以和Spring Data JPA共用

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

2、创建实体类,直接跟数据表绑定

package com.southwind.entity;

import lombok.Data;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;

@Data
@Entity
public class Account {
    @Id
    private Integer id;
    @Column
    private String name;
}

3、创建接口

package com.southwind.repository;

import com.southwind.entity.Account;
import org.springframework.data.jpa.repository.JpaRepository;

// 父类接口已经包含基本的增删改查方法
public interface AccountRepository extends JpaRepository<Account,Integer> {// Integer是主键类型
}

4、创建控制器

package com.southwind.controller;

import com.southwind.repository.AccountRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequestMapping("/account")
public class AccountController {

    @Autowired
    private AccountRepository accountRepository;

    @RequestMapping("/list")
    public String list(Model model){
        model.addAttribute("list", this.accountRepository.findAll());
        return "account_list";
    }

}

5、创建视图层

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <table border="1" width="300px">
        <tr>
            <th>编号</th>
            <th>姓名</th>
        </tr>
        <tr th:each="account:${list}">
            <td th:text="${account.id}"></td>
            <td th:text="${account.name}"></td>
        </tr>
    </table>
</body>
</html>

Spring53

SpringBoot整合MyBatisPlus的关键依赖如下:

<!-- MyBatis-Plus -->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.4.3.1</version>
</dependency>

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>

<!-- 逆向工程 -->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-generator</artifactId>
    <version>3.3.2</version>
</dependency>

<!-- 逆向工程模板 -->
<dependency>
    <groupId>org.apache.velocity</groupId>
    <artifactId>velocity</artifactId>
    <version>1.7</version>
</dependency>

Spring54

Spring Boot 底层原理

Spring Boot 如何启动:main 方法(即SpringBoot的启动类)相当于一个开关,只要运行起来,整个 Spring Boot 就会启动,根据开发者提前引入的依赖以及 application.yml 中的配置信息,实现所需组件的自动装配和依赖注入。

SpringBoot启动类源码分析:

SpringBoot的启动类被@SpringBootApplication注解标识,@SpringBootApplication注解被以下三个注解标识

  • @SpringBootConfiguration
    • @Configuration(相当于把SpringBoot启动类标注为一个配置类)
  • @EnableAutoConfiguration
    • @AutoConfigurationPackage
      • @Import({Registrar.class})(Registrar.class中返回的是包名,这个是获取目标类的包进行扫描,把对应的组件注入到IOC中。这里的目标类就是SpringBoot启动类)
    • @Import({AutoConfigurationImportSelector.class})(这个是查找项目路径下每个依赖的 META-INF/spring.factories 这个文件,这个文件记录了一些配置类的,配置类就是用来组件的注入的。即该文件中存储的就是对应依赖启动时所需要加载注入到IOC中的所有组件。每个依赖对应一个该文件。总结一句话就是:让SpringBoot可以加载第三方依赖所需要的bean)
  • @ComponentScan

@SpringBootConfiguration 是 Spring Boot 框架提供的

@Configuration 是 Spring 框架提供的。@Configuration 的作用就是将目标类标注成为一个配置类(相当于 XML)进行组件的注入

@Import 是根据传入的 Class 中所返回的信息,将对应的组件注入到 IOC 中

@ComponentScan 是通过包名来进行组件的注入。该注解有两个参数 String[] value() 和 String[] basePackages() 都可以用来接收要扫描的目标包,如果不指定参数,则默认扫描启动类所在的包,跟@Import({Registrar.class})这个注解功能一样,即扫描开发者自定义的组件进行注入。如果指定参数,则会覆盖这个@Import({Registrar.class})注解的功能

我们可以自定义Starter(体会一下SpringBoot的底层启动原理):

基本思路:

  • 创建一个 Spring Boot 工程 A,提供一个需要注入的组件 Service,进行配置,打 jar 包,导入本地 Maven
  • 在另一个 Spring Boot 工程 B 中导入 工程 A 依赖,启动工程 B 即可直接获取到工程 A 的 Service 这个组件

Spring55

微服务和分布式的区别

  • 分布式:把一个大的模块拆分成很多个小模块,比较泛的一种描述
  • 微服务:分布式更细致的一种描述,一般针对于项目,把一个大型项目拆分成多个小项目,分别负责不同的业务模块,这些小项目共同构建起了一个完整的业务系统。比如 ABCD 四个项目需要互相调用,远程调用 RPC,能被调用的就是服务,因为粒度很细,所以叫微服务。

Spring Boot 和 Spring Cloud 的区别

  • Spring Cloud 是基于 Spring Boot 的,Spring Boot 用来快速构建项目的,整合第三方框架的,任何一个业务系统都可以用 Spring Boot 来快速构建
  • Spring Boot 可以构建持久层、WEB、微服务相关的组件(注册中心、负载均衡、配置中心。这些微服务相关的组件就是SpringCloud)

Spring Cloud 类似于一个接口,会有各种不同的解决方案(实现类)

服务注册中心主要是帮助我们把各个微服务集中一起管理,服务之间的调用可以通过服务注册的名字进行调用,而不需要记住要调用的服务的IP和端口

Spring56

Spring57

MQ的作用:让不同的微服务之间的调用实现解耦。微服务—>MQ—>微服务

Spring58

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值