Spring Core:Core封装包是框架的最基础部分,提供IOC和依赖注入特性。这里的基础概念是BeanFactory,它提供对Factory模式的经典实现来消除对程序性单例模式的需要,并真正地允许你从程序逻辑中分离出依赖关系和配置。
Spring Context: 构建于Core封装包基础上的 Context封装包,提供了一种框架式的对象访问方法,有些象JNDI注册器。Context封装包的特性得自于Beans封装包,并添加了对国际化(I18N)的支持(例如资源绑定),事件传播,资源装载的方式和Context的透明创建,比如说通过Servlet容器。
Spring DAO: DAO (Data Access Object)提供了JDBC的抽象层,它可消除冗长的JDBC编码,保证数据库代码的简洁和解析数据库厂商特有的错误代码,它在各种不同的数据库的错误信息之上,提供了一个统一的异常访问层。 并且,它还利用 Spring 的 AOP 模块给 Spring 应用中的对象提供事务管理服务。而且对所有的POJOs(plain old Java objects)都适用。
Spring ORM: ORM 封装包提供了常用的“对象/关系”映射APIs的集成层。 其中包括JPA、JDO、Hibernate 和 iBatis 。利用ORM封装包,可以混合使用所有Spring提供的特性进行“对象/关系”映射,如前边提到的简单声明性事务管理。
Spring AOP: Spring的 AOP 封装包提供了符合AOP Alliance规范的面向方面的编程实现,让你可以定义,例如方法拦截器(method-interceptors)和切点(pointcuts),从逻辑上讲,从而减弱代码的功能耦合,清晰的被分离开。而且,利用source-level的元数据功能,还可以将各种行为信息合并到你的代码中。
Spring Web: Spring中的 Web 包提供了基础的针对Web开发的集成特性,例如多方文件上传,利用Servlet listeners进行IOC容器初始化和针对Web的ApplicationContext。当与WebWork或Struts一起使用Spring时,这个包使Spring可与其他框架结合。
Spring Web MVC: Spring中的MVC封装包提供了Web应用的Model-View-Controller(MVC)实现。Spring的MVC框架并不是仅仅提供一种传统的实现,它提供了一种清晰的分离模型,在领域模型代码和Web Form之间。并且,还可以借助Spring框架的其他特性。
Spring Boot 的核心设计思想是“约定优于配置”。基于这一设计原则,Spring Boot 极大地简化了项目和框架的配置。它采用了大量的默认配置来简化这些文件的配置过程,只需引入对应的 Starters(启动器)。
设计它就是为了使用最少的配置,以最快的速度来启动和运行 Spring 项目。
这个讲的很好呀!
Spring IOC
Spring 框架的核心是 Spring 容器。容器创建对象,将它们装配在一起,配置它们并管理它们的完整生命周期。Spring 容器使用依赖注入来管理组成应用程序的组件。容器通过读取提供的配置元数据来接收对象进行实例化,配置和组装的指令。该元数据可以通过 XML,Java 注解或 Java 代码提供。
在依赖注入中,您不必创建对象,但必须描述如何创建它们。您不是直接在代码中将组件和服务连接在一起,而是描述配置文件中哪些组件需要哪些服务。由 IoC 容器将它们装配在一起。
构造器方法注入: 在bean标签内部使用 constructor-arg标签
优点:创建时必须要指定构造方法中的全部参数,bean才能被创建,保证了对象创建出来之后,成员变量一定都有值
缺点:必须要指定全部参数,否则无法创建,使用该方式改变了对象的创建过程
Set属性注入:必须要有一个无参的构造方法,否则只能用构造方法构造
接口注入: 先定义一个接口,包含一个设置依赖的方法。然后依赖类,继承并实现这个接口。
spring 主要提供了两种 IOC 容器,一种是 BeanFactory,还有一种是 ApplicationContext。
功能上的区别:
加载方式的区别
因为加载方式的不同,导致BeanFactory无法提前发现spring存在的配置问题。(如果bean的某个属性没有注入,BeanFactory加载不会抛出异常,直至第一次调用getBean()方法时才会抛出异常。)但是ApplicationContext 在容器启动时就可以发现spring存在的配置问题,因为他是一次性加载的,有利于检测依赖属性是否注入(也因为其一次性加载的原因,导致占用内存空间,当Bean较多时,影响程序启动的速度)。
Spring 中的 IoC 的实现原理就是工厂模式加反射机制。
基于 xml 配置: bean 所需的依赖项和服务在 XML 格式的配置文件中指定。这些配置文件通常包含许多 bean 定义和特定于应用程序的配置选项。
基于注解配置: 通过在相关的类,方法或字段声明上使用注解,将 bean 配置为组件类本身。默认情况下,Spring 容器中未打开注解装配。因此,需要在使用它之前在 Spring 配置文件中启用它, 例如 @Component, @Named, @Service, @Controller, @Repository。
基于类的Java Config: Spring 的 Java 配置是通过使用 @Bean 和 @Configuration 来实现。
(1) @Configuration 标注在类上,相当于把该类作为spring的xml配置文件中的
(2) @Bean 注解扮演与 元素相同的角色, 它注释的方法将会被AnnotationConfigApplicationContext或AnnotationConfigWebApplicationContext类进行扫描,并用于构建bean定义,初始化Spring容器。
(2) @Configuration 类允许通过简单地调用同一个类中的其他 @Bean 方法来定义 bean 间依赖关系。
Spring bean 支持 5 种 scope:
仅当用户使用支持 Web 的 ApplicationContext 时,最后三个才可用。
简化版:
实例化: 实例化该 Bean 对象
填充属性,给该 Bean 赋值
初始化
a. 如果实现了 Aware 接口,会通过其接口获取容器资源
b. 如果实现了 BeanPostProcessor 接口,则会回调该接口的前置和后置处理增强
c. 如果配置了 init-method 方法,会执行该方法
销毁
a.如果实现了 DisposableBean 接口,则会回调该接口的 destroy 方法
b.如果配置了 destroy-method 方法,则会执行 destroy-method 配置的方法
在java的实际开发过程中,我们可能常常需要使用到init method和destroy method,比如初始化一个对象(bean)后立即初始化(加载)一些数据,在销毁一个对象之前进行垃圾回收等。
public class Person {
private String name;
private Integer age;
// 无参构造器
public Person() {
System.out.println("Person NoConstructor");
}
// 充当init method
public void init() {
System.out.println("Person init-method");
}
// 充当destroy method
public void destroy() {
System.out.println("Person destroy-method");
}
}
创建好这个类,我们就可以使用@Bean注解的方式指定两个方法,以让他们生效。
@Configuration
@ComponentScan(value = "com.why.test.common.config")
public class AppConfig {
@Bean(initMethod = "init", destroyMethod = "destroy")
public Person getPerson() {
return new Person();
}
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
Person person = applicationContext.getBean(Person.class);
System.out.println(person.getClass().getName());
applicationContext.close();
}
}
@Configuration注解是告诉spring这个类是一个配置类,相当于的xml文件
@ComponentScan则是指定需要spring来扫描的包,相当于xml中的context:component-scan属性
@Bean后边的 initMethod 和 destroyMethod 就是在声明这是一个 bean 的同时指定了 init 和 destroy 方法,方法名从功能实现上来说可以随意。
通过 bean 标签的 init-method 和 destroy-method 属性指定方法
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="person" class="com.why.test.common.config" init-method="init" destroy-method="destroy">
bean>
beans>
public static void main(String[] args) {
// 用配置文件启动一个 ApplicationContext
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("context.xml");
// 从context中取出我们的Bean,而不是用 new 对象的方式
Person person = (Person) applicationContext.getBean("person");
System.out.println(person.getClass().getName());
applicationContext.close();
}
在init和destroy方法上加入了两个注解,@PostConstruct和上边@Bean后的initMethod相同,而@PreDestroy则是和destroyMethod做用相同。
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
public class Person {
private String name;
private Integer age;
// 无参构造器
public Person() {
System.out.println("Person NoConstructor");
}
// 充当init method
// 初始化方法的注解方式 等同于init-method
@PostConstruct
public void init() {
System.out.println("Person init-method");
}
// 充当destroy method
// 销毁方法的注解方式 等同于destroy-method
@PreDestroy
public void destroy() {
System.out.println("Person destroy-method");
}
}
@Configuration
@ComponentScan(value = "com.why.test.common.config")
public class AppConfig {
@Bean
public Person getPerson() {
return new Person();
}
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
Person person = applicationContext.getBean(Person.class);
System.out.println(person.getClass().getName());
applicationContext.close();
}
}
public class Person implements InitializingBean, DisposableBean {
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("Person init-method");
}
@Override
public void destroy() throws Exception {
System.out.println("Person destroy-method");
}
}
测试形式与3相同
当 bean 在 Spring 容器中组合在一起时, 它被称为装配或 bean 装配。
Spring容器需要知道需要什么 bean 以及容器应该如何使用依赖注入来将 bean 绑定在一起,同时装配 bean。
自动装配就是指 Spring 容器在不使用 和 标签的情况下,可以自动装配(autowire)相互协作的 Bean 之间的关联关系,将一个 Bean 注入其他 Bean 的 Property 中。
自动装配是一种机制,用于告诉依赖注入容器如何为一个组件(通常是一个Bean或类)自动解析和注入其依赖。自动装配可以减少手动配置和编码的工作量,使得系统更容易维护和扩展。
Spring框架是一个典型的使用自动装配的例子。在Spring中,您可以使用注解或XML配置来启用自动装配。Spring支持不同的自动装配模式,包括:
自动注入是依赖注入(Dependency Injection)的一种实现方式。它是一种设计模式,通过它,一个类的依赖关系(通常是其他类或对象)被注入到该类中,而不是由该类自己创建或查找依赖。这样可以实现松耦合,提高了代码的可测试性、可维护性和可扩展性。
自动注入的方式包括:
a. 构造器注入:依赖通过类的构造器注入。这是一种常见的自动注入方式,通常用于注入不变的依赖关系。
b. 属性注入:依赖通过类的属性(通常是setter方法)注入。这是另一种常见的自动注入方式,通常用于可变的依赖关系。
c. 方法注入:依赖通过类的方法注入。这是一种相对较少使用的方式,通常在特殊情况下使用。
Spring 容器能够自动装配 bean。也就是说,可以通过检查 BeanFactory 的内容让 Spring 自动解析 bean 的协作者。
使通过xml文件 元素的autowire属性或者注解(需要在xml 中写入context:annotation-config/ 开启注解功能) 进行自动装配。
autowire 属性有五个值:
@Autowired:可以应用到 Bean 的属性变量、属性的 setter 方法、非 setter 方法及构造函数等,配合对应的注解处理器完成 Bean 的自动配置工作。默认按照 Bean 的类型进行装配。只要容器中有唯一的一个bean对象类型和要注入的变量类型匹配,就可以注入成功,如果ioc容器中没有任何bean类型和要注入的变量类型匹配则报错。如果想按照名称来装配注入,则需要结合 @Qualifier 一起使用;
@Qualifier:与 @Autowired 注解配合使用,会将默认的按 Bean 类型装配修改为按 Bean 的实例名称装配,Bean 的实例名称由 @Qualifier 注解的参数指定。它在给类成员注入时不能单独使用必须要和@Autowired,但是在给方法参数注入时可以单独使用。
@Resource:作用于@Autowired相同,区别在于@Resource可以通过Bean实例名称进行装配,也就是@Resource中的两个重要属性name和type, 与@Autowired不一样的是,它默认是按照组件名称进行装配的,按照组件名称找不到在根据属性类型去查找,再找不到就报错;他们另一个不同的地方就是 @Autowired 是Spring定义的, @Resource是jdk提供的。
@Component :这将 java 类标记为 bean。它是任何 Spring 管理组件的通用构造型。spring 的组件扫描机制现在可以将其拾取并将其拉入应用程序环境中。
@Controller :这将一个类标记为 Spring Web MVC 控制器。标有它的Bean 会自动导入到 IoC 容器中。
@Service :此注解是组件注解的特化。它不会对 @Component 注解提供任何其他行为。您可以在服务层类中使用@Service 而不是 @Component,因为它以更好的方式指定了意图。
@Repository :这个注解是具有类似用途和功能的 @Component 注解的特化。它为 DAO 提供了额外的好处。它将 DAO 导入 IoC 容器,并使未经检查的异常有资格转换为 Spring DataAccessException。
@Required 注解主要用在 setter 方法上,它表示该 setter 方法的属性必须要在配置时注入值。否则就会报 BeanInitializationException 异常。
@RequestMapping 注解用于将特定 HTTP 请求方法映射到将处理相应请求的控制器中的特定类/方法。
此注释可应用于两个级别:
只有将 bean 用作另一个 bean 的属性时,才能将 bean 声明为内部 bean。为了定义 bean,Spring 的基于 XML 的配置元数据在 或 中提供了 元素的使用。
内部 bean 总是匿名的。
public class Student {
private Person person;
}
public class Person {
private String name;
private String address;
}
<property name="person">
<bean class="com.why.test.common.config.Person">
property>
<property name="address" value="北京">property>
bean>
property>
bean>
AOP(Aspect-Oriented Programming), 即面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。在 OOP 中, 我们以类(class)作为我们的基本单元, 而 AOP 中的基本单元是 Aspect(切面)。
AOP 是 OOP(面向对象编程) 的延续,是 Spring 框架中的一个重要内容,是函数式编程的一种衍生范型。利用 AOP 可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
实现 AOP 的技术,主要分为两大类:
静态代理
AOP 框架在 编译阶段 对程序源代码进行修改,生成了静态的 AOP 代理类(生成的 *.class 文件已经被改掉了,需要使用特定的编译器),比如 AspectJ。
a. 编译时编织(特殊编译器实现)
b. 类加载时编织(特殊的类加载器实现)。
动态代理
AOP 框架在 运行阶段 对动态生成代理对象(在内存中以 JDK 动态代理,或 CGlib 动态地生成 AOP 代理类),如 SpringAOP。
a. JDK 动态代理
b. CGLIB
spring 中 AOP 的实现是「通过动态代理实现的」,如果是实现了接口就会使用 JDK 动态代理,否则就使用 CGLIB 代理。
静态代理
a.由程序员创建或由特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在
b.静态代理通常只代理一个类
c.静态代理事先知道要代理的是什么
动态代理
a.在程序运行时,运用反射机制动态创建而成
b.动态代理是代理一个接口下的多个实现类
c.动态代理不知道要代理什么东西,只有在运行时才知道
Spring AOP 在运行时增强,通过动态代理实现,如 Java JDK的动态代理(Proxy,底层通过反射实现)或者CGLIB的动态代理(底层通过继承实现)。作用于运行期,字节码加载后,使用了反射,所以性能较差。
AspectJ 是编译时增强,原理是静态代理, 通过修改代码来实现的。
编译时织入(CTW):在编译时织入中,AspectJ编译器(ajc)会在编译源代码时,根据AspectJ的语法和指令,将切面的逻辑织入到目标代码中。这种织入方式要求使用AspectJ编译器进行编译,并且在编译时指定切面的织入规则和目标代码。
编译后织入(PCW):编译后织入是指在目标代码编译完成后,通过特定的工具或框架将切面逻辑织入到目标类中。这种织入方式可以在不修改原始源代码的情况下,对已编译的目标类进行增强。常用的工具包括AspectJ提供的ajc命令行工具和Maven或Gradle等构建工具的插件。
类加载时织入(LTW):类加载时织入是指在类加载过程中,通过特定的类加载器和字节码增强技术,在目标类被加载到JVM时将切面逻辑织入其中。这种织入方式相对于编译时织入和编译后织入更为灵活,可以在运行时动态地将切面逻辑织入目标类。AspectJ提供了基于Java Agent的LTW实现,可以通过Java Agent机制来实现类加载时织入。
Spring AOP | AspectJ |
---|---|
Java 中实现 | 使用 Java 编程语言的扩展实现 |
不需要单独的编译过程 | 除非设置 LTW,否则需要 AspectJ 编译器(ajc) |
只能使用运行时织入 | 运行时织入不可用。支持编译时、编译后和类加载时织入 |
仅支持方法级编织 | 可以编织字段、方法、构造函数、静态初始值设定项、最终类/方法等 |
只能在由 Spring 容器管理的 bean 上实现 | 可以在所有域对象上实现 |
仅支持方法执行切入点 | 支持所有切入点 |
代理是由目标对象创建的,并且切面应用在这些代理上 | 在执行应用程序之前(运行时) 直接在代码中进行织入 |
比 AspectJ 慢多 | 更好的性能 |
易于学习和应用 | 相对于Spring AOP来说更复杂 |
aspect 由 pointcount 和 advice 组成, 它既包含了横切逻辑的定义, 也包括了连接点的定义. Spring AOP 就是负责实施切面的框架, 它将切面所定义的横切逻辑编织到切面所指定的连接点中。
AOP 的工作重心在于如何将增强编织目标对象的连接点上, 这里包含两个工作:
程序运行中的一些时间点, 例如一个方法的执行, 或者是一个异常的处理。在 Spring AOP 中, join point 总是方法的执行点。
特定 JoinPoint 处的 Aspect 所采取的动作称为 Advice。Spring AOP 使用一个 Advice 作为拦截器,在 JoinPoint “周围”维护一系列的拦截器。
Spring 配备构建 Web 应用的全功能 MVC 框架。Spring 可以很便捷地和其他 MVC 框架集成,如 Struts,Spring 的 MVC 框架用控制反转把业务对象和控制逻辑清晰地隔离。它也允许以声明的方式把请求参数和业务对象绑定。
Spring Web MVC 框架提供 模型-视图-控制器 架构和随时可用的组件,用于开发灵活且松散耦合的 Web 应用程序。
MVC 模式有助于分离应用程序的不同方面,如输入逻辑,业务逻辑和 UI 逻辑,同时在所有这些元素之间提供松散耦合。
Spring 的 MVC 框架是围绕 DispatcherServlet 来设计的,它用来处理所有的 HTTP 请求和响应。
向服务器发送 HTTP 请求,请求被前端控制器 DispatcherServlet 捕获。
DispatcherServlet 根据 -servlet.xml 中的配置对请求的 URL 进行解析,得到请求资源标识符(URI)。然后根据该 URI,调用 HandlerMapping 获得该 Handler 配置的所有相关的对象(包括 Handler 对象以及 Handler 对象对应的拦截器),最后以 HandlerExecutionChain 对象的形式返回。
DispatcherServlet 根据获得的 Handler,选择一个合适的 HandlerAdapter。(附注:如果成功获得 HandlerAdapter 后,此时将开始执行拦截器的 preHandler(…)方法)。
提取 Request 中的模型数据,填充 Handler 入参,开始执行 Handler( Controller)。在填充 Handler 的入参过程中,根据你的配置,Spring 将帮你做一些额外的工作:
4.1 HttpMessageConveter:将请求消息(如 Json、xml 等数据)转换成一个对象,将对象转换为指定的响应信息。
4.2 数据转换:对请求消息进行数据转换。如 String转换成 Integer、Double等。
4.3 数据格式化:对请求消息进行数据格式化。如将字符串转换成格式化数字或格式化日期等。
4.4 数据验证:验证数据的有效性(长度、格式等),验证结果存储到BindingResult或 Error中。
Handler(Control ler)执行完成后,向 DispatcherServlet 返回一个ModelAndView 对象;
根据返回的 ModelAndView,选择一个适合的 ViewResolver(必须是已经注册到 Spring 容器中的 ViewResolver)返回给 DispatcherServlet。
ViewResolver 结合 Model 和 View,来渲染视图。
视图负责将渲染结果返回给客户端。