Spring最的精髓就是依赖注入(DI),bean就是IOC容器管理的对象。
本文主要参考了《Spring教程》、《spring中bean配置和bean注入》
本文件所有源码:https://github.com/BuildSoftwareBetter/SpringLessons
0. Spring Bean基础
0.0 bean引用
关键字:ref(spring4.0 以后不再支持 local)
多个xml配置文件和单个xml配置文件都是一样的使用方式。
<!--SystemRole.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"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="RoleSystem" class="com.david.SL.Role">
<property name="name">
<value type="java.lang.String">System</value>
</property>
</bean>
<bean id="RoleAdmin" class="com.david.SL.Role">
<property name="name" value="Admin"/>
</bean>
<!--p模式-->
<bean id="RoleCommon" class="com.david.SL.Role" p:name="Common"/>
</beans>
<!--SystemUser.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.xsd">
<bean id="UserAdmin" class="com.david.SL.User">
<property name="name" value="AdminUser"/>
<property name="role">
<ref local="RoleAdmin"/>
</property>
</bean>
</beans>
0.1 依赖注入方式
属性注入:
<!--SystemRole.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"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="RoleSystem" class="com.david.SL.Role">
<property name="name">
<value type="java.lang.String">System</value>
</property>
</bean>
<bean id="RoleAdmin" class="com.david.SL.Role">
<property name="name" value="Admin"/>
</bean>
<!--p模式-->
<bean id="RoleCommon" class="com.david.SL.Role" p:name="Common"/>
</beans>
注意:p模式需申明xmlns:p=”http://www.springframework.org/schema/p”
构造函数注入:
<!--构造函数注入-->
<bean id="helloBeanCons1" class="com.david.SL.HelloWorld">
<constructor-arg value="name1"/>
</bean>
<!--构造函数注入-->
<bean id="helloBeanCons2" class="com.david.SL.HelloWorld">
<constructor-arg value="name2" type="java.lang.String"/>
</bean>
<!--构造函数注入-->
<bean id="helloBeanCons3" class="com.david.SL.HelloWorld">
<constructor-arg>
<value type="java.lang.String">name3</value>
</constructor-arg>
</bean>
注意:当构造函数有重载时,为了防止使用错误的构造函数,务必指定参数类型type的值。
0.2 加载多个配置文件
方法一:
public static void main(String[] args) {
// 加载多个xml配置文件
ApplicationContext context=new ClassPathXmlApplicationContext(new String[]{"SystemRole.xml","SystemUser.xml"});
User user=(User)context.getBean("UserAdmin");
System.out.println(user.toString());
}
说明:SystemRole.xml和SystemUser.xml是 0.0 示例中使用的的两个文件。
方法二:
关键字:import
配置文件
<!--SystemAuthority.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.xsd">
<import resource="SystemRole.xml"/>
<import resource="SystemUser.xml"/>
</beans>
加载
public static void main(String[] args) {
ApplicationContext context=new ClassPathXmlApplicationContext("SystemAuthority.xml");
User user=(User)context.getBean("UserAdmin");
System.out.println(user.toString());
}
0.3 内部bean实例
成员不通过引用进行实例化,而是自定义生成新实例。
<bean id="UserGuest" class="com.david.SL.User">
<property name="name" value="Guest1"/>
<property name="role">
<!--内部bean实例-->
<bean class="com.david.SL.Role">
<property name="name" value="Guest"/>
</bean>
</property>
</bean>
0.4 作用域scope(生命周期)
看到有些地方将spring scope翻译成作用域,是不是翻译成生命周期会准确一些?
scope枚举:
- singleton,单例,全局只有一个此对象,生命周期与应用程序生命周期一致,scope默认为singleton。
- prototype,原型,每次调用getBean都会返回一个新对象,离开作用域后立即失效。
测试一下:
创建Helloworld类:
public class HelloWorld {
private String name;
/*属性注入*/
public void setName(String name) {
this.name = name;
}
public void printHello() {
System.out.println("Spring :Hello " + name);
}
}
xml配置文件:
<!--applicationContext.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.xsd">
<bean id="helloBeanSig" class="com.david.SL.HelloWorld">
<property name="name" value="SingletonBean"/>
</bean>
<bean id="helloBeanPro" class="com.david.SL.HelloWorld" scope="prototype">
<property name="name" value="PrototypeBean"/>
</bean>
</beans>
测试:
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
HelloWorld single = (HelloWorld) context.getBean("helloBeanSig");
single.printHello();
single.setName("single2");
HelloWorld single2 = (HelloWorld) context.getBean("helloBeanSig");
single2.printHello();
HelloWorld proto = (HelloWorld) context.getBean("helloBeanPro");
proto.printHello();
proto.setName("proto2");
HelloWorld proto2 = (HelloWorld) context.getBean("helloBeanPro");
proto2.printHello();
}
第一个(默认scope=”singleton”)修改后再次getBean为修改后的值,第二个(scope=”prototype”)修改后不生效。
0.5 集合类型注入
不想写了,参考:Spring集合 (List,Set,Map,Properties) 实例
0.6 注入日期到bean
先写一种方法:
<!--注入时间,FactoryBean 方式-->
<bean id="dateFormat" class="java.text.SimpleDateFormat">
<constructor-arg value="yyyy/MM/dd"/>
</bean>
<bean id="HelloWorldFactoryBeanCreateTime" class="com.david.SL.HelloWorld">
<property name="createTime">
<bean factory-bean="dateFormat" factory-method="parse">
<constructor-arg value="2018/07/07"/>
</bean>
</property>
</bean>
0.7 Spring PropertyPlaceholderConfigurer实例
新建properties文件,(IDEA新建方式:src右键=>Resource Bundle)。
# database.properties
jdbc.url=jdbc:mysql://localhost:3306/test_db
xml文件引用:
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location">
<value>database.properties</value>
</property>
</bean>
<bean id="HelloWorldPlaceholder" class="com.david.SL.HelloWorld">
<property name="name" value="${jdbc.url}"/>
</bean>
0.8 配置继承
<?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.xsd">
<!--非抽象bean,可以被实例化-->
<bean id="ChinaCustomer" class="com.david.SL.Customer">
<property name="country" value="China"/>
</bean>
<!--继承-->
<bean id="xiaoming" class="com.david.SL.Customer" parent="ChinaCustomer">
<property name="name" value="XiaoMing"/>
</bean>
<!--继承,并覆盖属性值-->
<bean id="xiaoli" class="com.david.SL.Customer" parent="ChinaCustomer">
<property name="name" value="XiaoLi"/>
<!--覆盖-->
<property name="country" value="Japen"/>
</bean>
<!--抽象bean,不可以被实例化-->
<bean id="ChinaCustomerAbstract" class="com.david.SL.Customer" abstract="true">
<property name="country" value="China"/>
</bean>
<!--继承抽象类-->
<bean id="lisi" class="com.david.SL.Customer" parent="ChinaCustomerAbstract">
<property name="name" value="LiSi"/>
</bean>
<!--模板,不指定类型-->
<bean id="CountryTemplate" abstract="true">
<property name="country" value="China"/>
</bean>
<!--继承模板-->
<bean id="zhangsan" class="com.david.SL.Customer" parent="CountryTemplate">
<property name="name" value="ZhangSan"/>
</bean>
</beans>
0.9 依赖检查
关键字:@Required
确保特定属性已经设置:
public class Customer
{
private Person person;
private int type;
private String action;
public Person getPerson() {
return person;
}
// 必须为person属性注入对象,否则将抛出异常
@Required
public void setPerson(Person person) {
this.person = person;
}
}
在基于注解中使用:
@Component
public class Customer
{
private Person person;
private int type;
private String action;
public Person getPerson() {
return person;
}
// 必须为person属性注入对象,否则将抛出异常
@Autowired(required=true)
public void setPerson(Person person) {
this.person = person;
}
}
除了使用@Required进行注解,我们还可以自定义同等功能的注解(是否可以称为取别名?),参考:《Spring自定义@Required-style注解》
0.10 bean初始化和销毁接口
关键字:InitializingBean、init-method、DisposableBean、destroy-method、@PostConstruct、@PreDestroy
初始化在Spring中有两种方式:
- 实现InitializingBean接口
- 在配置中指定init-method方法
销毁在Spring中的两种方式:
- 实现DisposableBean接口
- 在配置中指定destroy-method方法
在上面强调了在Spring中,是因为J2EE库(common-annotations.jar)中也有方法可以实现相同的功能。
使用方式:在方法上注解@PostConstruct、@PreDestroy,再将(需申明xmlns:context=”http://www.springframework.org/schema/context”)或配置到xml配置文件中。
代码:
// Customer.java
public class Customer {
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
private String name;
private String country;
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
public String toString() {
return ("Country " + this.country + ", Name " + this.name);
}
@PostConstruct
public void initIt() throws Exception {
System.out.println("Init method after properties are set : ");
}
@PreDestroy
public void cleanUp() throws Exception {
System.out.println("Spring Container is destroy! Customer clean up");
}
}
配置文件:
<!--CustomerConfig.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.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!--不添加此代码将不会生效,context需申明xmlns:context="http://www.springframework.org/schema/context"-->
<!--<context:annotation-config />-->
<!--或者添加此代码-->
<bean class="org.springframework.context.annotation.CommonAnnotationBeanPostProcessor" />
<bean id="xiaoming" class="com.david.SL.Customer" parent="ChinaCustomer">
<property name="name" value="XiaoMing"/>
</bean>
</beans>
测试:
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("CustomerConfig.xml");
Customer c = (Customer) context.getBean("xiaoming");
System.out.println(c.getName());
((ClassPathXmlApplicationContext) context).close();
}
Init method after properties are set :
XiaoMing
Spring Container is destroy! Customer clean up
1. bean配置方式
- xml配置
- 基于注解
- JavaConfig类定义
2. 依赖注入方式
- 属性注入
- 构造函数注入
- 工厂方式注入
3. Spring表达式语言(Spring EL)
Spring表达式可以通过xml和注解使用。
xml 中使用EL
bean代码:
// Book.java
package com.david.SL;
public class Book {
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
// EL需要使用的属性必须是public,否则会抛出异常
public String name;
public String getFullName(String prefix){
return prefix+this.name;
}
}
// BookDetail.java
package com.david.SL;
public class BookDetail {
public Book getBook() {
return book;
}
public void setBook(Book book) {
this.book = book;
}
private Book book;
public String getBookName() {
return bookName;
}
public void setBookName(String bookName) {
this.bookName = bookName;
}
private String bookName;
@Override
public String toString() {
return this.bookName;
}
}
配置:
<?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.xsd">
<bean id="bookBean" class="com.david.SL.Book">
<!--普通属性设置-->
<!--<property name="name" value="Computer Networking"/>-->
<!--使用EL,并调用String的toUpperCase()方法设置属性-->
<property name="name" value="#{'Computer Networking'.toUpperCase()}"/>
</bean>
<bean id="bookDetail" class="com.david.SL.BookDetail">
<property name="book" value="#{bookBean}"/>
<!--使用EL并使用bean对象的属性设置属性值-->
<!--<property name="bookName" value="#{bookBean.name}"/>-->
<!--使用EL并调用bean对象的无参方法设置属性值-->
<property name="bookName" value="#{bookBean.getName()}"/>
<!--使用EL并调用bean对象的有参方法设置属性值-->
<!--<property name="bookName" value="#{bookBean.getFullName('v1.0 ')}"/>-->
</bean>
</beans>
测试:
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("BookELConfig.xml");
BookDetail bd = (BookDetail)context.getBean("bookDetail");
System.out.println(bd.toString());
}
注解方式使用EL
bean代码及注解:
// Book.java
package com.david.SL;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
// 注解设置自动扫描组件,并设置组件名称
@Component("bookBean")
public class Book {
public String getName() {
return name;
}
// 注解设置属性值
@Value("Computer Networking")
public String name;
// 注解设置属性,使用String运算符toUpperCase()
//@Value("#{'Computer Networking'.toUpperCase()}")
//public String name;
public String getFullName(String prefix){
return prefix+this.name;
}
}
// BookDetail.java
package com.david.SL;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
// 注解设置自动扫描组件,并设置组件名称
@Component("bookDetail")
public class BookDetail {
// 使用EL设置属性值
@Value("#{bookBean}")
private Book book;
// 使用EL设置属性值
@Value("#{bookBean.name}")
private String bookName;
// 使用EL设置属性值,通过函数
//@Value("#{bookBean.getName()}")
//private String bookName;
// 使用EL设置属性值,通过带参数的函数
//@Value("#{bookBean.getFullName('v1.0 ')}")
//private String bookName;
@Override
public String toString() {
return this.bookName;
}
}
配置自动扫描:
<!--AutoScanConfig.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"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.david.SL">
<context:include-filter type="regex" expression="com.david.SL.Book*"/>
</context:component-scan>
</beans>
测试:
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("AutoScanConfig.xml");
BookDetail bd = (BookDetail)context.getBean("bookDetail");
System.out.println(bd.toString());
}
EL运算符
基本运算符
Spring EL支持大多数标准的属性、逻辑和关系运算符。
- 关系运算符 – 等于 (==, eq), 不等于 (!=, ne), 小于 (<, lt), 小于或等于 (<= , le), 大于 (>, gt), 和大于或等于 (>=, ge).
- 逻辑运算符 – 且, 或, 非 (!).
- 数学运算符 – 加法(+), 减法 (-), 乘法 (*), 除法(/), 除模(%) 和指数幂 (^).
详细可参考《Spring EL运算符实例》
三元运算符
EL也支持三元运算符,例如:
@Value("#{10 < 100 ? true : false}")
private boolean warning;
List和Map
EL支持Lists和Maps实例,例如:
//在bookMap中查找为key = 'Math'的Book
@Value("#{booksBean.bookMap['Math']}")
private Book Math;
//返回booksList中第一个对象
@Value("#{booksBean.bookList[0]}")
private Book book;
正则表达式
@Value("#{'100' matches '\\d+' }")
private boolean isDigit;
ExpressionParser
EL解析器,可以及解析EL语法,并通过getVaue获得语法的返回值。
详细参考《测试 Spring EL与ExpressionParser》
4. Spring自动组件扫描
在之前的配置中需要手动配置每个bean组件太繁琐了,现在我们尝试自动扫描的方式来使用bean组件。在组件上添加自动扫描注解,并配置在系统xml配置中添加context:component-scan配置即可实现自动扫描,并可实现自动装配。
在Spring2.5中,有4种类型的组件自动扫描注解类型:
- @Component – 指示自动扫描组件。
- @Repository – 表示在持久层DAO组件。
- @Service – 表示在业务层服务组件。
- @Controller – 表示在表示层控制器组件。
因此,使用哪一个?其实并不那么重要。参见 @Repository,@Service 或 @Controller 源代码。你可能会发现,所有的 @Repository, @Service 或 @Controller 被注解为 @Component。因此,我们可以只使用 @Component 对所有组件进行自动扫描?是的,Spring会自动扫描所有组件的 @Component 注解。它工作正常,但不是一个好的做法,为便于阅读,应该始终声明@Repository,@ Service 或 @Controller 在指定的层,使你的代码更易于阅读。
下面直接上代码:
两个bean组件:
// CustomerDAO.java
package com.david.SL.AutoScan;
import org.springframework.stereotype.Component;
// 注解为自动扫描组件
@Component
public class CustomerDAO {
@Override
public String toString(){
return "this is a CustomerDAO";
}
}
// CustomerService.java
package com.david.SL.AutoScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
// 注解为自动扫描组件
@Component
public class CustomerService {
// Autowired实现自动装配
@Autowired
private CustomerDAO customerDAO;
@Override
public String toString() {
return "CustomerService [customerDAO=" + customerDAO + "]";
}
}
写配置文件:
*注意:需要申明xmlns:context=”http://www.springframework.org/schema/context”,同时xsi:schemaLocation也需添加http://www.springframework.org/schema/context和
http://www.springframework.org/schema/context/spring-context.xsd两项*
<?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.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.david.SL.AutoScan"/>
</beans>
测试:
注意:默认情况下,Spring 将小写组件的第一字符设为组件名称
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("AutoScanConfig.xml");
// 默认情况下,Spring 将小写组件的第一字符设为组件名称- CustomerService组件在Spring中的名称为到'customerService'。
CustomerService cs= (CustomerService) context.getBean("customerService");
System.out.println(cs.toString());
}
也可以自定义组件名称:
@Component("CustomerServiceA")
public class CustomerService {
/***/
}
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("AutoScanConfig.xml");
CustomerService cs= (CustomerService) context.getBean("CustomerServiceA");
System.out.println(cs.toString());
}
过滤组件-包含
关键字:context:include-filter
为了更精准的扫描包内组件,可以设置过滤器进行精准匹配:
<context:component-scan base-package="com.david.SL">
<!--type="regex" 匹配正则表达式-->
<context:include-filter type="regex" expression="com.david.SL.AutoScan.*"/>
</context:component-scan>
type 有多种类型,其他类型可不一一列举了
过滤组件-不包含
关键字:context:exclude-filter
<context:component-scan base-package="com.david.SL">
<!--type="regex" 匹配正则表达式-->
<context:exclude-filter type="regex" expression="com.david.SL.AutoScan.*"/>
</context:component-scan>
@Qualifier 限定组件名称
如果容器中有一个以上匹配的Bean时,则可以通过@Qualifier注解限定Bean的名称,如下所示:
package com.david.SL.AutoScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
@Component
public class CustomerService {
// @Qualifier指定bean组件的名称
@Autowired
@Qualifier("CustomerDAOA")
private CustomerDAO customerDAO;
@Override
public String toString() {
return "CustomerService [customerDAO=" + customerDAO + "]";
}
}
5. 自动装配Bean
关键字:autowire
在Spring中,支持 5 自动装配模式(Spring3以后只有4种,autodetect被废弃):
- no – 缺省情况下,自动配置是通过“ref”属性手动设定。
- byName – 根据属性名称自动装配。如果一个bean的名称和其他bean属性的名称是一样的,将会自装配它。
- byType – 按数据类型自动装配。如果一个bean的数据类型是用其它bean属性的数据类型,兼容并自动装配它。若有两个及以上相同类型bean组件对象将会抛出异常。
- constructor – 在构造函数参数的byType方式。
- autodetect – 如果找到默认的构造函数,使用“自动装配用构造”; 否则,使用“按类型自动装配”【在Spring3.0以后的版本被废弃,已经不再合法了】。
以下demo的使用的User和Role的代码如下:
// Role.java
package com.david.SL;
public class Role {
private String name;
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
// User.java
package com.david.SL;
public class User {
public User(){
}
public User(Role role){
this.role=role;
}
private String name;
public void setName(String name) {
this.name = name;
}
private Role role;
public void setRole(Role role) {
this.role = role;
}
public String toString() {
return "Name = " + this.name + ",Role " + this.role.getName();
}
}
全部配置:
<!--AutoWireConfig.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.xsd">
<bean id="RoleAdmin" class="com.david.SL.Role">
<property name="name" value="Admin"/>
</bean>
<!--有两个Role类的bean时,byType方式自动装配将抛出异常-->
<!--<bean id="RoleSystem" class="com.david.SL.Role">-->
<!--<property name="name">-->
<!--<value type="java.lang.String">System</value>-->
<!--</property>-->
<!--</bean>-->
<!--自动装配-->
<!--no 默认装配方式,使用ref-->
<!--<bean id="UserAdminDefault" class="com.david.SL.User">-->
<!--<property name="name" value="UserAdminDefault"/>-->
<!--<property name="role">-->
<!--<ref bean="RoleAdmin"/>-->
<!--</property>-->
<!--</bean>-->
<!--byType-->
<!--<bean id="UserAdminByType" class="com.david.SL.User" autowire="byType">-->
<!--<property name="name" value="UserAdminByType"/>-->
<!--</bean>-->
<!--byName-->
<!--<bean id="UserAdminByName" class="com.david.SL.User" autowire="byName">-->
<!--<property name="name" value="UserAdminByName"/>-->
<!--</bean>-->
<bean id="role2" class="com.david.SL.Role">
<property name="name" value="AdminRole"/>
</bean>
<!--constructor-->
<bean id="UserAdminByConstructor" class="com.david.SL.User" autowire="constructor">
<property name="name" value="UserAdminByConstructor"/>
</bean>
</beans>
测试代码:
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("AutoWireConfig.xml");
// no
// User UserAdminDefault = (User) context.getBean("UserAdminDefault");
// System.out.println(UserAdminDefault.toString());
// byType
// User UserAdminByType = (User) context.getBean("UserAdminByType");
// System.out.println(UserAdminByType.toString());
// byName
// User UserAdminByName = (User) context.getBean("UserAdminByName");
// System.out.println(UserAdminByName.toString());
// constructor
User UserAdminByConstructor = (User) context.getBean("UserAdminByConstructor");
System.out.println(UserAdminByConstructor.toString());
}
no
默认方式,使用ref手动设定。
xml配置:
<bean id="RoleAdmin" class="com.david.SL.Role">
<property name="name" value="Admin"/>
</bean>
<!--no 默认装配方式,使用ref-->
<bean id="UserAdminDefault" class="com.david.SL.User">
<property name="name" value="UserAdminDefault"/>
<property name="role">
<ref bean="RoleAdmin"/>
</property>
</bean>
byType
<bean id="RoleAdmin" class="com.david.SL.Role">
<property name="name" value="Admin"/>
</bean>
<!--有两个Role类的bean时,byType方式自动装配将抛出异常-->
<!--<bean id="RoleSystem" class="com.david.SL.Role">-->
<!--<property name="name">-->
<!--<value type="java.lang.String">System</value>-->
<!--</property>-->
<!--</bean>-->
<!--自动装配-->
<!--byType-->
<bean id="UserAdminByType" class="com.david.SL.User" autowire="byType">
<property name="name" value="UserAdminByType"/>
</bean>
byName
必须是完全相同的名称:
<!--byName-->
<bean id="UserAdminByName" class="com.david.SL.User" autowire="byName">
<property name="name" value="UserAdminByName"/>
</bean>
<!--User中有Role类型的role属性-->
<bean id="role" class="com.david.SL.Role">
<property name="name" value="Admin"/>
</bean>
constructor
配置:
<bean id="RoleAdmin" class="com.david.SL.Role">
<property name="name" value="Admin"/>
</bean>
<bean id="UserAdminByConstructor" class="com.david.SL.User" autowire="constructor">
<property name="name" value="UserAdminByConstructor"/>
</bean>
输出结果:
Name = UserAdminByConstructor,Role Admin
上面的代码是可以运行的,如果有两个Role类型的bean组件会是什么情况:
<bean id="RoleAdmin" class="com.david.SL.Role">
<property name="name" value="Admin"/>
</bean>
<!--将会使用此bean组件,因为User构造函数的参数名为role-->
<bean id="role" class="com.david.SL.Role">
<property name="name" value="AdminRole"/>
</bean>
<bean id="UserAdminByConstructor" class="com.david.SL.User" autowire="constructor">
<property name="name" value="UserAdminByConstructor"/>
</bean>
输出结果:
Name = UserAdminByConstructor,Role AdminRole
说明使用的是id=”role”的bean组件。由此可以推断,构造函数自动装配将首先查找同类型组件,然后在所有同类型组件中查找与参数同名的组件进行装配。还有一种情况,有多个与构造函数参数同类型的组件,但是没有同名的组件将会是什么情况?测试的结果是将不会装配。
6. Spring AOP(面向方面编程)
Advice
AOP是一个对象中函数过程的拦截器。AOP可以劫持一个正在执行的方法,在方法执行前、执行后、抛出异常后等时间点添加额外的功能。
在Spring AOP中有4种类型通知(Advice):
- MethodBeforeAdvice 方法执行之前通知
- AfterReturningAdvice 方法返回之后通知
- ThrowsAdvice 抛出异常之后通知
- MethodInterceptor 环绕通知,结合了上面的三个通知,必须调用methodInvocation.proceed()执行原方法。
Pointcut、Advisor
Advice会拦截对象的所有方法,但是通常情况我们需要拦截某一特定的方法或一类方法,所以Advice并不适用。接下来引入Pointcut。
关键字:NameMatchMethodPointcut、RegexpMethodPointcutAdvisor
- NameMatchMethodPointcut,匹配名称
- RegexpMethodPointcutAdvisor,正则表达式匹配
<bean class="org.springframework.aop.support.NameMatchMethodPointcut">
<!--匹配名称为run的方法-->
<property name="mappedName" value="run"/>
</bean>
<bean class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<!--匹配方法中含run字符串的方法-->
<property name="pattern" value=".*run.*"/>
</bean>