文章目录
一、Spring概述
-
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和持久层的Spring JDBC)。
-
Spring模块
二、IOC和DI
1、简要概述
- IOC(Inversion of Control):控制反转
反转控制的思想完全颠覆了应用程序组件获取资源的传统方式:反转了资源的获取方向——改由容器主动的将资源推送给需要的组件,开发人员不需要知道容器是如何创建资源对象的,只需要提供接收资源的方式即可,极大的降低了学习成本,提高了开发的效率。这种行为也称为查找的被动形式。 - DI(Dependency Injection):依赖注入
IOC的另一种表述方式:即组件以一些预先定义好的方式(例如:setter 方法)接受来自于容器的资源注入。相对于IOC而言,这种表述更直接。
IOC描述的只是一种思想,而DI是IOC思想的具体表现。
2、IOC容器在Spring中的实现
- 在通过IOC容器读取Bean的实例之前,需要先将IOC容器本身实例化
- Spring中IOC容器的两种表现方式:
<1>BeanFactory:IOC容器的基本实现,是Spring内部的基础设施,面向Spring本身的,开发人员不可以使用。
<2>ApplicationContext:BeanFactory的子接口,提供了更多高级特性,几乎所有场合都使用ApplicationContext而不是底层的BeanFactory.
3、给bean属性赋值
<1>、通过bean的set方式注入
<!--DI依赖注入,set方法-->
<bean id="car" class="spring.di.Car">
<property name="brand" value="法拉利"></property>
<property name="crop" value="Italy"></property>
<property name="price" value="6000000"></property>
</bean>
//获取容器对象
ApplicationContext context = new ClassPathXmlApplicationContext("spring-di.xml");
Car car = context.getBean("car", Car.class);
System.out.println(car);
<2>、通过bean的构造器
1、Spring自动匹配合适的构造器
<!--DI依赖注入,Constructor方法
index:指定参数位置
type:指定参数的类型
-->
<bean id="car1" class="spring.di.Car">
<constructor-arg value="奥迪" ></constructor-arg>
<constructor-arg value="一汽" ></constructor-arg>
<constructor-arg value="400000"></constructor-arg>
</bean>
public Car(String brand, String crop, Double price) {
this.brand = brand;
this.crop = crop;
this.price = price;
}
2、通过构造器索引赋值
<bean id="car1" class="spring.di.Car">
<constructor-arg value="奥迪" index="0" name="brand"></constructor-arg>
<constructor-arg value="一汽" index="1"></constructor-arg>
<constructor-arg value="400000" index="2"></constructor-arg>
</bean>
注:
1.通过类型区分重载的构造器:当同时有多构造器的时候在参数种类相同的时候优先匹配最后定义的构造器,这样就会产生一个问题,会让我们匹配到不想匹配的构造器,此时应该多声明一个type
<bean id="car1" class="spring.di.Car">
<constructor-arg value="奥迪" index="0" name="brand" type="java.lang.String"></constructor-arg>
<constructor-arg value="一汽" index="1" type="java.lang.String"></constructor-arg>
<constructor-arg value="400000" index="2" type="java.lang.Double"></constructor-arg>
</bean>
<3>、p命名空间
<!--使用p命名空间配置bean-->
<bean id="car3" class="spring.di.Car"
p:brand="奔驰" p:crop="梅赛德斯" p:price="100000">
</bean>
<4>、字面量
- 可以使用字符串表示的值,可以通过value属性或者value子节点的方式指定
- 基本数据类型及其封装类、String等类型都可以采用字面注入的方式
- 若字面值中包含特殊字符,可以使用<![CDATA[]]>把字面值包裹起来
<!--字面量
特殊字符:1、使用实体:  &:& <:< >:> ":"
2、<![CDATA[任意字符]]>
-->
<bean id="book" class="spring.di.Book">
<property name="bookId" value="1001"></property>
<property name="bookName">
<value><![CDATA[《Thinking In Java》]]></value>
</property>
</bean>
package spring.di;
public class Book {
private Integer bookId;
private String bookName;
public Integer getBookId() {
return bookId;
}
public void setBookId(Integer bookId) {
this.bookId = bookId;
}
public String getBookName() {
return bookName;
}
public void setBookName(String bookName) {
this.bookName = bookName;
}
@Override
public String toString() {
return "Book{" +
"bookId=" + bookId +
", bookName='" + bookName + '\'' +
'}';
}
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-di.xml");
Book book = context.getBean("book", Book.class);
System.out.println(book);
}
}
<5>、引用外部声明的bean
<!--引用其他的bean-->
<bean id="person" class="spring.di.Person">
<property name="id" value="1001"></property>
<property name="name" value="zz"></property>
<property name="car" ref="car"></property>
</bean>
注意:只可以引用IOC内部的对象实例
<6>、内部bean
当bean实例仅仅给一个特定的属性使用时,可以将其声明为内部bean。内部bean声明直接包含在<property>
或<constructor-org>
元素里,不需要设置任何id或name属性。
<!--内部bean-->
<bean id="person1" class="spring.di.Person">
<property name="id" value="521"></property>
<property name="name" value="Tom"></property>
<property name="car">
<!--内部bean只能在内部使用-->
<bean class="spring.di.Car">
<property name="brand" value="兰博基尼"></property>
<property name="crop" value="德国"></property>
<property name="price" value="10000000"></property>
</bean>
</property>
</bean>
<7>、给bean属性附空值
<bean id="person2" class="spring.di.Person">
<property name="id" value="1002"></property>
<property name="name" value="John"></property>
<property name="car"><null/></property>
</bean>
<8>、给bean属性附数组和List
<bean id="personList" class="spring.di.PersonList">
<property name="name" value="Mary"></property>
<property name="cars">
<list>
<ref bean="car"/>
<ref bean="car1"/>
<ref bean="car3"/>
</list>
</property>
</bean>
package spring.di;
import java.util.List;
public class PersonList {
private String name;
private List<Car> cars;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<Car> getCars() {
return cars;
}
public void setCars(List<Car> cars) {
this.cars = cars;
}
@Override
public String toString() {
return "PersonList{" +
"name='" + name + '\'' +
", cars=" + cars +
'}';
}
@Test
public void testList(){
ApplicationContext context = new ClassPathXmlApplicationContext("spring-di.xml");
PersonList personList = context.getBean("personList", PersonList.class);
System.out.println(personList);
}
}
注意:
1、List也可以使用Array,因为IOC内部也封装了标签,因为Array底层也是List。
2、如果ref引用的值在之前的ref过程中被修改过值,那么这里再使用ref引用的话就是被修改之后的值。
<9>、给bean属性附Map
<bean id="personMap" class="spring.di.PersonMap">
<property name="name" value="Jerry"></property>
<property name="map">
<map>
<entry key="zz" value-ref="car"></entry>
<entry key="pp" value-ref="car1"></entry>
<entry key="ww" value-ref="car3"></entry>
</map>
</property>
</bean>
package spring.di;
import java.util.Map;
public class PersonMap {
private String name;
private Map<String, Car> map;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Map<String, Car> getMap() {
return map;
}
public void setMap(Map<String, Car> map) {
this.map = map;
}
@Override
public String toString() {
return "PersonMap{" +
"name='" + name + '\'' +
", map=" + map +
'}';
}
@Test
public void testMap(){
ApplicationContext context = new ClassPathXmlApplicationContext("spring-di.xml");
PersonMap personMap = context.getBean("personMap", PersonMap.class);
System.out.println(personMap);
}
}
<10>、集合类型的bean
<util:list id="listBean">
<ref bean="car"/>
<ref bean="car1"/>
<ref bean="car3"/>
</util:list>
上面我们已经给bean打包好了listBean,所以我们可以在外面使用list的时候去调用listBean
bean id="personList" class="spring.di.PersonList">
<property name="name" value="Mary"></property>
<property name="cars" ref="listBean">
<!--<list>-->
<!--<ref bean="car"/>-->
<!--<ref bean="car1"/>-->
<!--<ref bean="car3"/>-->
<!--</list>-->
</property>
</bean>
4、FactoryBean
Spring中有两种类型的bean,一种是普通bean,另一种是工厂bean,即FactoryBean。
工厂bean跟普通bean不同,其返回的对象不是指定类的一个实例,其返回的是该工 厂bean的getObject方法所返回的对象。
工厂bean必须实现org.springframework.beans.factory.FactoryBean接口。
package spring.factoryBean;
import org.springframework.beans.factory.FactoryBean;
import spring.di.Car;
public class CarFactoryBean implements FactoryBean<Car> {
/**
* 工厂bean具体创建的bean对象是有getObject方法返回的
* @return
* @throws Exception
*/
@Override
public Car getObject() throws Exception {
return new Car("五菱", "五菱", 50000.0);
}
/**
* 返回具体的bean对象的类型
* @return
*/
@Override
public Class<?> getObjectType() {
return Car.class;
}
/**
* bean可以是单例的,也可以是原型的(非单例)
* @return
*/
@Override
public boolean isSingleton() {
return true;
}
}
配置文件如下:
<bean id="factoryBean" class="spring.factoryBean.CarFactoryBean"></bean>
5、bean的高级配置
<1>、配置信息的继承
<!--继承关系
parent="父类对象" 继承之后的bean对象不必再写class
abstract="true" 表示是抽象类,不能创建对象因此不需要再写class
-->
<bean id="address1" abstract="true">
<property name="city" value="beijing"></property>
<property name="street" value="ChangAnJie"></property>
</bean>
<bean id="address2" class="spring.relation.Address" parent="address1">
<!--<property name="city" value="beijing"></property>-->
<property name="street" value="WuDaoKou"></property>
</bean>
<2>、bean之间的依赖
<!--依赖关系
注意:存在依赖关系 依赖的那个对象必须存在否则不能创建bean
-->
<bean id="address1" abstract="true">
<property name="city" value="beijing"></property>
<property name="street" value="ChangAnJie"></property>
</bean>
<bean id="address3" class="spring.relation.Address" parent="address1" depends-on="address4"></bean>
<bean id="address4" class="spring.relation.Address"></bean>
6、bean的作用域
<!--
bean的作用域:
singleton:单例的(默认值),在整个IOC容器中只能存在一个bean对象,而且在IOC容器对象被创建时,就创建单例的bean的对象,
后续每次通过getBean()方法获取bean对象时,返回的都是同一个对象
prototype:原型的/多例的 在整个IOC容器中可以有多个bean对象,在IOC容器对象被创建时,不会创建原型的bean对象
等到getBean()方法获取bean对象时,才会创建一个新的bean对象返回。
request:一次请求对应一个bean对象
session:一次会话对应一个bean对象
-->
<bean id="car" class="spring.scope.Car" scope="singleton">
<property name="brand" value="奥迪"></property>
<property name="price" value="4000000"></property>
</bean>
@Test
public void testSingleton(){
ApplicationContext context = new ClassPathXmlApplicationContext("spring-scope.xml");
Car car = context.getBean("car", Car.class);
Car car1 = context.getBean("car", Car.class);
System.out.println(car == car1);
}//输出结果为true
7、bean的生命周期
- 通过构造器或者工厂方法创建实例
- 为bean的属性设置值和对其他bean的引用
- 调用bean的初始化方法
- 使用bean
- 容器关闭,调用bean的销毁方法
配置文件如下:
<bean id="car" class="spring.life.Car" init-method="init" destroy-method="destroy">
<property name="brand" value="宝马"/>
<property name="price" value="450000"/>
</bean>
package spring.life;
public class Car {
private String brand;
private Double price;
public Car() {
System.out.println("====>1、通过构造器创建bean对象");
}
/**
* 初始化方法
*/
public void init(){
System.out.println("====>3、调用bean的初始化方法");
}
/**
* 当IOC容器关闭的时候、调用bean的销毁方法
*/
public void destroy(){
System.out.println("====>5、调用bean的销毁方法");
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
System.out.println("====>2、通过set方法为bean的属性设置值");
}
public Double getPrice() {
return price;
}
public void setPrice(Double price) {
this.price = price;
}
@Override
public String toString() {
return "Car{" +
"brand='" + brand + '\'' +
", price=" + price +
'}';
}
public static void main(String[] args) {
ConfigurableApplicationContext context = new ClassPathXmlApplicationContext("spring-life.xml");
Car car = context.getBean("car", Car.class);
System.out.println("====>4、使用bean对象");
System.out.println(car);
//5、当IOC容器关闭的时候调用bean的销毁方法
context.close();
}
}
输出结果如下:
四月 20, 2019 12:26:22 上午 org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@7aec35a: startup date [Sat Apr 20 00:26:22 CST 2019]; root of context hierarchy
四月 20, 2019 12:26:22 上午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from class path resource [spring-life.xml]
====>1、通过构造器创建bean对象
====>2、通过set方法为bean的属性设置值
postProcessBeforeInitialization
====>3、调用bean的初始化方法
postProcessAfterInitialization
====>4、使用bean对象
Car{brand='宝马', price=450000.0}
四月 20, 2019 12:26:22 上午 org.springframework.context.support.ClassPathXmlApplicationContext doClose
信息: Closing org.springframework.context.support.ClassPathXmlApplicationContext@7aec35a: startup date [Sat Apr 20 00:26:22 CST 2019]; root of context hierarchy
====>5、调用bean的销毁方法
bean的后置处理器
是对bean生命周期中的特定操作
package spring.life;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
/**
* bean的后置处理器
*/
public class MyBeanPostProcessor implements BeanPostProcessor {
/**
*在生命周期的二三步之间执行,对bean进行操作,需要返回一个bean
*/
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("postProcessBeforeInitialization");
return bean;
}
/**
*在生命周期的三四步之间执行,对bean进行操作,需要返回一个bean
*/
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("postProcessAfterInitialization");
return bean;
}
}
配置信息如下:
<bean class="spring.life.MyBeanPostProcessor"></bean>
8、自动装配
- 手动装配:以value或ref的方式明确指定属性值都是手动装配。
- 自动装配:根据指定的装配规则,不需要明确指定,Spring自动将匹配的属性值注入bean中
<bean id="car" class="spring.autowire.Car">
<property name="brand" value="奔驰"/>
<property name="price" value="500000"/>
</bean>
<bean id="address" class="spring.autowire.Address">
<property name="province" value="天津"/>
<property name="city" value="天津"/>
</bean>
<!--自动装配
byName:通过bean属性名与IOC容器中<bean>的id值进行匹配,匹配成功则装配成功
byType:使用bean的属性的类型与IOC容器中<bean>的class进行匹配,如果唯一匹配则匹配成功
-->
<bean id="person" class="spring.autowire.Person" autowire="byType">
<property name="name" value="Tom"/>
</bean>
9、通过注解配置bean
<1>、常用注解标识组件
- 普通组件:@Component
标识一个受Spring IOC容器管理的组件 - 持久化层组件:@Repository
标识一个受Spring IOC容器管理的持久化层组件 - 业务逻辑层组件:@Service
标识一个受Spring IOC容器管理的业务逻辑层组件 - 表述层控制器组件:@Controller
标识一个受Spring IOC容器管理的表述层控制器组件
<2>、扫描组件
<!--组件扫描:扫描加了注解的类,并管理到IOC容器中,
base-package:基包,Spring会扫描指定包以及子包下的所有类,将带有注解的类管理到IOC容器中
-->
<context:component-scan base-package="spring.annotation">
DAO层
@Repository
public class UserDaoJdbcImpl implements UserDao {
}
Service层
@Service
public class UserServiceImpl implements UserService {
}
Controller层
/**
* Controller注解的作用:
* 相当于在xml文件中:
* <bean id="userController" class="spring.annotation.controller.UserController"></bean>
* 注解默认的id值就是类名首字母小写,可以在注解中手动指定id值,:@Controller(value = "id值")可以简写为:@Controller("")
*/
@Controller
public class UserController {
}
测试:
@Test
public void testAnnotation(){
ApplicationContext context = new ClassPathXmlApplicationContext("spring-annotation.xml");
UserController userController = context.getBean("userController", UserController.class);
System.out.println(userController);
UserServiceImpl userServiceImpl = context.getBean("userServiceImpl", UserServiceImpl.class);
System.out.println(userServiceImpl);
UserDaoJdbcImpl userDaoJdbcImpl = context.getBean("userDaoJdbcImpl", UserDaoJdbcImpl.class);
System.out.println(userDaoJdbcImpl);
}
输出结果:
spring.annotation.controller.UserController@6328d34a
spring.annotation.service.UserServiceImpl@145eaa29
spring.annotation.dao.UserDaoJdbcImpl@15bb6bea
<3>、包含扫描
(1)context:include-filter子节点表示要包含的目标类
注意:通常需要与use-default-filters属性配合使用才能够达到“仅包含某些组件”这样的效果。即:通过将use-default-filters属性设置为false禁用默认过滤器,然后扫描的就只是include-filter中的规则指定的组件了。
第一种方法:
type=“annotation”:注解的全列名
--<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"></context:include-filter>
第二种方法:
type=“assignable”:类的全列名
<context:include-filter type="assignable" expression="spring.annotation.controller.UserController"></context:include-filter>
<4>、排除扫描
use-default-filters=“true”,默认情况下就是true
第一种方法:type=“annotation”:注解的全列名
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"></context:exclude-filter>
第二种方法:
type=“assignable”:类的全列名
<context:exclude-filter type="assignable" expression="spring.annotation.controller.UserController"></context:exclude-filter>
<5>、组件装配
DAO
import org.springframework.stereotype.Repository;
@Repository
public class UserDaoJdbcImpl implements UserDao {
@Override
public void addUser() {
System.out.println("UserDao JDBC");
}
}
Service
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import spring.annotation.dao.UserDao;
@Service
public class UserServiceImpl implements UserService {
/**
* @Autowired工作机制 完成bean属性的自动装配
*
* 工作机制:首先使用byType的方式进行自动装配,如果能唯一匹配则装配成功
* 如果匹配到多个兼容类型的bean,那么会尝试使用byName的方式进行唯一确定,
* 如果能唯一确定,则装配成功,否则抛出异常。
*
* 默认情况下,使用@Autowired标注的属性必须装配,如果装配不了,则会抛出异常
* 可以使用required = false来设置不是必须装配
*
* 如果匹配到多个兼容类型的bean,则可以使用@Qualifier来进一步指定要装配的bean的id值
*
* @Autowired @Qualifier 注解既可以加到成员变量上还可以加到set方法上
*/
@Autowired(required = false)
@Qualifier("userDaoMybatisImpl")
private UserDao userDao;
// @Autowired(required = false)
// @Qualifier("userDaoMybatisImpl")
// public void setUserDao(UserDao userDao) {
// this.userDao = userDao;
// }
@Override
public void handle_addUser() {
userDao.addUser();
}
}
Controller
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import spring.annotation.service.UserService;
/**
* Controller注解的作用:
* 相当于在xml文件中:
* <bean id="userController" class="spring.annotation.controller.UserController"></bean>
* 注解默认的id值就是类名首字母小写,可以在注解中手动指定id值,:@Controller(value = "id值")可以简写为:@Controller("")
*/
@Controller
public class UserController {
@Autowired
private UserService userService;
public void regist(){
userService.handle_addUser();
}
}
测试方法:
@Test
public void testAnnotation2(){
ApplicationContext context = new ClassPathXmlApplicationContext("spring-annotation.xml");
UserController userController = context.getBean("userController", UserController.class);
userController.regist();
}