延迟初始化
Spring默认的容器在启动时会将
所有的Bean都初始化完成,
所以我们设置延迟初始化只是告诉容器
在第一次使用Bean时完后初始化,而非启动时完成。
<!--延迟初始化-->
<bean id="lazyExampleBean" class="com.dyy.springcore.LazyExampleBean" lazy-init="true"/>
LazyExampleBean lazyExampleBean = (LazyExampleBean) context.getBean("lazyExampleBean");
System.out.println(lazyExampleBean);
当我们在上述代码的执行前后分别打断点,进行Debug调试时
会发现除了设置延迟初始化的Bean之外所有的都在启动时初始化好了。(如下图所示)
自动装配
在此前我们进行了手工完成Bean之间依赖关系的装配,除此之外,Bean可以进行自动装配。
自动装配功能的定义:
不需要在Spring配置文件中描述Bean之间的依赖关系,如:配置<property>、<constructor-arg>
而IoC容器会自动建立Bean之间的联系
public class Customer {
private final Bar bar;
public Customer(Bar bar) {
this.bar = bar;
}
public Bar getBar() {
return bar;
}
@Override
public String toString() {
return "Customer{" +
"bar=" + bar +
'}';
}
}
public class Customer2 {
private Bar bar;
public Bar getBar() {
return bar;
}
public void setBar(Bar bar) {
this.bar = bar;
}
@Override
public String toString() {
return "Customer{" +
"bar=" + bar +
'}';
}
}
<!--自动装配-->
<bean id="customer" class="com.dyy.springcore.auto.Customer " autowire="constructor"/>
<bean id="customer2" class="com.dyy.springcore.auto.Customer2" autowire="byName"/>
Spring的四种自动装配模式
(1)no:缺省情况下,自动配置通过“ref”属性自动设定
(2)byName:根据属性名称自动装配,如果属性名和其他Bean的属性名相同,都会自动装配。
(3)byType:根据参数类型进行自动装配,如果一个bean的数据类型是用其他bean属性的数据类型,
则兼容并自动进行装配,但是如果同一个类型bean有多个,则无法进行自动装配。
(4)constructor:构造函数参数是通过byType方式进行自动装配。
Bean的作用域
Bean的作用域是描述一个Bean实例在SpringIoC容器中的状态。
Spring所支持的作用域
singleton
这是Spring默认的作用域
此作用域下的Bean在IoC容器中只创建一个实例,
所有的对象对其的引用都返回同一个。
prototype
在这个作用域下的进行的Bean请求每次都会创建新的实例。每次调用的返回值均不同
request
每次http请求会创建新的Bean实例,类似于prototype。(限定于在SpringMVC中使用)
session
在一个http session中,定义一个Bean实例(限定于在SpringMVC中使用)
global session
类似于http session,但是限定于在protlet web中使用
application session
自定义作用域
上面我们看到了Spring自带的作用域,除此之外我们还可以通过自定义作用域
来实现更多的作用域
我们通过实现一个简单的线程作用域为例
实现Scope接口
package com.dyy.springcore.auto;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.config.Scope;
import org.springframework.core.NamedThreadLocal;
import java.util.HashMap;
import java.util.Map;
/**
*@Description: 用于线程的自定义作用域
*@Author: dyy
*/
public class SingleThreadScope implements Scope {
private ThreadLocal<Map<String,Object>> threadLocal = new NamedThreadLocal<Map<String, Object>>("SingleThreadScope"){
@Override
protected Map<String, Object> initialValue() {
return super.initialValue();
}
};
public Object get(String s, ObjectFactory<?> objectFactory) {
Map<String ,Object> map = this.threadLocal.get();
Object beanName = map.get(s);
if(beanName==null){
beanName = objectFactory.getObject();
map.put(s,beanName);
}
return beanName;
}
public Object remove(String s) {
return this.threadLocal.get().remove(s);
}
public void registerDestructionCallback(String s, Runnable runnable) {
}
public Object resolveContextualObject(String s) {
return null;
}
public String getConversationId() {
return null;
}
}
注册作用域
(1)使用代码方式注册自定义作用域
context.getBeanFactory().registerScope("thread",new SingleThreadScope());
(2)使用配置XML的方式注册自定义作用域
<!--配置方式设置自定义作用域-->
<bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
<property name="scopes">
<map>
<entry key="thread" value-ref="scope"/>
</map>
</property>
</bean>
编码验证
我们使用ExampleBean4类进行多线程下的验证,在XML中对类进行配置
<!--配置用于多线程验证的类-->
<bean id="bean44" class="com.dyy.springcore.ExampleBean4" scope="thread">
<property name="name" value="lemon"/>
<property name="id">
<null/>
</property>
</bean>
进行验证
package com.dyy.springcore.auto;
import com.dyy.springcore.ExampleBean4;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.util.concurrent.CountDownLatch;
/**
*@Description: 此类用于多线程下自定义作用域的验证
*@Author: dyy
*/
public class IoCAutoApplication {
public static void main(String[] args) {
final ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("application-content.xml");
//此语句是使用代码注册方式定义作用域,与使用XML进行配置具有等同效果
// context.getBeanFactory().registerScope("thread",new SingleThreadScope());
final int count = 3;
final CountDownLatch countDownLatch = new CountDownLatch(count);
for(int i = 0; i < count ; i++) {
new Thread(new Runnable() {
public void run() {
ExampleBean4 exampleBean4 = (ExampleBean4) context.getBean("bean44");
System.out.println("currentThread " + Thread.currentThread().getName() + " " + exampleBean4.hashCode());
countDownLatch.countDown();
}
}).start();
}
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
通过结果我们可以看出,每个线程从容器中获取的实例都是不同的,达到了我们的预期目标。
基于注解的配置
不论我们通过XML还是通过注解进行配置,它们都是表达Bean的载体,
本质都是为Spring容器提供Bean定义的信息,在表现形式上都是将XML定义的内容通过注解进行描述。
#
Spring容器启动成功地三要素:
(1)Bean的定义
(2)Bean实现类
(3)Spring本身
使用注解定义Bean
采用基于注解配置文件,则Bean的定义信息时通过在Bean上使用注解标识实现的。
而采用XML的配置,则Bean定义信息和Bean实现类是分离的。
#
基于注解的配置
@Component
public class Circle implements Shape {
}
实则上述代码等同于基于XML配置中的
<bean class="com.dyy.springcore.auto.Circle"/>
使用@Component标识的类会被Spring容器识别,自动转化为被容器管理的Bean。
在我们使用注解进行配置时,同样需要在配置文件中加入如下字样来告诉Spring容器开启自动Bean扫描
<!--使用注解进行配置时需要加入-->
<contex:component-scan base-package="com.dyy.springcore"/>
//注解的验证
public class IoCAutoApplication2 {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("application-content.xml");
ShapeService shapeService = context.getBean(ShapeService.class);
System.out.println(shapeService);
}
}
与@Compent注解等同的注解来进行类作用的标识:
(1)@Repository:标识类属于应用
(2)@Serivce:标识类负责业务逻辑
(3)@Controller:标识类属于数据访问呢
使用注解装配
进行自动装配@Autowired
@Autowired默认是按照类型进行装配(byType)在容器中查找匹配的Bean
当有且仅有一个匹配的Bean四,Spring会将其注入到@Autowired标注的变量中
若含有多个即产生歧义时,则应该使用@Qualifier来进行装配
@Service
public class ShapeService {
@Autowired
private Shape shape;
public void setShape(Shape shape) {
this.shape = shape;
}
public Shape getShape() {
return shape;
}
@Override
public String toString() {
return "ShapeService{" +
"shape=" + shape +
'}';
}
}
若在进行自动装配时未找到匹配的Bean,则Spring容器会报出NoSuchBeanDefinitionException异常信息。
当我们希望即使未找到也不要给我们进行错误提示信息时,则应使用@Autowired(required=false)来进行标注(默认requried为true)
指定注入Bean的名称@Qualifier
当采用自动注入时会有多个与之类型匹配的Bean时,则我们需要对其进行限定,则应该使用
@Qualifier注解来进行指定名称的标注
#
如:当我们同时具有triangle和circle满足shape的需求时,则进行自动装配时会
出现歧义,我们可以使用如下指定名称来进行装配。
@Service
public class ShapeService {
@Autowired
@Qualifier(value = "triangle")
private Shape shape;
public void setShape(Shape shape) {
this.shape = shape;
}
public Shape getShape() {
return shape;
}
@Override
public String toString() {
return "ShapeService{" +
"shape=" + shape +
'}';
}
}
其他注解
@Scope:指定作用域注解
@PostConstruct:此注解标明在实例化之后就要做的操作
@PreDestory:此注解标明在对象销毁之前要做的操作。
package com.dyy.springcore.auto;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.time.LocalDate;
@Component
@Scope(value = "prototype")
public class ChineseCurrence {
public ChineseCurrence() {
System.out.println("构造方法");
}
public LocalDate localDateTime(){
return LocalDate.now();
}
@PostConstruct
public void init(){
System.out.println("对象实例化完成后执行");
}
@PreDestroy
public void destory(){
System.out.println("对象销毁前执行");
}
}
public class IoCAutoApplication2 {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("application-content.xml");
ShapeService shapeService = context.getBean(ShapeService.class);
System.out.println(shapeService);
ChineseCurrence chineseCurrence = context.getBean(ChineseCurrence.class);
System.out.println(chineseCurrence);
context.getBeanFactory().destroyBean(chineseCurrence);
}
}