前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >spring应用篇

spring应用篇

作者头像
用针戳左手中指指头
发布2021-01-29 10:39:50
4230
发布2021-01-29 10:39:50
举报
文章被收录于专栏:学习计划

文章目录

控制反转和依赖注入

spring有名的就是控制反转和依赖注入了,而其真正的意思我并未真正了解过。

官网开篇说:控制反转就是依赖注入

IoC is also known as dependency injection (DI)

翻译过来就是:控制反转(IOC)也称依赖注入(DI)。

这里涉及到几个概念:

容器:在spring应用中,对象生存在spring容器中,由容器负责创建、装配对象和管理它们的生命周期。

bean:属于应用程序的一部分,且由容器管理的对象成为bean。bean一定是java对象,但java对象不一定是bean。

控制反转: 对象仅通过构造函数参数、工厂方法的参数或从工厂方法构造或返回对象后在对象实例上设置的属性来定义其依赖关系(即它们使用的其他对象) ,而容器在创建bean时,注入这些依赖项,从人为手动控制,变成由容器控制,从过程上来说是bean的反向,故称控制反转。

这里有一个知识点:bean标签里的property标签里的name并不是对应bean对象里的属性名称,它对应的是set方法的名称,就如下面的例子一样,property里的name属性值跟着set方法改变而改变。

代码语言:javascript
复制
public class AService {
}
代码语言:javascript
复制
public class BService {

    private AService aService333;

    public AService getaService() {
        return aService333;
    }

    public void setaService(AService aService) {
        this.aService333 = aService;
    }
}

这种方式是手动注入:显示的将bean设置到某一个bean单中。

代码语言:javascript
复制
<?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="aService" class="com.lry.spring.service.AService">
    </bean>

    <bean id="bService" class="com.lry.spring.service.BService">
        <property name="aService" ref="aService"/>
    </bean>
</beans>

自动注入

在xml配置了它有四种自动注入模型

  • no:默认装配方式,不使用自动装配
  • byName:按名字装配
  • byType:按类型装配
  • construction:按构造器装配

下面xml方式自动注入:

代码语言:javascript
复制
<?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"
       <!--这里指定装配类型为byType-->
        default-autowire="byType"
>

    <bean id="aService" class="com.lry.spring.service.AService">
    </bean>

    <bean id="bService" class="com.lry.spring.service.BService">
<!--        <property name="aService" ref="aService"/>-->
    </bean>
</beans>

使用byType、byName模型,都需要设置setter方法。

我们常使用的@Autowired这个注解,从严格意义上说不应该叫做自动注入,为什么这么说?

image-20200920223839160
image-20200920223839160

如果,我们将它的autowireModel改成2(可以到AutowireCapableBeanFactory查看对应的值),上面代码增加下面这一行:

代码语言:javascript
复制
        bService.setAutowireMode(2);

结果已经注入了。

image-20200920224952140
image-20200920224952140

这里要区分注入模型,和注入方式,两种方式在找bean和注入bean的技术不一样,一般说的注入方式指的是手动注入,那么手动注入的方式有两种:构造器和setter方法。

手动注入

setter方法注入

setter方法注入方式很简单,就需要注入的属性必须创建set方法。

xml方式注入和上面的例一样,通过property标签里的ref链接bean对象。

代码语言:javascript
复制
<property name="aService" ref="aService"/>

注解的方式使用Resource和Autowired,两者区别在于Resource是JDK实现,他会先按名称查找,然后再按类型查找,而Autowired有Spring实现,先按类型查找,再按名称查找,而且找不到时会抛出异常。

代码语言:javascript
复制
    @Autowired
    private AService aService;

或者是:

代码语言:javascript
复制
@Autowired
    public void setaServicesss(AService aService) {
        this.aService = aService;
    }

构造器注入

基于构造器注入的方式和setter方式的注入会有些不同;构造器的方式支持索引。

  • 一般构造器的注入方式

定义构造器了参数对象,如果有多个构造器,每个构造器有多个参数,多个类型,很难控制注入的构造器时哪一个,所以会提供了这些功能。

代码语言:javascript
复制
 public BService(AService aService) {
        this.aService = aService;
    }
代码语言:javascript
复制
    <bean id="aService" class="com.lry.spring.service.AService">
    </bean>

    <bean id="bService" class="com.lry.spring.service.BService">
        <constructor-arg ref="aService"/>
    </bean>
  • 构造器参数有基本类型参数

使用构造器注入他会匹配参数,使用基本类型作为参数,无法确定值的类型,所以应该想下面设置类型(type)去匹配参数:

代码语言:javascript
复制
 public BService(int a, AService aService) {
        this.a = a;
        this.aService = aService;
    }
