文章大纲
在xml中声明bean和注入bean (setter注入,构造器注入)
在xml中声明bean和自动装配bean(byName、byType、constructor、autodetect)
注解注入Bean(@Autowired、@Qualifier、@Resource、@Inject)
自动扫描bean(<context:annotation-scan/>,@Component、@Service、@Controller、@Repository等)
对自动扫描bean增加约束条件
首次接触spring请参考 Spring 3.0 学习-环境搭建和三种形式访问 而在本文中,你将看到第四种形式,即测试类自动加载xml配置文件。
0、新的声明
在@Autawired 类似的注解注入出现之前,代码中一直使用的是 setter注入 或者 构造器注入;
假设我们已在在xml文件中声明了两个 Spring bean ,BeanA 和 BeanB
<bean id="beanA" class="A.B.BeanA"/>
<bean id="beanB" class="A.B.BeanB"/>
针对需求-BeanB需要引用到BeanA,我们进行以下的讨论:
0.1、setter注入的解决方案:setter 注入的前提是 BeanA 在 BeanB 中作为全局变量进行声明且提供 BeanA变量的setter方法,
public class BeanB ...{
private BeanA beanA;
private String str;
public void setBeanA(BeanA beanA){
this.beanA=beanA;
}
public void setStr(String str){
this.str=str;
}
}
其次是在xml方法中增加 proterty属性,如果是普通变量用value="",如果注入bean 则使用 ref="beanA".
<bean id="beanA" class="A.B.BeanA"/>
<!--通过 setter 方法 将beanA 注入到 beanB,BeanB需要提供BeanA的变量声明和setter方法-->
<bean id="beanB" class="A.B.BeanB">
<property name="str" value="10"/>
<property name="beanA" ref="beanA"/>
</bean>
0.2、构造器注入解决方案:构造器注入的前提是BeanB提供两个构造方法,第一个是默认构造方法,第二个是含参数构造方法
且BeanB中需要将BeanA 声明为全局变量,并在构造方法中进行赋值.
public class BeanB ...{
private BeanA beanA;
private String str;
public BeanB(BeanA beanA){
this.str=str;
this.beaA=beanA;
}
}
并在xml 增增加
<bean id="beanA" class="A.B.BeanA"/>
<!--通过构造器将beanA 注入到 beanB,BeanB需要提供构造函数,和BeanA的变量声明,构造函数与xml顺序需一直-->
<bean id="beanB" class="A.B.BeanB">
<constructor-arg value="15"/><!--注入常量-->
<constructor-arg ref="beanA"/><!--注入 bean-->
</bean>
0.3、自动装配 byName、byType、constructor、autodetect
自动装配是对 setter、构造器 装配的补充,其最直接的表现是可以去除 xml 配置中对属性对注入配置,但是Java代码中的 setter 方法和构造器依旧需要存在,下面说下他们的具体用法和区别
声明位置有两种,可以同时存在
全局xml文件全局声明 default-autowired="byName"
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"
default-autowire="byName">
每一个bean 单独声明,这个粒度更细,
<bean id="beanA" class="A.B.BeanA"/>
<!--BeanB 中提供构造方法或者 setter 方法,java代码参考上面0.2-->
<bean id="beanB" class="A.B.BeanB" autowire="byName"/>
byName、byType、constructor、autodetect 的区别
byName 没啥好说的,只要BeanB中声明的BeanA的变量名字和Spring中BeanA 的id一致即可,如果BeanA没有指定id,则就是BeanA的类(首字母小写)
byType 是按照类型注入的,如果BeanB 引用BeanA,但是BeanA 是个接口,且有两个实现类,那么按照类型注入就会报错,这个时候需要将多余的bean 增加
<!--这里 BeanA 和 BeanATwo 都是一个接口的实现类,对于Spring来说,属于同一类型 -->
<bean class="A.B.BeanA"/>
<!-- byType 的话需要把多余的自动注掉 -->
<bean class="A.B.A.B.BeanATwo" autowire-candidate="false"/>
byName 和 byType 可以和xml 的 property 配置一起用,既可以补充,也可以覆盖,以xml为主
constructor 的话要么全部使用xml,要么全部使用自动装配
autodetect 默认是constructor,如果失败则使用byType
0.4、@Autowired 自动注入的作用是完全去除 xml 和 setter、构造方法 的注入
需要在xml文件中增加标签 <context:annotation-config/>
使用 @Autowired 注入仅仅需要在BeanB 中 声明 BeanA 的全局变量,并且给该变量增加@Autowired注解
更多和@Autowired 类型的注解和特性(https://blog.csdn.net/bestcxx/article/details/79203756#_Autowired__ResourceInject_133)
public class BeanB ...{
@Autowired
private BeanA beanA;
}
但是 将 BeanA 和 BeanB声明为Sping bean的工作依旧是必不可少的,声明<bean>标签或者使用<context:annotation-scan>
0.4、声明Spring Bean 的简单方式-扫描加注解 <context:annotation-scan>
<context:annotation-scan/> 可以代替<context:annotation-config/>,但是不单是启动自动注入还可以扫描包,
context:include-filter 和 annotation-config="true" 不是必须写的,前者用于限制某类注解被注册为Spring Bean,后者这是将默认启动注解注入(@Autowired)显示出来,当然如果你想关闭自动注入
<context:component-scan base-package="A.B" annotation-config="false"/>
需要被注册为 Spring Bean 的类需要加注解 @Component 、@Controller 等(https://blog.csdn.net/bestcxx/article/details/79203756#ComponentRepositoryServiceControllerNamed_161)
如下
<!-- 自动扫描注解-指定包-注解注入默认可以使用,这里显性展示 -->
<context:component-scan base-package="stu.bestcxx.springtest.scanimpl" annotation-config="true">
<!-- 限制类型:包含-注解类-@Component -->
<context:include-filter type="annotation" expression="org.springframework.stereotype.Component"/>
</context:component-scan>
0.5、关于循环注入的问题
第一点:区别循环注入和循环调用
循环调用就是,BeanA的方法A 引用 BeanB的方法B,BeanB的方法B引用BeanA的方法A,这样肯定会栈溢出
循环注入的意思是,BeanA需要注入BeanB,BeanB需要注入BeanA,
第二点:Spring bean 的作用域
singleton 在每一个Spring容器中,一个Bean定义只有一个对象实例(默认)。
prototype 允许Bean的定义可以被实例化任意次(每次调用都创建一个实例)。
request 在一次HTTP请求中,每个Bean定义对应一个实例,该作用域仅在基于Web的Spring上下文(例如Spring MVC)中才有效。
session 在一个HTTPSession中,每个Bean定义对应一个实例。该作用域仅在基于Web的Spring上下文中才有效。
global-session 在一个全局HTTP Session中,每个bean定义对应一个实例。该作用域在portlet才有效。
<bean id="beanA" class="A.B.BeanA" scope="prototype"/>
结论如下:
@Autuwird只支持 单例作用域的相互注入,@Autowired 默认可以避免循环注入,因为Spring 默认就是单例的,@Autowired 增加作用域注解(https://blog.csdn.net/bestcxx/article/details/79203756#Scopeprototype_ScopeConfigurableBeanFactorySCOPE_PROTOTYPE_170)
完全使用 xml 配置注入时,setter注入仅支持 单例作用域的相互注入
完全使用xml配置注入时,构造器注入 不支持相互注入
自动装配模式下,使用 autowire="constructor" 或者 autowire="byName"都支持所有作用域都相互依赖
自动装配模式下,autowire="byType"和完全使用xml 配置注入的setter 注入一致,仅支持 单例作用域的相互注入
1、典型的Spring XML 配置文件表头
<?xml version="1.0" encoding="UTF-8"?><!-- 一般化的Spring XML 配置 -->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd">
2、Bean的作用域
bean的本质是某个接口的实现类,或者某个实现了接口的实现类的继承类,比如连接数据库,总之其可以返回具备某个功能的对象。
所有的Spring bean 默认都是单例。即当容器分配一个Bean时,只要是spring容器装配或者调用容器的getBean()方法,它总是返回bean的同一个实例。
<!-- 杂技师-juggler1,实现 Performer 接口 -->
<bean id="juggler1" class="stu.bestcxx.spring.impl.PerformJuggler" scope="prototype"/>
作用域 定义
singleton 在每一个Spring容器中,一个Bean定义只有一个对象实例(默认)。
prototype 允许Bean的定义可以被实例化任意次(每次调用都创建一个实例)。
request 在一次HTTP请求中,每个Bean定义对应一个实例,该作用域仅在基于Web的Spring上下文(例如Spring MVC)中才有效。
session 在一个HTTPSession中,每个Bean定义对应一个实例。该作用域仅在基于Web的Spring上下文中才有效。
global-session 在一个全局HTTP Session中,每个bean定义对应一个实例。该作用域在portlet才有效。
3、典型Spring XML中bean的配置
<!-- 杂技师-juggler1,实现 Performer 接口 -->
<bean id="juggler1" class="stu.bestcxx.spring.impl.PerformJuggler"/>
4、通过构造器注入参数
<!-- 司机-driver1,实现Performer 接口 -->
<bean id="driver1" class="stu.bestcxx.spring.impl.PerformDriver">
<constructor-arg value="15"/>
</bean>
5、通过构造器注入其他bean
<!-- beanA,id=poem_chunxiao -->
<bean id="poem_chunxiao" class="stu.bestcxx.spring.impl.PoemChunxiao"></bean>
<!-- beanB,内部提供构造函数,构造参数之一为poem_chunxiao,这里指定注入beanA -->
<bean id="juggerpoem1" class="stu.bestcxx.spring.impl.PerformeJuggerPoem1">
<constructor-arg value="15"/>
<constructor-arg ref="poem_chunxiao"/>
</bean>
6、通过get、set方法装配
<!--beanA id被声明为fangyang_instrument -->
<bean id="fangyang_instrument" class="stu.bestcxx.spring.impl.PerformInstrument"/>
<!-- beanB,内部有三个变量 song、age、instrument 都要提供 setter 方法,用下面方法注入变量和 beanA -->
<bean id="instrument1" class="stu.bestcxx.spring.impl.PerformInstrument">
<property name="song" value="《彩云追月1》"/>
<property name="age" value="10"/>
<property name="instrument" ref="fangyang_instrument"/>
</bean>
7、p装配-简化的get、set方法装配 xmlns:p="http://www.springframework.org/schema/p"
<?xml version="1.0" encoding="UTF-8"?><!-- 一般化的Spring XML 配置 -->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<!-- 鼓接口-凤阳鼓实现 -->
<bean id="fangyang_instrument" class="stu.bestcxx.spring.impl.InstrumentFengyang"/>
<!-- 鼓手instrument3表演乐器-注入的凤阳大鼓 -->
<bean id="instrument3" class="stu.bestcxx.spring.impl.PerformInstrument"
p:song="《彩云追月3》"
p:age="12"
p:instrument-ref="fangyang_instrument"/>
</beans>
8、通过自动装配
四种类型的自动装配
byName 适用于 setter 类型,可以结合 xml 的 property 属性适用
byType 适用于 setter 类型,可以结合 xml 的 property属性使用
Constructor 必须自动装配所有入参,而不能对一部分参数使用<constructor-arg>
Autodetect 先是选择constructor,如果没有匹配的选择byType
8.1 autowire="byName"
<bean id="performer" class="stu.bestcxx.spring.impl.PerformInstrumentbyname" autowire="byName">
<property name="age" value="20"/>
<property name="song" value="《彩云追月》"/>
</bean>
<bean id="instrument" class="stu.bestcxx.spring.impl.InstrumentKuaishu"/>
8.2 autowire="byType"
<bean id="performer" class="stu.bestcxx.spring.impl.PerformInstrumentbytype" autowire="byType">
<property name="age" value="20"/>
<property name="song" value="《彩云追月》"/>
</bean>
<bean id="instrument1" class="stu.bestcxx.spring.impl.InstrumentKuaishu"/>
<!-- byType 的话需要把多余的自动注掉 -->
<bean id="instrument2" class="stu.bestcxx.spring.impl.InstrumentFengyang" autowire-candidate="false"/>
8.3 头部多了 default-autowire,配置为全局的自动装配
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"
default-autowire="byName">
9、工厂模式-没有提供构造方法怎么在spring中构造bean
实例的创建和调用是分开的,工厂模式还可以分为简单工厂模式和工厂方法模式。
简单工厂模式是指工厂是实例的,产品由抽象接口和实际产品组成。实际工厂具有返回抽象产品的实际方法,而且是具有多个这样的实际方法,即返回的产品是确定的,但是抽象产品的实现是由多个具体的抽象产品的实现类决定的。
(具体工厂返回抽象产品的实现)+抽象产品的多个实现
工厂方法模式是指,工厂是抽象的工厂加实际继承抽象的实际工厂,抽象工厂有返回抽象产品的抽象方法,实际工厂则实现抽象工厂,实际工厂是多个,抽象产品的实现也是多个,不同的实际工厂返回不同的产品接口的实现。
抽象工厂的多个实现+(抽象工厂返回抽象产品-具体工厂返回抽象产品的实现)+抽象产品的多个实现
<!-- 工厂模式-缺乏构造方法-单例模式 -->
<bean id="stage" class="stu.bestcxx.spring.impl.Stage"
factory-method="getInstance"/>
10、混合装配(property、构造器装配和自动装配)、配置为null
自动配置加显示配置可以同时使用,最终以显示配置为准。
<property name=”instrument”><null/></property>
11、内部类
<bean id="poem_chunxiao" class="stu.bestcxx.spring.impl.PoemChunxiao"></bean>
<!-- 杂技师-juggerpome1,一边抛豆袋子,一边唱诗歌 -->
<bean id="juggerpoem1" class="stu.bestcxx.spring.impl.PerformeJuggerPoem1">
<constructor-arg value="15"/>
<constructor-arg ref="poem_chunxiao"/>
</bean>
<!-- 杂技师-juggerpome2,一边抛豆袋子,一边唱诗歌 -->
<bean id="juggerpoem2" class="stu.bestcxx.spring.impl.PerformeJuggerPoem1">
<constructor-arg value="15"/>
<constructor-arg>
<bean class="stu.bestcxx.spring.impl.PoemChunxiao"/>
</constructor-arg>
</bean>
12、SpEL表达式
<!-- 鼓接口-凤阳鼓实现 -->
<bean id="fangyang_instrument" class="stu.bestcxx.spring.impl.InstrumentFengyang"/>
<!-- 鼓手instrument1表演乐器-注入的凤阳大鼓 -->
<bean id="instrument1" class="stu.bestcxx.spring.impl.PerformInstrument">
<property name="song" value="《彩云追月1》"/>
<property name="age" value="10"/>
<property name="instrument" value="#{fangyang_instrument}"/>
</bean>
13、Spring 加载标准属性值配置文件application.properties的两种方式
application.properties的内容,下面都是以取 “test.age”来进行试验的。
13.1 SpEL方式读取配置文件属性值
applicationContextSpEL.xml的内容
<?xml version="1.0" encoding="UTF-8"?><!-- 一般化的Spring XML 配置 -->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd">
<!-- 鼓接口-凤阳鼓实现 -->
<bean id="fangyang_instrument" class="stu.bestcxx.spring.impl.InstrumentFengyang"/>
<!-- 从配置文件注入数据,使用SpEL读取数据 -->
<util:properties id="settings" location="classpath:config/application.properties"/>
<!-- 鼓手instrument1表演乐器-注入的凤阳大鼓 -使用SeEL注入-->
<bean id="instrument1" class="stu.bestcxx.spring.impl.PerformInstrument" >
<property name="song" value="#{'《彩云追月'+T(java.lang.Math).PI+'》'}"/>
<property name="age" value="#{settings['test.age']}"/>
<property name="instrument" value="#{fangyang_instrument}"/>
</bean>
</beans>
13.2 PropertyPlaceholderConfigurer 方式读取配置文件属性值
applicationContextPlaceholder.xml的内容,下面的测试方法要用到
<?xml version="1.0" encoding="UTF-8"?><!-- 一般化的Spring XML 配置 -->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd">
<!-- 鼓接口-凤阳鼓实现 -->
<bean id="fangyang_instrument" class="stu.bestcxx.spring.impl.InstrumentFengyang"/>
<!-- 定义受环境影响易变的变量 -->
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
<property name="ignoreResourceNotFound" value="true" />
<property name="locations">
<list>
<!-- 标准配置 -->
<value>classpath:config/application.properties</value>
<!-- 数据库配置 -->
<value>classpath:config/jdbc.properties</value>
</list>
</property>
</bean>
<!-- 鼓手instrument1表演乐器-注入的凤阳大鼓 -使用SeEL注入-->
<bean id="instrument1" class="stu.bestcxx.spring.impl.PerformInstrument" >
<property name="song" value="《彩云追月1》"/>
<property name="age" value="${test.age}"/>
<property name="instrument" ref="fangyang_instrument"/>
</bean>
</beans>
14、下面是具体的例子,包含了在测试方法中借用注入方式以及spring自带的测试方法来完成配置文件的预装载
14.1 spring配置文件的位置
14.2 测试方法PerformInstrumentPlaceholderTest.java
由于我们借助于spring自身的测试方法,所以在初始阶段使用了@Autowired注入,具体用法下下面提到
package stu.bestcxx.spring.impl;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.transaction.TransactionConfiguration;
import stu.bestcxx.springtest.facade.Performer;
@DirtiesContext
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"classpath:spring/applicationContextPlaceholder.xml"})
@TransactionConfiguration(transactionManager = "defaultTransactionManager",defaultRollback=false)
public class PerformInstrumentPlaceholderTest {
/*
* Performer 是接口
* juggerpoem2是spring XML 配置文件中的bean名字
*
*/
@Autowired
private Performer performer;
@Test
public void testperform(){
performer.perform();
}
}
15、从Spring 2.5开始,可以使用注解来完成自动装配Bean属性的过程。+@Resource
需要注意的是,Spring容器默认禁用注解装配。启动方式是,使用Spring的context命名空间配置中的<context:annotation-config/>元素。
@Autowired 相当于byType,所以在存在多个接口实现类时,需要额外使用标注@Qualifier("kuaishu_instrument"),这样联合使用的效果就和使用byName差不多了。
@Autowired与@Resource的区别:点击打开链接
15.1接口类,Performer.java
Performer.java内容:
package stu.bestcxx.springtest.facade;
/**
* spring通过调用接口,而接口由不同的实现类来执行具体的实现,
* 通过spring的配置文件或者注解完成"依赖注入"(DI)
*
* @author WuJieJecket
*/
public interface Performer {
public void perform();
}
15.2 接口类 Instrument.java
package stu.bestcxx.springtest.facade;
/**
* 接口类
* 鼓手
* @author WuJieJecket
*/
public interface Instrument {
/*
* 鼓手表演什么?
*/
public void play();
}
15.3 乐器的实现类InstrumentFengyang.java
InstrumentFengyang.java内容:
package stu.bestcxx.spring.impl;
import stu.bestcxx.springtest.facade.Instrument;
public class InstrumentFengyang implements Instrument{
@Override
public void play() {
// TODO Auto-generated method stub
System.out.println("鼓手表扬了凤阳大鼓:咚咚锵,咚咚锵");
}
}
15.4乐器的实现类InstrumentKuaishu.java
InstrumentKuaishu.java内容:
package stu.bestcxx.spring.impl;
import stu.bestcxx.springtest.facade.Instrument;
public class InstrumentKuaishu implements Instrument{
@Override
public void play() {
// TODO Auto-generated method stub
System.out.println("选手表扬了山东快书:浪里格朗,浪里格朗");
}
}
15.5 配置文件 applicationContextannotation.xml和application.properties
applicationContextannotation.xml
<?xml version="1.0" encoding="UTF-8"?><!-- 一般化的Spring XML 配置 -->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd">
<!-- 加载标准属性文件 -->
<util:properties id="settings" location="classpath:config/application.properties"/>
<!-- @Autowired 相当于 byType,凤阳鼓和山东快书都实现了乐器接口,所以需要额外使用 @Qualifier("kuaishu_instrument")注解 ,就相当于byName了-->
<!-- 鼓接口-凤阳鼓实现 -->
<bean id="fangyang_instrument" class="stu.bestcxx.spring.impl.InstrumentFengyang"/>
<!-- 鼓接口-山东快书实现 -->
<bean id="kuaishu_instrument" class="stu.bestcxx.spring.impl.InstrumentKuaishu"/>
<!-- 鼓手instrument1表演乐器-注入的凤阳大鼓 -使用SeEL注入-->
<bean id="instrument1" class="stu.bestcxx.spring.impl.PerformInstrumentAnnotation"/>
</beans>
application.xml
15.6 表演者的实现类 PerformInstrumentAnnotation.java
PerformInstrumentAnnotation.java内容:
package stu.bestcxx.spring.impl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import stu.bestcxx.springtest.facade.Instrument;
import stu.bestcxx.springtest.facade.Performer;
public class PerformInstrumentAnnotation implements Performer {
/*
* 使用面向注解的注入,不必提供set、get 方法
*/
@Autowired
@Value("《彩云追月1》")//直接赋值
private String song;
@Autowired
@Value("#{settings['test.age']}")//SpEL的赋值
private int age;
@Autowired(required=false)//默认注入的类不存在则报错,即不允许为null,这里required=false是允许为null的意思
@Qualifier("kuaishu_instrument")//@Autowired 相当于 byType,凤阳鼓和山东快书都实现了乐器接口,所以需要额外使用@Qualifier("kuaishu_instrument")注解 ,就相当于byName了 ,如果只有一个实现乐器的接口则可以去掉@Autowired
private Instrument instrument;
@Override
public void perform() {
// TODO Auto-generated method stub
System.out.println("表演者年龄是 "+age+" 使用乐器表扬的歌曲是 "+song+" 请欣赏 ");
//乐器表扬
instrument.play();
}
}
15.7、测试类 PerformInstrumentAnnotationTest.java
PerformInstrumentAnnotationTest.java内容:
package stu.bestcxx.spring.impl;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.transaction.TransactionConfiguration;
import stu.bestcxx.springtest.facade.Performer;
@DirtiesContext
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"classpath:spring/applicationContextannotation.xml"})
@TransactionConfiguration(transactionManager = "defaultTransactionManager",defaultRollback=false)
public class PerformInstrumentAnnotationTest{
/*
* Performer 是接口
* juggerpoem2是spring XML 配置文件中的bean名字
*/
@Autowired
private Performer performer;
@Test
public void testperform(){
performer.perform();
}
}
16、Java依赖注入规范 @Inject
这个是需要一个jar包支持的,即javax.inject.jar
import javax.inject.Inject;
下载路径:http://download.csdn.net/detail/bestcxx/9633326
为了统一各种依赖注入框架的编程模型,JCP(Java Community Process)发布了Java依赖注入规范,JCP将其称为JSP-330,更常见的叫法是@inject。从Spring3开始,Spring已经开始兼容该依赖注入模型。
上面的Spring的@Autowired和@Qualifier是搭配的,在Java注入规范中,是@Inject和@Named搭配,即@Autowired可以用@inject代替,@Qualifier可以用@Named代替。
但是注意,@Autowired有一种格式允许注解不存在,@Autowired(required=false),但是@inject是必须存在的。
17、自动检测Bean
我们已经实现了这样的过程:构造器注入、set\get注入,然后是自动注入去掉了xml文件中一些bean的引用代码<property>\<constructor>,然后是注解,去掉了xml文件中byName等自动注入代码,现在是时候把xml文件中基本的bean去掉了——不再去逐个声明Bean,直接让spring去程序中寻找,当然我们指定了范围和规则。
17.1 <context:annotation-scan>元素的使用
我们需要使用<context:annotation-scan>代替<context:annotation-config>元素(前者包含了后者),<context:annotation-scan>元素会扫描指定的包及其所有子包,并查找出能自动注册为Spring Bean的类。Base-package属性标识了会被扫描的包。
如果不加构造型注解,系统不会将其视为bean?在默认情况下是这样的,但是在有其他条件的情况下,可以不加注解也被扫描,具体看17.3。
17.2.1、接口类
ScanSingerFacade.java内容:
package stu.bestcxx.springtest.facade;
/**
* 歌手接口
* @author WuJieJecket
*
*/
public interface ScanSingerFacade {
/**
* 表演唱歌
*/
public void sing();
}
17.2.2、实现类
有两个java实现类,刚才已经说过,“如果不加构造型注解,系统不会将其视为bean?在默认情况下是这样的,但是在有其他条件的情况下,可以不加注解也被扫描,具体看17.3。”
Singerscan01.java是对比类,放开里面的注释报错证明注解是byType,仅这一个实现类也报错证明默认情况下,没有注解没有实现类被识别,内容:
package stu.bestcxx.springtest.scanimpl;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import stu.bestcxx.springtest.facade.ScanSingerFacade;
/*
* 不加 构造器注解,scan默认不会扫描
* 如果增加,注解相当于byType,具有多个实现类被注解会报错
* @Component @Component("demoname") demoname是自定义的
*/
//@Component//可以将这个注解放开和注释分别运行测试类 Singerscan0Test.java
public class Singerscan01 implements ScanSingerFacade {
@Value("#{settings['test.song']}")
private String song;
@Override
public void sing() {
// TODO Auto-generated method stub
System.out.println("singer1 表演:"+song);
}
}
Singerscanannotation.java内容:
package stu.bestcxx.springtest.scanimpl;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import stu.bestcxx.springtest.facade.ScanSingerFacade;
//@Compoent 自动扫描注册为通用bean,如果需要制定名字就加括号,否则默认bean名字为 无限定类名-singer1
@Component("singer1")
public class Singerscanannotation implements ScanSingerFacade {
@Value("#{settings['test.song']}")
private String song;
@Override
public void sing() {
// TODO Auto-generated method stub
System.out.println("singer1 表演:"+song);
}
}
17.2.3 xml设置扫描路径
这种模式下,需要被识别为bean的必须加注解标识,不加注解的方法参考17.3
applicationContextscan.xml内容:
<?xml version="1.0" encoding="UTF-8"?><!-- 一般化的Spring XML 配置 -->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd">
<!-- 使用注解注入 -->
<context:component-scan base-package="stu.bestcxx.springtest.scanimpl" annotation-config="true">
</context:component-scan>
<!-- 加载标准属性文件 -->
<util:properties id="settings" location="classpath:config/application.properties"/>
</beans>
17.2.4 测试方法-涉及Spring自带的测试模式
Singerscan0Test.java内容
package stu.bestcxx.springtest.scanimpl;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.transaction.TransactionConfiguration;
import stu.bestcxx.springtest.facade.ScanSingerFacade;
@DirtiesContext
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"classpath:spring/applicationContextscan.xml"})
@TransactionConfiguration(transactionManager="defaultTransactionManager",defaultRollback=false)
public class Singerscan0Test {
@Autowired
private ScanSingerFacade ssfade;
@Test
public void testsing(){
ssfade.sing();
}
}
17.3 增加组件过滤扫描
17.3.1 格式
在使用scan功能时,默认情况下所有的构造型注解标注(@Component,@Service,@Repository,@Controller)的实现类都会被注册为bean
但是在具有限定功能的情况下,可以减小Spring扫描的粒度,提升效率。
下面的规则都可以分为正向的和反向的,即该类型的需要扫描以及该类型的不需要扫描。
<context:component-scan base-package=”被扫描的包路径”>
正向的写在
<context:include-filter type=”5种情况” expression=”如下所示”/>
反向的写在
<context:exclude-filter type=”5种情况” expression=”如下所示”/>
<context:component-scan/>
17.3.2 对type值5种情况使用详解
如果应用扫描的话,默认是需要加注解的,但是在非annotation限定模式下则可以不加注解也可以自动识别,具体看下面的例子。
17.3.2.1 type=annotation,必须由注解,过滤器扫描使用指定注解所标注的那些类。具体来说,就是@Component,@Service,@Repository,@Controller这几个构造型注解所在的类。
expression=有4个值,这种限定方式是必须有@Component等注解的。
org.springframework.stereotype.Component;
org.springframework.stereotype.Controller;
org.springframework.stereotype.Repository;
org.springframework.stereotype.Service;
举例:
<!-- 自动扫描注解-指定包-注解注入默认可以使用,这里显性展示 -->
<context:component-scan base-package="stu.bestcxx.springtest.scanimpl" annotation-config="true">
<!-- 限制类型:包含-注解类-@Component -->
<context:include-filter type="annotation" expression="org.springframework.stereotype.Component"/>
</context:component-scan>
17.3.2.2 type=assignable,注解不是必要的,过滤器扫描派生于expression属性所指定类型的那些类,所谓的派生,具体的实现类的路径。
expression=接口类路径
举例:
接口类:
ScanSinger2Facade.java代码:
package stu.bestcxx.springtest.facade;
/**
* 歌手接口
* @author WuJieJecket
*
*/
public interface ScanSinger2Facade {
/**
* 表演唱歌
*/
public void sing();
}
实现类:
Singerscanassignable.java内容:
package stu.bestcxx.springtest.scanimpl;
import org.springframework.beans.factory.annotation.Value;
import stu.bestcxx.springtest.facade.ScanSinger2Facade;
//在scan-assignable 模式下,可以去掉结构型注解
public class Singerscanassignable implements ScanSinger2Facade {
@Value("#{settings['test.song']}")
private String song;
@Override
public void sing() {
// TODO Auto-generated method stub
System.out.println("singer1 表演:"+song);
}
}
配置文件applicationContextscanassignable.xml
内容:
<?xml version="1.0" encoding="UTF-8"?><!-- 一般化的Spring XML 配置 -->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd">
<!--自动扫描注解-指定被派生的类-->
<!-- 限制类型:包含-派生类-具体的实现类路径 -->
<context:component-scan base-package="stu.bestcxx.springtest.scanimpl">
<context:include-filter type="assignable" expression="stu.bestcxx.springtest.scanimpl.Singerscanannotation"/>
</context:component-scan>
<!-- 加载标准属性文件 -->
<util:properties id="settings" location="classpath:config/application.properties"/>
</beans>
测试类SingerscanassignableTest.java
内容:
package stu.bestcxx.springtest.scanimpl;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.transaction.TransactionConfiguration;
import stu.bestcxx.springtest.facade.ScanSingerFacade;
@DirtiesContext
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"classpath:spring/applicationContextscanassignable.xml"})
@TransactionConfiguration(transactionManager="defaultTransactionManager",defaultRollback=false)
public class SingerscanassignableTest {
@Autowired
private ScanSingerFacade ssfade;
@Test
public void testsing(){
ssfade.sing();
}
}
17.3.2.3 type=aspectj,过滤器扫描与expression属性所指定的AspectJ表达式所匹配的那些类。
expression=暂无
17.3.2.4 type=custom,使用自定义的org.springframework.core.type.TypeFilter实现类,该类由expression属性指定。
expression=暂无
17.3.2.5 type=regex,过滤器扫描类的名称与expression属性所指定的正则表达式所匹配的那些类。
expression=暂无
18、Spring基于Java的配置
依旧需要极少的xml配置,只是注解的名字变了
<context:component-scan base-package=”实现类的包路径”/>
Java代码中,类名上增加注解@Configuration,告知spring该类中包含一个或多个Spring Bean的定义,需要注册为bean的方法上增加注解@Bean,如下
@Configuration
Public class SpringIdolConfig{
@Bean
public Performer duck(){
return new anotherBeanName(BeanName);
}
}
2017年06月12日补充
如果需要在junit控制回滚的话,需要测试类继承 AbstractTransactionalJUnit4SpringContextTests
public class TClassServiceTest extends AbstractTransactionalJUnit4SpringContextTests{
2017年06月16日补充-Spring bean 循环调用仅单粒作用域可以,其余构造、set注入都不行
不要构建成bean1引用bean2,然后bean2直接引用bean1或者间接引用bean1的情况,Spring 会报错,会造成bean的调用形成死循环
如果使用构造器注入,Requested bean is currently in creation: Is there an unresolvable circular reference?
如果使用set方法注入,java.lang.StackOverflowError
只有一种情况可以避免这种问题,就是使用Spring 的单例作用域,循环注入不同于循环调用方法
循环调用对方方法-报错
@Component
public class AInterfaceImpl implements AInterface {
@Autowired
private BInterface bInterface;
@Override
public void testA() {
System.out.println("a");
bInterface.testB();
}
}
@Component
public class BInterfaceImpl implements BInterface {
@Autowired
private AInterface aInterface;
@Override
public void testB() {
System.out.println("B");
aInterface.testA();
}
}
测试内容和报错信息:
@Autowired
private AInterface aInterface;
@Test
public void test() {
aInterface.testA();
}
但是这是代码逻辑问题,a引用b,b引用a
单独声明是没问题的,即 A中声明B,B中声明A,A中声明自己都没有问题.
2019年05月08日补充-Spring bean 内,自己可以注入自己
自己注入自己-可以
@Component
public class AInterfaceImpl implements AInterface {
@Autowired
private AInterface aInterface;
@Override
public void testA() {
System.out.println("a");
//bInterface.testB();
}
}
2019年11月11日 补充
接口Top ,类A实现接口 Top, 类A 和 其子类 B 可以同时被注册为Spring bean
事实上,类A 不实现接口Top也可以
# 接口类
public interface InterfaceTop {
void methodTop();
}
# 类A 实现接口Top
import org.springframework.stereotype.Component;
@Component("aaa")
public class ClassA implements InterfaceTop{
public void test1() {
System.out.println("ClassA.test1()");
}
@Override
public void methodTop() {
System.out.println("ClassA.methodTop");
}
}
# 类B 集成 类A
import org.springframework.stereotype.Component;
@Component("bbb")
public class ClassB extends ClassA {
public void test2() {
System.out.println("CLassB.test2()");
}
}
# 测试方法
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestDel {
public static void main(String[] args) {
ApplicationContext application=new ClassPathXmlApplicationContext("classpath:application-del.xml");
InterfaceTop classB=application.getBean("bbb", ClassB.class);
classB.methodTop();
InterfaceTop classA=application.getBean("aaa",ClassA.class);
classA.methodTop();
}
}
# 输出结果
ClassA.methodTop
ClassA.methodTop