官网:https://spring.io/
Spring框架是一个开放源代码的J2EE应用程序框架,由Rod Johnson发起,是针对bean的生命周期进行管理的轻量级容器(lightweight container)。 Spring解决了开发者在J2EE开发中遇到的许多常见的问题,提供了功能强大IOC、AOP及Web MVC等功能。
一句话:Spring是一个开源,轻量化,具有IOC和AOP两大核心功能的容器型框架。
对于Spring而言,发展到今天已经不单单是一个框架了,而是一个全家桶技术(包含了很多模块)。有:Spring、SpringMVC、SpringBoot、SpringData、SpringSecurity等等。
在正式学习Spring前,我们先了解一个重要的概念:Bean规范
,所谓Bean规范,就是对创建Java类提出一些要求,满足这些要求的类就是一个满足Bean规范的类。
public class User { //一个满足Bean规范的类
private int id;
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
前面咱们说到Spring可以帮我们创建和管理对象,那么类必须满足Bean规范,Spring才能进行管理。
咱们先来学习他的第一个特性IOC控制反转,让框架来帮我们创建对象。
1.创建普通maven项目,导入spring-context包
即可。
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-contextartifactId>
<version>5.2.22.RELEASEversion>
dependency>
这里需要注意,虽然只导入了spring-context包,但是会关联导入其他相关的依赖包。
也就是说,如果要自己全部来导一遍的话,配置应该如下:
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-contextartifactId>
<version>5.2.22.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-coreartifactId>
<version>5.2.22.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-beansartifactId>
<version>5.2.22.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-expressionartifactId>
<version>5.2.22.RELEASEversion>
dependency>
<dependency>
<groupId>commons-logginggroupId>
<artifactId>commons-loggingartifactId>
<version>1.2version>
dependency>
1.准备一个满足Bean规范的类
2.在resource目录下,准备配置文件:applicationContext.xml
<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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="user" class="cn.spring.bean.User"> bean>
beans>
3.读取配置文件创建IOC容器,获取对象。这里注意,在spring中把对象/实例也叫做bean。
//1.加载配置文件,并创建IOC容器对象
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//2.获取容器中的bean(对象)
//参数1:bean的id 参数2:对应的类型
User user = context.getBean("user", User.class);
System.out.println(user);
这里spring构建对象默认调用的是类的无参构造。
ApplicationContext容器 和 BeanFactory容器 都是IOC容器。也就是说使用BeanFactory也能进行对象的管理。
//1.读取配置文件
Resource resource = new ClassPathResource("applicationContext.xml");
//2.创建容器对象
BeanFactory factory = new XmlBeanFactory(resource);
//3.获取bean
User user = factory.getBean("user", User.class);
System.out.println(user);
那么他俩有什么区别呢?
1.ApplicationContext 是 BeanFactory 的子接口。BeanFactory 是所有容器的顶级接口。
2.ApplicationContext是立即加载bean,容器启动就会把bean创建好。而BeanFactory是懒加载,容器启动不会加载bean,调用时才会加载bean。
默认情况下bean是在容器启动时就创建好了,如何实现懒加载呢?即:调用的时候再创建,不调用就不创建。只需要为bean增加配置:lazy-init="true"
<bean id="user" class="包路径.User" lazy-init="true"> bean>
默认情况下bean是单例的,只会创建一个对象,就算获取多次,也返回的是同一个对象。
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
User user1 = context.getBean("user", User.class);
User user2 = context.getBean("user", User.class);
System.out.println(user1); //cn.spring.bean.User@57baeedf
System.out.println(user2); //cn.spring.bean.User@57baeedf
如何设置为多例呢?(这里一把不叫多例,而是:原型)即:每次获取都创建新的对象,需要增加配置:scope="prototype"
原型,默认是scope="singleton"
单例。
<bean id="user" class="包路径.User" scope="prototype"> bean>
除了singleton单例和prototype原型这两个作用域范围,Spring还有提供有request(表示每个请求需要容器创建一个全新Bean)、session(表示每个会话需要容器创建一个全新Bean)、GlobalSession(似于标准的HTTP Session作用域)等3种作用域。
1.使用无参构造(默认的),上边我们一直使用的就是该方式。
<bean id="xx" class="xxx">
2.静态工厂:构建工厂类,并提供静态方法来创建bean
public class MyBean {
}
//使用静态工厂的方式创建bean
public class MyBeanFactory {
//提供创建bean的静态方法
public static MyBean getBean(){
return new MyBean();
}
}
配置如下:
<bean class="包路径.MyBeanFactory" factory-method="getBean">bean>
3.实例工厂:构建工厂类,并提供实例方法来创建bean
public class MyBean {
}
//使用实例工厂
public class MyBeanFactory {
//提供创建bean的实例方法
public MyBean getBean(){
return new MyBean();
}
}
配置如下:
<bean id="beanFactory" class="包路径.MyBeanFactory">bean>
<bean id="myBean" factory-bean="beanFactory" factory-method="getBean">bean>
4.SpringBean工厂:实现Spring提供的FactoryBean接口来创建bean
public class MyBean {
}
//实现spring提供的FactoryBean接口
public class MyBeanFactoryBean implements FactoryBean<MyBean> {
@Override //创建对象的方法,最终会调用该方法创建实例
public MyBean getObject() throws Exception {
return new MyBean();
}
@Override //获取bean的类型
public Class<?> getObjectType() {
return MyBean.class;
}
@Override //设置是否为单例
public boolean isSingleton() {
return true;
}
}
配置如下:
<bean id="myBean" class="包路径.MyBeanFactoryBean">bean>
面试题:BeanFactory 和 FactoryBean 的区别?
- BeanFactory:他是Spring IOC 容器的最顶层接口,它的作用是管理Bean,即实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。
- FactoryBean:就是一个工厂Bean,一个能生产对象的工厂Bean,通过其提供的getObject方法获取你定制化实现的bean,并可以通过isSingleton方法控制是否为单例。
假设我们有一个类,类中还有属性,按照目前的方式创建的对象,属性都只有默认值,看案例。
public class MyBean {
private int id;
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override //增加 toString() 方便打印看结果
public String toString() {
return "MyBean{" + "id=" + id + ", name='" + name +"'}";
}
}
2.在xml中进行bean的配置
<bean id="myBean" class="包路径.MyBean">bean>
3.执行结果:
MyBean{id=0, name='null'} //都是默认值
所谓DI(Dependency Injection)依赖注入:就是在创建对象时为对象的属性进行赋值。
<bean id="myBean" class="包路径.MyBean">
<property name="id" value="1">property>
<property name="name" value="张三">property>
bean>
此时的执行结果为:MyBean{id=1, name='张三'}
为8种基本类型 和 String类型 的属性赋值,使用上述方式即可。
案例:如果MyBean的属性中还是一个对象属性,该如何赋值呢?
//MyBean类
public class MyBean {
private int id;
private String name;
private User user; //对象属性 User
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
@Override
public String toString() {
return "MyBean{" +
"id=" + id +
", name='" + name + '\'' +
", user=" + user +
'}';
}
}
//User类
public class User {
private String username;
private String password;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}
使用 ref
为对象属性赋值:
<bean id="user" class="cn.spring.bean.User">
<property name="username" value="admin">property>
<property name="password" value="123456">property>
bean>
<bean id="myBean" class="cn.spring.bean.MyBean">
<property name="id" value="1">property>
<property name="name" value="张三">property>
<property name="user" ref="user">property>
bean>
public class OtherBean {
}
public class MyBean {
// 数组
private String[] arrays;
// 集合中存字符串
private List<String> list;
private Set<String> set;
// 集合中存对象
private List<OtherBean> otherBeanList;
private Set<OtherBean> otherBeanSet;
//get和set
public String[] getArrays() {
return arrays;
}
public void setArrays(String[] arrays) {
this.arrays = arrays;
}
public List<String> getList() {
return list;
}
public void setList(List<String> list) {
this.list = list;
}
public Set<String> getSet() {
return set;
}
public void setSet(Set<String> set) {
this.set = set;
}
public List<OtherBean> getOtherBeanList() {
return otherBeanList;
}
public void setOtherBeanList(List<OtherBean> otherBeanList) {
this.otherBeanList = otherBeanList;
}
public Set<OtherBean> getOtherBeanSet() {
return otherBeanSet;
}
public void setOtherBeanSet(Set<OtherBean> otherBeanSet) {
this.otherBeanSet = otherBeanSet;
}
@Override
public String toString() {
return "MyBean{" +
"arrays=" + Arrays.toString(arrays) +
", list=" + list +
", set=" + set +
", otherBeanList=" + otherBeanList +
", otherBeanSet=" + otherBeanSet +
'}';
}
}
注入配置:
<bean id="otherBean" class="包路径.OtherBean">bean>
<bean id="myBean" class="包路径.MyBean">
<property name="arrays">
<array>
<value>arrays1value>
<value>arrays2value>
<value>arrays3value>
array>
property>
<property name="list">
<list>
<value>list1value>
<value>list2value>
<value>list3value>
list>
property>
<property name="set">
<set>
<value>set1value>
<value>set2value>
<value>set3value>
set>
property>
<property name="otherBeanList">
<list>
<bean class="包路径.OtherBean">bean>
<bean class="包路径.OtherBean">bean>
<ref bean="otherBean">ref>
<ref bean="otherBean">ref>
list>
property>
<property name="otherBeanSet">
<set>
<bean class="包路径.OtherBean">bean>
<bean class="包路径.OtherBean">bean>
<ref bean="otherBean">ref>
<ref bean="otherBean">ref>
set>
property>
bean>
到这里咱们已经把Spring的IOC和DI有了一定的了解,会发现IOC和DI是相辅相成的两个技术,IOC构建出对象,DI辅助IOC为对象的属性赋值。
前面都是使用xml配置文件实现依赖注入,还是比较麻烦,Spring还提供了注解的方式实现依赖注入,该注解是:@Autowired
,也是后期常用的,在Spring的单元测试中就可以体会到。
前面咱们在xml中的bean标签
中使用property 标签
为属性赋值,用到的就是set方式,也就是调用属性对应的set方法进行值的注入,这也是为什么我们一开始要求必须要有get和set方法的原因。
使用有参构造进行属性值的注入,案例如下:
public class MyBean {
private Long id; //基本属性
private String name; //基本属性
//提供全参构造
public MyBean(Long id, String name) {
this.id = id;
this.name = name;
}
@Override
public String toString() {
return "MyBean{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
为基本属性赋值:
<bean id="myBean" class="包路径.MyBean">
<constructor-arg name="id" value="1">constructor-arg>
<constructor-arg name="name" value="张三">constructor-arg>
bean>
<bean id="myBean" class="包路径.MyBean">
<constructor-arg index="0" value="2">constructor-arg>
<constructor-arg index="1" value="李四">constructor-arg>
bean>
<bean id="myBean" class="包路径.MyBean">
<constructor-arg type="java.lang.Long" value="3">constructor-arg>
<constructor-arg type="java.lang.String" value="王五">constructor-arg>
bean>
为对象属性赋值:
public class OtherBean {
}
public class MyBean { //为MyBean的属性赋值
private Long id; //基本属性
private String name; //基本属性
private OtherBean otherBean; //对象属性
//提供全参构造
public MyBean(Long id, String name,OtherBean otherBean) {
this.id = id;
this.name = name;
this.otherBean = otherBean;
}
@Override
public String toString() {
return "MyBean{" +
"id=" + id +
", name='" + name + '\'' +
", otherBean=" + otherBean +
'}';
}
}
<bean id="otherBean" class="包路径.OtherBean">bean>
<bean id="myBean" class="包路径.MyBean">
<constructor-arg name="id" value="1">constructor-arg>
<constructor-arg name="name" value="赵六">constructor-arg>
<constructor-arg name="otherBean" ref="otherBean">constructor-arg>
bean>
<bean id="myBean" class="包路径.MyBean">
<constructor-arg name="id" value="1">constructor-arg>
<constructor-arg name="name" value="赵六">constructor-arg>
<constructor-arg name="otherBean">
<bean class="包路径.OtherBean">bean>
constructor-arg>
bean>
1.导包,在原有基础上加入 spring-test 和 junit的包
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-testartifactId>
<version>5.2.22.RELEASEversion>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.12version>
dependency>
2.编写测试类
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml") //加载配置文件并创建IOC容器
public class SpringTest {
//如果容器中有User的实例,就会根据类型注入
//如果容器中没有User的实例,就会注入失败
@Autowired
private User user; //将容器中的bean注入给属性user(自动装配)
//特殊情况下,如果User在容器中有两个实例,那么就会根据属性名user去容器中去匹配两个实例中哪个的id是user,
//如果找到了就注入,没找到就注入失败。
//所以,咱们说@Autowired优先按类型注入,其次按名字注入
@Test
public void test1(){
System.out.println(user);
}
}
3.用于注入(自动装配)的注解有@Autowired和@Resource
(面试题)
@Autowired
:该注解由Spring提供,优先按照类型注入,相同类型有多个时,再按照名字注入。
@Resource
:该注解由Java提供(javax.annotation.Resource),也可以完成依赖注入,但是不同在于它优先按名字查询对应的bean。
bean的生命周期,即:一个bean从生到死的过程。大致分为:创建 - 初始化 - 提供服务 - 销毁。
public class MyBean {
public MyBean() {
System.out.println("-----MyBean创建了------");
}
public void init() {
System.out.println("-----MyBean初始化了------");
}
public void destroy() {
System.out.println("-----MyBean销毁了------");
}
}
配置如下:
<bean id="myBean" class="包路径.MyBean" init-method="init" destroy-method="destroy">bean>
最终的执行顺序是:
默认情况下容器启动就执行:构造器 和 初始化方法,在容器关闭时执行销毁方法。
如果设置了懒加载,则容器启动时不会创建对象,在调用时才会调用构造器创建对象,并调用初始化方法,在容器关闭时执行销毁方法。
Spring提供如下几个注解来标注Spring Bean:
试想,如果有很多对象都需要交给Spring管理,那么在xml中就需要配置很多的
标签,这个问题Spring也提供了对应的注解来简化配置。仅需进行包扫描,就会把包下被上述4种注解修饰的类创建好对应的实例。使用方式为:1.在xml配置文件中加上包扫描的配置 2.在类上使用注解。
<context:component-scan base-package="需要扫描的包"/>
整个案例试试:
1.编写类,并使用注解修饰
package cn.spring.bean;
import org.springframework.stereotype.Component;
@Component
public class MyBean {
}
2.配置文件如下
<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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="cn.spring.bean"/>
beans>
3.测试,启动容器打印对象看看
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class SpringTest {
@Autowired
private MyBean myBean;
@Test
public void test1(){
System.out.println(myBean); //cn.spring.bean.MyBean@3b69e7d1
}
}
前面咱们使用了xml进行相关配置,其实Spring支持纯注解编程。咱们来感受下:
1.准备一个配置类 ,在全注解的模式下,会使用配置类来替代xml
@Configuration //标记该类是一个Spring的配置类
public class SpringConfig {
@Bean //用于修饰方法
//将该方法返回的User实例交给Spring管理,相当于
public User user(){
return new User();
}
}
2.进行测试
方式1:自己创建容器进行测试
//1.创建IOC容器
//使用AnnotationConfigApplicationContext来读取配置类,并创建对象
ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
//2.获取容器中的实例
User user = context.getBean("user", User.class);
System.out.println(user);
方式2:使用Spring的单元测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class) //加载配置类
public class SpringTest {
@Autowired
private User user;
@Test
public void test1(){
System.out.println(user);
}
}
好,到这里关于Spring的IOC就差不多了,咱们接着去讨论下Spring的AOP。