spring自定义作用域 依赖注入之手动注入

自定义作用域

  1. spring容器自带的有2种作用域,分别是singleton和prototype;还有3种分别是spring web容器
    环境中才支持的request、session、application
  2. singleton是spring容器默认的作用域,一个spring容器中同名的bean实例只有一个,多次获取得
    到的是同一个bean;单例的bean需要考虑线程安全问题
  3. prototype是多例的,每次从容器中获取同名的bean,都会重新创建一个;多例bean使用的时候
    需要考虑创建bean对性能的影响
  4. 一个应用中可以有多个spring容器
  5. 自定义scope 3个步骤,实现Scope接口,将实现类注册到spring容器,使用自定义的sope
    我们来看一下这个接口定义
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.lang.Nullable;
public interface Scope {
    
    
/**
* 返回当前作用域中name对应的bean对象
* name:需要检索的bean的名称
* objectFactory:如果name对应的bean在当前作用域中没有找到,那么可以调用这个
ObjectFactory来创建这个对象
**/
Object get(String name, ObjectFactory<?> objectFactory);
/**
* 将name对应的bean从当前作用域中移除
**/
@Nullable
Object remove(String name);
/**
* 用于注册销毁回调,如果想要销毁相应的对象,则由Spring容器注册相应的销毁回调,而由自定义作
用域选择是不是要销毁相应的对象
*/
void registerDestructionCallback(String name, Runnable callback);
/**
* 用于解析相应的上下文数据,比如request作用域将返回request中的属性。
*/
@Nullable
Object resolveContextualObject(String key);
/**
* 作用域的会话标识,比如session作用域将是sessionId
*/
@Nullable
String getConversationId();
}

将实现类注册到spring容器需要调用org.springframework.beans.factory.config.ConfigurableBeanFactory#registerScope的方
法,看一下这个方法的声明

依赖注入之手动注入

通常情况下,系统中类和类之间是有依赖关系的,如果一个类对外提供的功能需要通过调用其他类的方法来实现的时候,说明这两个类之间存在依赖关系
spring容器内部都提供了支持,这个在spirng中将被依赖的对象设置到依赖的对象中,叫做依赖注入。
spring中依赖注入主要分为手动注入和自动注入,手动注入需要我们明确配置需要注入的对象。

  • 通过构造器注入
    构造器的参数就是被依赖的对象,构造器注入又分为3种注入方式:
    根据构造器参数索引注入
    根据构造器参数类型注入
    根据构造器参数名称注入

根据构造器参数索引(位置)注入

用法

UserController userController = new UserController();
userController.setService(userService);
//然后就可以使用userController对象了
<bean id="diByConstructorParamIndex"
class="com.javacode2018.lesson001.demo5.UserModel">
<constructor-arg index="0" value="xx"/>
<constructor-arg index="1" value="上海市"/>
</bean>

constructor-arg用户指定构造器的参数
index:构造器参数的位置,从0开始
value:构造器参数的值,value只能用来给简单的类型设置值,value对应的属性类型只能为byte,int,long,float,double,boolean,Byte,Long,Float,Double,枚举,spring容器内部注入的时候会将value的值转换为对应的类型。

UserModel.java

public class UserModel {
    
    
private String name;
private int age;
//描述信息
private String desc;
public UserModel() {
    
    
}
public UserModel(String name, String desc) {
    
    
this.name = name;
this.desc = desc;
}
public UserModel(String name, int age, String desc) {
    
    
this.name = name;
this.age = age;
this.desc = desc;
}
@Override
public String toString() {
    
    
return "UserModel{" +
"name='" + name + '\'' +
", age=" + age +
", desc='" + desc + '\'' +
'}';
}
}

diByConstructorParamIndex.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd">
<!-- 通过构造器参数的索引注入 -->
<bean id="diByConstructorParamIndex"
class="com.javacode2018.lesson001.demo5.UserModel">
<constructor-arg index="0" value="路人甲Java"/>
<constructor-arg index="1" value="我是通过构造器参数位置注入的"/>
</bean>
</beans>

上面创建UserModel实例代码相当于下面代码:

UserModel userModel = new UserModel("路人甲Java","我是通过构造器参数类型注入的");

工具类IoUtils.java

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class IocUtil {
    
    
public static ClassPathXmlApplicationContext context(String beanXml) {
    
    
return new ClassPathXmlApplicationContext(beanXml);
}
}

