学习视频:6001 Spring概述_哔哩哔哩_bilibili~7012 Bean的生命周期_哔哩哔哩_bilibili
目录
b.数据访问及集成模块(Data Access/Integration)
1.Spring概述
1.1框架的核心技术
Spring是由Rod Johnson组织和开发的一个分层的Java SE/EE一站式(full-stack)轻量级开源框架。它最为核心的理念是IoC(控制反转)和AOP(面向切面编程),其中,IoC是Spring的基础,它支撑着Spring对JavaBean的管理功能;AOP是Spring 的重要特性,AOP是通过预编译方式和运行期间动态代理实现程序功能,也就是说可以在不修改源代码的情况下,给程序统一添加功能。
1.2Spring在表现层、业务逻辑层和持久层的作用
包括但不限于:
- 在表现层它提供了Spring MVC框架,并且Spring还可以与Struts框架整合。
- 在业务逻辑层可以管理事务、记录日志等。
- 在持久层可以整合MyBatis、Hibernate、JdbcTemplate等技术。
2.框架优点
a.非侵入式设计
Spring是一种非侵入式(non-invasive)框架,所谓非侵入式是指Spring框架的API不会在业务逻辑上出现,也就是说业务逻辑应该是纯净的,不能出现与业务逻辑无关的代码。由于业务逻辑中没有Spring的API,所以业务逻辑代码也可以从Spring框架快速地移植到其他框架。
b.降低耦合性
Spring就是一个大工厂,可以将所有对象的创建和依赖关系的维护工作都交给Spring容器管理,大大降低了组件之间的耦合性。
c.支持AOP编程
Spring提供了对AOP的支持,AOP可以将一些通用的任务进行集中处理,如安全、事务和日志等,以减少通过传统OOP方法带来的代码冗余和繁杂。
d.支持声明式事务
在Spring中,可以直接通过Spring配置文件管理数据库事务,省去了手动编程的繁琐,提高了开发效率。
e.方便程序的测试
Spring提供了对Junit的支持,开发人员可以通过Junit进行单元测试。
f.方便集成框架
Spring提供了一个广阔的基础平台,其内部提供了对各种框架的直接支持,如Struts、Hibernate、MyBatis、Quartz等,这些优秀框架可以与Spring无缝集成。
g.降低Java EE API的使用难度
Spring对Java EE开发中的一些API(如JDBC、JavaMail等)都进行了封装,大大降低了这些API的使用难度。(之前12345才能完成,现在123即可)
3.Spring的体系结构
结构图
a.核心容器模块(Core Container)
核心容器模块在Spring的功能体系中起着支撑性作用,是其他模块的基石。核心容器层主要由Beans模块、Core模块、Contex模块和SpEL模块组成。
核心容器模块各模块组成
- Beans模块。它提供了BeanFactory类,是工厂模式的经典实现,Beans模块的主要作用是创建和管理Bean对象。
- Core模块。它提供了Spring框架的基本组成部分,包括IoC和DI功能。
- Context模块。它构建于Beans模块和Core模块的基础之上,它可以通过ApplicationContext接口提供上下文信息。
- SpEL模块。它是Spring 3.0后新增的模块,提供了对SpEL表达式语言(Spring Expression Language)的支持,SpEL表达式语言是一个在程序运行时支持操作对象图的表达式语言。
b.数据访问及集成模块(Data Access/Integration)
数据访问及集成模块用于访问和操作数据库中的数据,它主要包含JDBC模块、ORM模块、OXM模块、JMS模块和Transactions模块。
- JDBC模块。它提供了一个JDBC的抽象层,消除了冗长的JDBC编码并能够解析数据库供应商特有的错误代码。
- ORM模块。它为主流的对象关系映射API提供了集成层,用于集成主流的对象关系映射框架。
- OXM模块。它提供了对XML映射的抽象层的支持,如JAXB、Castor等。
- JMS模块。它主要用于传递消息,包含消息的生产和消费。自4.1版本后,JMS模块支持与Spring-message模块的集成。
- Transactions模块。它的主要功能是事务管理。
c.Web模块
Web模块的实现基于APPlicationContext基础之上,它提供了Web应用的各种工具类,包括了Web模块、Servlet模块、WebSocket模块和Portlet模块。
Web模块各模块组成
- Web模块。它提供了针对Web开发的集成特性,如大部分文件上传功能等。此外,Web模块还包含一个HTTP客户端和Spring远程处理支持的Web相关部分。
- Servlet模块。它提供了Spring的模型、视图、控制器以及Web应用程序的REST Web服务实现。
- WebSocket模块。它是Spring 4.0以后新增的模块,它提供了WebSocket 和SockJS的实现,以及对STOMP的支持。
- Portlet模块。它类似Servlet模块的功能,提供了Portlet环境下的MVC实现。
d.其他模块
Spring框架的其他模块还有AOP模块、Aspects模块、Instrumentation模块以及Test模块。
其他模块各模块组成
- AOP模块。它提供了对面向切面编程的支持,程序可以定义方法拦截器和切入点,将代码按照功能进行分离,以降低程序的耦合性。
- Aspects模块。它提供了与AspectJ集成的支持。
- Instrumentation模块。它提供了对类工具的支持,并且实现了类加载器,该模块可以在特定的应用服务器中使用。
- Messaging模块。它是Spring 4.0以后新增的模块,它提供了对消息传递体系结构和协议的支持。
- Test模块。它提供了对程序单元测试和集成测试的支持。
4.Spring5的新特性
a.更新JDK基线
因为Spring 5代码库运行于JDK 8之上,所以Spring 5对JDK的最低要求是JDK 8,这可以促进Spring的使用者积极运用Java 8新特性。
b.修订核心框架
- 基于JDK 8的反射增强,通过Spring 5提供的方法可以更加高效的对类或类的参数进行访问。
- 核心的Spring接口提供了基于JDK 8的默认方法构建的选择性声明。
- 用@Nullable和@NotNull注解来表明可为空的参数以及返回值,可以在编译时处理空值而不是在运行时抛出NullPointerExceptions异常。
c.更新核心容器
Spring 5支持候选组件索引作为类路径扫描的替代方案。从索引读取实体类,会使加载组件索引开销更低,因此,Spring程序的启动时间将会缩减。
d.支持响应式编程
响应式编程是另外一种编程风格,它专注于构建对事件做出响应的应用程序。Spring 5包含响应流和Reactor(ReactiveStream的Java实现),响应流和Reactor支撑了Spring自身的功能及相关API。
e.支持函数式Web框架
Spring 5提供了一个函数式Web框架。该框架使用函数式编程风格来定义端点,它引入了两个基本组件: HandlerFunction和RouterFunction。HandlerFunction 表示处理接收到的请求并生成响应函数;RouterFunction替代了@RequestMapping注解,用于将接收到的请求转发到处理函数。
f.支持Kotlin
Spring 5提供了对Kotlin语言的支持。Kotlin是一种支持函数式编程风格的面向对象语言,它运行在JVM之上,可以让代码更具有表现力、简洁性和可读性。有了对Kotlin的支持,开发人员可以进行深度的函数式Spring编程,这拓宽了Spring的应用领域。
g.提升测试功能
Spring 5完全支持Junit 5 Jupiter,因此可以使用Junit 5编写测试代码。除此之外,Spring 5还提供了在Spring TestContext Framework中进行并行测试的扩展。针对响应式编程模型,Spring 5引入了支持Spring webFlux的WebTestClient集成测试。
2.Spring的目录结构
目录结构下文件夹的介绍
docs文件夹:该文件夹下存放Spring的相关文档,包括开发指南、API参考文档。 libs文件夹:该文件夹下存放开发所需的jar包和源码。整个Spring框架由21个模块组成,libs目录下Spring为每个模块都提供了三个压缩包,因此,libs文件夹下一共有63个jar包。这63个jar包分为三类。
schema文件夹:该文件夹下存放Spring各种配置文件的XML Schema文档。
3.Spring的入门程序
public class HelloSpring {
private String userName;
public void setUserName(String userName) {
this.userName = userName;
}
public void show()
{
System.out.println(userName+",你好,spring");
}
}
配置文件
<?xml version="1.0" encoding="UTF-8"?>
<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="helloSpring" class="com.it.HelloSpring">
<property name="userName" value="张三"></property>
</bean>
</beans>
public class HelloSpringTest {
public static void main(String[] args) {
//1.获取spring容器
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
//2.从spring容器获取对象
HelloSpring helloSpring=(HelloSpring)applicationContext.getBean("helloSpring");
//3.调用对象方法
helloSpring.show();
}
}
5.控制反转与依赖注入
控制反转
传统面向对象程序设计原则
控制反转(Inversion of Control,缩写为IoC)是面向对象编程中的一个设计原则,用来降低程序代码之间的耦合度。在传统面向对象编程中,获取对象的方式是用new关键字主动创建一个对象,也就是说应用程序掌握着对象的控制权。传统面向对象程序设计原则如图。
IoC设计原则
IoC控制反转机制指的是对象由Ioc容器统一管理,当程序需要使用对象时,可以直接从IoC容器中获取。这样对象的控制权就从应用程序转移到了IoC容器。IoC设计原则如图,它是借助于IoC容器实现具有依赖关系对象之间的解耦,各个对象类封装之后,通过IoC容器来关联这些对象类。
依赖注入
依赖注入(Dependency Inject,缩写DI)就是由IoC容器在运行期间动态地将某种依赖资源注入对象之中。例如,将对象B注入(赋值)给对象A的成员变量。依赖注入的基本思想是:明确地定义组件接口,独立开发各个组件,然后根据组件的依赖关系组装运行。
依赖注入和控制反转的比较
依赖注入(DI)和控制反转(IoC)是从不同角度来描述了同一件事情。依赖注入是从应用程序的角度描述,即应用程序依赖IoC容器创建并注入它所需要的外部资源;而控制反转是从IoC容器的角度描述,即IoC容器控制应用程序,由IoC容器反向地向应用程序注入应用程序所需要的外部资源。这里所说的外部资源可以是外部实例对象,也可以是外部文件对象等。
依赖注入的实现方式
依赖注入的作用就是在使用Spring框架创建对象时,动态的将其所依赖的对象注入到Bean组件中。依赖注入通常有两种实现方式,一种是构造方法注入,另一种是属性setter方法注入。这两种实现方式具体介绍如下。
构造方法注入
构造方法注入是指Spring容器调用构造方法注入被依赖的实例,构造方法可以是有参的或者是无参的。Spring在读取配置信息后,会通过反射方式调用实例的构造方法,如果是有参构造方法,可以在构造方法中传入所需的参数值,最后创建类对象。
代码实现
public class User1 {
private int id;
private String username;
private String password;
@Override
public String toString() {
return "User1{" +
"id=" + id +
", name='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
public User1(String username, String password, int id) {
this.username = username;
this.password = password;
this.id = id;
}
}
<bean id="user1" class="com.it.User1">
<constructor-arg name="id" value="1"></constructor-arg>
<constructor-arg name="username" value="张三"></constructor-arg>
<constructor-arg name="password" value="123"></constructor-arg>
</bean>
public class User1Test {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
User1 user1 = (User1) context.getBean("user1");
System.out.println(user1);
}
}
属性setter方法注入
属性setter方法注入是Spring最主流的注入方法,这种注入方法简单、直观,它是在被注入的类中声明一个setter方法,通过setter方法的参数注入对应的值。
public class User2 {
private int id;
private String username;
private String password;
private User1 user1;
public void setUser1(User1 user1) {
this.user1 = user1;
}
public void setIdd(int id) {
this.id = id;
}
public void setUsername(String username) {
this.username = username;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User2{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
", user1=" + user1 +
'}';
}
}
<bean id="user2" class="com.it.User2">
<property name="idd" value="2"></property>
<property name="username" value="李四"></property>
<property name="password" value="1234"></property>
<property name="user1" ref="user1"></property>
</bean>
public class User2Test {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
User2 user2 = (User2)applicationContext.getBean("user2");
System.out.println(user2);
}
}
注意:这里name="idd"这个是去掉set的命名,以及搞引用的话比如user1对象得用ref
依赖注入的应用
public interface UserDao {
public boolean login(String username, String password);
}
public class UserDaoImpl implements UserDao {
@Override
public boolean login(String username, String password) {
if ("张三".equals(username) && "123".equals(password)) {
return true;
}else
{
return false;
}
}
}
public interface UserService {
public boolean login(String username, String password);
}
public class UserServiceImpl implements UserService {
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public boolean login(String username, String password) {
return userDao.login(username, password);
}
}
<bean id="userDao" class="com.it.dao.impl.UserDaoImpl"></bean>
<bean id="userService" class="com.it.service.impl.UserServiceImpl">
<property name="UserDao" ref="userDao">
</property>
</bean>
public class LoginTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService =(UserService) context.getBean("userService");
boolean result = userService.login("张三", "123");
if(result)
{
System.out.println("登录成功");
}else
{
System.out.println("登录失败");
}
}
}
6.Spring中的Bean的管理
BeanFactory接口
BeanFactory接口实例的语法格式
BeanFactory beanFactory=new XmlBeanFactory (new FileSystemResource(”D:/bean.xml”));
public interface OrderService {
}
import com.it.service.OrderService;
public class OrderServiceImpl implements OrderService {
public OrderServiceImpl()
{
System.out.println("构造方法调用了");
}
}
<bean id="orderService" class="com.it.service.impl.OrderServiceImpl"></bean>
public class App {
public static void main(String[] args) {
/* BeanFactory默认是第一次获取对象时,创建对象
*/
BeanFactory beanFactory = new XmlBeanFactory(new FileSystemResource("D:\\03HANJIA\\Days007\\src\\main\\resources\\applicationContext.xml"));
OrderServiceImpl orderService=(OrderServiceImpl)beanFactory.getBean("orderService");
System.out.println(orderService);
OrderService orderService1 =beanFactory.getBean(OrderService.class);
System.out.println(orderService1);
}
}
ApplicationContext接口
ApplicationContext接口建立在BeanFactory接口(第一次获取对象时创建)的基础之上,它丰富了BeanFactory接口的特性,例如,添加了对国际化、资源访问、事件传播等方面的支持。 ApplicationContext接口可以为单例的Bean实行预初始化,并根据<property>元素执行setter方法,单例的Bean可以直接使用,提升了程序获取Bean实例的性能。
ApplicationContext接口的常用实现类
是BeanFactory的子接口,在容器初始化时,就创建bean了
Bean的配置
Spring容器所支持的配置文件格式
Spring容器支持XML和Properties两种格式的配置文件,在实际开发中,最常用的是XML格式的配置文件。XML是标准的数据传输和存储格式,方便查看和操作数据。在Spring中,XML配置文件的根元素是<beans>,<beans>元素包含<bean>子元素,每个<bean>子元素可以定义一个Bean,通过<bean>元素将Bean注册到Spring容器中。
<bean>元素的常用属性
<bean>元素的常用子元素
普通的Bean通常只需定义id(或者name)和class两个属性
静态工厂实例化
public class Bean1 {
public Bean1()
{
System.out.println("bean1的构造方法");
}
}
public class MyBean1Factory {
public static Bean1 createBean1() {
System.out.println("工厂的静态方法");
return new Bean1();
}
}
<bean id="bean1" class="com.it.factory.MyBean1Factory" factory-method="createBean1"></bean>
public class Bean1Test {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
Bean1 bean1 = (Bean1) context.getBean("bean1");
System.out.println(bean1);
}
}
如果是接口只能用这个 (因为接口没构造方法)
实例工厂实例化
public class Bean2 {
public Bean2() {
System.out.println("这是Bean2");
}
}
import com.it.beans.Bean2;
public class MyBean2Factory {
public MyBean2Factory()
{
System.out.println("实例化工厂的构造方法");
}
public Bean2 createBean()
{
return new Bean2();
}
}
<bean id="bean2Factory" class="com.it.factory.MyBean2Factory"></bean>
<bean id="bean2" factory-bean="bean2Factory" factory-method="createBean"></bean>
public class Bean2Test {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
Bean2 bean2 = (Bean2) context.getBean("bean2");
System.out.println(bean2);
}
}
优化版
public class Bean3 {
public Bean3() {
System.out.println("这是bean3");
}
}
public class MyBean3Factory implements FactoryBean<Bean3> {
@Override
public Bean3 getObject() throws Exception {
System.out.println("FactoryBean接口的getObject方法");
return new Bean3();
}
@Override
public Class<?> getObjectType() {
return Bean3.class;
}
}
<bean id="bean3" class="com.it.factory.MyBean3Factory"></bean>
public class Bean3Test {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
Object bean3= context.getBean("bean3");
System.out.println(bean3);
}
}
7.Bean的作用域
Spring支持的5种作用域
<bean id="bean1" class="com.it.beans.Bean1" scope="singleton"></bean>
public class SingletonTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
Object bean1=context.getBean("bean1");
Object bean2=context.getBean("bean1");
System.out.println(bean1==bean2);
}
}
scope改为prototype结果会是false,因为会获取一个新的对象
8.Bean的装配方式
两种基于XML的装配方式
在基于XML的装配就是读取XML配置文件中的信息完成依赖注入,Spring容器提供了两种基于XML的装配方式,属性setter方法注入和构造方法注入。下面分别对这两种装配方式进行介绍。
a.属性setter方法注入
属性setter方法注入要求一个Bean必须满足以下两点要求。
(1)Bean类必须提供一个默认的无参构造方法。
(2)Bean类必须为需要注入的属性提供对应的setter方法。
b.构造方法注入
使用构造方法注入时,在配置文件里,需要使用<bean>元素的子元素<constructor-arg>来定义构造方法的参数,例如,可以使用其value属性(或子元素)来设置该参数的值。
基于注解的装配
XML与注解装配的比较
在Spring中,使用XML配置文件可以实现Bean的装配工作,但在实际开发中如果Bean的数量较多,会导致XML配置文件过于臃肿,给后期维护和升级带来一定的困难。为解决此问题,Spring提供了注解,通过注解也可以实现Bean的装配。
Spring的常用注解
基于注解的装配
自动扫描:
<context:component-scan base-package="com.it"></context:component-scan>
public interface UserDao {
public void save();
}
@Repository("userDao")
public class UserDaoImpl implements UserDao {
@Override
public void save() {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
User user = (User) context.getBean("user");
System.out.println(user);
System.out.println("调用了userDao的save方法");
}
}
public interface UserService {
public void save();
}
@Service("userService")
public class UserServiceImpl implements UserService {
@Resource(name="userDao")
private UserDao userDao;
@Override
public void save() {
userDao.save();
System.out.println("调用了userService的save方法");
}
}
@Controller("userController")
public class UserController {
@Resource(name="userService")
private UserService userService;
public void save()
{
userService.save();
System.out.println("调用了userController的save方法");
}
}
public class AnnotationTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserController userController= (UserController) context.getBean("userController");
userController.save();
}
}
自动装配
如何实现自动装配
Spring的<bean>元素中包含一个autowire属性,可以通过设置autowire属性的值实现Bean的自动装配。
autowire属性的值
public class User1 {
private int id;
private String username;
private String password;
private Bean1 bean1;
public User1() {
}
@Override
public String toString() {
return "User1{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
", bean1=" + bean1 +
'}';
}
public Bean1 getBean1() {
return bean1;
}
public void setBean1(Bean1 bean1) {
this.bean1 = bean1;
}
public User1(int id, String username, String password, Bean1 bean1) {
this.id = id;
this.username = username;
this.password = password;
this.bean1 = bean1;
}
}
<bean id="bean1" class="com.it.beans.Bean1" ></bean>
<bean id="user1" class="com.it.beans.User1" autowire="byType">
</bean>
public class XMLDITest {
public static void main(String[] args) {
ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml");
User1 user1=(User1)context.getBean("user1");
System.out.println(user1.getBean1());
}
}
使用Autowired注解
测试
如果userDao创建了多个,可以用Qualifier配合使用
Bean的生命周期
Bean在不同作用域内的生命周期
Bean的生命周期是指Bean实例被创建、初始化和销毁的过程。在Bean的两种作用域singleton和prototype中,Spring容器对Bean的生命周期的管理是不同的。在singleton作用域中,Spring容器可以管理Bean的生命周期,控制着Bean的创建、初始化和销毁。在prototype作用域中, Spring容器只负责创建Bean实例,不会管理其生命周期。
Bean生命周期的两个时间节点
在Bean的生命周期中,有两个时间节点尤为重要,这两个时间节点分别是Bean实例初始化后和Bean实例销毁前,在这两个时间节点通常需要完成一些指定操作。因此,常常需要对这两个节点进行监控。
监控时间节点的方式
监控两个节点的方式有两种,一种是使用XML配置文件,一种是使用注解。
@Component("student")
public class Student {
@Value("1")
private int id;
@PostConstruct
public void init()
{
System.out.println("初始时要调用的方法");
}
@PreDestroy
public void destroy()
{
System.out.println("销毁时要调用的方法:destroy");
}
}
public class AnnotationTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
Student student =(Student) context.getBean("student");
System.out.println(student);
AbstractApplicationContext ac=(AbstractApplicationContext) context;
ac.registerShutdownHook();
}
}