Spring框架1 - 入门简述
框架
首先我们要理解框架是什么?框架跟类库有什么区别?
框架,框架其实是一个半成品,它对基础的代码进行了封装并提供相应的API,开发者在使用框架是直接调用封装好的api可以省去很多代码编写,从而提高工作效率和开发速度。
其组成部分: jar包、配置文件(全局+单元)、启动代码等。
类库,类库(Class Library),字面意思就是类的集合,是一个综合性的面向对象的可重用类型集合,这些类型包括:接口、抽象类和具体类。
如何学习框架?
对于框架的学习,我们应该要理解框架的理念以及要解决的问题、学习它的配置和调用方式(其实可以理解为如何套用)、按照套路编码。
什么是Spring?
-
Spring 是最受欢迎的企业级 Java 应用程序开发框架,是为了解决企业应用程序开发复杂性而创建的。
-
Spring 框架是一个开源的 Java 平台,它最初是由 Rod Johnson 编写的,并且于 2003 年 6 月首次在 Apache 2.0 许可下发布。
-
Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架。Spring使用基本的JavaBean来完成以前只可能由EJB(Enterprise Java Bean)完成的事情。
-
Spring 框架的核心特性是可以用于开发任何 Java 应用程序,但是在 Java EE 平台上构建 web 应用程序是需要扩展的。 Spring 框架的目标是使 J2EE 开发变得更容易使用,通过启用基于 POJO 编程模型来促进良好的编程实践。
Spring框架的好处
1.放便解耦,简单开发
通过Spring提供的IoC容器,我们可以将对象之间的依赖关系交由Spring进行控制,避免硬编码所造成的过度程序耦合。有了Spring,用户不必再为单实例模式类、属性文件解析等这些很底层的需求编写代码,可以更专注于上层的应用。
2.Aop 编程支持
通过Spring提供的AOP功能,方便进行面向切面的编程,许多不容易用传统OOP实现的功能可以通过AOP轻松应付。
3.声明事务的支持
在Spring中,我们可以从单调烦闷的事务管理代码中解脱出来,通过声明式方式灵活地进行事务的管理,提高开发效率和质量。
4.方便程序的测试
可以用非容器依赖的编程方式进行几乎所有的测试工作,在Spring里,测试不再是昂贵的操作,而是随手可做的事情。例如:Spring对Junit4支持,可以通过注解方便的测试Spring程序。
5.方便框架的集合
Spring不排斥各种优秀的开源框架,相反,Spring可以降低各种框架的使用难度,Spring提供 了对各种优秀框架(如Struts、Hibernate、Hessian、 Quartz) 等的直接支持。
6.降低开发难度
Spring对很多难用的Java EE API (如JDBC, JavaMail, 远程调用等)提供了一-个薄薄的封装层, 通过Spring的简易封装,这些Java EE API的使用难度大为降低。
Spring核心概念简述
控制反转(IoC):即Inversion of Control。举例来说,在以前的项目中,要想调用一个类中的普通方法,则必须创建这个类的实例,再通过这个实例来调用其方法。而在Spring中,你不必再使用这种形式来创建对象,具体过程都交由Spring进行配置来实现。
面向切面编程(AOP):aop是一种编程技术,它允许程序员对横切关注点或横切典型的职责分界线的行为(例如日志和事务管理)进行模块化。AOP 的核心构造是切面,它将那些影响多个类的行为封装到可重用的模块中。
Spirng模块
组成 Spring 框架的每个模块(或组件)都可以单独存在,或者与其他一个或多个模块联合实现。每个模块的功能如下:
-
核心容器:核心容器提供 Spring 框架的基本功能。核心容器的主要组件是 BeanFactory,它是工厂模式的实现。BeanFactory 使用控制反转 (IOC) 模式将应用程序的配置和依赖性规范与实际的应用程序代码分开。
-
Spring 上下文:Spring 上下文是一个配置文件,向 Spring 框架提供上下文信息。Spring 上下文包括企业服务,例如 JNDI、EJB、电子邮件、国际化、校验和调度功能。
-
Spring AOP:通过配置管理特性,Spring AOP 模块直接将面向方面的编程功能集成到了 Spring 框架中。所以,可以很容易地使 Spring 框架管理的任何对象支持 AOP。Spring AOP 模块为基于 Spring 的应用程序中的对象提供了事务管理服务。通过使用 Spring AOP,不用依赖 EJB 组件,就可以将声明性事务管理集成到应用程序中。
-
Spring DAO:JDBC DAO 抽象层提供了有意义的异常层次结构,可用该结构来管理异常处理和不同数据库供应商抛出的错误消息。异常层次结构简化了错误处理,并且极大地降低了需要编写的异常代码数量(例如打开和关闭连接)。Spring DAO 的面向 JDBC 的异常遵从通用的 DAO 异常层次结构。
-
Spring ORM:Spring 框架插入了若干个 ORM 框架,从而提供了 ORM 的对象关系工具,其中包括 JDO、Hibernate 和 iBatis SQL Map。所有这些都遵从 Spring 的通用事务和 DAO 异常层次结构。
-
Spring Web 模块:Web 上下文模块建立在应用程序上下文模块之上,为基于 Web 的应用程序提供了上下文。所以,Spring 框架支持与 Jakarta Struts 的集成。Web 模块还简化了处理多部分请求以及将请求参数绑定到域对象的工作。
-
Spring MVC 框架:MVC 框架是一个全功能的构建 Web 应用程序的 MVC 实现。通过策略接口,MVC 框架变成为高度可配置的,MVC 容纳了大量视图技术,其中包括 JSP、Velocity、Tiles、iText 和 POI。
-
spring-beans : ioc 实现,配置文件的访问、创建和管理
-
SpEL : spring的表达式支持
-
Aspects: 对AspectJ框架的支持
-
instrumentation: 字节码操作
-
messaging : 对消息服务的支持
-
transactions : 事务支持
-
JDBC : spring对JDBC的封装
-
ORM : spring对于ORM框架的支持
-
web : 对于web开发的支持
Spring项目示例
核心理念: IoC inversion of control 控制反转 / DI dependency injection 依赖注入
控制权从调用的类转换到spring容器,由spring来实例化对象及依赖注入
1) 创建对象 2)依赖注入 ( 为属性填入实例)
Spring项目搭建(基于Idea)
建一个简单的登录项目吧。
之前已经配置好了maven,在此就不多说。
导包
在pom.xml加入依赖。自动导入jar包
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
搭建结构
DAO
public interface UserDAO {
String login(String username, String password);
}
DAO实现
public class UserDAOImpl implements UserDAO {
@Override
public String login(String username, String password) {
//暂时不与数据库交互,直接返回字符串测试是否执行
return "UserDAOImpl执行了。。。";
}
}
Service
public interface UserService {
String login(String username, String password);
}
Service实现
public class UserServiceImpl implements UserService {
//此时我们声明UserDAO,但并不创建对象
private UserDAO userDAO;
@Override
public String login(String username, String password) {
//直接通过声明调用方法
return userDAO.login(username,password);
}
//创建setter方法
public void setUserDAO(UserDAO userDAO) {
this.userDAO = userDAO;
}
}
Controller
public class UserController {
//声明userService
private UserService userService;
public String login(String username,String password){
//通过声明直接调用方法
return userService.login(username,password);
}
//创建setter方法
public void setUserService(UserService userService) {
this.userService = userService;
}
}
创建xml文件并配置
创建applicationContext.xml。通过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">
<!--通过setter注入-->
<bean id="userController" class="com.alter.spring.controller.UserController">
<!--通过ref引入已存在的bean-->
<property name="userService" ref="userService"/>
</bean>
<bean id="userService" class="com.alter.spring.service.impl.UserServiceImpl">
<property name="userDAO" ref="userDAO"/>
</bean>
<bean id="userDAO" class="com.alter.spring.dao.impl.UserDAOImpl"/>
</beans>
编写测试类
public class App {
public static void main(String[] args) {
//创建一个spring容器
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
//从容器中获取一个叫做userController的bean并转换为userController的实例
UserController userController = ac.getBean("userController", UserController.class);
//调用
String result = userController.login("abc", "123");
System.out.println(result);
}
}
运行之后打印如下:
从这个例子可以看出,spring框架通过xml配置来获得实例,我们可以将Ioc理解成一个容器,通过xml配置来获得bean实例,并将这些实例保存在容器中。
但是得到实例之后里面的属性又如何注入呢?bean实例化配置只有这种方法吗?
下面我们就讲一讲xml其他的配置方式。
xml具体配置方式
1. xml属性配置(下面以UserController为例)
-
通过value 注入基本类型
首先我们在controller加入一个基本类型的属性agepublic class UserController { //基本属性age private String age; //setter方法 public void setAge(String age) { this.age = age; } ... }
xml配置:
<bean id="userDAO" class="com.alter.spring.dao.impl.UserDAOImpl"> <!-- 通过value配置基本属性--> <property name="age" value="18" /> </bean>
-
ref 注入已有的bean
java类:public class UserController { //声明userservice private UserService userService; //setter方法 public void setUserService(UserService userService) { this.userService = userService; } ... }
xml配置:
<bean id="userController" class="com.alter.spring.controller.UserController"> <!-- 通过ref配置容器中已存在的bean--> <property name="userService" ref="userService"/> </bean>
-
List \ Set \ Map等复杂类型配置
java配置public class UserController { //声明list集合 private List<String> list; //创建setter方法 public void setList(List<String> list) { this.list = list; } //声明map集合 private Map<String, String> map; //创建setter方法 public void setMap(Map<String, String> map) { this.map = map; } //声明set集合 private Set<Integer> set; //创建setter方法 public void setSet(Set<Integer> set) { this.set = set; } ... }
xml配置
<bean id="userDAO" class="com.alter.spring.controller.UserController"> <!-- 通过name指定List属性名,value配置具体的属性值--> <property name="list"> <list> <value>manager</value> <value>farmer</value> <value>driver</value> </list> </property> <!-- 通过name指定Map属性名,<entry>配置具体的属性值--> <property name="map"> <map> <entry key="pig" value="pink"/> <entry key="cat" value="orange"/> </map> </property> <!-- 通过name指定Set属性名,value配置具体的属性值--> <property name="set" > <set> <value>2</value> </set> </property> </bean>
最后我们验证这些属性是否配置成功,在login方法中加入输出语句。
public class UserController{ ... public String login(String username,String password){ System.out.println("age="+age); System.out.println("list="+list); System.out.println("map="+map); System.out.println("set="+set); return userService.login(username,password); } }
这时IDEA打印如下,说明属性配置成功。
2. bean的实例化
-
默认构造器实例化
上述的实例中,在xml文件中配置bean实例其实是调用指定类的默认的无参构造方法。(可以给定其有参构造,此时就会抛出异常,在此省略验证步骤) -
静态工厂方法实例化
<!--用静态工厂方法创建一个connection作为springbean--> <bean id="connection" factory-method="getConnection" class="com.alter.spring.common.JDBCUtils"/> <!--某个类(JDBCUtils)的静态方法(getConnection),返回值作为bean-->
-
工厂bean实例化
<!--工厂bean--> <bean id="holidayFactory" class="com.alter.spring.factory.HolidayFactory"/> <!--holidayBean--> <bean id="holiday" factory-bean="holidayFactory" factory-method="create"></bean> public class HolidayFactory { public Holiday create(){ return new Holiday(); } }
-
Spring的FactoryBean接口实例化
-
一个类实现FactoryBean接口,实现其方法
-
配置之后,bean的实例为FactoryBean的getObject方法的返回值
public class CarFactory implements FactoryBean<Car> { @Override public Car getObject() throws Exception { return new Car(); } @Override public Class<?> getObjectType() { return Car.class; } }
<bean id="car" class="com.alter.spring.factory.CarFactory"/>
-
3. 核心API
- BeanFactory IOC容器的基本接口
- ApplicationContext 可用的IOC容器接口
- ClassPathXmlApplicationContext xml配置文件的IOC容器
4. 依赖注入
- setter注入
如上面的例子就是采用setter方法注入属性。 - 构造器注入
-
<constructor-arg name index>
- index : 用于指定要注入的数据给构造函数中指定索引位置的参数赋值,索引的位置时从0开始
- name(常用) : 用于指定给构造函数中指定名称的参数赋值
-
设置 value ref list set
<bean id="userService" scope="singleton" class="com.alter.spring.injection.UserServiceImpl" > //可以使用name <constructor-arg name="userDAO" ref="userDAO"></constructor-arg> <constructor-arg name="serviceId" value="12306"/> <constructor-arg name="data" > //或者使用index <constructor-arg index="0" ref="userDAO"></constructor-arg> <constructor-arg index="1" value="12306"/> <constructor-arg index="2" > <list> <value>xiong</value> <value>nian</value> <value>haha</value> </list> </constructor-arg> </bean>
public class UserServiceImpl implements UserService { private UserDAO userDAO; private int serviceId; private List<String> data; public UserServiceImpl(UserDAO userDAO,int serviceId,List<String> data){ this.userDAO = userDAO; this.serviceId=serviceId; this.data =data; }; }
特点:在获取 bean 对象时,注入数据是必须的操作,否则无法操作成功
弊端:改变了 bean 对象的实例化方式,使我们在用不到这些数据的情况下也必须提供带参构造函数,因此开发中较少使用此方法,除非避无可避
-
5. bean的作用域 默认单例
- prototype 原型 => 每次创建一个实例
- [*]singleton 单例 => 一个bean的定义,只有一个实例,不是一个类只有一个实例
- request 一个请求一个实例
- session 一个会话一个实例
- websocket 一次websocket链接一个实例
6. bean的生命周期
-
在bean配置上写init-method和destroy-method
<bean id="cycle" class="com.alter.spring.lifeCycle.Cycle" init-method="beginning" destroy-method="ending"/>
public class Cycle { public void beginning(){ System.out.println("开始"); } public void ending(){ System.out.println("结束"); } }
-
实现InitializingBean和DisposableBean及其方法
<bean id="cycle2" class="com.alter.spring.lifeCycle.Cycle2"/> //因为实现了接口,spring可以识别,所以不需要写intit和destroy
public class Cycle2 implements InitializingBean, DisposableBean { @Override public void afterPropertiesSet() throws Exception { System.out.println("开始...."); } @Override public void destroy() throws Exception { System.out.println("结束了...."); } }