1 Spring概述
- 轻量级:Spring是非侵入性的——基于Spring的开发不需要实现Spring的接口,不用继承任何父类
- 依赖注入(Dependancy Injection, DI),或反转控制(Inversion of Control, IOC)
- 面向切面编程(AOP——Aspect Oriented Programming)
- 容器:Spring是一个容器,因为它包含并且管理应用对象的生命周期
- 框架:Spring实现了使用简单的组件配置组合成一个复杂的应用,在Spring中可以使用XML和Java注解组合这些对象
- 一站式:在IOC和AOP的基础上可以整合各种企业应用的开源框架和优秀的第三方类库
2 IOC和DI
IOC的思想是反转资源获取的方向。容器主动地将资源推送给它所管理的组件,组件所要做的仅是选择一种合适的方式来接受资源。
DI——IOC的另一种表述方式:组件以一些预先设定好的方式(如setter方法)接受来自如容器的资源注入。
3 配置bean:
3.1 基于XML文件的方式配置bean
① 配置形式:基于XML文件的方式;基于注解的方式
② Bean的配置方式:通过全类名(反射)、通过工厂方法(静态工厂方法&实例工厂方法)、FactoryBean
③ IOC容器:ApplicationContext
- ApplicationContext的主要实现类:ClassPathXmlApplicationContext:从类路径下加载配置文件,以及FileSystemXmlApplicationContext:从文件系统中加载配置文件。
- ConfigurableApplicationContext扩展于ApplicationContext,新增两个主要方法:refresh()和close(),让ApplicationContext具有启动、刷新和关闭上下文的功能。
- ApplicationContext在初始化上下文时就实例化所有单例Bean。
- WebApplicationContext是专门为WEB应用而准备的,它允许从相对于WEB根目录的路径中完成初始化工作。
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
④ 依赖注入的方式:属性注入,构造器注入
属性注入:
- 属性注入即通过setter方法注入bean的属性值或依赖的对象
- 属性注入使用
<property>
元素,使用name属性指定Bean的属性名称,value属性或<value>
子节点指定属性值 - 属性注入是实际应用中最常用的注入方式。
<bean id="helloWorld" class="com.zc.spring.beans.HelloWorld">
<property name="name" value="Spring"></property>
</bean>
构造方法注入:
- 通过构造方法注入Bean的属性值或依赖的对象,它保证了Bean实例在实例化后可以使用。
- 构造器注入在
<constructor-arg>
元素里声明属性,<constructor-arg>
中没有name属性。
<!-- 使用构造器注入属性值可以指定参数的位置和参数的类型,以区分重载的构造方法-->
<bean id="car" class="com.zc.spring.beans.Car">
<constructor-arg value="Audi" index="0"></constructor-arg>
<constructor-arg index="1">
<!--如果字面值包含特殊字符可以使用<![CDATA[]]> 包裹起来-->
<!--属性值也可以使用value子节点进行配置-->
<value><![CDATA[<ShangHai^>]]></value>
</constructor-arg>
<constructor-arg type="double">
<value>3000</value>
</constructor-arg>
</bean>
<bean id="car2" class="com.zc.spring.beans.Car">
<constructor-arg value="Baoma" type = "java.lang.String"></constructor-arg>
<constructor-arg value="Shanghai" type="java.lang.String" ></constructor-arg>
<constructor-arg value="200" type="int"></constructor-arg>
</bean>
引用其他Bean:组成应用程序的Bean经常需要相互协作以完成应用程序的功能。在Bean的配置文件中,通过<ref>
或ref属性为Bean的属性或构造器参数指定对Bean的引用,使得Bean能够互相访问。也可以在属性或构造器里包含Bean的声明,这样的Bean成为内部Bean。
内部Bean:当Bean实例仅仅给一个特定的属性使用时,可以将其声明为内部Bean。内部Bean声明直接包含在< property>或< constructor-arg>元素里,不需要设置任何id或name属性。内部Bean不能使用在其他任何地方。
<bean id="person" class="com.zc.spring.beans.Person">
<property name="name" value="Tom"></property>
<property name="age" value="18"></property>
<!--使用property的ref属性建立bean之间的引用关系-->
<!--
<property name="car" ref="car2"></property>
-->
<!--使用<ref>子节点或ref属性都可以
<property name="car">
<ref bean = "car2" />
</property>
-->
<!--内部bean,不能被外部引用,只能在内部使用-->
<property name="car">
<bean class="com.zc.spring.beans.Car">
<constructor-arg value="Ford"></constructor-arg>
<constructor-arg value="Changan"></constructor-arg>
<constructor-arg value="200000" type="double"></constructor-arg>
</bean>
</property>
</bean>
<bean id="person2" class="com.zc.spring.beans.Person">
<constructor-arg value="Jerry"></constructor-arg>
<constructor-arg value="25"></constructor-arg>
<constructor-arg ref="car"></constructor-arg>
</bean>
3.2 基于注解的方式配置bean
① 在classpath中扫描组件
- 组件扫描(component scanning):Spring能够从classpath下自动扫描,侦测和实例化具有特定注解的组件。
- 特定组件包括:
-@Component:基本注解,标识一个受Spring管理的组件
-@Respository:标识持久层组件
-@Service:标识业务层组件
-@Controller:标识表现层组件 - 对于扫描到的组件,Spring有默认的命名策略:其一,使用非限定类名(UserService),第一个字母小写(userService);其二,也可以在注解中通过value属性值标识组件的名称,如UserService是一个接口,可以将接口的实现类UserServiceImpl通过value标识为userService。
Component部分:
package com.zc.spring.beans.annotation;
import org.springframework.stereotype.Component;
@Component
public class TestObject {
}
Repository部分:
UserRepository接口:
package com.zc.spring.beans.annotation.repository;
public interface UserRepository {
void save();
}
UserRepositoryImpl接口实现类:
package com.zc.spring.beans.annotation.repository;
import org.springframework.stereotype.Repository;
@Repository("userRepository")
public class UserRepositoryImpl implements UserRepository {
@Override
public void save() {
System.out.println("UserRepository save...");
}
}
Service部分:
package com.zc.spring.beans.annotation.service;
import org.springframework.stereotype.Service;
@Service
public class UserService {
public void add(){
System.out.println("UserService add...");
}
}
Contoller部分:
package com.zc.spring.beans.annotation.controller;
import org.springframework.stereotype.Controller;
@Controller
public class UserController {
public void execute(){
System.out.println("UserController execute ");
}
}
在组件类上使用了特定的注解之后,还需要在Spring的配置文件中声明
<context:component-scan>
:-base-packge属性制定一个需要扫描的基类包,Spring容器将会扫描这个基类包里及其子包中的所有类。
-当需要扫描多个包时,可以使用逗号分隔。
beans-annotation.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">
<!--指定Srping IOC容器扫描的包-->
<context:component-scan base-package="com.zc.spring.beans.annotation"></context:component-scan>
</beans>
以上,测试类为:
package com.zc.spring.beans.annotation;
import com.zc.spring.beans.annotation.controller.UserController;
import com.zc.spring.beans.annotation.repository.UserRepository;
import com.zc.spring.beans.annotation.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Main {
public static void main(String[] args){
ApplicationContext ctx = new ClassPathXmlApplicationContext("beans-annotation.xml");
TestObject to = (TestObject) ctx.getBean("testObject");
System.out.println(to);
UserController userController = (UserController) ctx.getBean("userController");
System.out.println(userController);
UserService userService = (UserService) ctx.getBean("userService");
System.out.println(userService);
UserRepository userRepository = (UserRepository)ctx.getBean("userRepository");
System.out.println(userRepository);
}
}
测试结果:通过注解配置了4个bean,分别打印出来。
如果仅希望扫描特定的类而非基包下的所有类,可使用resource-pattern属性过滤特定的类,如:
<context:component-scan base-package="com.zc.spring.beans.annotation" resource-pattern="repository/*.class">
</context:component-scan>
- 子节点
context:include-filter
标识要包含的目标类,该子节点需要use-default-filters="false"
(不使用默认的过滤器)来配合使用
<context:component-scan base-package="com.zc.spring.beans.annotation" use-default-filters="false">
<!--<context:include-filter type="annotation" expression="org.springframework.stereotype.Repository"></context:include-filter>-->
<context:include-filter type="assignable" expression="com.zc.spring.beans.annotation.repository.UserRepository"></context:include-filter>
</context:component-scan>
- context:exclude-filter 子节点指定排除哪些指定表达式的组件
<context:component-scan base-package="com.zc.spring.beans.annotation">
<!--<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository"></context:exclude-filter>-->
<context:exclude-filter type="assignable" expression="com.zc.spring.beans.annotation.repository.UserRepository"></context:exclude-filter>
</context:component-scan>
过滤表达式中type="annotation"
表示过滤所有标注了expression表达式表明的类,type="assignable"
表示过滤所有继承或扩展expression表达式表明的类。
当Bean和Bean之间有关联关系时
- 使用@Autowired注解自动装配具有兼容类型的单个Bean 属性如构造方法,普通字段(即使是非public),一切具有参数的方法都可以应用,如:
package com.zc.spring.beans.annotation.controller;
import com.zc.spring.beans.annotation.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
@Controller
public class UserController {
//UserController和UserService有关联
@Autowired
private UserService userService;
public void execute(){
System.out.println("UserController execute ");
userService.add();
}
}
- 默认情况下,所有使用@Autowired注解的属性都需要被设置。当Spring找不到匹配的Bean装配属性时,会抛出异常,若某一属性允许不被设置,可以设置@Autowired注解的required属性为false。
若TestObject类为:
package com.zc.spring.beans.annotation;
import org.springframework.stereotype.Component;
//此处没有注解
public class TestObject {
}
则
package com.zc.spring.beans.annotation.repository;
import com.zc.spring.beans.annotation.TestObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
@Repository("userRepository")
public class UserRepositoryImpl implements UserRepository {
//设置required=false
@Autowired(required = false)
private TestObject testObject;
@Override
public void save() {
System.out.println("UserRepository save...");
System.out.println(testObject);
}
}
测试类:
package com.zc.spring.beans.annotation;
import com.zc.spring.beans.annotation.repository.UserRepository;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Main {
public static void main(String[] args){
ApplicationContext ctx = new ClassPathXmlApplicationContext("beans-annotation.xml");
UserRepository userRepository = (UserRepository)ctx.getBean("userRepository");
userRepository.save();
}
}
结果:
- 默认情况下,当IOC容器存在多个类型兼容的Bean时(如一个接口有多个实现类),通过类型的自动装配将无法工作。此时,除了在注解中通过value属性值标识组件的名称外,还可以在@Qualifier注解里提供Bean的名称,Spring允许对方法的入参标注@Qualifier以指定注入Bean的名称。
import com.zc.spring.beans.annotation.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
@Service
public class UserService {
@Autowired
@Qualifier("userRepositoryImpl")
private UserRepository userRepository;//此时接口userRepository有多个实现类
public void add(){
System.out.println("UserService add...");
userRepository.save();
}
}
或者
package com.zc.spring.beans.annotation.service;
import com.zc.spring.beans.annotation.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
@Service
public class UserService {
private UserRepository userRepository;
@Autowired
public void setUserRepository(@Qualifier("userRepositoryImpl") UserRepository userRepository) {
this.userRepository = userRepository;
}
public void add(){
System.out.println("UserService add...");
userRepository.save();
}
}