Spring体系结构
Spring 框架是一个分层架构,它包含一系列的功能要素并被分为大约20个模块。这些模块分为Core Container、Data Access/Integration、Web、AOP(Aspect Oriented Programming)、Instrumentation和测试部分,如下图所示:
面向接口编程
- 结构设计中,分清层次及调用关系,每层只向外(上层)提供一组功能接口,各层间仅依赖接口而非实现类。
- 接口实现的变动不影响各层间的调用,这一点在公共服务中尤为重要。
- “面向接口编程”中的“接口”是用于隐藏具体实现和实现多态性的组件。
IOC
- 什么是IOC?
- IOC:控制反转,控制权的转移,应用程序本身不负责依赖对象的创建和维护,而是由外部容器负责创建和维护。
- DI(依赖注入)是其一种实现方式
- 目的:创建对象并且组装对象之间的关系
实例
- 提供UserService接口和实现类
- 获得UserService实现类的实例
由Spring创建对象实例–> IoC 控制反转(Inverse of Control)
public interface UserService { public void addUser(); } public class UserServiceImpl implements UserService { @Override public void addUser() { System.out.println("tyshawn"); } } public class TestIOC { @Test public void test() throws Exception { String xmlPath = "org/a/IOC/beans.xml"; ApplicationContext context = new ClassPathXmlApplicationContext(xmlPath); UserService service = (UserService) context.getBean("userServiceId"); service.addUser(); //tyshawn } } <beans <!--IOC--> <bean id="userServiceId" class="org.a.IOC.UserServiceImpl"> </bean> </beans>
DI
- 依赖:一个对象需要使用另一个对象
- 注入:通过setter方法进行另一个对象实例设置。
模拟spring执行过程:
- 创建service实例:BookService bookService = new BookServiceImpl() –>IoC
- 创建dao实例:BookDao bookDao = new BookDaoImple() –>IoC
将dao设置给service:bookService.setBookDao(bookDao); –>DI
public interface BookDao { public void addBook(); } public class BookDaoImpl implements BookDao { @Override public void addBook() { System.out.println("add book"); } } public interface BookService { public void addBook(); } public class BookServiceImpl implements BookService{ private BookDao bookDao; public void setBookDao(BookDao bookDao) { this.bookDao = bookDao; } @Override public void addBook() { this.bookDao.addBook(); } } public class TestDI { @Test public void test() throws Exception { String xmlPath = "org/b/DI/beans.xml"; ApplicationContext context = new ClassPathXmlApplicationContext(xmlPath); BookService service = (BookService) context.getBean("BookServiceId"); service.addBook(); //add book } } <beans <!--控制反转--> <bean id="BookServiceId" class="org.b.DI.BookServiceImpl"> <!--依赖注入--> <property name="bookDao" ref="BookDaoId"></property> </bean> <bean id="BookDaoId" class="org.b.DI.BookDaoImpl"></bean> </beans>
核心API
- BeanFactory:这是一个工厂,用于生成任意bean。采取延迟加载,第一次getBean时才会初始化Bean。
- ApplicationContext:是BeanFactory的子接口,功能更强大。(国际化处理、事件传递、Bean自动装配、各种不同应用层的Context实现)。当配置文件被加载,就进行对象实例化。
- ClassPathXmlApplicationContext:用于加载classpath(类路径、src)下的xml
FileSystemXmlApplicationContext:用于加载指定盘符下的xml
@Test public void demo02(){ //使用BeanFactory --第一次条用getBean实例化 String xmlPath = "org/a/IOC/beans.xml"; BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource(xmlPath)); BookService bookService = (BookService) beanFactory.getBean("bookServiceId"); bookService.addBook(); }
Bean实例化方式
3种bean实例化方式:默认构造、静态工厂、实例工厂。
默认构造:
<bean id="" class=""> 必须提供默认构造
静态工厂:用于生成实例对象,所有的方法必须是static
<bean id="" class="工厂全限定类名" factory-method="静态方法">
public interface UserService {
public void addUser();
}
public class UserServiceImpl implements UserService {
@Override
public void addUser() {
System.out.println("tyshawn");
}
}
public class MyBeanFactory {
/**
* 创建实例,方法必须是静态的
*/
public static UserServiceImpl createService(){
return new UserServiceImpl();
}
}
public class TestStaticFactory {
@org.junit.Test
public void test() throws Exception {
String xmlPath = "org/c/staticfactory/beans.xml";
ApplicationContext context = new ClassPathXmlApplicationContext(xmlPath);
UserService service = (UserService) context.getBean("userServiceId");
service.addUser(); //tyshawn
}
}
<beans
<!--将静态工厂创建的实例交予spring
factory-method 确定静态方法名
在实际开发中Factory类已写好,直接调用
-->
<bean id="userServiceId" class="org.c.staticfactory.MyBeanFactory" factory-method="createService"> </bean>
</beans>
实例工厂:必须先有工厂实例对象,通过实例对象创建对象。提供所有的方法都是“非静态”的。
public interface UserService {
public void addUser();
}
public class UserServiceImpl implements UserService {
@Override
public void addUser() {
System.out.println("tyshawn");
}
}
public class MyBeanFactory {
/**
* 创建实例
*/
public UserServiceImpl createService(){
return new UserServiceImpl();
}
}
public class TestFactory {
@org.junit.Test
public void test() throws Exception {
String xmlPath = "org/d/factory/beans.xml";
ApplicationContext context = new ClassPathXmlApplicationContext(xmlPath);
UserService service = (UserService) context.getBean("userServiceId");
service.addUser();
}
}
<beans
<!--
获得UserService
factory-bean确定工厂
factory-method确定工厂方法
-->
<bean id="userServiceId" factory-bean="myBeanFactoryId" factory-method="createService"> </bean>
<!--创建工厂实例-->
<bean id="myBeanFactoryId" class="org.d.factory.MyBeanFactory" ></bean>
</beans>
Bean种类
- 普通bean:之前操作的都是普通bean。< bean id=”” class=”A”> ,spring直接创建A实例,并返回
FactoryBean:是一个特殊的bean,具有工厂生成对象能力,只能生成特定的对象。bean必须使用 FactoryBean接口,此接口提供方法 getObject() 用于获得特定bean。< bean id=”” class=”FB”> 先创建FB实例,使用调用getObject()方法,并返回方法的返回值
FB fb = new FB(); return fb.getObject();
- BeanFactory 和 FactoryBean 对比?
- BeanFactory:工厂,用于生成任意bean。
- FactoryBean:特殊bean,用于生成另一个特定的bean。例如:ProxyFactoryBean ,此工厂bean用于生产代理。< bean id=”” class=”….ProxyFactoryBean”> 获得代理对象实例。AOP使用。
作用域
用于确定spring创建bean实例个数
public interface UserService {
public void addUser();
}
public class UserServiceImpl implements UserService {
@Override
public void addUser() {
System.out.println("tyshawn");
}
}
public class TestFactory {
@org.junit.Test
public void test() throws Exception {
String xmlPath = "org/f/scope/beans.xml";
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
UserService userService = applicationContext.getBean("userServiceId" ,UserService.class);
UserService userService2 = applicationContext.getBean("userServiceId" ,UserService.class);
System.out.println(userService);
System.out.println(userService2);
}
}
<beans
<bean id="userServiceId" class="org.f.scope.UserServiceImpl"
scope="singleton" ></bean>
</beans>
生命周期
初始化和销毁
目标方法执行前和执行后,将进行初始化和销毁。
<bean id="" class="" init-method="初始化方法名称" destroy-method="销毁的方法名称">
public interface UserService {
public void addUser();
}
public class UserServiceImpl implements UserService {
@Override
public void addUser() {
System.out.println("tyshawn");
}
public void myInit(){
System.out.println("初始化");
}
public void myDestroy(){
System.out.println("销毁");
}
}
public class TestFactory {
@org.junit.Test
public void test() throws Exception {
String xmlPath = "org/g/lifecycle/beans.xml";
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
UserService userService = (UserService) applicationContext.getBean("userServiceId");
userService.addUser();
/*执行销毁方法要求:
* 1.容器必须close
* 2.必须是单例的
* */
// 使用反射关闭容器
// applicationContext.getClass().getMethod("close").invoke(applicationContext);
applicationContext.close();
}
}
<beans
<!--
init-method 用于配置初始化方法,准备数据等
destroy-method 用于配置销毁方法,清理资源等
-->
<bean id="userServiceId" class="org.g.lifecycle.UserServiceImpl"
init-method="myInit" destroy-method="myDestroy" ></bean>
</beans>
结果:
初始化
e_lifecycle add user
销毁
Spring注入
Spring注入是指在启动Spring容器加载bean配置的时候,完成对变量的赋值行为。
注入方式有:构造注入、Setter注入、p命名空间、SpEL。
构造注入
public class User {
private Integer uid;
private String username;
private Integer age;
public User(Integer uid, String username) {
this.uid = uid;
this.username = username;
}
public User(String username, Integer age) {
this.username = username;
this.age = age;
}
//getters、setters、toString
}
public class TestCons {
@org.junit.Test
public void test() throws Exception {
String xmlPath = "org/e/xml/DI_constructor/beans.xml";
ApplicationContext context = new ClassPathXmlApplicationContext(xmlPath);
User user = (User) context.getBean("userId");
System.out.println(user);
}
}
<beans
<!-- 构造方法注入
* <constructor-arg> 用于配置构造方法的一个参数argument
name :参数的名称
value:设置普通数据
ref:引用数据,一般是另一个bean id值
index :参数的索引号,从0开始 。如果只有索引,匹配到了多个构造方法时,默认使用第一个。
type :确定参数类型
例如:使用名称name
<constructor-arg name="username" value="jack"></constructor-arg>
<constructor-arg name="age" value="18"></constructor-arg>
例如2:【类型type 和 索引 index】
<constructor-arg index="0" type="java.lang.String" value="1"></constructor-arg>
<constructor-arg index="1" type="java.lang.Integer" value="2"></constructor-arg>
-->
<bean id="userId" class="org.e.xml.DI_constructor.User">
<constructor-arg index="0" type="java.lang.String" value="1"></constructor-arg>
<constructor-arg index="1" type="java.lang.Integer" value="2"></constructor-arg>
</bean>
</beans>
Setter注入
public class Person {
private String pname;
private Integer age;
private Address homeAddr; //家庭地址
private Address companyAddr; //公司地址
//getters、setters、toString
}
public class Address {
private String addr; //地址信息
private String tel; //电话
//getters、setters、toString
}
public class TestSetter {
@org.junit.Test
public void test() throws Exception {
String xmlPath = "org/e/xml/DI_setter/beans.xml";
ApplicationContext context = new ClassPathXmlApplicationContext(xmlPath);
Person person = (Person) context.getBean("personId");
System.out.println(person);
}
}
<beans
<bean id="personId" class="org.e.xml.DI_setter.Person">
<property name="pname" value="Tom"></property>
<property name="age">
<value>22</value>
</property>
<property name="homeAddr" ref="homeAddrId"></property>
<property name="companyAddr">
<ref bean="companyAddrId"></ref>
</property>
</bean>
<bean id="homeAddrId" class="org.e.xml.DI_setter.Address">
<property name="addr" value="beijing"></property>
<property name="tel" value="120"></property>
</bean>
<bean id="companyAddrId" class="org.e.xml.DI_setter.Address">
<property name="addr" value="shanghai"></property>
<property name="tel" value="119"></property>
</bean>
</beans>
P命令空间
- 对“setter方法注入”进行简化,替换< property name=”属性名”>,而是在< bean p:属性名=”普通值” p:属性名-ref=”引用值”>
p命名空间使用前提,必须添加命名空间
public class Person { private String pname; private Integer age; private Address homeAddr; //家庭地址 private Address companyAddr; //公司地址 //getters、setters、toString } public class Address { private String addr; //地址信息 private String tel; //电话 //getters、setters、toString } public class TestSetter { @org.junit.Test public void test() throws Exception { String xmlPath = "org/e/xml/DI_p/beans.xml"; ApplicationContext context = new ClassPathXmlApplicationContext(xmlPath); Person person = (Person) context.getBean("personId"); System.out.println(person); } } <beans <!--对property进行简化--> <bean id="personId" class="org.e.xml.DI_p.Person" p:pname="Tom" p:age="22" p:homeAddr-ref="homeAddrId" p:companyAddr-ref="companyAddrId"> </bean> <bean id="homeAddrId" class="org.e.xml.DI_p.Address" p:addr="beijing" p:tel="120"> </bean> <bean id="companyAddrId" class="org.e.xml.DI_p.Address" p:addr="shanghai" p:tel="119"> </bean> </beans>
SpEL
对< property>进行统一编程,所有的内容都使用value。
public class Customer {
private String cname = "jack";
private Double pi ;// = Math.PI;
//getters、setters、toString
}
public class TestSpEL {
@Test
public void test(){
String xmlPath = "org/e/xml/DI_spel/beans.xml";
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
Customer customer = (Customer) applicationContext.getBean("customerId");
System.out.println(customer);
}
}
<beans
<bean id="customerId" class="org.e.xml.DI_spel.Customer">
<property name="cname" value="#{'Tom'}"></property>
<property name="pi" value="#{T(java.lang.Math).PI}"></property>
</bean>
</beans>
集合注入
public class CollData {
private String[] arrayData;
private List<String> listData;
private Set<String> setData;
private Map<String, String> mapData;
private Properties propsData;
//getters、setters、toString
}
public class TestColl {
@org.junit.Test
public void test() throws Exception {
String xmlPath = "org/e/xml/DI_collection/beans.xml";
ApplicationContext context = new ClassPathXmlApplicationContext(xmlPath);
CollData data = (CollData) context.getBean("collDataId");
System.out.println(data);
}
}
<beans
<!--
集合的注入都是给<property>添加子标签
数组:<array>
List:<list>
Set:<set>
Map:<map> ,map存放k/v 键值对,使用<entry>描述
Properties:<props> <prop key=""></prop>
普通数据:<value>
引用数据:<ref>
-->
<bean id="collDataId" class="org.e.xml.DI_collection.CollData" >
<property name="arrayData">
<array>
<value>a</value>
<value>b</value>
<value>c</value>
</array>
</property>
<property name="listData">
<list>
<value>Tom</value>
<value>Alice</value>
<value>Adam</value>
</list>
</property>
<property name="setData">
<set>
<value>I</value>
<value>Love</value>
<value>You</value>
</set>
</property>
<property name="mapData">
<map>
<entry key="Jack" value="杰克"></entry>
<entry>
<key><value>rose</value></key>
<value>露丝</value>
</entry>
</map>
</property>
<property name="propsData">
<props>
<prop key="高富帅">嫐</prop>
<prop key="白富美">嬲</prop>
<prop key="男屌丝">挊</prop>
</props>
</property>
</bean>
</beans>
装配Bean——基于注解
- 注解:就是一个类,使用@注解名称
开发中:使用注解 取代 xml配置文件。
1. @Component取代<bean class=""> @Component("id") 取代 <bean id="" class=""> 2.web开发,提供3个@Component注解衍生注解(功能一样)取代<bean class=""> @Repository :dao层 @Service:service层 @Controller:web层 3.依赖注入,给私有字段设置,也可以给setter方法设置 普通值:@Value("") 引用值: 方式1:按照【类型】注入 @Autowired 方式2:按照【名称】注入1 @Autowired @Qualifier("名称") 方式3:按照【名称】注入2 @Resource("名称") 4.生命周期 初始化:@PostConstruct 销毁:@PreDestroy 5.作用域 @Scope("prototype") 多例
- 注解使用前提,添加命名空间,让spring扫描含有注解类
IOC
public interface UserService {
public void addUser();
}
@Component("userServiceId")
public class UserServiceImpl implements UserService {
@Override
public void addUser() {
System.out.println("g_annotation.a_ioc add user");
}
}
public class TestAnnoIoC {
@Test
public void test(){
String xmlPath = "org/h/annotation/IOC/beans.xml";
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
UserService userService = (UserService) applicationContext.getBean("userServiceId");
userService.addUser();
}
}
<beans xmlns="http://www.springframework.org/schema/beans"
<!-- 组件扫描,扫描含有注解的类 -->
<context:component-scan base-package="org.h.annotation.IOC"></context:component-scan>
</beans>
DI
public interface StudentDao {
public void save();
}
@Repository("studentDaoId")
public class StudentDaoImpl implements StudentDao {
@Override
public void save() {
System.out.println("好好学习,天天向上!");
}
}
public interface StudentService {
public void study();
}
@Service()
public class StudentServiceImpl implements StudentService {
@Autowired
@Qualifier("studentDaoId")
private StudentDao dao;
@Override
public void study() {
dao.save();
}
}
@Controller("studentActionId")
public class StudentAction {
@Autowired
private StudentService service;
public void execute(){
service.study();
}
}
public class TestAnnoWeb {
@Test
public void test(){
String xmlPath = "org/h/annotation/web/beans.xml";
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
StudentAction action = (StudentAction) applicationContext.getBean("studentActionId");
action.execute();
}
}
<beans xmlns="http://www.springframework.org/schema/beans"
<!-- 组件扫描,扫描含有注解的类 -->
<context:component-scan base-package="org.h.annotation.IOC"></context:component-scan>
</beans>
生命周期和作用域
public interface UserService {
public void addUser();
}
@Service("userServiceId")
//@Scope("prototype")
public class UserServiceImpl implements UserService {
@Override
public void addUser() {
System.out.println("d_scope add user");
}
@PostConstruct
public void myInit(){
System.out.println("初始化");
}
@PreDestroy
public void myDestroy(){
System.out.println("销毁");
}
}
public class TestOther {
@Test
public void test(){
String xmlPath = "org/h/annotation/other/beans.xml";
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
UserService userService = applicationContext.getBean("userServiceId" ,UserService.class);
UserService userService2 = applicationContext.getBean("userServiceId" ,UserService.class);
System.out.println(userService);
System.out.println(userService2);
applicationContext.close();
}
}
<beans xmlns="http://www.springframework.org/schema/beans"
<!-- 组件扫描,扫描含有注解的类 -->
<context:component-scan base-package="org.h.annotation.IOC"></context:component-scan>
</beans>