1,Spring 概述
2,IOC 容器和 Bean 的配置
Spring是一个开源框架
Spring为简化企业级开发而生,使用Spring,JavaBean就可以实现很多以前要靠EJB才能实现的功能。同样的功能,在EJB中要通过繁琐的配置和复杂的代码才能够实现,而在Spring中却非常优雅和简洁
Spring是一个IOC(DI)和AOP容器框架
Spring的优良特性
①,非侵入式:基于Spring开发的应用中的对象可以不依赖于Spring的API
②,依赖注入:DI–Dependency Injection,反转控制(IOC)最经典的实现
③,面向切面编程:Aspect Oriented Programming – AOP
④,容器:Spring是一个容器,因为它包含并且管理应用对象的生命周期
⑤,组件化:Spring实现了使用简单的组件配置组合成一个复杂的应用。在Spring中可以使用xml和Java注解组合这些对象
⑥,一站式:在IOC和AOP的基础上可以整合各种企业应用的开源框架和优秀的第三方类库(实际上Spring自身也提供了表述层的SpringMVC和持久层SpringJDBC)
使用eclipse需要加入JAR包
Spring自身JAR包:Spring-framework-4.0.0.RELEASE\libs目录下
spring-beans-4.0.0.RELEASE.jar
spring-context-4.0.0.RELEASE.jar
spring-core-4.0.0.RELEASE.jar
spring-expression-4.0.0.RELEASE.jar
commons-logging-1.1.1.jar
在Spring Tool Suite工具中通过如下步骤创建Spring的配置文件
- 创建一个Spring项目
- 创建项目后idea自带Spring的jar包,所以不需要导入
public class Person {
private Integer age;
private String name;
}
<?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>:定义spring所管理的一个对象
<id>:该对象的唯一标示,注意:不能重复
<class>:此对象所属类的权限定名-->
<bean id="person" class="com.Person">
<!-- <property> :为对象的某个属性赋值
name:属性名
value:属性值-->
<property name="age" value="1"></property>
<property name="name" value="Tommmey周"></property>
</bean>
</beans>
public class PersonTest {
@Test
public void demo01(){
//初始化容器
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
//通过getBean()获取对象
//Person person = (Person)ac.getBean("person");
// 使用此方法获取对象时,要求spring所管理的此类型的对象只能有一个
// Person person = ac.getBean(Person.class);
Person person = ac.getBean("person2",Person.class);
System.out.println(person);
}
}
IOC(Inversion of Control):反转控制(把原来由程序员管理的对象的权力,反转给程序本身,让程序自己管理),也叫控制反转
在应用程序中的组件需要获取资源时,传统的方式是组件主动的从容器中获取所有需要的资源,在这样的模式下开发人员往往需要知道在具体容器中特点资源的获取方式,增加了学习成本,同时降低了开发效率
反转控制的思想完全颠覆了应用程序组件获取资源的传统方式:反转了资源的获取方向-- -改由容器 主动的将资源推送给需要的组件,开发人员不需要知道容器是如何创建资源对象的,只需要提供接收资源的方式即可,极大的降低了学习成本,提高了开发的效率。这种行为也称为查找的被动形式。
传统方式:我想吃饭 我需要买菜做饭
反转控制:我想吃饭 饭来张口
DI(Dependency Injection):依赖注入
总结:IOC就是一种反转控制的思想,而DI是对IOC的一种具体实现。
IOC容器在Spring中的实现
前提: Spring 中有IOC思想,IOC 思想必须基于IOC 容器来完成,而IOC容器在最底层实质上就是一个对象工厂
在通过IOC容器读职Bean的实例之前,需要先将IOC容器本身实例化。
Spring提供了IOC容器的两种实现方式:
①,BeanFactory:IOC
容器的基本实现,是Spring内部的基础设施,是面向Spring本身的,不是提供给开发人员使用的。
②,ApplicationContext:BeanFactory
的子接口,提供了更多高级特性。面向Spring的使用者,几乎所有场合都使用ApplicationContext而不是底层的BeanFactory.
ApplicationContext
的主要实现类
①,ClassPathXmlApplicationContext
:对应类路径下的XML格式的配置文件
②,FileSystemXmlApplicationContext
: 对应文件系统中的XML格式的配置文件
③,在初始化时就创建单例的 bean,也可以通过配置的方式指定创建的Bean是多实例的
ConfigurableApplicationContext
①,是AplcationContext的子接口,包含一些扩展方法
②,refresh()和 close()让ApliatinonContext具有启动、关闭和刷新上下文的能力。
通过bean的setXxx()方法赋值
通过bean的构造器赋值
<bean id="person3" class="com.Person">
<constructor-arg value="3" />
<constructor-arg value="Tommey3" index="1"/><!-- index通过索引指定参数位置 -->
</bean>
①,字面量
可以使用字符串表示的值, 可以通过value属性或value子节点的方式指定
基本数据类型及其封装类、 String 等类型都可以采取字面值注入的方式
若字面值中 包含特殊字符,可以使用把字 面值包裹起来
②,null值
③,给bean的级联属性赋值
④,外部已声明的bean,引用其他的bean
⑤,内部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: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="person4" class="com.Person" p:age="4" p:name="Tommey周4"></bean>
<bean id="person3" class="com.Person">
<constructor-arg value="3" />
<constructor-arg value="Tommey3" index="1"/>
</bean>
<bean id="person5" class="com.Person">
<property name="age" value="4"></property>
<property name="name"><null /></property>
<!-- <property name="workerMan" ref="workerMan"> </property>-->
<!-- <property name="workerMan.job" value="架构师"> </property>-->
<!-- 内部bean -->
<property name="workerMan">
<bean class="com.WorkerMan">
<property name="job" value="高级工程师"></property>
<property name="sal" value="15092.33"></property>
</bean>
</property>
</bean>
<bean id="workerMan" class="com.WorkerMan">
<property name="job" value="程序员"></property>
<property name="sal" value="19999.8"></property>
</bean>
在Spring中可以通过一组内置的XML标签来配置集合属性,例如:,
数组和List
<bean id="workerMan" class="com.WorkerMan">
<property name="job" value="程序员"></property>
<property name="sal" value="19999.8"></property>
<property name="level">
<list>
<value>A</value>
<value>B</value>
<value>C</value>
</list>
</property>
<property name="people">
<ref bean="person2" />
</property>
</bean>
Map
<bean id="workerMan" class="com.WorkerMan">
<property name="job" value="程序员"></property>
<property name="sal" value="19999.8"></property>
<property name="map">
<map>
<description>键值对</description>
<entry>
<key><value>1001</value></key>
<ref bean="person2" />
</entry>
</map>
</property>
</bean>
集合类型的bean
<bean id="workerMan1" class="com.WorkerMan">
<property name="job" value="程序员"></property>
<property name="sal" value="19999.8"></property>
<property name="people" ref="persons"> </property>
</bean>
<util:list id="persons">
<ref bean="person"/>
<ref bean="person2"/>
<ref bean="person3"/>
</util:list>
Spring中有两种类型的bean,一种是普通bean,另一种是工厂bean,即FactoryBean。
工厂bean跟普通bean不同,其返回的对象不是指定类的一个实例,其返回的是该工厂bean的getObject方法所返回的对象。
工厂bean必须实现org.springframework.beans.factory.FactoryBean接口。
public class Car {
private String brand;
private Double price;
}
public class MyFactory implements FactoryBean<Car> {
@Override
public Car getObject() throws Exception {
Car car = new Car("奥迪",99999.99);
return car;
}
@Override
public Class<?> getObjectType() {
return Car.class;
}
@Override
public boolean isSingleton() {
return false;
}
}
<?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="factory" class="com.MyFactory"></bean>
</beans>
@Test
public void demo02(){
ApplicationContext ac = new ClassPathXmlApplicationContext("factory-bean.xml");
Object factory = ac.getBean("factory");
System.out.println(factory);
}
Spring有两种类型的Bean,一种普通bean,另一种是工厂bean(FactoryBean)。
普通bean:在配置文件中定义 bean 类型就是返回类型
工厂 bean :在配置文件定义 bean 类型可以和返回类型不一样。第一步 创建类,让这个类作为工厂 bean,实现接口 FactoryBean;第二步 实现接口里面的方法,在实现的方法中定义返回的 bean 类型
示例代码:
public class MyBean implements FactoryBean<Person> {
@Override
public Person getObject() throws Exception {
Person person = new Person();
person.setName("Tommey周");
return person;
}
@Override
public Class<?> getObjectType() {
return null;
}
@Override
public boolean isSingleton() {
return false;
}
}
@Test
public void test01() {
ApplicationContext context =
new ClassPathXmlApplicationContext( "bean1.xml");
Person person = context.getBean( "myBean", Person. class);
System.out.println(person);
}
类别 | 说明 |
---|---|
singleton | 在SpringIOC容器中仅存在一个Bean实例,Bean以单实例的方式存在 |
prototype | 每次调用 getBean() 时都会返回一个新的实例 |
request | 每次 HTTP 请求都会创建一个新的Bean,该作用域仅适用于WebApplicationContext环境 |
sesion | 同一个HTTP session 共享一个Bean,不同的HTTP Session 使用不同的Bean。该作用域仅适用于WebApplicationContext环境 |
当bean的作用域为单例时,Spring会在IOC容器对象创建时就创建bean的对象实例。
而当bean的作用域为prototype时,IOC容器在获取bean的实例时创建bean的实例对象。
示例代码:
<bean id="car" class="com.Car" scope="singleton">
<property name="brand" value="bmw"></property>
<property name="price" value="300000.22"></property>
</bean>
singleton 和 prototype 区别?
①,singleton 单实例,prototype 多实例
②, 设置 scope 值是 singleton 时候,加载 spring 配置文件时候就会创建单实例对象设置 scope 值是 prototype 时候,不是在加载 spring 配置文件时候创建 对象,在调用getBean 方法时候创建多实例对象
Spring IOC容器可以管理bean的生命周期,Spring允许在bean生命周期内特定的时间点执行指定的任务。
Spring IOC容器对bean的生命周期进行管理的过程:
①, 通过构造器或工厂方法创建bean实例
②, 为bean的属性设置值和对其他bean的引用
③, 调用bean的初始化方法
④ , 当容器关闭时,调用bean的销毁方法
在配置bean时,通过init-method和destroy-method 属性为bean指定初始化和销毁方法
bean的后置处理器
①, bean后置处理器允许在调用初始化方法前后对bean进行额外的处理
②, bean后置处理器对IOC容器里的所有bean实例逐一处理,而非单一实例。其典型应用是:检查bean属性的正确性或根据特定的标准更改bean的属性。
③, bean后置处理器需要实现接口:org.springframework.beans.factory.config.BeanPostProcessor
。在初始化方法被调用前后,Spring将把每个bean实例分别传递给上述接口的以下两个方法:
●postProcessBeforeInitialization(Object, String)
●postProcessAfterInitialization(Object, String)
添加bean后置处理器后bean的生命周期
①,通过构造器或工厂方法创建bean实例
②,为bean的属性设置值和对其他bean的引用
③,将bean实例传递给bean后置处理器的postProcessBeforeInitialization()方法
④,调用bean的初始化方法
⑤,将bean实例传递给bean后置处理器的postProcessAfterInitialization()方法
⑥,当容器关闭时调用bean的销毁方法
当bean的配置信息逐渐增多时,查找和修改一些bean的配置信息就变得愈加困难。这时可以将一部分信息提取到bean配置文件的外部,以properties格式的属性文件保存起来,同时在bean的配置文件中引用properties属性文件中的内容,从而实现一部分属性值在发生变化时仅修改properties属性文件即可。这种技术多用于连接数据库的基本信息的配置。
直接配置
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="username" value="root"/>
<property name="password" value="root"/>
<property name="url" value="jdbc:mysql:///test"/>
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
</bean>
jdbc.driver = com.mysql.jdbc.Driver
jdbc.url = jdbc:mysql://localhost:3306/test
jdbc.username = root
jdbc.password = root
<?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 https://www.springframework.org/schema/context/spring-context.xsd">
<bean id="dataSource1" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driver}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<!-- 加载资源文件 -->
<!-- <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">-->
<!-- <property name="location" value="db.properties"></property>-->
<!-- </bean>-->
<context:property-placeholder location="db.properties"/>
</beans>
@Test
public void demo03() throws SQLException {
ApplicationContext ac = new ClassPathXmlApplicationContext("dataSource.xml");
DruidDataSource bean = ac.getBean("dataSource",DruidDataSource.class);
System.out.println(bean.getConnection());
}
自动装配的概念
手动装配:以value或ref的方式明确指定属性值
都是手动装配。
自动装配:根据指定的装配规则,不需要明确指定
,Spring自动将匹配的属性值注入bean中。
装配模式:
①,根据类型自动装配
:将类型匹配的bean作为属性注入到另一个bean中。若IOC容器中有多个与目标bean类型一致的bean,Spring将无法判定哪个bean最合适该属性,所以不能执行自动装配
②,根据名称自动装配
:必须将目标bean的名称和属性名设置的完全相同
③,通过构造器自动装配
:当bean中存在多个构造器时,此种自动装配方式将会很复杂。不推荐使用。
选用建议
相对于使用注解的方式实现的自动装配,在XML文档中进行的自动装配略显笨拙,在项目中更多的使用注解的方式实现。
代码实现
<!--
autowire:根据某种策略自动为非字面量属性赋值
byName:通过属性名和spring容器中的bean的id进行比较,若一致则可直接赋值
byType:通过spring容器中bean的类型,为兼容性的属性赋值
在使用byType的过程中,要求spring容器中只能有一个能为属性赋值的bean
当设置autowire属性,会作用于该bean中所有的非字面量属性,因此谁都不用
-->
<bean id="person" class="com.Person" autowire="byType">
<property name="age" value="1"></property>
<property name="name" value="Tommey周"></property>
</bean>
<bean id="workerMan" class="com.WorkerMan">
<property name="sal" value="11111.11"></property>
<property name="job" value="中级工程师"></property>
</bean>
概述
使用注解标识组件
①,普通组件:@Component
标识一个受Spring IOC容器管理的组件
②,持久化层组件:@Repository
标识一个受Spring IOC容器管理的持久化层组件
③,业务逻辑层组件:@Service
标识一个受Spring IOC容器管理的业务逻辑层组件
④,表述层控制器组件:@Controller
标识一个受Spring IOC容器管理的表述层控制器组件
⑤,组件命名规则
默认情况:使用组件的简单类名首字母小写后得到的字符串作为bean的id
使用组件注解的value属性指定bean的id
注意:事实上Spring并没有能力识别一个组件到底是不是它所标记的类型,即使将@Respository
注解用在一个表述层控制器组件上面也不会产生任何错误,所以@Respository、@Service、@Controller
这几个注解仅仅是为了让开发人员自己明确 当前的组件扮演的角色。
代码示例:
UserController类:
@Controller
public class UserController {
public UserController(){
System.out.println("UserController");
}
}
public interface UserService {
}
@Service
public class UserServiceImpl implements UserService {
public UserServiceImpl(){
System.out.println("UserServiceImpl");
}
}
public interface UserDao {
}
@Repository
public class UserDaoImpl implements UserDao {
public UserDaoImpl(){
System.out.println("UserDaoImpl");
}
}
扫描组件
组件被上述注解标识后还需要通过Spring进行扫描才能够侦测到。
<context:component-scan base-package="com"/>
详细说明
①,base-package属性指定一个需要扫描的基类包,Spring容器将会扫描这个基类包及其子包中的所有类。
②,当需要扫描多个包时可以使用逗号分隔。
③,如果仅希望扫描特定的类而非基包下的所有类,可使用resource-pattern属性过滤特定的类,示例:
④,包含与排除
子节点表示要包含的目标类注意:通常需要与use-default-filters属性配合使用才能够达到“仅包含某些 组件”这样的效果。即:通过将use-default-filters属性设置为false,禁用默认过滤器,然后扫描的就只是include-filter中的规则指定的组件了。
子节点表示要排除在外的目标类
component-scan
下可以拥有若干个include-filter和exclude-filter子节点
过滤表达式
类别 | 示例 | 说明 |
---|---|---|
annotation | com.XxxAnnotation | 过滤所有标注了XxxAnnotation的类。这个规则根据目标组件是否标注了指定类型的注解进行过滤。 |
assignable | com.BaseXxx | 过滤所有BaseXxx类的子类。这个规则根据目标组件是否是指定类型的子类的方式进行过滤。 |
aspectj | com.*Service+ | 所有类名是以Service结束的,或这样的类的子类。这个规则根据AspectJ表达式进行过滤。 |
regex | com\.anno\.* | 所有com.anno包下的类。这个规则根据正则表达式匹配到的类名进行过滤。 |
custom | com.XxxTypeFilter | 使用XxxTypeFilter类通过编码的方式自定义过滤规则。该类必须实现org.springframework.core.type.filter.TypeFilter接口 |
<!--
<context:include-filter>:在设定的包结构下,再次通过注解或类型具体包含到某个或某几个类
注意:在使用包含时,一定要设置use-default-filters="false",将默认的过滤(即扫描包下)关闭
-->
<context:component-scan base-package="com">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
<context:include-filter type="assignable" expression="com.service.UserServiceImpl"/>
</context:component-scan>
一个
中可以出现多个include,也可以同时出现多个exclue,但是这两个不能同时出现
组件装配
需求
Controller组件中往往需要用到Service组件的实例,Service组件中往往需要用到 Repository组件的实例。Spring可以通过注解的方式帮我们实现属性的装配。
基于注解的组件化管理:
@Component
,@Controller
(控制层),@Service
(业务层),Repository
(持久层),这四个注解功能完全相同,不过在实际开发中,要在实现不同功能的类上加上相应的注解
完成组件化管理的过程
①,在需要被spring管理的类上加上相应注解
②,在配置文件中通过
对所设置的包结构进行扫描,就会将加上注解的类,作为spring的组件进行加载
实现依据
在指定要扫描的包时,
元素会自动注册一个bean的后置处 理器:AutowiredAnnotationBeanPostProcessor
的实例。该后置处理器可以自动装配标记 了@Autowired、@Resource或@Inject注解的属性。
@Autowired
注解
①,@Autowired
装配时,会默认使用byType的方式,此时要求spring容器中只有一个能为其赋值,当byType实现不了装配时,会自动切换byName,此时要求spring容器中,有一个bean的id和属性名一致。
②,自动装配:在需要赋值的非字面量属性,加上@Autowired
,就可以在spring容器中,通过不同的方式匹配到相应的bean,构造器、普通字段(即使是非public)、一切具有参数的方法都可以应用@Autowired
注解
③,默认情况下,所有使用@Autowired
注解的属性都需要被设置。当Spring找不到匹 配的bean装配属性时,会抛出异常。
④,若某一属性允许不被设置,可以设置@Autowired
注解的required属性为 false
⑤,若自动装配时,匹配到多给能够赋值的bean,可使用@Qualifier(value="beanId")
指定使用的bean,@Autowired
和@Qualifier(value="beanId")
可以一起作用域一个带形参的方法上,此时@Qualifier(value="beanId")
所指定的bean作用于形参
⑥,@Autowired
注解也可以应用在数组类型的属性上,此时Spring将会把所有匹配的bean进行自动装配。
⑦,@Autowired
注解也可以应用在集合属性上,此时Spring读取该集合的类型信息,然后自动装配所有与之兼容的bean。
⑧,@Autowired
注解用在java.util.Map上时,若该Map的键值为String,那么 Spring将自动装配与值类型兼容的bean作为值,并以bean的id值作为键。
@Resource
@Resource
注解要求提供一个bean名称的属性,若该属性为空,则自动采用标注处的变量或方法名作为bean的名称。
@Inject
@Inject
和@Autowired
注解一样也是按类型注入匹配的bean,但没有reqired属性。
下一章,(2)Spring————AOP详解