发布于2021-06-12 16:19 阅读(709) 评论(0) 点赞(25) 收藏(1)
对于 Java 编程来说,使用 Spring 能完成的更加快速,更容易并更安全。Spring 专注于速度,便捷与开发效率,也正是如此,让Spring成为了全世界最流行的 Java 框架。
Spring Framework 属于其中最基础,最核心的部分,Spring下的其他大部分框架都依赖 Spring Framework 。
官网给出了一张Spring4.x的结构图。目前最新的5.x版本中Web模块的Portlet组件已经被废弃掉,同时 增加了用于异步响应式处理的WebFlux组件。
对于整个Spring Framework来说,是学习、使用Spring生态项目(如Spring Boot、Spring Cloud等)的基石。也就是说,我们要引入其他Spring项目作为我们的依赖框架时,也会使用Spring Framework。
以上子模块包括的内容我们只学习其中最重要的三个部分:Core Container、AOP、WebMVC
本模块由 spring-core , spring-beans , spring-context , spring-context-support , and spring- expression 4 个模块组成。
其中spring-core 和 spring-beans 模块,这两个模块提供了整个Spring框架最基础的设施:IoC (Inversion of Control,控制反转) 和 DI (Dependency Injection,依赖注入)。
IoC (Inversion of Control,控制反转) ,是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。只是因为该理论时间成熟相对较晚,并没有包含在GoF中。
系统中通过引入实现了IoC模式的IoC容器,即可由IoC容器来管理对象的生命周期、依赖关系等,从而使得应用程序的配置和依赖性规范与实际的应用程序代码分离。
从使用上看,以前手动new对象,并设置对象中属性的方式,控制权是掌握在应用程序自身。现在则全部转移到了容器,由容器来统一进行管理对象。因为控制权发生了扭转,所以叫控制反转。
实现了IoC思想的容器就是IoC容器,比如:SpringFremework.
DI (Dependency Injection,依赖注入) 是实现IoC的方法之一。所谓依赖注入,就是由IOC容器在运行期间,动态地将某种依赖关系注入到对象之中。
所以,依赖注入(DI)和控制反转(IoC)是从不同的角度的描述的同一件事情,就是指通过引入 IoC 容器,利用依赖关系注入的方式,实现对象之间的解耦。
Spring容器的API有 BeanFactory 和 ApplicationContext 两大类,他们都是顶级接口。其中ApplicationContext 是 BeanFactory 的子接口。我们主要使用 ApplicationContext 应用上下文接口。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>spring-study</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<java.version>1.8</java.version>
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring-framework.version>5.2.10.RELEASE</spring-framework.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring-framework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring-framework.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.16</version>
</dependency>
</dependencies>
<build>
<plugins>
<!-- 明确指定一些插件的版本,以免受到 maven 版本的影响 -->
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<version>3.2.0</version>
</plugin>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<plugin>
<artifactId>maven-site-plugin</artifactId>
<version>3.3</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.2</version>
</plugin>
</plugins>
</build>
</project>
配置完成记得要刷新下maven面板哦
之后就可以使用Spring框架了,Spring提供了通过xml配置文件,来定义Bean,但是定义Bean的方式需 要通过包扫描的方式注册到容器中(其实还有其他方式,我们这里主要只掌握包扫描的方式)
写一个入口类:
package org.example;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App {
public static void main(String[] args) {
//根据Spring配置文件路径创建容器:应用上下文对象
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
//关闭容器
((ClassPathXmlApplicationContext) context).close();
}
}
定义需要加载的Bean配置文件,在src/main/resources下,创建beans.xml文件:
<?xml version="1.0" encoding="UTF-8"?>
-<beans xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd" xmlns:context="http://www.springframework.org/schema/context" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans">
<context:component-scan base-package="org.example"/>
</beans>
在类上使用注解 @Controller (前端业务交互层), @Service(业务处理层) , @Repository(数据交互层DB) , @Component(注解) 。需要保证该类会被Spring 扫描到,这种定义方式默认会注册一个名称为类名首字母小写的Bean对象到容器中(但如果是USERlogin这种,注册的对象仍为USERlogin,底层源码实现)。以下定义一个模拟数据库操作的类org.example.dao.LoginRepository @Repository 注解会注册名称为loginRepository的对象到容器中:
package org.example.dao;
import org.example.model.User;
import org.springframework.stereotype.Repository;
@Repository
public class LoginRepository {
}
定义好了Bean对象,注册到容器中以后,就可以获取Bean对象了,在入口类org。example.APP中,可以通过 AplicattionContext对象获取Bean,有两种方式获取:
package org.example;
import org.example.dao.LoginRepository;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App {
public static void main(String[] args) {
//根据Spring配置文件路径创建容器:应用上下文对象ApplicationContext context = new
ClassPathXmlApplicationContext("beans.xml"); LoginRepository loginRepository1 = (LoginRepository)
context.getBean("loginRepository"); LoginRepository loginRepository2 =
context.getBean(LoginRepository.class); System.out.printf("loginRepository: %s%n", loginRepository1 ==
loginRepository2);
//关闭容器
((ClassPathXmlApplicationContext) context).close();
}
}
当前类被 Spring 扫描到时,可以在方法上使用@Bean注解,通过方法返回类型,也可以定义、注册Bean对象,默认使用方法名作为Bean的名称。
先定义一个用户类 org.example.model.User ,注意这里没有使用任何Spring的注解,类不会被扫描到
package org.example.model
import lombok.Getter; import lombok.Setter; import lombok.ToString;
@Getter
@Setter
@ToString
public class User {
private String username; private String password;
}
在loginController 中定义两个用户类:
@Bean
public User user1(){
User user = new User(); user.setUsername("abc"); user.setPassword("123"); return user;
}
@Bean
public User user2(){
User user = new User(); user.setUsername("我不是汤神"); user.setPassword("tang"); return user;
}
同类型的Bean有多个时,注入该类型Bean需要指定Bean的名称:
以下 loginController 中定义了5个用户对象,且在方法参数中注入了Bean对象
package org.example.controller;
import org.example.model.User;
import org.example.service.LoginService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Controller;
@Controller
public class LoginController {
@Autowired
private LoginService loginService;
@Autowired
@Qualifier("user1")
private User u;
@Autowired
private User user1;
@Bean
public User user1(){
User user = new User();
user.setUsername("abc");
user.setPassword("123");
return user;
}
@Bean
public User user2(){
User user = new User();
user.setUsername("我不是汤神");
user.setPassword("tang");
return user;
}
@Bean
public User user3(LoginService loginService){
System.out.printf("user3: %s%n", loginService == this.loginService);
return new User();
}
@Bean
public User user4(User user1){
System.out.printf("user4: user1=%s%n", user1);
return new User();
}
@Bean
public User user5(@Qualifier("user2") User u){
System.out.printf("user5: user2=%s%n", u);
return new User();
}
}
在类被Spring扫描到时,使用@configuration 注解,可以注册一个配置类到容器中。配置类一般用来自定义配置某些资源。之后会在SpringMVC中用到。
FactoryBean接口实现如下:
FactoryBean以Bean结尾,表示它是一个Bean,不同于普通Bean的是:实现了FactoryBean接口的Bean,根据该Bean的ID从容器中获取的实际上是FactoryBean的getObject()返回的对象,而不是FactoryBean本身,如果要获取FactoryBean对象,请在id前面加一个&符号来获取。
示例:如下先定义一个Bean,实现FactoryBean接口,指定泛型为User类,会将getObject方法的返回对象注册到容器中。
package org.example.model;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.stereotype.Component;
@Component
public class ByFactoryBean implements FactoryBean<User> { @Override
public User getObject() throws Exception { User user = new User(); user.setUsername("abc"); user.setPassword("123");
return user;
}
@Override
public Class<?> getObjectType() { return User.class;
}
}
在org.example.App获取该Bean对象:
User user = (User) context.getBean("byFactoryBean");
System.out.printf("get bean by FactoryBean: %s%n", user);
因为已经定义过User对象了,所以只能通过 id 获取,通过类型获取会报错。
当前类被 Spring 扫描到时,可以在属性上使用**@Autowrid**注解,会将容器中的Bean对象装配进来。
package org.example.service;
import org.example.dao.LoginRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class LoginService {
@Autowired
private LoginRepository loginRepository; }
也可以在setter方法上使用@Autowrid注解:
说明:其实只要写在方法上的 @Autowrid注解,都会将容器中的Bean对象注入方法参数,setter注入本质也是这样,只是setter方法一般都是设置属性用的,所以也归到属性注入。
package org.example.service;
import org.example.dao.LoginRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class LoginServiceBySetter {
private LoginRepository loginRepository;
@Autowired
public void setLoginRepository(LoginRepository loginRepository) {
System.out.printf("LoginServiceBySetter: loginRepository=%s%n",
loginRepository);
this.loginRepository = loginRepository;
}
}
当前类被 Spring 扫描到时,可以在构造方法上使用@Autowrid注解(也可以不加@Autowrid注解),作用和setter类似,会将容器中的Bean对象注入方法参数。
package org.example.service;
import org.example.dao.LoginRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class LoginServiceByConstructor {
private LoginRepository loginRepository;
@Autowired
public LoginServiceByConstructor(LoginRepository loginRepository){
System.out.printf("LoginServiceByConstructor: %s%n",
loginRepository);
this.loginRepository = loginRepository;
}
}
Spring 容器在初始化一个 Bean 的实例时,同时会指定该实例的作用域。Spring有6个作用域,最后四种是基于Spring WebMVC生效,可用scope()指定使用某个作用域。
描述:该作用域下的Bean在IoC容器中只存在一个实例:获取Bean(即通过applicationContext.getBean等方法获取)及装配Bean(即通过@Autowired注入)都是同一个对象。
场景:通常无状态的Bean使用该作用域。无状态表示Bean对象的属性状态不需要更新
备注:Spring默认选择该作用域
描述:每次对该作用域下的Bean的请求都会创建新的实例:获取Bean(即通过applicationContext.getBean等方法获取)及装配Bean(即通过@Autowired注入)都是新的对象实例。
场景:通常有状态的Bean使用该作用域
描述:每次http请求会创建新的Bean实例,类似于prototype
场景:一次http的请求和响应的共享Bean
备注:限定SpringMVC中使用
描述:在一个http session中,定义一个Bean实例
场景:用户回话的共享Bean, 比如:记录一个用户的登陆信息
备注:限定SpringMVC中使用
描述:在一个http servlet Context中,定义一个Bean实例
场景:Web应用的上下文信息,比如:记录一个应用的共享信息
备注:限定SpringMVC中使用
描述:在一个HTTP WebSocket的生命周期中,定义一个Bean实例
场景:WebSocket的每次会话中,保存了一个Map结构的头信息,将用来包裹客户端消息头。第一
次初始化后,直到WebSocket结束都是同一个Bean。
备注:限定Spring WebSocket中使用
生命周期概览
Spring容器中Bean的生命周期,类似于流水线上制造一辆汽车,一台电脑,将繁多的工序流水线化、流程化以后,才可以方便的管理,如生产电脑经历了组装CPU,主板,显卡等等工序,需要在安装CPU以前检查是否有美国的监听器,就可以方便的到具体的流水线上某个点执行。Bean对象生命周期也是如此。
总结
对于Bean的生命周期,主要步骤为:
补充说明:第一步的实例化是指new对象,Spring的语义中说初始化Bean包含Bean生命周期中的初始
化步骤。
LifeCycleTest.java
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.*;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
public class LifeCycleTest implements InitializingBean, DisposableBean,
ApplicationContextAware, BeanNameAware, BeanFactoryAware {
public void beanInit(){
System.out.println("init-method: 注解是使用@Bean(initMethod=\"方法名\")的方式,xml是使用<bean init-method=\"方法名\" />");
}
@PostConstruct
public void postConstruct(){
System.out.println("@PostConstruct");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("InitializingBean");
}
public void beanDestroy(){
System.out.println("destroy-method: 注解是使用@Bean(destroyMethod=\"方法名\")的方式,xml是使用<bean destroy-method=\"方法名\" />");
}
@PreDestroy
public void preDestroy(){
System.out.println("@PreDestroy");
}
@Override
public void destroy() throws Exception {
System.out.println("DisposableBean");
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
System.out.println("ApplicationContextAware");
}
@Override
public void setBeanName(String name) {
System.out.println("BeanNameAware");
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
System.out.println("BeanFactoryAware");
}
}
Appconfig.java
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AppConfig implements BeanPostProcessor {
@Bean(initMethod = "beanInit", destroyMethod = "beanDestroy")
public LifeCycleTest lifeCycleTest(){
return new LifeCycleTest();
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("BeanPostProcessor Before");
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("BeanPostProcessor After");
return bean;
}
}
AppLifecycle.java
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class AppLifecycle {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("lifecycle.xml");
((ClassPathXmlApplicationContext) context).close();
}
}
运行结果:
原文链接:https://blog.csdn.net/qq_45661125/article/details/117638713
作者:快起来搬砖啦
链接:http://www.javaheidong.com/blog/article/222549/17573cf4751046b980a9/
来源:java黑洞网
任何形式的转载都请注明出处,如有侵权 一经发现 必将追究其法律责任
昵称:
评论内容:(最多支持255个字符)
---无人问津也好,技不如人也罢,你都要试着安静下来,去做自己该做的事,而不是让内心的烦躁、焦虑,坏掉你本来就不多的热情和定力
Copyright © 2018-2021 java黑洞网 All Rights Reserved 版权所有,并保留所有权利。京ICP备18063182号-2
投诉与举报,广告合作请联系vgs_info@163.com或QQ3083709327
免责声明:网站文章均由用户上传,仅供读者学习交流使用,禁止用做商业用途。若文章涉及色情,反动,侵权等违法信息,请向我们举报,一经核实我们会立即删除!