Spring必知必会
本文通过总结Spring相关教学视频和面试题,从概念和实战简单的回顾了Spring学习过程,适合入门和复习。
1.概念
1.1 基本概念
1.1.1 什么是Spring
1.1.2 Spring优点
- 方便解耦,简化开发
- 提供面向切面编程,方便实现对程序进行权限拦截、运行监控等功能
- 内部提供了对各种优秀框架的直接支持
- 降低JavaEE API的使用难度
1.2 控制反转
1.2.1 概念
-
含义:控制反转把传统上由程序代码直接操控的对象的调用权交给容器,通过容器来实现对象组件的装配和管理。即对组件对象控制权的转移,从程序代码本身转移到了外部容器。 -
作用:管理对象的创建和依赖关系的维护 -
优点:降低代码量;易于测试;降低侵入性;支持加载服务时的饿汉式初始化和懒加载。 -
依赖注入:是控制反转的实现方式,代码不直接组装你的组件和服务,而在配置文件里描述哪些组件需要哪些服务,由容器负责组装。
1.2.2 Spring IOC容器
- 作用:负责创建对象,管理对象,装配对象, 配置对象,管理对象的生命周期。
1.3 面向切面编程
2.Bean管理
2.1 xml方式
2.1.1 创建对象
2.1.2 注入基本属性
-
根据set方法,设置各变量的值 <bean id="ObjectName" class="package.ClassName">
<property name="variableName" value="value"></property>
</bean>
-
根据构造器参数,设置各变量的值 <bean id="ObjectName" class="package.ClassName">
<constructor-arg name="variableName" value="value"></property>
</bean>
-
使用p名称空间,结合set方法方便注入 <beans xmlns:p="http://www.springframework.org/schema/p">
<bean id="ObjectName" class="package.ClassName" p:variableName="value"></bean>
</beans>
2.1.3 注入字面量
-
null <bean id="ObjectName" class="package.ClassName">
<property name="name"><null/></property>
</bean>
-
特殊符号,使用<![CDATA[...]]> ,将特殊符号包含 <bean id="ObjectName" class="package.ClassName">
<property name="name">
<value><![CDATA[<<special>>]]></value>
</property>
</bean>
2.1.4 注入对象
-
注入外部bean <bean id="ObjectName" class="package.ClassName">
<property name="objectName2" ref="value"></property>
</bean>
<bean id="value" class="package.ClassName2"></bean>
-
注入内部bean(一个bean仅被当做另一个bean的属性) <bean id="ObjectName" class="package.ClassName">
<property name="variableName">
<bean id="ObjectName2" class="package.ClassName2">
<property name="variableName2" value="value2"></property>
</bean>
</property>
</bean>
-
级联赋值,即直接对关联对象的某个属性赋值,需要在本类中添加get方法返回关联对象 private AsClass asObject;
public AsClass getAsClass {return asObject;}
<bean id="ObjectName" class="package.ClassName">
<property name="asObject" ref="value"></property>
<property name="asObject.variableName" value="value2"></property>
</bean>
<bean id="value" class="package.ClassName2"></bean>
-
注入数组和集合
-
数组 <bean id="ObjectName" class="package.ClassName">
<property name="arrayName">
<array>
<value>value1</value>
<value>value2</value>
</array>
</property>
</bean>
-
列表list <bean id="ObjectName" class="package.ClassName">
<property name="listName">
<list>
<value>value1</value>
<value>value2</value>
</list>
</property>
</bean>
-
映射map <bean id="ObjectName" class="package.ClassName">
<property name="mapName">
<map>
<entry key="key1" value="value1"></entry>
<entry key="key2" value="value2"></entry>
</map>
</property>
</bean>
-
集合set <bean id="ObjectName" class="package.ClassName">
<property name="setName">
<set>
<value>value1</value>
<value>value2</value>
</set>
</property>
</bean>
-
集合中设置对象,例如list <bean id="ObjectName" class="package.ClassName">
<property name="listName">
<list>
<ref bean="object1"></ref>
<ref bean="object2"></ref>
</list>
</property>
</bean>
<bean id="object1" class="package.ClassName2"></bean>
<bean id="object2" class="package.ClassName2"></bean>
2.1.5 提取集合注入
可以将注入集合的部分抽取出来,方便使用。
-
添加util名称空间 <beans xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
-
提取集合 <bean id="ObjectName" class="package.ClassName">
<property name="listName" ref="listRef"></property>
</bean>
<util:list id="listRef">
<value>value1</value>
<value>value2</value>
</util:list>
2.1.6 注入外部属性文件
-
直接配置 例如配置德鲁伊连接池 <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/aDatabaseName"></property>
<property name="username" value="aUsername"></property>
<property name="password" value="aPassword"></property>
</bean>
-
通过properties文件
<beans xmlns:util="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:property-placeholder location="classpath:druid.properties"/>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${driverClassName}"></property>
<property name="url" value="${url}"></property>
<property name="username" value="${username}"></property>
<property name="password" value="${password}"></property>
</bean>
</beans>
2.1.7 使用对象
2.1.7 装配
2.2 注解方式
2.2.1 概念
2.2.2 组件扫描
-
作用:对某个包或类启用注解 -
基本实现: <context:component-scan base-package="anno"></context:component-scan>
-
filter:可设置仅扫描或不扫描哪些注解 <context:component-scan base-package="anno" use-default-filters="false"> <context:include-filter type="annotation" expression="org.springframework.stereotype.Component"/>
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
2.2.3 配置类
2.2.4 对象注解
-
作用:把类标注为可以进行自动装配的类 -
类型
类型 | 描述 |
---|
Component | 将类标记为bean,是任何Spring管理组件的通用构造型 | Controller | 将类标记为SpringMVC控制器 | Service | 适用与服务层类 | Repository | 适用于持久层(DAO) |
2.2.5 属性注入
2.2.6 其他注解
类型 | 描述 |
---|
Required | 表明bean属性必须在配置的时候设置,即决定是否强制注入,通常修饰set方法。 | Scope | 声明bean的作用域 |
2.3 bean的生命周期
2.3.1 概念
- 理解bean的生命周期可以帮助利用Spring提供的扩展点来自定义bean的创建过程。
2.3.2 过程
顺序 | 过程 | 描述 |
---|
1 | bean实例化 | Spring启动,查找并加载需要被Spring管理的bean,进行bean的实例化 | 2 | bean属性注入 | Spring将值和bean的引用注入到对应的属性 | 3 | 调用BeanNameAware.setBeanName() | 若实现该接口,将id传递给该方法 | 4 | 调用BeanFactortyAware.setBeanFactory() | 若实现该接口,将BeanFactory容器传入 | 5 | 调用ApplicationContextAware.setApplicationContext() | 若实现该接口,将bean所在应用上下文传入 | 6 | 调用BeanPostProcessor.postProcessBeforeInitialization() | 若实现该接口,调用前置处理方法 | 7 | 调用InitializingBean.afterPropertiesSet() | 若实现该接口,调用该方法 | 8 | 调用自定义初始化方法(init-method) | bean内声明的初始化方法 | 9 | 调用BeanPostProcessor.postProcessAfterInitialization() | 若实现该接口,调用后置处理方法 | | bean可以使用了,直到被应用上下文销毁 | | 10 | 调用DisposableBean.destroy() | 若实现该接口,调用该方法 | 11 | 调用自定义销毁方法(destroy-method) | bean内声明的销毁方法 |
2.3.3 自定义方法
-
xml实现:在xml定义bean中的某个方法为初始化或销毁方法 <bean id="ObjectName" class="package.ClassName"
init-method="theInitMethod"
destroy-method="theDestroyMethod"></bean>
-
注解实现:在初始化和销毁方法前分别使用@PostConstruct 和@Predestroy 。
3.面向切面编程AOP
3.1 基本概念
3.1.1 名词解释
- 连接点:指方法,是切面能够织入的位置,即能够被增强的方法。
- 切入点:切面具体织入的位置
- 通知:切面的工作,即增强的部分。
- 切面:把通知引用到切入点的过程
- 织入:将切面代码插入到目标对象的过程
3.1.2 面向切面编程
-
含义:在运行时,动态地将代码切入到类的指定方法、指定位置上的编程思想。 -
使用:把与业务无关但却对多个对象产生影响的公共行为和逻辑,抽取并封装为一个可重用的模块 。 -
优点:减少系统中的重复代码,降低了模块间的耦合度,提高了系统的可维护性。 -
用途:权限认证,日志,事务处理等。
3.2 动态代理
3.2.1 与AOP
3.2.2 JDK动态代理
-
当目标对象是基于接口实现的,AOP框架会采用JDK动态代理。 -
过程:
- 创建接口和实现类
- 通过实现InvocationHandler接口,并重写invoke方法创建代理对象
- 使用Proxy.newProxyInstance()返回增强的对象。
-
简单实现 public interface Addition
{
public int add(int a, int b);
}
public class AdditionImpl implements Addition
{
@Override
public int add(int a, int b)
{
return a + b;
}
}
class AdditionProxy implements InvocationHandler{
private Object obj;
public AdditionProxy(Object obj){
this.obj = obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
{
System.out.println("在原方法之前执行");
Object res = method.invoke(obj, args);
System.out.println("在原方法之后执行");
return res;
}
public class JDKProxy
{
public static void main(String[] args)
{
Class[] interfaces = {Addition.class};
Addition addition = (Addition)Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new AdditionProxy(Addition));
int result = addition.add(2,3);
System.out.println("result: " + result);
}
}
3.2.3 CGLIB动态代理
- 如果代理类没有实现接口,那么框架会选择使用CGLIB来动态代理目标类。
- CGLIB是一个代码生成的类库,在运行时生成指定类的一个子类对象,并覆盖其中特定方法并添加增强代码,从而实现AOP。
3.2.4 动态代理与静态代理
- 静态代理
- 静态代理在编译阶段生成代理类,即在编译阶段将切面织入到字节码中,运行时就是增强之后的对象。
- 优点:效率高
- 代表:AspectJ
- 动态代理
- 每次运行时在内存中临时为方法生成一个代理对象
- 代表:Spring AOP
3.3 使用
3.3.1 Spring AOP与AspectJ
-
Spring AOP:通过Spring IoC提供一个简单的AOP实现,以解决常见问题。但并不是完整的AOP解决方案,它只能用于Spring容器管理的bean。只支持方法连接点。 -
AspectJ:EcLipse发起的一个面向切面的框架,AspectJ定义了AOP语法,它有一个专门的编译器用来生成遵守Java字节编码规范的Class文件。支持多种连接点。 -
AspectJ独立于Spring,但是Spring使用了AspectJ样式的注解。
3.3.2 xml与注解实现
-
在xml中打开注解扫描,开启AspectJ生成代理对象
<beans xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<context:component-scan base-package="aop"></context:component-scan>
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
-
创建目标对象,并添加注解 @Component
public class User
{
public void create(){
System.out.println("Create a user.");
}
}
-
创建代理对象,并添加通知 @Component
@Aspect
public class UserProxy
{
@Before(value = "execution(* aop.User.create(..))")
public void before(){
System.out.println("before");
}
}
3.3.3 execution表达式
3.3.4 通知类型
3.3.5 其他注解
3.3.6 纯注解实现
3.3.7 xml实现
4. JDBC
4.1 概念
4.1.1 Spring JDBC
- Spring JDBC是Spring框架的模块之一,用于简化JDBC编程。
- Spring JDBC负责数据库资源管理和错误处理。开发者只需声明 SQL 语句、调用合适的 Spring JDBC框架 API、处理结果集即可。
4.1.2 JDBCTemplate
- JdbcTemplate 类是 Spring JDBC 的核心类,提供了一组操作 SQL 语句的 API。
4.2 使用
4.2.1 相关jar包
- 数据库相关:druid, mysql-connector-java
- Spring相关:spring-jdbc, spring-orm, spring-tx
4.2.2 配置文件
-
配置druid连接池 <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/aDatabaseName"></property>
<property name="username" value="aUsername"></property>
<property name="password" value="aPassword"></property>
</bean>
-
将数据源注入JdbcTemplate <bean id ="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"></property>
</bean>
-
在DAO类中使用注解注入JdbcTemplate对象,进行增删改查等操作
4.2.3 JDBCTemplate基础操作
-
类型
方法名 | 返回值(参数) | 描述 |
---|
execute | void(String sql) | 一般执行数据定义语言(DDL) | execute | <T> T (StatementCallback<T> action) | | update | int(String sql, Object…args) | 一般执行数据操作语言(DML),返回影响的行数 | batchUpdate | int[](String sql, List<Object[]> batchArgs) | 添加多条记录 | queryForObject | <T> T(String sql, RowMapper<T> rowMapper, Object…args) | 查询单条数据,并使用返回数据创建对象 | query | <T> List<T>(String sql, RowMapper<T> rowMapper, Object…args) | 查询多条数据,并使用返回数据创建对象列表 |
-
简单实现
String sql = "insert into `users` values(?,?,?)";
int update = jdbcTemplate.update(sql, user.getUserId(), user.getUsername(), user.getPassword());
String sql = "select count(*) from `users`";
Integer num = jdbcTemplate.queryForObject(sql,Integer.class);
String sql = "select * from `users` where id = ?";
int id = 1;
User user = jdbcTemplate.queryForObject(sql,new BeanPropertyRowMapper<User>(User.class),id);
List<Object[]> batchArgs = new ArrayList<>();
batchArgs.add(new Object[]{1,"Amy","123"});
batchArgs.add(new Object[]{2,"Bob","456"});
String sql = "insert into `users` values(?,?,?)";
int[] affected = jdbcTemplate.batchUpdate(sql,batchArgs);
4.3 事务
4.3.1 概念
-
Spring支持的事物管理类型
- 编程式事务管理:通过编程方式管理事务。灵活性强但可维护性低。
- 声明式事务管理:用注解和xml配置文件管理事务,即业务代码和事务管理分离。
-
Spring事务实现原理
- Spring并不直接管理事务,而是提供了多种事务管理器,本质其实就是数据库对事务的支持 。
- Spring事务管理器的接口是
PlatformTransactionManager ,为各个平台如JDBC、Hibernate等提供了对应的事务管理器。 -
核心接口
名称 | 描述 |
---|
PlatformTransactionManager | 按照给定的规则执行提交回滚等操作 | TransactionDefinition | 定义了事务属性 | TransactionStatus | 事务运行状态,如是否提交、是否有保存点等 |
4.3.2 Spring事务属性
事务属性是事务的基本配置,描述了事务策略如何应用到方法上。主要定义在TransactionDefinition 接口。
属性 | 描述 |
---|
传播行为(propagation behavior) | 当多事务同时存在时,Spring处理事务的行为。 主要解决业务层方法之间的相互调用的问题 | 隔离级别(isolation level) | 若干个并发的事务之间的隔离程度 | 是否超时(timeout) | 一个事务所允许执行的最长时间,超时则自动回滚事务 | 是否只读(read only) | 对事务性资源进行只读操作或者是读写操作 | 回滚规则 | 出现异常时的回滚规则 |
4.3.3 传播行为
名称 | 值 | 描述 |
---|
PROPAGATION_REQUIRED | 0 | 当前方法必须运行在事务中。如果当前事务存在,方法将会在该事务中运行,否则启动一个新的事务 | PROPAGATION_SUPPORTS | 1 | 如果当前存在事务,就加入该事务,如果当前不存在事务,就以非事务执行。 | PROPAGATION_MANDATORY | 2 | 该方法必须在事务中运行,如果当前事务不存在,则抛出异常。 | PROPAGATION_REQUIRES_NEW | 3 | 无论当前存不存在事务,都创建新事务。 | PROPAGATION_NOT_SUPPORTED | 4 | 以非事务方式执行操作,如果存在当前事务,就把当前事务挂起。 | PROPAGATION_NEVER | 5 | 以非事务方式执行,如果当前存在事务,则抛出异常。 | PROPAGATION_NESTED | 6 | 如果当前已经存在一个事务,那么该方法将会在嵌套事务中运行。嵌套的事务可以独立于当前事务进行单独地提交或回滚。如果当前事务不存在,那么其行为与REQUIRED一样。 |
4.3.4 隔离级别
名称 | 值 | 描述 |
---|
ISOLATION_DEFAULT | -1 | 默认值,使用底层数据库的默认隔离级别 | ISOLATION_READ_UNCOMMITTED | 1 | 读未提交。事务未提交前就可被其他事务读取(会出现幻读、脏读、不可重复读) | ISOLATION_READ_COMMITTED | 2 | 读已提交。事务提交后才可被其他事务读取(会出现幻读和不可重复读) | ISOLATION_REPEATABLE_READ | 4 | 可重复读。对同一字段的多次读取结果都是一致的(会出现幻读) | ISOLATION_SERIALIZABLE | 8 | 序列化。 |
4.3.5 声明式事务管理实现
-
配置事务管理器
-
使用xml配置文件配置
<beans xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<tx:annotation-driven transaction-manager="transactionManager"> </tx:annotation-driven></tx:annotation-driven>
</beans>
-
或使用配置类配置 @Configuration
@ComponentScan(basePackages="jdbcTemplate")
@EnableTransactionManagement
public class Config
{
@Bean
public DruidDataSource getDruidDataSource()
{
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/xxxx");
dataSource.setUsername("xxx");
dataSource.setPassword("xxx");
return dataSource;
}
@Bean
public JdbcTemplate getJdbcTemplate(DataSource dataSource)
{
JdbcTemplate jdbcTemplate = new JdbcTemplate();
jdbcTemplate.setDataSource(dataSource);
return jdbcTemplate;
}
@Bean
public DataSourceTransactionManager getDataSourceTransactionManager(DataSource dataSource){
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
transactionManager.setDataSource(dataSource);
return transactionManager;
}
}
-
事务操作
-
基于注解 @Service
@Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.REPEATABLE_READ)
public class UserService
{
@Autowired
private UserDao userDao;
public void transfer(){
userDao.out();
userDao.in();
}
}
-
或使用xml
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="transfer" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut expression="execution(* jdbcTemplate.*(..))" id="pointCut"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="pointCut"/>
</aop:config>
-
事务操作
-
基于注解 @Service
@Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.REPEATABLE_READ)
public class UserService
{
@Autowired
private UserDao userDao;
public void transfer(){
userDao.out();
userDao.in();
}
}
-
或使用xml
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="transfer" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut expression="execution(* jdbcTemplate.*(..))" id="pointCut"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="pointCut"/>
</aop:config>
|