一、Spring介绍
Spring是一个开源框架,Spring是于2003 年兴起的一个轻量级的Java 开发框架,由Rod Johnson 在其著作Expert One-On-One J2EE Development and Design中阐述的部分理念和原型衍生而来。它是为了解决企业应用开发的复杂性而创建的。框架的主要优势之一就是其分层架构,分层架构允许使用者选择使用哪一个组件,同时为 J2EE 应用程序开发提供集成的框架。Spring使用基本的JavaBean来完成以前只可能由EJB完成的事情。然而,Spring的用途不仅限于服务器端的开发。从简单性、可测试性和松耦合的角度而言,任何Java应用都可以从Spring中受益。
**Spring的核心是控制反转(IoC)和面向切面(AOP)。**简单来说,Spring是一个分层的JavaSE/EEfull-stack(一站式) 轻量级开源框架。
1.1 控制反转(IOC/DI)
控制反转(Inversion of Control),就是应用本身不负责对象的创建和维护,对象和依赖对象创建完全交给Spring的容器去管理和维护,这个权利反转给容器。
项目中分层开发,学Spring之前bean都是我们自己创建;我们自己创建DaoImpl();带来了很大的耦合性,项目不好维护,测试成本也高
依赖注入(DI) :IOC的别名 ,
控制被反转之后,获得依赖对象的过程由自身管理变为了由IOC容器主动注入。于是,他给“控制反转”取了一个更合适的名字叫做“依赖注入(Dependency Injection)”。他的这个答案,实际上给出了实现IOC的方法:注入。所谓依赖注入,就是由IOC容器在运行期间,动态地将某种依赖关系注入到对象之中。
1.2 面向切面编程(AOP)
那么AOP也是一种编程思想,作用是在不改变原始设计的基础上为其进行功能增强。
1.3 Spring的其他功能
Core Container(核心容器):
- Beans:负责Bean工厂中Bean的装配,所谓Bean工厂即是创建对象的工厂,Bean的装配也就是对象的创建工作;
- Core:这个模块即是负责IOC(控制反转)最基本的实现;
- Context:Spring的IOC容器,因大量调用Spring Core中的函数,整合了Spring的大部分功能。Bean创建好对象后,由Context负责建立Bean与Bean之间的关系并维护。所以也可以把Context看成是Bean关系的集合;
- SpEl:即Spring Expression Language(Spring表达式语言);
Data Access/Integration(数据访问/集成):
- JDBC:对JDBC的简单封装;
- ORM:支持数据集成框架的封装(如Mybatis,Hibernate);
- OXM:即Object XML Mapper,它的作用是在Java对象和XML文档之间来回转换;
- JMS:生产者和消费者的消息功能的实现;
- Transations:事务管理,不多BB;
Web:
- WebSocket:提供Socket通信,web端的的推送功能;
- Servlet:Spring MVC框架的实现;
- Web:包含web应用开发用到Spring框架时所需的核心类,包括自动载入WebApplicationContext特性的类,Struts集成类、文件上传的支持类、Filter类和大量辅助工具类;
- Portlet:实现web模块功能的聚合(如网站首页(Port)下面可能会有不同的子窗口(Portlet));
AOP:
- 面向切面;
Aspects:
- 同样是面向切面的一个重要的组成部分,提供对AspectJ框架的整合;
Instrumentation(设备):
- 相当于一个检测器,提供对JVM以及对Tomcat的检测;
Messaging(消息):
- Spring提供的对消息处理的功能;
Test(测试):
- 我们在做单元测试时,Spring会帮我们初始化一些测试过程当中需要用到的资源对象
二、环境搭建
1、jar包引入
2、创建spring的配置文件,默认名称叫ApplicationContext.xml
<?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-3.2.xsd">
</beans>
3、创建bean
public class UserDaoImpl implements UserDao {
@Override
public void save() {
System.out.println("user被保存了");
}
}
4、Spring配置
<bean id="userDao" class="com.test.spring.dao.impl.UserDaoImpl">
</bean>
5、创建Spring的容器
@Test
public void test() {
//初始化spring容器
ApplicationContext ctx = new ClassPathXmlApplicationContext("ApplicationContext.xml");
//获得bean
UserDao userDao = (UserDao) ctx.getBean("userDao");
userDao.save();
}
三、Bean的创建方式
3.1 使用构造器创建
1、使用构造器方式创建bean,要求bean必须要有默认的构造器,这是工作中最常用的方式。
public class User {
public User() {
}
}
2、配置bean
<bean id="user" class="com.test.spring.model.User"></bean>
3.2 静态工厂方式创建
1、提供静态工厂
public class FactoryBean {
/**
* 必须是静态方法
* @return
*/
public static User createUser(){
return new User();
}
}
2、配置bean
<!-- id:唯一标识
class:静态工厂的类,factory-method:静态工厂的方法
-->
<bean id="user" class="com.test.spring.factory.FactoryBean" factory-method="createUser"></bean>
3.3 实例工厂方式创建
1、实例工厂
public class FactoryBean1 {
public User createUser(){
return new User();
}
}
2、配置bean
<!-- 通过Spring来定义实例工厂 -->
<bean id="factoryBean" class="com.rl.spring.factory.FactoryBean1"></bean>
<!-- 指定要创建的bean
factory-bean:指定实例工厂类,factory-method:工厂的创建bean的方法
-->
<bean id="user" factory-bean="factoryBean" factory-method="createUser"></bean>
四、延迟加载
所有的bean默认情况下,非延迟加载的,是spring的容器创建的时候就把bean给创建出来了,我们getBean的时候直接从容器中去拿这个Bean就可以了。
是否延迟加载由lazy-init来控制,默认是false,如果变成true就在getBean的时候去创建user。
<bean id="lazyInitBean" class="com.javacode2018.lesson001.demo11.LazyInitBean" lazy-init="true"/>
五、Bean的作用域
默认情况下,bean都是单例的,是容器初始化的时候被创建的,就这么一份
Scope:singleton单例,prototype多例,默认使用singleton
如果是singleton我们可以设置非延迟加载(容器初始化时创建bean)和延迟加载(getBean的时候才创建)方式创建bean
如果是prototype我们没得选择只能是延迟加载方式创建(getBean的时候才创建)
六、Bean的生命周期
Bean的生命周期和容器的生命周期一致,容器被创建bean就被创建,容器销毁bean就被销毁;创建是和销毁时,会调用指定的方法
1、方法
public class User {
public User() {
}
public void init(){
System.out.println("我出来了");
}
public void destroy(){
System.out.println("20年后又是一条好汉");
}
}
2、Bean配置
<!--
init-method:bean被创建时调用,destroy-method:bean被销毁时调用
-->
<bean id="user" class="com.test.spring.model.User" init-method="init" destroy-method="destroy"></bean>
七、依赖注入
7.1 属性注入(set注入)
7.1.1 常量注入
<!-- bean的常量注入-->
<bean id="user" class="com.test.spring.model.User">
<!-- property:class里面的属性,name:属性名-->
<property name="userId" value="1"></property>
<property name="username" value="renliang"></property>
<property name="password" value="123"></property>
</bean>
7.1.2 集合注入
1、对象
@Data
public class CollectionInjection {
private Set<String> set;
private List<String> list;
private Map<String, String> map;
private Properties prop;
}
2、注入
<bean id="ci" class="com.rl.spring.model.CollectionInjection">
<property name="set">
<set>
<value>football</value>
<value>basketball</value>
</set>
</property>
<property name="list">
<list>
<value>male</value>
<value>female</value>
</list>
</property>
<property name="map">
<map>
<entry key="key1" value="value1"></entry>
<entry key="key2" value="value2"></entry>
</map>
</property>
<property name="prop">
<props>
<prop key="name">任亮</prop>
<prop key="job">讲师</prop>
</props>
</property>
</bean>
7.1.3 外部bean注入(对象)
1、外部Bean注入:必须提供set方法
public class UserServiceImpl implements UserService {
private UserDao userDao;
/**
* 外部bean的方式注入,必须提供要注入的bean的set方法
* @param userDao
*/
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void save() {
userDao.save();
}
}
2、配置
<!-- 定义UserDao的bean -->
<bean id="userDao" class="com.test.spring.dao.impl.UserDaoImpl"></bean>
<!-- 定义Service的bean -->
<bean id="userService" class="com.test.spring.service.impl.UserServiceImpl">
<!-- 通过属性方式注入
name:bean的属性名
ref:要注入的bean
-->
<property name="userDao" ref="userDao"></property>
</bean>
7.1.4 内部bean注入(对象)
内部bean不能其他bean注入
1、外部Bean注入:必须提供set方法
public class UserServiceImpl implements UserService {
private UserDao userDao;
/**
* 外部bean的方式注入,必须提供要注入的bean的set方法
* @param userDao
*/
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void save() {
userDao.save();
}
}
2、配置
<bean id="userService" class="com.rl.spring.service.impl.UserServiceImpl">
<property name="userDao">
<bean class="com.rl.spring.dao.impl.UserDaoImpl"></bean>
</property>
</bean>
7.2 构造器注入
7.2.1 常量注入
<!-- 构造器方式注入 -->
<bean id="user" class="com.test.spring.model.User">
<!--
index:构造方法的参数的索引顺序
type:构造方法的参数的类型(不是必须 的)
value:值
-->
<!-- <constructor-arg index="0" type="java.lang.Integer" value="2"/>
<constructor-arg index="1" type="java.lang.String" value="renliang"/>
<constructor-arg index="2" type="java.lang.String" value="666"/> -->
<constructor-arg index="0" value="2"/>
<constructor-arg index="1" value="renliang"/>
<constructor-arg index="2" value="666"/>
</bean>
7.2.2 对象注入
public class UserServiceImpl implements UserService {
private UserDao userDao;
public UserServiceImpl() {
}
public UserServiceImpl(UserDao userDao) {
super();
this.userDao = userDao;
}
@Override
public void save() {
userDao.save();
}
}
<!-- 定义UserDao的bean -->
<bean id="userDao" class="com.test.spring.dao.impl.UserDaoImpl"></bean>
<!-- 定义UserService的Bean -->
<bean id="userService" class="com.test.spring.service.impl.UserServiceImpl">
<!-- 通过构造器的方式指定注入的bean
type:指定UserDao接口,不要指定实现类
ref:就是要注入的bean
-->
<constructor-arg index="0" type="com.test.spring.dao.UserDao" ref="userDao"/>
</bean>
7.3 注解方式注入
引入jar包、引入约束文件、开启注解的驱动
<?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-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd">
<!-- 开启注解的驱动 -->
<context:annotation-config/>
<bean id="userDao" class="com.rl.spring.dao.impl.UserDaoImpl"></bean>
<bean id="userService" class="com.rl.spring.service.impl.UserServiceImpl"></bean>
</beans>
7.3.1 Resource注解
1、Resource时java的注解
2、既可以加在方法上也可以加在属性上
3、默认按照name进行匹配的,如果不写,默认是变量名,如果找不到会按照Type进行匹配,再找不到就会报错
4、加在方法上,会先按照(属性名、set后面小写名、入参名)进行匹配,未匹配则会按照type进行匹配;
5、一旦指定了name,就不会按照type进行匹配
7.3.2 Autowird注解
1、Autowird是Spring提供的注解
2、既可以加在方法上也可以加在属性上
3、Autowird默认是按照Type进行匹配的,如果接口有多个实现的话,会先根据默认名进行匹配,匹配不上则报错;最好配合Qualifier显示的指定
八、Spring的扫描器
用来扫描管理Bean的
实际项目如果很大就会出现大量配置,我们使用扫描器可以解决这个问题
扫描器扫描带有@Controller,@Service, @Repository,@Component的类
@Controller:控制层的类
@Service:服务层的类
@Repository:数据层的类
@Component:无法分层的类上
以上注解标注的类的bean的id默认类名的首字符小写
九、Spring整合Junit
/**
* @RunWith:指定spring对junit提供的一个运行器
* @ContextConfiguration:指定spring配置文件位置
* @author renliang
*
*/
@RunWith(value=SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"classpath:ApplicationContext.xml"})
public class TestSpring1 {
/**
* 在junit的bean中注入UserService
* 即UserService的实现类一定加入了4种注解之一,创建的对象赋值给了UserService
*/
@Autowired
UserService userService;
@Test
public void test2() {
userService.save();
}
}
十、AOP
十一、JdbcTemplate
1、引入jdbc的包
2、配置数据源
<!-- 数据源配置 -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/spring3.2_10"></property>
<property name="username" value="root"></property>
<property name="password" value="root"></property>
<!-- 初始化的连接数 -->
<property name="initialSize" value="1"></property>
<!-- 连接池的最大连接数-->
<property name="maxActive" value="5"></property>
<!-- 最大的空闲的连接数 -->
<property name="maxIdle" value="2"></property>
<!-- 最小的空闲连接数 -->
<property name="minIdle" value="1"></property>
</bean>
3、使用
public class UserDaoImpl implements UserDao {
private DataSource dataSource;
private JdbcTemplate jt;
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
this.jt = new JdbcTemplate(dataSource);
}
@Override
public void saveUser(User user) {
String sql = "insert into t_user values(null, ?, ?)";
jt.update(sql, new Object[]{user.getUsername(), user.getPassword()});
}
@Override
public void updateUser(User user) {
String sql = "update t_user set username = ?, password=? where user_id = ?";
jt.update(sql, new Object[]{user.getUsername(), user.getPassword(), user.getUserId()});
}
@Override
public User queryUserById(int userId) {
String sql = "select * from t_user t where t.user_id = ?";
User user = jt.queryForObject(sql, new Object[]{userId}, new UserRowMapper());
return user;
}
@Override
public List<User> queryUserAll() {
String sql = "select * from t_user";
List<User> userList = jt.query(sql, new UserRowMapper());
return userList;
}
}
十二、事务控制
12.1 什么是事物?
一荣俱荣,一损俱损,很多复杂的操作我们可以把它看成是一个整体,要么同时成功,要么同时失败。
事务的四个特征ACID:
原子性(Atomic):表示组成一个事务的多个数据库的操作的不可分割的单元,只有所有的操作成功才算成功,整个事务提交,其中任何一个操作失败了都是导致整个所有操作失败,事务会回滚。
一致性(Consistentcy):事务操作成功后,数据库所处的状态和业务规则一致。如果A账户给B账户汇100,A账户减去100,B加上100,两个账户的总额是不变的。
隔离性(islation):在多个数据库的操作相同的数据并发时,不同的事务有自己的数据空间,事务与事务之间不受干扰(不是绝对的)。干扰程度受数据库或者操作事务的隔离级别来决定,隔离级别越高,干扰就越低,数据的一致性越好,并发性就越差。
持久性(Druability):一旦事务提交成功,数据就被持久化到数据库,不可以回滚。