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)
- @AutoConfigurationPackage
- @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—>微服务