测试用例DiTest.java

import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* 公众号:路人甲Java,工作10年的前阿里P7分享Java、算法、数据库方面的技术干货!坚信用技术改变
命运,让家人过上更体面的生活!
*/
public class DiTest {
    
    
/**
* 通过构造器的参数位置注入
*/
@Test
public void diByConstructorParamIndex() {
    
    
String beanXml =
"classpath:/com/javacode2018/lesson001/demo5/diByConstructorParamIndex.xml";
ClassPathXmlApplicationContext context = IocUtils.context(beanXml);
System.out.println(context.getBean("diByConstructorParamIndex"));
}
}

参数位置的注入对参数顺序有很强的依赖性,若构造函数参数位置被人调整过,会导致注入出错。
不过通常情况下,不建议去在代码中修改构造函数,如果需要新增参数的,可以新增一个构造函数来实现,这算是一种扩展,不会影响目前已有的功能

根据构造器参数类型注入

用法:

<bean id="diByConstructorParamType"
class="com.javacode2018.lesson001.demo5.UserModel">
<constructor-arg type="参数类型" value="参数值"/>
<constructor-arg type="参数类型" value="参数值"/>
</bean>

constructor-arg用户指定构造器的参数
type:构造函数参数的完整类型,如:java.lang.String,int,double
value:构造器参数的值,value只能用来给简单的类型设置值

案例
diByConstructorParamType.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd">
<!-- 通过构造器参数的类型注入 -->
<bean id="diByConstructorParamType"
class="com.javacode2018.lesson001.demo5.UserModel">
<constructor-arg type="int" value="30"/>
<constructor-arg type="java.lang.String" value="路人甲Java"/>
<constructor-arg type="java.lang.String" value="我是通过构造器参数类型注入
的"/>
</bean>
</beans>

相当于:

UserModel userModel = new UserModel("路人甲Java",30,"我是通过构造器参数类型注入的");

新增测试用例
DiTest类中新增一个测试用例

@Test
public void diByConstructorParamType() {
    
    
String beanXml =
"classpath:/com/javacode2018/lesson001/demo5/diByConstructorParamType.xml";
ClassPathXmlApplicationContext context = IocUtils.context(beanXml);
System.out.println(context.getBean("diByConstructorParamType"));
}

运行diByConstructorParamType输出
UserModel{name=‘路人甲Java’, age=30, desc=‘我是通过构造器参数类型注入的’}

实际上按照参数位置或者按照参数的类型注入,都有一个问题,很难通过bean的配置文件,知道这个参数是对应UserModel中的那个属性的,代码的可读性不好,比如我想知道这每个参数对应UserModel中的那个属性,必须要去看UserModel的源码,下面要介绍按照参数名称注入的方式比上
面这2种更优秀一些。

根据构造器参数名称注入

<bean id="diByConstructorParamName"
class="com.javacode2018.lesson001.demo5.UserModel">
<constructor-arg name="参数类型" value="参数值"/>
<constructor-arg name="参数类型" value="参数值"/>
</bean>

constructor-arg用户指定构造器的参数
name:构造参数名称
value:构造器参数的值,value只能用来给简单的类型设置值

参数名称可能不稳定的问题,spring提供了解决方案,通过ConstructorProperties注解来定义参数的名称,将这个注解加在构造方法上面,如下:
@ConstructorProperties({“第一个参数名称”, “第二个参数的名称”,…“第n个参数的名称”})
public 类名(String p1, String p2…,参数n) {
}
案例
CarModel.java

public class CarModel {
private String name;
//描述信息
private String desc;
public CarModel() {
}
@ConstructorProperties({"name", "desc"})
public CarModel(String p1, String p2) {
this.name = p1;
this.desc = p2;
}
@Override
public String toString() {
return "CarModel{" +
"name='" + name + '\'' +
", desc='" + desc + '\'' +
'}';
}
}

diByConstructorParamName.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd">
<!-- 通过构造器参数的名称注入 -->
<bean id="diByConstructorParamName"
class="com.javacode2018.lesson001.demo5.CarModel">
<constructor-arg name="desc" value="我是通过构造器参数类型注入的"/>
<constructor-arg name="name" value="保时捷Macans"/>
</bean>
</beans>

相当于:

CarModel carModel = new CarModel("保时捷Macans","我是通过构造器参数类型注入的");

