7.Spring

##1.Spring框架
框架:相当于一个毛胚房,那么拿过来,直接个性格化装修就行。

##1.2.解决的问题
Spring框架主要解决了创建对象和管理对象的问题。

比如:解除对象的耦合度,就是解除对象与对象之间的关系。

1. Spring管理的对象的作用域与生命周期(不常用)

由Spring管理的对象,默认都是单例的!并且,都是饿汉式的单例模式。

在配置<bean>节点时,可以添加scope属性其是否单例,当取值为singleton时表示单例,该值也是默认值,当取值为prototype时表示非单例:

<bean id="user" 
	class="cn.tedu.spring.User"
	scope="prototype"></bean>

在单例模式的基础之上,还可以通过lazy-init属性配置它是否为懒汉式的单例模式,默认值为false,即非懒汉式,也就是饿汉式的单例模式,当取值为true时,表示懒汉式的单例模式:

<bean id="user" 
	class="cn.tedu.spring.User"
	scope="singleton"
	lazy-init="true"></bean>

如果某个类被配置为单例模式,还可以配置它的生命周期方法:首先,在类中声明2个方法,这2个方法都应该是public方法,返回值都是void,方法名称可以自由定义,方法必须没有参数,例如:

public void init() {
	System.out.println("User.init()");
}

public void destroy() {
	System.out.println("User.destroy()");
}

然后,在Spring的配置文件中,在<bean>节点中配置init-methoddestroy-method属性,即可配置初始化方法和销毁方法,这2个属性的取值都是需要调用的方法的名称:

<bean id="user" 
	class="cn.tedu.spring.User"
	scope="singleton"
	lazy-init="true"
	init-method="init"
	destroy-method="destroy"></bean>

2. Spring的IoC

2.1. 什么是IoC

IoC表示Inversion of control,即“控制反转”。传统模式下,对象的创建与管理都是由开发人员编写代码直接完成的,而使用Spring后,将创建与管理交给了框架,则称之为控制反转。

其中,比较重要的环节是为对象的某些属性进行赋值,称之为DI,即Dependency Injection,表示“依赖注入”,通俗的来说,是为其属性赋值,也称之为“为其属性注入值”。

Spring通过DI实现了IoC,即DI是实现手段,而IoC是需要实现的目标。

2.2. 通过SET方式注入属性的值

假设User类中有名为name的属性,需要为该属性注入值,首先,需要为该属性添加SET/GET方法(其实只有SET方法是必须的):

public void setName(String name) {
	this.name = name;
}

然后,在<bean>节点子级添加<property>节点进行配置:

<bean id="user" 
	class="cn.tedu.spring.User">
	<!-- 使用property节点为属性注入值 -->
	<!-- name:属性名 -->
	<!-- value:属性值 -->
	<property name="name" value="Kitty"></property>
</bean>

其实,框架在处理时,发现有<property>节点,就会尝试为属性赋值,它会基于该节点的name属性值得到SET方法的名称,规则就是set加上属性名且首字母改为大写,得到SET方法名称,如果属性名称是name,则框架将调用的方法就是setName,如果属性名称是password,则框架将调用的方法已经setPassword,然后,将value属性对应的值,作为将调用的方法的参数,以上示例代码中的配置,使得框架将调用对象.setName("Kityy");语句。

所以,在<property>节点中,name属性配置的其实是SET方法的方法名右侧的部分,且首字母改为小写!

但是,在实际使用时,SET方法都是由开发工具自动生成,生成规则与Spring框架处理时的规则是完全相同的,所以,也可以简单的认为<property>节点中配置的name就是属性的名称!

在某些情况下,需要注入的属性值并不是基本值(可以直接书写的值,例如数值、字符串等),而是另一个类的对象时,可以先使得Spring也管理另一个类的对象,然后,注入值时,通过ref属性引用那个<bean>即可:

[外链图片转存失败(img-N48XNp3P-1563607993417)(01.png)]

