一、基于Schema的配置格式:
命名空间的定义分为两步:第一步指定命名空间的名称;第二步指定命名空间的Schema文档格式文件的位置,用空格或回车换行进行分隔。
在第一步中需要指定命名空间的缩略名和全名,比如:xmlns:aop="http://www.springframework.org/schema/aop",aop为命名空间的别名,一般使用简介易记的名称,文档后面的元素可通过命名空间别名加以区分,如<aop:config/>等。如果命名空间的别名为空,则表示该命名空间为文档默认命名空间。文档中无命名空间前缀的元素都属于默认命名空间,如<beans/>、<bean/>等都属于在(1)处定义的默认命名空间。
在第二步中,为每个命名空间指定了对应的Schema文档格式的定义文件,定义的语法如下:
<命名空间1> <命名空间1Schema文件> <命名空间2> <命名空间2Schema文件>
二、Bean基本配置
在配置一个Bean时,需要为其指定一个id属性为Bean的名称。id在IoC容器中必须是唯一的,id的命名必须以字母开始,后面可以是字母、数字、连字符、下划线、句号、冒号等完成结束的符号,逗号和空格这些非完整结束符是非法的。也可以为bean指定name属性,name属性几乎可以使用任何字符。
当出现多个name相同的<bean>时,通过getBean(beanName)获取Bean时,将返回后面声明的那个Bean。
如果id和name属性都未指定,那么Spring自动将全限定类名作为Bean的名称。如果存在多个实现类相同的匿名<bean>,如下:
<bean class="com.smart.simple.Car"/>
<bean class="com.smart.simple.Car"/>
<bean class="com.smart.simple.Car"/>
第一个Bean通过getBean("com.smart.simple.Car")获得;第二个Bean通过getBean("com.smart.simple.Car#1")获得;第三个通过getBean("com.smart.simple.Car#2")获得,以此类推。
三、依赖注入
1、属性注入
属性注入要求Bean提供一个默认的构造函数,并为需要注入的属性提供对应的setter方法。Spring先调用Bean的默认构造函数实例化Bean对象,然后通过反射的方式调用setter方法注入属性值。如下代码所示:
<bean id="boss2" class="com.smart.ditype.Boss">
<property name="name" value="John" />
<property name="car" ref="car" />
</bean>
注:(1)、如果类中没有定义任何构造函数,则JVM会自动为其生成一个默认的构造函数,如果类中显式定义了一个带参构造函数,则JVM不会为其生成默认的构造函数,则需要提供一个带参构造函数和一个默认的构造函数。
(2)、配置文件中<property name="name"/>的属性配置项仅要求Boss类中拥有setName()方法,但Boss类不一定拥有name成员变量。
(3)JavaBean关于属性命名的特殊规范:JavaBean允许以大写字母开头的属性变量名,不过必须满足"变量的前两个字母要么全部大写,要么全部小写"的要求。
2、构造函数注入
使用构造函数注入的前提是Bean必须提供带参的构造函数。
(1)按类型匹配入参
<bean id="car1" class="com.smart.ditype.Car">
<constructor-arg type="java.lang.String">
<value>红旗CA72</value>
</constructor-arg>
<constructor-arg type="double">
<value>20000</value>
</constructor-arg>
</bean>
<constructor-arg>的元素中有一个type属性,它为Spring提供了判断配置项和构造函数入参对应关系的依据。注意<constructor-arg>的位置并不会对最终的配置效果产生影响。
(2)按索引匹配入参
<bean id="car4" class="com.smart.ditype.Car">
<constructor-arg index="0">
<value>红旗CA72</value>
</constructor-arg>
<constructor-arg index="1">
<value>中国一汽</value>
</constructor-arg>
</bean>
(3)联合使用类型和索引匹配入参
<bean id="car3" class="com.smart.ditype.Car">
<constructor-arg index="0" type="java.lang.String">
<value>红旗CA72</value>
</constructor-arg>
<constructor-arg index="1" type="java.lang.String">
<value>中国一汽</value>
</constructor-arg>
<constructor-arg index="2" type="int">
<value>200</value>
</constructor-arg>
</bean>
(4)通过自身类型反射匹配入参
当Bean构造函数入参的类型是可辨的(非基础数据类型且入参类型各异),由于Java反射机制可以获取构造函数入参的类型,构造函数注入的配置可以不提供类型和索引信息。如下所示:
<bean id="boss1" class="com.smart.ditype.Boss">
<constructor-arg>
<value>John</value>
</constructor-arg>
<constructor-arg>
<ref bean="car" />
</constructor-arg>
<constructor-arg>
<ref bean="office" />
</constructor-arg>
</bean>
注意:当两个Bean都采用构造函数注入,而且都通过构造函数入参引用对方,就会发生类似线程死锁的循环依赖问题,这种情况下要将构造函数注入方式调整为属性注入方式。
3、工厂方法注入
(1)非静态工厂方法
必须实例化工厂类后才能调用工厂方法
<bean id="carFactory" class="com.smart.ditype.CarFactory" />
<bean id="car5" factory-bean="carFactory" factory-method="createHongQiCar">
</bean>
(2)静态工厂方法
用户在无须创建工厂类实例的情况下就可以调用工厂类方法
四、注入参数详解
1、字面值
XML中有5个特殊字符,分别是&、<、>、"、'。如果配置文件中的注入值包括这些特殊字符,就需要进行特别处理。两种方法解决:1是采用<![CDATA[ ]]>特殊标签,将包含特殊字符的字符串封装起来;2是使用XML转义序列表示这些特殊字符,这5个特殊字符对应的XML转义序列如下所示:
![](https://img-blog.csdn.net/20170320200448920?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvc3VwZXJzbm93MDYyMg==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
2、引用其他Bean
<ref>元素可以通过以下3个属性引用容器中的其他Bean
(1)bean:通过该属性可以引用统一容器或父容器中的Bean
(2)local:通过该属性只能引用同一配置文件中定义的Bean
(3)parent:引用父容器中的Bean,如<ref parent="car">
3、内部Bean
如果car Bean只被boss Bean引用,而不被容器中任何其他的Bean引用,则可以将car以内部Bean的方式注入Boss中。如下所示:
<bean id="boss2" class="com.smart.attr.Boss">
<property name="car">
<bean class="com.smart.attr.Car">
<property name="maxSpeed" value="200" />
<property name="price" value="2000.00" />
</bean>
</property>
</bean>
4、null值
Spring会将<value></value>解析为空字符串。如果为属性设置为一个null的注入值,必须使用专用的<null/>元素标签,通过它可以为Bean的字符串或其他对象类型的属性注入null值。
5、级联属性
如果我们希望在定义Boss式直接为其属性Car的属性提供注入值,则可以采用以下配置:
<bean id="boss" class="com.smart.attr.Boss">
<property name="car.brand" value="吉利CT50"/>
</bean>
6、集合类型属性
(1)List和Set
如下为Boss添加一个List类型的favorites属性
<bean id="parentBoss" abstract="true" class="com.smart.attr.Boss">
<property name="favorites">
<list>//改成<set>
<value>看报</value>
<value>赛车</value>
<value>高尔夫</value>
</list>//改成</set>
</property>
</bean>
List属性既可以通过<value>注入字符串,也可以通过<ref>注入容器中其他的Bean。
(2)Map
<bean id="boss1" class="com.smart.attr.Boss">
<property name="jobs">
<map>
<entry >
<key><value>AM</value></key>
<value>会见客户</value>
</entry>
<entry>
<key><value>PM</value></key>
<value>公司内部会议</value>
</entry>
</map>
</property>
</bean>
如果某一Map元素的键和值都是对象,可以用如下方式配置:
<entry >
<key><ref bean="keyBean"/></key>
<ref bean="valueBean"/>
</entry>
(3)Properties
Properties类型其实可看作Map类型的特例。Map元素的键和值可以是任何类型的对象,而Properties属性的键和值都只能是字符串。
<property name="mails">
<props>
<prop key="jobMail">john-office@smart.com</prop>
<prop key="lifeMail">john-life@smart.com</prop>
</props>
</property>
(4)集合合并
Spring允许子<bean>继承父<bean>的同名属性集合元素,并将子<bean>中配置的集合属性值和父<bean>中配置的同名属性值合并起来作为最终Bean的属性值,如下所示:
<bean id="parentBoss" abstract="true"
class="com.smart.attr.Boss">
<property name="favorites">
<set>
<value>看报</value>
<value>赛车</value>
<value>高尔夫</value>
</set>
</property>
</bean>
<bean id="childBoss" parent="parentBoss">
<property name="favorites">
<set merge="true">
<value>爬山</value>
<value>游泳</value>
</set>
</property>
</bean>
7、简化配置方式
使用p命名空间
对于字面值属性,其格式为:p:<属性名>=“xxx”
对于引用对象的属性,其格式为:p<属性名>-ref="xxx"
8、自动装配
<bean>元素提供了一个指定自动装配类型的属性:autowire=“<自动装配类型>”。Spring提供了4中自动装配类型,如下:
<beans>元素标签中的default-autowire属性可以配置全局自动匹配,default-autowire属性默认值为no,表示不启用自动装配。在<beans>中定义的自动装配策略可以被<bean>的自动装配策略覆盖。在实际开发中,XML配置方式很少启用自动装配,而基于注解的配置方式默认采用byType自动装配策略。
五、方法注入
1、lookup方法注入
lookup方法注入是有一定使用范围的,一般希望通过一个singleton Bean获取一个prototype Bean时使用。
<bean id="magicBoss" class="com.smart.injectfun.MagicBoss" >
<lookup-method name="getCar" bean="car"/>
</bean>
通过lookup-method元素标签为MagicBoss的getCar()提供动态实现,返回prototype类型的car Bean,这样Spring将在运行期为MagicBoss接口提供动态实现。
2、方法替换
用于替换他人的Bean必须实现MethodReplacer接口,Spring将利用该接口的方法去替换目标Bean的方法。
<bean id="boss2" class="com.smart.injectfun.Boss2"/>
<bean id="boss1" class="com.smart.injectfun.Boss1">
<replaced-method name="getCar" replacer="boss2"></replaced-method>
</bean>
上面的代码使用Boss2的方法去替换Boss1的getCar()方法。
六、Bean之间的关系
1、继承
<bean id="abstractCar" class="com.smart.tagdepend.Car"
p:brand="红旗CA72" p:price="2000.00" p:color="黑色"
abstract="true"/>
<bean id="car3" parent="abstractCar">
<property name="color" value="红色"/>
</bean>
<bean id="car4" parent="abstractCar" >
<property name="color" value="白色"/>
</bean>
可以把子bean相同的属性抽取出来放在父bean中,一般在父bean中要声明abstract=“true”
2、依赖
Spring运行用户通过depends-on属性显式指定Bean前置依赖的Bean,前置依赖的Bean会在本Bean实例化之前创建好。如果前置依赖多个Bean,则可以通过逗号、空格或分号的方式创建Bean的名称。
<bean id="cacheManager" class="com.smart.tagdepend.CacheManager" depends-on="sysInit" />
<bean id="sysInit" class="com.smart.tagdepend.SysInit" />
3、引用
可以通过<idref>引用另一个<bean>的名字。
<bean id="car" class="com.smart.tagdepend.Car"/>
<bean id="boss" class="com.smart.tagdepend.Boss" >
<property name="carId" >
<idref bean="car"/>
</property>
</bean>
七、整合多个配置文件
Spring允许通过<import>将多个配置文件引入到一个文件中,进行配置文件的集成。这样,在启动Spring容器时,仅需指定这个合并好的配置文件即可。
八、Bean作用域
1、singleton作用域
Spring的ApplicationContext容器在启动时,自动实例化所有singleton的Bean并缓存于容器中。虽然启动时会花费一些时间,但有两个好处:一是对Bean提前进行实例化操作会及早发现一些潜在的配置问题;二是Bean以缓存的方式保存,当运行时用到该Bean时无须再实例化,提高了运行效率。如果用户不希望在容器启动时提前实例化singleton的Bean,则可以通过lazy-init属性进行控制。
<bean id="boss1" class="com.smart.scope.Boss" p:car-ref="car" lazy-init="true"/>
2、prototype作用域
默认情况下,Spring容器在启动时不实例化prototype的Bean。
3、与Web应用环境相关的Bean的作用域
如果用户使用Spring的WebApplicationContext,则可使用另外3种Bean作用域:request、session、globalSession。在使用这些作用域之前,必须在Web容器中进行一些额外的配置。
在低版本的Web容器中,用户可以使用HTTP请求过滤器进行配置。
<web-app>
<filter>
<filter-name>requestContextFilter</filter-name>
<filter-class>org.springframework.web.filter.RequestContextFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>requestContextFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
在高版本的Web容器中,则可以利用HTTP请求监听器进行配置
<web-app>
<listener>
<listener-class>
org.springframework.web.context.request.RequestContextListener
</listener-class>
</listener>
</web-app>
在整合Spring容器时使用ContextLoaderListener,它实现了ServletContextListener监听器接口,ServletContextListener只负责监听Web容器启动和关闭的事件。而RequestContextListener实现了ServletRequestListener监听器接口,该监听器监听HTTP请求事件,Web服务器接收的每一次请求都会通知该监听器。
(1)、request作用域
<bean name="car" class="com.smart.scope.Car" scope="request"/>
每次HTTP请求调用car Bean时,Spring容器就会创建一个新的car Bean;请求处理完毕后,就会销毁这个Bean。
(2)、session作用域
<bean name="car" class="com.smart.scope.Car" scope="session"/>
car Bean的作用域横跨整个HTTP Session,Session中的所有HTTP请求都共享同一个car Bean。当HTTP Session结束后,实例才被销毁。
(3)、globalSession作用域
globalSession作用域类似于session作用域,不过仅在Portlet的Web应用中使用。
(4)、作用域依赖问题
当非Web相关作用域的Bean引用Web作用域的Bean时,需要使用aop代理。如下:
当boss Bean在Web环境下调用car Bean时,Spring AOP将启用动态代理智能地判断boss Bean位于哪个HTTP请求线程中,并从对应的HTTP请求线程域获得对应的car Bean。
九、基于注解的配置
1、使用注解定义Bean
(1)@Component注解在某个类上,Spring容器自动将POJO类转换为容器管理的Bean
(2)@Repository:用于对DAO实现类进行标注
(3)@Service:用于对Service实现类进行标注
(4)@Controller:用于对Controller实现类进行标注
之所以要在@Component之外提供这3个特殊的注解,是为了让标注类本身的用途清晰化,完全可以用@Component替代这3个特殊的注解。
2、扫描注解定义的Bean
Spring提供了context命名空间,它提供了通过扫描类包以应用注解定义Bean的方式,如下:
<?xml version="1.0" encoding="UTF-8" ?>
<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-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<context:component-scan base-package="com.smart.anno"/>
</beans>
通过context命名空间的component-scan的base-package属性指定一个需要扫描的基类包,Spring容器将会扫描这个基类包里的所有类,并从类的注解信息中获取Bean的定义信息。
如果仅希望扫描特定的类而非基包下的所有类,那么可以使用resource-pattern属性过滤出特定的类,如下:
<context:component-scan base-package="com.smart" resource-pattern="anno/*.class"/>
这里将基类包设置为com.smart;默认情况下resource-pattern属性的值为“**/*.class”,即基类包里的所有类,将其设置为“anno/*.class”,则Spring仅会扫描基类包里anno子类包的类。
如果仅需要过滤基类包中实现了XxxService接口的类或标注了某个特定注解的类等,则要用<context:component-scan>的过滤子元素实现,如下:
<context:component-scan base-package="com.smart">
<context:include-filter type="regex" expression="com\.smart\.anno.*Dao"/>
<context:include-filter type="regex" expression="com\.smart\.anno.*Service"/>
<context:exclude-filter type="aspectj" expression="com.smart..*Controller+"/>
</context:component-scan >
<context:include-filter>表示要包含的目标类,而<context:exclude-filter>表示要排除的目标类。这两个过滤元素支持多种类型的过滤表达式,如下:
<context:component-scan/>的use-default-filters属性,其默认值为true,表示默认会对标注@Component、@Controller、@Service及@Reposity的Bean进行扫描。
3、自动装配Bean
(1)、使用@Autowired进行自动注入
@Autowired默认按类型(byType)匹配的方式在容器中查找匹配的Bean,当且仅有一个匹配的Bean时,Spring将其注入@Autowired标注的变量中。
如果容器中没有一个和标注变量类型匹配的Bean,那么Spring容器启动时将报NoSuchBeanDefinitionException异常。如果希望Spring即使找不到匹配的Bean完成注入也不要抛出异常,那么可以使用@Autowired(required=false)进行标注
(2)、使用@Qualifier指定注入Bean的名称
如果容器中有一个以上匹配的Bean时,则可以通过@Qualifier注解限定Bean的名称,如下:
@Autowired
@Qualifier("userDao")
private UserDao userDao;
(3)、对类方法进行标注
@Autowired可以对类成员变量及方法的入参进行标注,如果一个方法拥有多个入参,则在默认情况下,Spring将自动选择匹配入参类型的Bean进行注入。Spring允许对方法入参标注@Qualifier以指定注入Bean的名称。如下:
@Autowired
public void init(@Qualifier("userDao")UserDao userDao,LogDao logDao){
System.out.println("multi param inject");
this.userDao = userDao;
this.logDao =logDao;
}
(4)、对集合类进行标注
如果对类中集合类的变量或方法入参进行@Autowired标注,那么Spring会将容器中类型匹配的所有Bean都自动注入进来。
@Component
public class MyComponent {
@Autowired(required=false)
private List<Plugin> plugins;
@Autowired //(1)
private Map<String,Plugin> pluginMaps;
public List<Plugin> getPlugins() {
return plugins;
}
}
在(1)处将实现Plugin接口的Bean注入Map集合,是Spring4.0提供的新特性,其中key是Bean的名字,value是所有实现了Plugin的Bean。
在Spring4.0中可以通过@Order注解或实现Ordered接口来决定Bean加载的顺序,值越小,优先被加载。
(5)、对延迟依赖注入的支持
Spring4.0支持延迟依赖注入,即在Spring容器启动时,对于在Bean上标注@Lazy及@Autowired注解的属性,不会立即注入属性值,而是延迟到调用此属性的时候才会注入属性值。对Bean实施延迟依赖注入,要注意@Lazy注解必须同时标注在属性及目标Bean上。
(6)、对标准注解的支持
@Resource注解要求提供一个Bean名称的属性,如果属性为空,则自动采用标注处的变量名或方法名作为Bean的名称。
@Inject和@Autowired同样也是按类型匹配注入Bean的,只不过它没有required属性。
4、Bean作用范围及生命过程方法
通过注解配置的Bean和通过<bean>配置的Bean一样,默认的作用范围都是singleton。Spring为注解配置提供了一个@Scope注解,可以通过他显式指定Bean的作用范围。
Spring从2.5开始支持@PostConstruct和@PreDestroy注解,不过在使用注解时,可以在一个Bean中定义多个@PostConstruct和@PreDestroy方法
十、基于Java类的配置