增加测试类:

@Test
public void diByConstructorParamName() {
    
    
String beanXml =
"classpath:/com/javacode2018/lesson001/demo5/diByConstructorParamName.xml";
ClassPathXmlApplicationContext context = IocUtils.context(beanXml);
System.out.println(context.getBean("diByConstructorParamName"));
}

输出:
CarModel{name=‘保时捷Macans’, desc=‘我是通过构造器参数类型注入的’}
通常情况下,我们的类都是标准的javabean,javabean类的特点:
属性都是private访问级别的
属性通常情况下通过一组setter(修改器)和getter(访问器)方法来访问
setter方法,以set 开头,后跟首字母大写的属性名,如:setUserName,简单属性一般只有一个方法参数,方法返回值通常为void;
getter方法,一般属性以get 开头,对于boolean类型一般以is 开头,后跟首字母大写的属性名,如:getUserName,isOk;
spring对符合javabean特点类,提供了setter方式的注入,会调用对应属性的setter方法将被依赖的对象注入进去。
型。
若我们依赖的对象是容器中的其他bean对象的时候,需要用下面的方式进行注入。
注入容器中的bean
注入容器中的bean有两种写法:
ref属性方式
内置bean的方式

  • ref属性方式
    将上面介绍的constructor-arg或者property元素的value属性名称替换为ref,ref属性的值为容器中其他bean的名称,如:
    构造器方式,将value替换为ref:
<constructor-arg ref="需要注入的bean的名称"/>

setter方式,将value替换为ref:

<property name="属性名称" ref="需要注入的bean的名称" />
  • 内置bean的方式
    构造器的方式:
<constructor-arg>
<bean class=""/>
</constructor-arg>

setter:

<property name="属性名称">
<bean class=""/>
</property>

案例
PersonModel.java

public class PersonModel {
    
    
private UserModel userModel;
private CarModel carModel;
public PersonModel() {
    
    
}
public PersonModel(UserModel userModel, CarModel carModel) {
    
    
this.userModel = userModel;
this.carModel = carModel;
}
public UserModel getUserModel() {
    
    
return userModel;
}
public void setUserModel(UserModel userModel) {
    
    
this.userModel = userModel;
}
public CarModel getCarModel() {
    
    
return carModel;
}
public void setCarModel(CarModel carModel) {
    
    
this.carModel = carModel;
}
@Override
public String toString() {
    
    
return "PersonModel{" +
"userModel=" + userModel +
", carModel=" + carModel +
'}';
}
}

PersonModel中有依赖于2个对象UserModel、CarModel,下面我们通过spring将UserModel和CarModel创建好,然后注入到PersonModel中,下面创建bean配置文件
diBean.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd">
<bean id="user" class="com.javacode2018.lesson001.demo5.UserModel"></bean>
<!-- 通过构造器方式注入容器中的bean -->
<bean id="diBeanByConstructor"
class="com.javacode2018.lesson001.demo5.PersonModel">
<!-- 通过ref引用容器中定义的其他bean,user对应上面定义的id="user"的bean -->
<constructor-arg index="0" ref="user"/>
<constructor-arg index="1">
<bean class="com.javacode2018.lesson001.demo5.CarModel">
<constructor-arg index="0" value="宾利"/>
<constructor-arg index="1" value=""/>
</bean>
</constructor-arg>
</bean>

<!-- 通过setter方式注入容器中的bean -->
<bean id="diBeanBySetter"
class="com.javacode2018.lesson001.demo5.PersonModel">
<!-- 通过ref引用容器中定义的其他bean,user对应上面定义的id="user"的bean -->
<property name="userModel" ref="user"/>
<property name="carModel">
<bean class="com.javacode2018.lesson001.demo5.CarModel">
<constructor-arg index="0" value="保时捷"/>
<constructor-arg index="1" value=""/>
</bean>
</property>
</bean>
</beans>

测试类:

@Test
public void diBean(){
    
    
String beanXml = "classpath:/com/javacode2018/lesson001/demo5/diBean.xml";
ClassPathXmlApplicationContext context = IocUtils.context(beanXml);
System.out.println(context.getBean("diBeanByConstructor"));
System.out.println(context.getBean("diBeanBySetter"));
}

出自公众号路人甲java

猜你喜欢

转载自blog.csdn.net/qq_41358574/article/details/113854438