本文通过总结Spring相关教学视频和面试题,从概念和实战简单的回顾了Spring学习过程,适合入门和复习。
1.概念 1.1 基本概念 1.1.1 什么是Spring-
Spring是一个轻量级Java开发框架 。
-
目的是解决企业级应用开发的业务逻辑层和其他各层的耦合问题,即解决企业级应用开发的复杂性。
-
两个核心:控制反转(IOC)和面向切面编程(AOP)
- 方便解耦,简化开发
- 提供面向切面编程,方便实现对程序进行权限拦截、运行监控等功能
- 内部提供了对各种优秀框架的直接支持
- 降低JavaEE API的使用难度
-
含义:控制反转把传统上由程序代码直接操控的对象的调用权交给容器,通过容器来实现对象组件的装配和管理。即对组件对象控制权的转移,从程序代码本身转移到了外部容器。
-
作用:管理对象的创建和依赖关系的维护
-
优点:降低代码量;易于测试;降低侵入性;支持加载服务时的饿汉式初始化和懒加载。
-
依赖注入:是控制反转的实现方式,代码不直接组装你的组件和服务,而在配置文件里描述哪些组件需要哪些服务,由容器负责组装。
- 作用:负责创建对象,管理对象,装配对象, 配置对象,管理对象的生命周期。
- 详见第三章
-
在spring配置文件中,使用bean标签创建对象。提供对象id和类路径和类名
-
根据set方法,设置各变量的值
-
根据构造器参数,设置各变量的值
-
使用p名称空间,结合set方法方便注入
-
null
-
特殊符号,使用,将特殊符号包含
>]]>
-
注入外部bean
-
注入内部bean(一个bean仅被当做另一个bean的属性)
-
级联赋值,即直接对关联对象的某个属性赋值,需要在本类中添加get方法返回关联对象
private AsClass asObject; public AsClass getAsClass {return asObject;} -
注入数组和集合
可以将注入集合的部分抽取出来,方便使用。
-
添加util名称空间
-
提取集合
value1 value2
-
直接配置
例如配置德鲁伊连接池
-
通过properties文件
-
基本方法
- 代码实现
//加载配置文件 ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml"); //获取配置文件创建的对象 ClassName objectName = context.getBean("objectName",ClassName.class); //调用方法 objectName.aMethod();- ApplicationContext接口继承BeanFactory接口,与BeanFactory共为Spring的两大核心接口。
- ApplicationContext的实现类
类名 描述 FileSystemXmlApplicationContext 参数为相对项目根路径 ClassPathXmlApplicationContext 参数为classpath路径 WebXmlApplicationContext 用于web应用的bean -
类的作用域(scope)
-
singleton:默认,即bean在每个IOC容器中只有一个实例,在加载配置文件时就会创建单实例对象。
-
prototype:一个bean的定义可以有多个实例,在加载配置文件时不创建对象,而是在调用getbean方法返回对象时创造对象。
-
其他:request, session, global-session
-
代码实现
-
-
装备:在Spring容器中把bean组装到一起。
-
自动装配:在Spring中,对象无需自己查找或创建关联的其他对象,而是由容器进行关联。
-
自动装配的不足:
- 基本数据类型、字面量、类字面量无法使用自动装配来注入。
- 自动装配不如显示装配精确,装配依赖中若是出现匹配到多个bean,装配将会失败。
-
自动装配类型
- byType:通过参数类型自动装配(若有多个同类型的bean会报异常)
- byName:通过bean名称进行装配(即property name与bean id匹配)
- constructor:通过构造函数的参数根据byType装配
- autodetect:自动探测,如果有构造方法,通过construct,否则使用byType。
- no:默认方式,不进行自动装配
-
代码实现
-
注解:代码特殊标记,可作用与类、方法和属性
-
作用:通过注解的帮助进行Spring配置,简化xml配置。
-
过程:
-
通过xml开启组件扫描,或创建配置类替代xml配置文件
-
在类上方添加对象注解
-
在类中需要装配的位置添加注解
-
-
作用:对某个包或类启用注解
-
基本实现:
-
filter:可设置仅扫描或不扫描哪些注解
-
基本实现
@Configuration @ComponentScan(basePackages={"anno"}) public class SpringConfig{ }
-
作用:把类标注为可以进行自动装配的类
-
类型
类型 描述 Component 将类标记为bean,是任何Spring管理组件的通用构造型 Controller 将类标记为SpringMVC控制器 Service 适用与服务层类 Repository 适用于持久层(DAO)
-
类型
类型 描述 AutoWired 根据属性类型自动装配, Resource 默认按照名称装配,找不到再根据类型装配 Qualifier 当有多个同类型的bean时,与AutoWired一同使用指定名称 Value 注入普通类型 -
代码实现
@Service public class UserService { @Autowired private UserDao userDao; @Resource(name="userDaoImpl") private UserDao userDao2; @Autowired @Qualifier(value = "userDaoImpl") private UserDao userDao2; @Value(value = "valueFromAnnotation") private String name;
| 类型 | 描述 |
|---|---|
| Required | 表明bean属性必须在配置的时候设置,即决定是否强制注入,通常修饰set方法。 |
| Scope | 声明bean的作用域 |
- 理解bean的生命周期可以帮助利用Spring提供的扩展点来自定义bean的创建过程。
| 顺序 | 过程 | 描述 |
|---|---|---|
| 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内声明的销毁方法 |
-
xml实现:在xml定义bean中的某个方法为初始化或销毁方法
-
注解实现:在初始化和销毁方法前分别使用@PostConstruct和@Predestroy。
- 连接点:指方法,是切面能够织入的位置,即能够被增强的方法。
- 切入点:切面具体织入的位置
- 通知:切面的工作,即增强的部分。
- 切面:把通知引用到切入点的过程
- 织入:将切面代码插入到目标对象的过程
-
含义:在运行时,动态地将代码切入到类的指定方法、指定位置上的编程思想。
-
使用:把与业务无关但却对多个对象产生影响的公共行为和逻辑,抽取并封装为一个可重用的模块 。
-
优点:减少系统中的重复代码,降低了模块间的耦合度,提高了系统的可维护性。
-
用途:权限认证,日志,事务处理等。
-
Spring AOP基于动态代理实现
-
动态代理表明AOP框架不会修改字节码,而是每次运行时在内存中临时为方法生成一个代理对象,这个代理对象包含了目标对象的全部方法,并且在切点做了增强处理,并回调原对象的方法。
-
动态代理有JDK动态代理和CGLIB动态代理
-
当目标对象是基于接口实现的,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); } }
- 如果代理类没有实现接口,那么框架会选择使用CGLIB来动态代理目标类。
- CGLIB是一个代码生成的类库,在运行时生成指定类的一个子类对象,并覆盖其中特定方法并添加增强代码,从而实现AOP。
- 静态代理
- 静态代理在编译阶段生成代理类,即在编译阶段将切面织入到字节码中,运行时就是增强之后的对象。
- 优点:效率高
- 代表:AspectJ
- 动态代理
- 每次运行时在内存中临时为方法生成一个代理对象
- 代表:Spring AOP
-
Spring AOP:通过Spring IoC提供一个简单的AOP实现,以解决常见问题。但并不是完整的AOP解决方案,它只能用于Spring容器管理的bean。只支持方法连接点。
-
AspectJ:EcLipse发起的一个面向切面的框架,AspectJ定义了AOP语法,它有一个专门的编译器用来生成遵守Java字节编码规范的Class文件。支持多种连接点。
-
AspectJ独立于Spring,但是Spring使用了AspectJ样式的注解。
-
在xml中打开注解扫描,开启AspectJ生成代理对象
-
创建目标对象,并添加注解
@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"); } }
-
作用:用于匹配方法执行的连接点
-
完整格式
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern) throws-pattern?)
类型 是否必填 内容 修饰符 否 public等,*表示任意 返回值类型 是 *表示所有类型 类型声明 否 名称 是 包.类.方法 参数列表 是 ()表示没有参数,(…)任意参数 异常列表 否
-
五种类型
注解 类型 作用 @Before 前置通知 在目标方法执行前调用 @AfterReturning 后置通知 在目标方法执行后调用,除非抛出异常 @After 最终通知 在目标方法执行后调用 @AfterThrowing 异常通知 在目标方法异常后调用 @Around 环绕通知 在目标方法执行前后调用 -
环绕通知实现
@Around(value = "execution(* aop.User.create(..))") public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { //执行前调用 System.out.println("before around"); //执行目标方法 proceedingJoinPoint.proceed(); //执行后调用 System.out.println("after around"); }
-
Pointcut
-
作用:对相同的切入点进行抽取,减少重复。
-
内容:表达式和签名(方法)
-
代码实现:
@Pointcut(value = "execution(* aop.User.create(..))") public void aPointCut(){ } @Before(value = "aPointCut()") public void before(){ System.out.println("before"); } @After(value = "aPointCut()") public void after(){ System.out.println("after"); }
-
-
Order:在代理类上使用@order(int)可以表示代理类的执行顺序
-
通过创建配置类替代xml配置文件
@Configuration @ComponentScan(basePackages={"aop"}) @EnableAspectJAutoProxy(proxyTargetClass=true) public class SpringConfig{ }
-
在xml中创建类对象,并配置切入点和切面
- Spring JDBC是Spring框架的模块之一,用于简化JDBC编程。
- Spring JDBC负责数据库资源管理和错误处理。开发者只需声明 SQL 语句、调用合适的 Spring JDBC框架 API、处理结果集即可。
- JdbcTemplate 类是 Spring JDBC 的核心类,提供了一组操作 SQL 语句的 API。
- 数据库相关:druid, mysql-connector-java
- Spring相关:spring-jdbc, spring-orm, spring-tx
-
配置druid连接池
-
将数据源注入JdbcTemplate
-
在DAO类中使用注解注入JdbcTemplate对象,进行增删改查等操作
-
类型
方法名 返回值(参数) 描述 execute void(String sql) 一般执行数据定义语言(DDL) execute T (StatementCallback action) update int(String sql, Object…args) 一般执行数据操作语言(DML),返回影响的行数 batchUpdate int[](String sql, List 添加多条记录 queryForObject T(String sql, RowMapper rowMapper, Object…args) 查询单条数据,并使用返回数据创建对象 query List (String sql, RowMapper 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.class),id); //批量操作 List
-
Spring支持的事物管理类型
- 编程式事务管理:通过编程方式管理事务。灵活性强但可维护性低。
- 声明式事务管理:用注解和xml配置文件管理事务,即业务代码和事务管理分离。
-
Spring事务实现原理
- Spring并不直接管理事务,而是提供了多种事务管理器,本质其实就是数据库对事务的支持 。
- Spring事务管理器的接口是PlatformTransactionManager,为各个平台如JDBC、Hibernate等提供了对应的事务管理器。
-
核心接口
名称 描述 PlatformTransactionManager 按照给定的规则执行提交回滚等操作 TransactionDefinition 定义了事务属性 TransactionStatus 事务运行状态,如是否提交、是否有保存点等
事务属性是事务的基本配置,描述了事务策略如何应用到方法上。主要定义在TransactionDefinition接口。
| 属性 | 描述 |
|---|---|
| 传播行为(propagation behavior) | 当多事务同时存在时,Spring处理事务的行为。 主要解决业务层方法之间的相互调用的问题 |
| 隔离级别(isolation level) | 若干个并发的事务之间的隔离程度 |
| 是否超时(timeout) | 一个事务所允许执行的最长时间,超时则自动回滚事务 |
| 是否只读(read only) | 对事务性资源进行只读操作或者是读写操作 |
| 回滚规则 | 出现异常时的回滚规则 |
| 名称 | 值 | 描述 |
|---|---|---|
| PROPAGATION_REQUIRED | 0 | 当前方法必须运行在事务中。如果当前事务存在,方法将会在该事务中运行,否则启动一个新的事务 |
| PROPAGATION_SUPPORTS | 1 | 如果当前存在事务,就加入该事务,如果当前不存在事务,就以非事务执行。 |
| PROPAGATION_MANDATORY | 2 | 该方法必须在事务中运行,如果当前事务不存在,则抛出异常。 |
| PROPAGATION_REQUIRES_NEW | 3 | 无论当前存不存在事务,都创建新事务。 |
| PROPAGATION_NOT_SUPPORTED | 4 | 以非事务方式执行操作,如果存在当前事务,就把当前事务挂起。 |
| PROPAGATION_NEVER | 5 | 以非事务方式执行,如果当前存在事务,则抛出异常。 |
| PROPAGATION_NESTED | 6 | 如果当前已经存在一个事务,那么该方法将会在嵌套事务中运行。嵌套的事务可以独立于当前事务进行单独地提交或回滚。如果当前事务不存在,那么其行为与REQUIRED一样。 |
| 名称 | 值 | 描述 |
|---|---|---|
| ISOLATION_DEFAULT | -1 | 默认值,使用底层数据库的默认隔离级别 |
| ISOLATION_READ_UNCOMMITTED | 1 | 读未提交。事务未提交前就可被其他事务读取(会出现幻读、脏读、不可重复读) |
| ISOLATION_READ_COMMITTED | 2 | 读已提交。事务提交后才可被其他事务读取(会出现幻读和不可重复读) |
| ISOLATION_REPEATABLE_READ | 4 | 可重复读。对同一字段的多次读取结果都是一致的(会出现幻读) |
| ISOLATION_SERIALIZABLE | 8 | 序列化。 |
-
配置事务管理器
-
使用xml配置文件配置
-
或使用配置类配置
@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; } //创建JdbcTemplate对象 @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
-
-
事务操作
-
基于注解
@Service @Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.REPEATABLE_READ) public class UserService { @Autowired private UserDao userDao; public void transfer(){ userDao.out(); userDao.in(); } } -
或使用xml
-



