JAVA框架-Spring01(基础使用和Bean对象管理)
什么是Spring
Spring是一个开源的,轻量级Java开发框架; 其核心特性是可以用于开发任何 Java 应用程序,Spring 框架的目标是使 JavaEE应用程序的开发变得更加容易,核心概念是IOC和AOP;这也是学习Spring的重点所在;
Spring不是针对某个具体功能,具体层级的框架; 也就是说以前该有的系统分层,结构,设计模式都不需要改变,而是让Spring加入进来,让开发变得更简单; 记住Spring并不想取代某个已存在的框架,反而可以让各个框架的配合使用难度降低,它就像502胶水,可快速的在系统中集成其他优秀的框架
Spring的优点
- Spring 对JavaEE中的一些API(JDBC、JavaMail、远程调用等),提供了封装,使这些API使用难度降低;
- 一站式框架,可简单快速的集成其他框架;
- IOC,利用依赖注入,极大的降低各组件间的耦合,提高整体扩展性;
- AOP(面向切面)编程的支持,可以方便的对程序进行权限拦截,运行监控等;
- 声明式事务支持,通过配置就可以完成对事务的管理,无序进行手动编程;
- 容器化,Spring包含并管理应用对象的配置和生命周期,你可以配置每个bean如何被创建以及bean是一个单独的实例或者每次需要时都生成一个新的实例,以及它们是如何相互关联的。
什么是IOC和DI
控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。
将原本由程序实现的一部分逻辑反过来交给系统来完成就称之为控制反转
其中最常见的方式叫做依赖注入(Dependency Injection,简称DI)通过控制反转,可以说,依赖被注入到对象中。
依赖注入是实现控制反转的一种手段;
举个例子:自己搭配组装机,需要考虑各种部件的兼容性,和自己的性能的要求,如CPU,内存,显卡等等;但有了专门的组装机服务器商(IOC容器),你只要告诉他你的基本需求,比如,要一台吃鸡电脑,剩下的就交给服务商去做了;
Spring体系结构
core,提供了框架基础组成部分,包括IoC和DI;
beans,提供了BeanFactory,是工厂模式的实现,提供普通对象和单例对象的获取
context,建立在core和bean的基础上,可将其他库集成到Spring中
SpEL(spring-expression Language)提供了表达式语言支持,其对JSP中的EL进行了扩展
AOP,提供了面向切面编程实现
Aspects 模块提供了与 AspectJ 的集成,是一个功能强大且成熟的AOP框架
Instrumentation 用于代理监控JVM运行的JAVA程序,对字节码修改以实现AOP
Messaging 模块为 STOMP 提供了支持,主要处理来自 WebSocket 客户端的 STOMP 信息
第一次使用Spring的配置
首先我们建立一个maven项目,配置pom.xml
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.2.RELEASE</version>
</dependency>
</dependencies>
然后我们建立配置文件applicationContext.xml
实际上我们到这里就已经配置好了,applicationContext.xml
的内容我实际就是告诉spring我们需要得到一个什么样的Bean对象(Spring可以理解成能够自动生成对象的容器)
IOC容器
工厂只负责创建对象,而Spring当然不仅仅是一个对象工厂;其核心是一个对象容器,由于具备控制反转的能力,所以也叫它IOC容器;
容器可以理解为存放对象的地方,当然不仅仅是存储,还有对象的管理,包括-创建-销毁-装配; 这样原本程序要做的事情交给了Spring,所以这属于IOC,称之为IOC容器;
Spring有两个容器接口ApplicationContext是BeanFactory的子接口。它们都可以作为Spring的容器;
- BeanFactory采取的懒加载的方式,在获取对象时才会实例化
- ApplicationContext会在工厂初始化时立即实例化对象
- BeanFactory作为顶级接口主要面向于Spring框架本身,仅提供了基本的容器功能如DI
- ApplicationContext,是BeanFactory的子接口,意味着功能比BeanFactory更多,诸如国际化,注解配置,XML配置等等,因此ApplicationContext使用更多
- ApplicationContext的两个实现类区别:
- ClassPath表示从类路径中获取配置文件,
- FileSystem表示从文件系统获取配置文件
- ApplicationContext的两个实现类区别:
Bean的实例化
使用类构造器(默认无参构造方法)
在书写applicationContext.xml
之前,先把具体的接口和实现类写好
package service;
/**
* Created by Jeason Luna on 2020/6/23 11:19
*/
public interface PersonService {
public void sayHi();
}
package service;
/**
* Created by Jeason Luna on 2020/6/23 11:22
*/
public class PersonServiceImpl implements PersonService {
@Override
public void sayHi() {
System.out.println("Hello Spring!");
}
}
加入我们是在MVC设计模式下进行的Spring获取对象操作,那么我们应该还有Contrller
层。故我们搭建的目录结构如下:
然后我们在applicationContext.xml
添加如下,告诉spring我们要得到的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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--1.调用空参构造器来实例化对象-->
<bean id="PersonService" class="service.PersonServiceImpl">
</beans>
这样我们就可以直接得到Bean对象:
package contrller;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import service.PersonService;
/**
* Created by Jeason Luna on 2020/6/23 11:23
*/
public class PersonContrller {
private PersonService service;
public PersonContrller(){
//加载spring配置文件
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
Object personService = context.getBean("PersonService");
this.service = (PersonService)personService;
}
public void sayHi(){
service.sayHi();
}
public static void main(String[] args) {
PersonContrller personContrller = new PersonContrller();
personContrller.sayHi();
}
}
使用静态工厂方法
同时我们还可以使用工厂模式获得Bean对象,首先我们写好工厂类:
package service;
/**
* Created by Jeason Luna on 2020/6/23 14:58
*/
public class PsesonFactory {
public static PersonService getService(){
System.out.println("静态工厂!!!");
return new PersonServiceImpl();
}
public PersonService getService2(){
System.out.println("工厂对象方法!!!");
return new PersonServiceImpl();
}
}
这里我们配置文件要替换成:
<bean id="PersonService" class="service.PsesonFactory" factory-method="getService"></bean>
使用工厂对象方法
xml配置;
<bean id="factory" class="service.PsesonFactory"></bean>
<bean id="PersonService" factory-bean="factory" factory-method="getService2"></bean>
生命周期
Spring提供了非入侵(不强制类继承或实现)方式的生命周期方法,可以在Bean的初始化以及销毁时做一些额外的操作。
执行顺序及其含义:
1 构造对象
2 设置属性
3 了解Bean在容器中的name
4 了解关联的beanFactory
5 初始化前处理
6 属性设置完成
7 自定义初始化方法
8 初始化后处理
9 业务方法
10 Bean销毁方法
11 自定义销毁方法
依赖注入
依赖指的是当前对象在运行过程中需要使用到的其他参数或对象,Spring可以帮助我们来完成这个依赖关系的建立,说的简单点就是把你需要参数的给你,而你不用管参数怎么来的,已达到尽可能的解耦 ;
举个例子:
Controller 中需要Service对象,Spring可以把Service自动丢到你的Controller中,你不需要关心Service是怎么来的,用就完了;
要使用依赖注入,必须现在需要依赖的一方(Controller)中为被依赖的一方(Service)定义属性,用以接收注入;
注入有两种方法,一种是直接利用有参数的构造方法,另一种是可以依靠无参的构造方法(此方式必须要有无参构造方法),得到了对象后使用set方法来关联依赖:
构造方法注入
public class User2 {
private String name;
private int age;
private Phone phone;
public User2(String name, int age, Phone phone) {
this.name = name;
this.age = age;
this.phone = phone;
}
@Override
public String toString() {
return "User2{" +
"name='" + name + '\'' +
", age=" + age +
", phone=" + phone +
'}';
}
}
xml
<!--user需要的依赖phoneBean-->
<bean id="phone" class="service.Phone"/>
<bean id="user" class="service.User2">
<!--按参数名称注入 -->
<constructor-arg name="name" value="jeason"/>
<!--按参数位置注入 -->
<constructor-arg index="1" value="18"/>
<!--参数类型为其他bean对象时value换成ref -->
<constructor-arg name="phone" ref="phone"/>
</bean>
setter方法注入
<!--setter方法注入(属性注入) -->
<bean id="user2" class="service.User2">
<property name="name" value="jeason"/> <!--注入常量值-->
<property name="age" value="20"/>
<property name="phone" ref="phone"/> <!--注入其他Bean-->
</bean>
注意:上述配置要求User2必须存在空参构造器
名称空间
上面通过嵌套标签constructor的方式注入依赖,在需要注入的依赖较多时导致xml显得很臃肿,C名称空间来简化xml中<constructor-arg>
标签的书写
使用前需要先在xml头部进行声明
xmlns:c="http://www.springframework.org/schema/c"
这样我们就不用写一大堆的对象属性的配置了:
<!--c命名空间的使用-->
<bean id="user3" class="service.User2" c:name="jeason" c:_1="21" c:phone-ref="phone"></bean>
<!--
c:name 指定为name参数赋值
c:_1 指定为构造函数的第2个参数赋值
c:phone-ref 指定为构造函数的phone参数赋值为id为"phone"的Bean
-->
同样的,我们还有p命名空间:xmlns:p="http://www.springframework.org/schema/p"
容器类型的注入
<!-- 容器数据类型注入-->
<bean id="user100" class="service.User3">
<!--set注入 -->
<property name="set">
<set>
<value>3</value>
<value>3</value>
<value>a</value>
</set>
</property>
<!--list注入 -->
<property name="list">
<list>
<value>3</value>
<value>3</value>
<value>a</value>
</list>
</property>
<!--map注入 -->
<property name="map">
<map>
<entry key="name" value="jerry"/>
<entry key="age" value="18"/>
<entry key="sex" value="man"/>
</map>
</property>
<!--properties注入 -->
<property name="properties">
<props>
<prop key="jdbc.user">root</prop>
<prop key="jdbc.password">3692512</prop>
<prop key="jdbc.driver">com.mysql.jdbc.Driver</prop>
</props>
</property>
</bean>
注解方法获得Bean
注解配置
上面的配置方法还是太麻烦了,需要写一堆的配置文件,spring支持注解模式:
通用注解
@Component 用于在Spring中加入Bean
MVC场景下
@Controller 等价于 @Component 标注控制层
@Service 等价于 @Component 标注业务层
@Repository 等价于 @Component 标注数据访问层(DAO)
在实现上没有任何不同,仅仅是为了对Bean进行分层是结构更清晰
使用步骤:
1.需要依赖context和aop两个jar包
2.添加命名空间(idea下在xml中输入<context:可自动补全)
<!--指定要扫描注解的包 会自动扫描子包 -->
<context:component-scan base-package="service.demo"/>
3.指定扫描的注解所在的包
4.书写需要注册的Bean
import org.springframework.stereotype.Component;
@Component("userService")
public class UserService {
public String hello(String name){
return "hello " + name;
}
}
5.测试
public class Tester {
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService service = (UserService) context.getBean("userService");
System.out.println(service.hello("jeason"));
}
}
注解注入
@Value用于对基本类型属性进行注入
@Autowired将容器中的其他Bean注入到属性中
@Qualifier("BeanID") 指定要注入的Bean的ID(和Autowired搭配使用)
准备UserDAO类:
import org.springframework.stereotype.Repository;
@Repository("userDAO")
public class UserDAO {
public void save(){
System.out.println("user saved!");
}
}
UserService类:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component("userService")
public class UserService {
@Value("hello")//基本类型
private String info;
@Value("#{true and false}") //SPEL表达式
private String info1;
@Value("${jdbc.user}") //注入属性配置
private String info1;
//@Autowired(required = false) //默认为true表示属性时必须的不能为空
@Autowired //注入类型匹配的Bean
//@Qualifier("userDAO") //明确指定需要的BeanID
private UserDAO userDAO;
//set/get.....
}
测试:
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Tester {
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService service = (UserService) context.getBean("userService");
System.out.println(service.getInfo());//普通属性测试
service.getUserDAO().save();//对象属性测试
}
}
Qualifier和Autowired书写繁琐,@Resource可将两个标签的功能整合,即注入指定ID的Bean
@Resource标准注解的支持是JSR-250中定义的,所以时使用需要导入扩展包,Maven依赖如下:
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.3.2</version>
</dependency>
Resource默认按照使用属性名称作为name查找,查找失败则使用类型查找
可以利用name属性指定要查找的id
也可通过type指定类型,当出现相同类型的多个Bean时抛出异常
import javax.annotation.Resource;
@Component("userService")
public class UserService {
//@Resource()//默认按照id/name
//@Resource(name="xx")//指定name
//@Resource(type = PersonDaoImpl1.class) //指定type
@Resource(name="xx",type = PersonDaoImpl1.class)//同时指定name和type
private PersonDao personDao;
}
@Scope用于标注Bean的作用域
@Repository()
@Scope("prototype") //每次get都创建新的
public class UserDAO {
public void save(){
System.out.println("user saved!");
}
}
XML和注解配合使用
因为注解的表达能力有限,很多时候无法满足使用需求;我们可以将注解和XML配合使用,让XML负责管理Bean,注解仅负责依赖注入;当然也需要先声明context名称空间;
<!--注册Bean-->
<bean id="userDao" class="demo2.UserDao"/>
<bean id="userService" class="demo2.UserService"/>
<!--在已经注册的Bean中启用注解注入-->
<context:annotation-config/>
UserService:
import javax.annotation.Resource;
public class UserService {
@Resource
private UserDao userDao;
public UserDao getUserDao() {return userDao;}
public void setUserDao(UserDao userDao) {this.userDao = userDao;}
}
与注解扫描的不同之处,注解扫描用于将Bean放入容器中同时进行依赖注入,而后者仅仅是在已存在的Bean中进行依赖注入;
多配置文件的使用
当配置文件中的内容过多是不便于维护,Spring支持多配置文件
方法1:
在初始化容器时指定要加载的所有配置文件
@Test public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml","applicationContext2.xml");
UserService userService = (UserService) context.getBean("userService");
userService.getUserDao().save();
this.getClass().getClassLoader().getResource("");
}
方法2:
在配置文件中使用import来引入其他的配置文件
<import resource="applicationContext2.xml"/>