2.3. 通过构造方法注入属性的值(不常用)

如果某个属性是通过构造方法设置值的,例如:

public class Person {

	// 25
	private Integer age;

	public Person(Integer age) {
		super();
		this.age = age;
	}

	@Override
	public String toString() {
		return "Person [age=" + age + "]";
	}

}

则在配置时,应该使用<constructor-arg>节点进行配置:

<bean id="person" class="cn.tedu.spring.Person">
	<constructor-arg index="0" value="25" />
</bean>

以上属性的配置中,index表示第几个参数,从0开始顺序编号,然后,根据值的类型选择使用valueref属性进行配置即可!

2.4. 注入集合类型的值

如果某个类中的属性是List集合类型的,并需要注入值:

public class SampleBean {
	
	// Alex, Lucy, Kitty, Henry
	public List<String> names;

	public void setNames(List<String> names) {
		this.names = names;
	}

}

然后,在Spring的配置文件中:

<bean id="sampleBean"
	class="cn.tedu.spring.SampleBean">
	<property name="names">
		<list>
			<value>Alex</value>
			<value>Lucy</value>
			<value>Kitty</value>
			<value>Henry</value>
		</list>
	</property>
</bean>

如果需要注入Set类型的值,例如:

// Beijing, Shanghai, Guangzhou, Shenzhen
public Set<String> cities;

在配置注入时,使用<set>节点即可:

<property name="cities">
	<set>
		<value>Beijing</value>
		<value>Shanghai</value>
		<value>Guangzhou</value>
		<value>Shenzhen</value>
	</set>
</property>

另外,关于Map类型集合的配置例如:

<property name="session">
	<map>
		<entry key="username" value="Jack" />
		<entry key="password" value="1234" />
		<entry key="from" value="Nanjing" />
	</map>
</property>

关于数组类型集合的配置例如:

<property name="numbers">
	<array>
		<value>9</value>
		<value>5</value>
		<value>2</value>
		<value>7</value>
	</array>
</property>

在配置数组时,也可以使用<list>节点,反之,在配置List集合时,也可以使用<array>节点,但是,推荐使用匹配的节点进行配置。

关于Properties类型的配置:

<property name="config">
	<props>
		<prop key="driver">com.mysql.jdbc.Driver</prop>
		<prop key="username">root</prop>
		<prop key="password">root</prop>
	</props>
</property>

在配置以上集合类型的值时,也可以事先使用例如<util:list>这类节点先将值配置好:

<util:list id="names">
	<value>Tom</value>
	<value>Alex</value>
	<value>Lucy</value>
	<value>Kitty</value>
	<value>Henry</value>
</util:list>

然后再注入到属性中:

<property name="names" ref="names" />

比较特殊的是读取Properties类型的数据,在Spring中,可以通过<util:properties>节点的location属性指定需要读取的文件:

<util:properties id="config"
	location="classpath:db.properties" />

然后,就可以注入到相应的属性中:

<property name="config" ref="config" />

1. Spring表达式

当某个Bean的某些属性值来自于另一个Bean的某些属性,则可以使用Spring表达式,例如:

public class ValueBean {

	// SampleBean中names的第3个值
	public String name;
	// SampleBean中session的from
	public String from;
	// SampleBean中config的driver
	public String driver;