代码语言:javascript
复制
  <bean id="bService" class="com.lry.spring.service.BService">
        <constructor-arg ref="aService"/>
        <constructor-arg type="int" value="1"/>
    </bean>
  • 指定参数索引位置

相同类型参数时无法确定参数位置,可以使用索引方式注入

代码语言:javascript
复制
public BService(int a, int c){
        
    }
代码语言:javascript
复制
<bean id="bService" class="com.lry.spring.service.BService">
        <constructor-arg index="0" value="1"/>
        <constructor-arg index="1" value="2"/>
    </bean>
  • 参数名称指定
代码语言:javascript
复制
 <bean id="bService" class="com.lry.spring.service.BService">
        <constructor-arg name="a" value="1"/>
        <constructor-arg name="c" value="2"/>
    </bean>

官网上说需要ConstructorProperties注解显示指定参数名称,我测试的例子,在没有这个注解时也成功注入了

代码语言:javascript
复制
@ConstructorProperties({"c", "a"})
    public BService(int a, int c){
        System.out.println("a : " + a);
        System.out.println("c : " + c);
    }

构造参数为基本类型的注入方式,我想不出这有什么实际应用场景,但作为了解也好。

bean的作用域

spring创建bean的方式:

  • 构造行数实例化
  • 静态工厂方法进行实例化
  • 实例工厂方法实例化

bean有6个作用域:

  • singleton 单例,只有new出了就不再new了;默认模式
  • prototype 原型对象,每次获取都会new一个
  • request 每个http请求生命周期内new一个
  • session 每个http请求生命周期内new一个
  • application 整个应用内new一个
  • websocket

单例和原型模式,要在bean上加上注解@Scope(ConfigurableBeanFactory.SCOPE_SINGLETON),request、session这些模式,有针对的注解@RequestScope@SessionScope,当然也可以自定义一个作用域,

方法注入

一般使用@Autowire、@Resource、@Component等这些注解都是单例的signleton

如果在单例bean中注入一个设定了prototype的bean,那么它也成了单例的,要实现在单例bean中多次获取不同的实例,可以通过方法注入实现;

  1. 通过实现ApplicationContextAware接口,或是注入ApplicationContext对象,然后通过它调用getBean方法去创建一个bean,
代码语言:javascript
复制
@Component
public class SpringUtils {

    @Autowired
    private ApplicationContext applicationContext;

    public <T> T getBean(String beanName, Class<T> tClass){
        return applicationContext.getBean(beanName, tClass);
    }

}
代码语言:javascript
复制
@Component
public class SpringUtils implements ApplicationContextAware {

    private ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    public <T> T getBean(String beanName, Class<T> tClass){
        return applicationContext.getBean(beanName, tClass);
    }
}
  1. 通过Lookup

首先要获取的bean要设置scope为prototype

代码语言:javascript
复制
@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class AService implements IService {
    public AService() {
        System.out.println("this aService hascode" + this);
    }
}
代码语言:javascript
复制
@Component
public class CService {

    @Lookup
    public AService getAService() {return null;}
}

官网给的格式是这样的:

<public|protected> [abstract] theMethodName(no-arguments);

也可以定义为抽象方法:

代码语言:javascript
复制
@Component
public abstract class CService {

    @Lookup
    public abstract AService getAService();
}
image-20200920145019392
image-20200920145019392

xml的配置方式:

代码语言:javascript
复制
  <bean id="aService" class="com.lry.spring2.service.Impl.AService" scope="prototype"/>
  <bean id="cService" class="com.lry.spring2.service.Impl.CService">
        <lookup-method name="getAService" bean="aService"/>
  </bean>

生命周期回调

有3种回调:初始化回调、销毁回调和启动关闭回调。

bean 的生命周期回调有两个:初始化回调和销毁回调;容器的回调有启动和销毁回调。

  • 初始化回调
  1. 第一种方式需要实现InitializingBean类的afterPropertiesSet方法;
代码语言:javascript
复制
public class DService implements InitializingBean {
    @Override
    public void afterPropertiesSet() throws Exception {
		//...
    }
}
  1. 第二种方式使用注解@PostConstruct 注解在任意方法上,在初始化后就会执行该方法。
  2. 第三种方式使用xml的配置方式; 这里我在bean里定义了一个post方法,用作初始化后执行。
代码语言:javascript
复制
 <bean id="dService" class="com.lry.spring2.service.Impl.DService" init-method="post"/>
  • 销毁回调 和初始化回调一样,同样有实现类(DisposableBean)、注解(@PreDestroy)和xml配置方式(destroy-method),
  • 启动和关闭回调 需要实现Lifecycle或者SmartLifecycle Lifecycle有3个方法
代码语言:javascript
复制
// isRunning 返回false时执行 
void start();
// isRunning 返回true时执行
 void stop();
// 当返回true是表示启动中,可以调用stop方法,
// 当放回false是表示没有启动,可以调用start方法
boolean isRunning();

当容器启动完成后,会调用Lifecycle的start方法,关闭时会调用stop方法,但是如果程序退出,容器也跟着退出,监听不到容器,就不会执行stop方法。 在执行start和stop方法之前,会先调用isRunning判别bean是否启动。 它不好的地方在于,需要显示调用start和stop方法,但一般的web项目都是使用了springboot、springMvc等,容器入口得被封装起来了,无法去调用这些方法,这时候就扩展出了SmartLifecycle,它不需要显示调用start方法,可以控制多个smartLifecycle实例的调用顺序。

SmartLifecycle有6个方法,3个是新增的,其他3个是lifeCycle里的。

代码语言:javascript
复制
// 返回true表示会自动执行start方法
boolean isAutoStartup();
// 容器关闭是调用;使用这个方法的时候,需要调用callback.run() 去结束程序;
// 只调用stop(),默认会等待30秒,等待所有任务结束才会关闭,调用callback会立刻退出
void stop(Runnable callback);
// 多个smartlifecycle实例存在,会按照该方法返回的值进行优先级调用;
// 值越小,执行start方法的优先级越高,值越大,执行stop方法的优先级越高
int getPhase();
// 在容器初始化完成,会调用finishRefresh方法,里面通过Lifecycle处理器,获取到所有的smartLifecycle,然后调用start方法。
void start();
void stop();
boolean isRunning();

同一个bean中用了不同方式配置,那么它的执行顺序应该是:

1.注解@PostConstruct

2.InitializingBean类的afterPropertiesSet方法

3.xml配置的方法

销毁也是相同顺序。

类路径扫描和组件管理

大型应用会创建静态索引,是扫描更快,但需要加一个content-index jar包

inject jar包可以替代spring的注解,可能会有人不喜欢Autowired注解,可以换成inject里的,也可以自定义注入注解(反射)

注解的使用可以让我们省掉很多代码,并且代码看起来很更加简洁干净,spring中就用了很多注解,像@Autowired、@Service、@Controller、@Component等等,然后通过componentScan去扫描这些类,或是使用xml方式配置扫描。

代码语言:javascript
复制
@ComponentScan(basePackages = "com.lry.spring2",
               // 包含的指定的类
        includeFilters = @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = AService.class),
               // 根据正则排除某些类
        excludeFilters = @ComponentScan.Filter(type = FilterType.REGEX, pattern = "com.lry.*"))

类路径扫描的方式很快,但是在大型项目中,需要扫描的bean比较多,可以引用spring-context-indexer包,它会生成静态索引使得应用启动加载更快。

代码语言:javascript
复制
<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context-indexer</artifactId>
        <version>5.2.9.RELEASE</version>
        <optional>true</optional>
    </dependency>
</dependencies>

我们在使用spring的最常用到的就是Autowired、Resource、Service等等注解,其实也可以替换成其他注解,相同的功能。需要引入jar包,它属于JSR 330规范。

代码语言:javascript
复制
<dependency>
    <groupId>javax.inject</groupId>
    <artifactId>javax.inject</artifactId>
    <version>1</version>
</dependency>

之前使用的@Autowired就可以替换成@Inject,可以通过@Named指定要注入的bean的名称,@Component等效于@Named@ManagedBean

代码语言:javascript
复制
// 可以不指定名称
@Named
public class EService {

    @Inject
    // 这个named可以不用,不过根据名称来指定注入需要
    @Named("ffService")
    private FService fService;

    public void print() {
        System.out.println("fService :" + fService);
    }
}
代码语言:javascript
复制
@Named("ffService")
// 作为bean的注解 @ManageBean("ffService")也有同样的效果
public class FService {
}

扫描bean的注解都是@ComponentScan

spring

javax.*

说明

@Autowired

@Inject

@Inject按类型装配,如果需要指定名称需要配合@Named使用,而@Autowired是先按类型装配,找不到就按名称。

@Component

@Named/@ManagedBean

指定bean。

@Scope(“singleton”)

@Singleton

JSR 330规范中默认是Singleton,若要设置其他类型应该使用spring中的@Scope。

@Qualifier

@Qualifier / @Named

确定注入的具体类。

@Value

-

@Required

-

@Lazy

-

把业务对象放到spring容器中的方式:

我的博客即将同步至腾讯云+社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=2x3a8wy49pwkc

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021/01/17 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 文章目录
  • 控制反转和依赖注入
    • 自动注入
      • 手动注入
        • setter方法注入
        • 构造器注入
    • bean的作用域
    • 方法注入
    • 生命周期回调
    • 类路径扫描和组件管理
    相关产品与服务
    容器服务
    腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档