	// ...

则,首先,需要确定注入值的方式,例如通过SET方式注入,则需要为这些属性添加SET方法:

public void setName(String name) {
	this.name = name;
}

public void setFrom(String from) {
	this.from = from;
}

public void setDriver(String driver) {
	this.driver = driver;
}

然后,在Spring的配置文件中进行配置:

<bean id="valueBean"
	class="cn.tedu.spring.ValueBean">
	<property name="name" 
		value="#{sampleBean.names[2]}" />
	<property name="from"
		value="#{sampleBean.session.from}" />
	<property name="driver"
		value="#{sampleBean.config.driver}" />
</bean>

Spring表达式的基本语法格式是使用#{},其内部的编写方式取决于获取哪些值。

如果需要获取数组或List集合中的某个元素:

#{bean-id.数组或list集合名称[下标]}

如果需要获取Map或Properites中的某个元素:

#{bean-id.Map或Properties名.属性名}

也可以是:

#{bean-id.Map或Properties名['属性名']}

2. Spring自动装配(不推荐)

可以配置Spring中的<bean>节点中的autowire属性,使之尝试自动为其属性注入值,而不再需要使用<property>节点进行配置,减少配置的代码量。

该属性的取值可以是byName,表示将根据名称实现自动装配,要求被装配的属性有SET方法,且SET方法名称右侧的部分与某个bean的id是匹配的!

该属性的取值还可以是byType,表示将根据类型实现自动装配,即Spring会在容器管理范围之内查找类型匹配的对象,并尝试实现装配。

使用byType实现自动装配时,必须保证在Spring管理的范围之内,匹配类型的对象只有1个,如果超过1个,则程序会报错!

关于autowire属性还可以配置其它值,一般不关心这些问题。

并且,这种自动装配的做法其实是不推荐的!因为仅仅只使用autowire属性进行配置,某个类的哪些属性已装配、哪些属性未装配是不明确的!另外,自动装配会尝试为所有属性装配值,但是,也许某些属性是不希望被装配值的!

3. Spring注解

3.1. 通用注解

如果某个类需要被Spring创建对象并进行管理,首先,应该在Spring的配置文件中添加组件扫描的配置,告诉Spring框架需要扫描哪个包中的类:

<!-- 组件扫描 -->
<context:component-scan 
	base-package="cn.tedu.spring" />

然后,确保那些需要被Spring创建对象并进行管理的类在这个包中,并在类的声明之前添加@Component注解即可:

package cn.tedu.spring;

import org.springframework.stereotype.Component;

@Component
public class UserServlet {
}

在单元测试中:

public class Tests {

	@Test
	public void test() {
		ClassPathXmlApplicationContext ac
			= new ClassPathXmlApplicationContext(
				"spring.xml");
		
		UserServlet userServlet
			= ac.getBean("userServlet", UserServlet.class);
			
		System.out.println(userServlet);

		ac.close();
	}

}

以上组件扫描的配置中,配置的是需要扫描的根包,例如配置为cn.tedu.spring,则其子包cn.tedu.spring.dao也会在扫描范围之内!

被Spring管理的对象,默认使用的bean-id就是将类名的首字母转为小写的名称,例如类名是UserSerlvet,则它的bean-id就是userServlet,也可以在@Component注解中显式的配置bean-id:

@Component("servlet")
public class UserServlet {
}

@Component相关的注解还有:@Controller通常添加在控制器类之前,@Service通常添加在业务类之前的,@Repository通常添加在处理持久层的类之前,它们的作用和使用方式是相同的!另外,@Component通常添加在其它定位的类之前。

3.2. 关于作用域和生命周期的注解(不常用)

通过@Scope注解可以配置某个类的对象是否为单例,如果需要配置为非单例的,可以在类的声明之前:

@Scope("prototype")

如果需要是单例的,可以是@Scope("singleton"),或者@Scope,甚至完全不配置这个注解!

在类之前添加@Lazy注解可以设置为单例模式的懒汉单例。在@Lazy中也可以配置布尔值,例如@Lazy(false)表示非懒汉式,而@Lazy(true)表示懒汉式,但是,没有必要添加详细配置。

还可以通过@PostConstruct@PreDestroy配置生命周期方法:

@PostConstruct
public void init() {
	System.out.println("UserDao.init()");
}

@PreDestroy
public void destroy() {
	System.out.println("UserDao.destroy()");
}

注意:这2个注解是JavaEE中的注解,并不是Spring的注解,在使用之前,需要添加Tomcat运行环境,以导入JavaEE相关的jar包,才可以使用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员西柚柚

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值