# Spring框架学习
[TOC]
## Spring的核心思想-IOC和AOP
- **IOC**
- **AOP**
### IOC的基本介绍
#### 什么是IOC?
IOC = Inversion of Control(控制反转),IOC只是一种技术思想
在java中使用IOC开发,无需自己new对象,使用IOC容器帮助我们实例化对象并管理它,我们需要使用哪个对象就从IOC容器中获取,对象的生命周期都由IOC容器进行管理
**为什么称为控制反转?**
- **控制**:指的是对象创建(实例化,管理)的权利
- **反转**:控制权交由外部管理
#### IOC解决了什么问题?
解决了对象之间耦合的问题

#### IOC与DI的区别?
IOC与DI描述的是一件事件,**IOC是目的,DI是手段**,DI就是依赖注入,也是Spring中实现IOC最常用的方式,站在容器的角度上看,DI将对象所需要依赖的对象都从容器中主动注入,达到IOC的目的
### AOP的基本介绍
#### 什么是AOP?
AOP = Aspect oriented Programming 面向切面编程
在多个流程中出现了相同子流程的代码,我们称之为横切逻辑代码,例如在获取方法调用的时间消耗,如果手工在每个方法上面增加,导致编码重复并且不易修改,与业务逻辑代码混杂在一起,不方便管理
AOP提出来切面的概念,将横切逻辑代码与业务代码抽离出来,单独管理,达到非侵入式的效果
#### AOP解决了什么问题?
在不改变原有业务逻辑代码的同时,增强横切逻辑代码,从根本上解耦合,避免了横切代码重复的问题
#### 为什么叫面向切面编程?
**切**:指的是横切的逻辑代码,在不修改原有业务逻辑代码的同时,只操作横切逻辑代码,面向横切逻辑编程
**面**:横切逻辑代码往往影响多个方法,每一个方法都像一个点,多个点构成一个面,称为切面
## 手写简单版本的IOC和AOP
### 常规版本代码,不使用IOC和AOP
一个简单的service层调用DAO层示例代码
**pojo类**:
```java
public class Account {
private String cardNo;
private String name;
private int money;
}
```
**DAO类**:
```java
public interface AccountDao {
Account queryAccountByCardNo(String cardNo) throws Exception;
int updateAccountByCardNo(Account account) throws Exception;
}
public class JdbcAccountDaoImpl implements AccountDao {
@Override
public Account queryAccountByCardNo(String cardNo) throws Exception {
//从连接池获取连接
Connection con = DruidUtils.getInstance().getConnection();
String sql = "select * from account where cardNo = ?";
PreparedStatement preparedStatement = con.prepareStatement(sql);
preparedStatement.setString(1,cardNo);
ResultSet resultSet = preparedStatement.executeQuery();
Account account = new Account();
while(resultSet.next()) {
account.setCardNo(resultSet.getString("cardNo"));
account.setName(resultSet.getString("name"));
account.setMoney(resultSet.getInt("money"));
}
resultSet.close();
preparedStatement.close();
con.close();
return account;
}
@Override
public int updateAccountByCardNo(Account account) throws Exception {
// 从连接池获取连接
// 改造为:从当前线程当中获取绑定的connection连接
Connection con = DruidUtils.getInstance().getConnection();
String sql = "update account set money=? where cardNo=?";
PreparedStatement preparedStatement = con.prepareStatement(sql);
preparedStatement.setInt(1,account.getMoney());
preparedStatement.setString(2,account.getCardNo());
int i = preparedStatement.executeUpdate();
preparedStatement.close();
con.close();
return i;
}
}
```
**service类**:
```java
public interface TransferService {
void transfer(String fromCardNo,String toCardNo,int money) throws Exception;
}
public class TransferServiceImpl implements TransferService {
private AccountDao accountDao = new JdbcAccountDaoImpl();
@Override
public void transfer(String fromCardNo, String toCardNo, int money) throws Exception {
Account from = accountDao.queryAccountByCardNo(fromCardNo);
Account to = accountDao.queryAccountByCardNo(toCardNo);
from.setMoney(from.getMoney()-money);
to.setMoney(to.getMoney()+money);
accountDao.updateAccountByCardNo(to);
accountDao.updateAccountByCardNo(from);
}
}
```
**初始数据**:

**测试类**:
```java
public class Test {
public static void main(String[] args) throws Exception {
TransferService transferService = new TransferServiceImpl();
transferService.transfer("111111","222222",100);
}
}
```
**执行一次结果为**:

### 常规代码问题分析
- TransferService与AccountDao 两个接口都是用new 出来的对象,如果新增了新的接口实现类,需要切换实现类的时候,要修改相关的代码,耦合性太严重
- `JdbcAccountDaoImpl#updateAccountByCardNo`两个方法之间没有用同一个事务进行控制,如果出现异常会导致数据异常
#### 问题解决方案
1. 使用配置文件将对应接口的实现类注册在XML配置文件中,并使用反射进行初始化实例类,可以使用简单工厂模式获取对应实例
2. 同一个线程中,使用同一个Connection进行请求数据库,并增加事务控制
### 修正后代码实现
#### 解析配置文件并使用工厂模式获取对应类实例
**新增配置文件**:
```xml
<?xml version="1.0" encoding="utf-8" ?>
<beans>
<bean id = "accountDao" class ="com.lagou.edu.dao.impl.JdbcAccountDaoImpl"/>
<bean id = "transferService" class ="com.lagou.edu.service.impl.TransferServiceImpl">
<property id = "AccountDao" ref = "accountDao"/>
</bean>
</beans>
```
**新建BeansFactory**
```java
public class BeansFactory {
private static Map<String, Object> classMap = new HashMap<>();
static {
InputStream resourceAsStream = BeansFactory.class.getClassLoader().getResourceAsStream("config.xml");
SAXReader saxReader = new SAXReader();
try {
//使用dom4j解析XML配置文件
Document read = saxReader.read(resourceAsStream);
Element rootElement = read.getRootElement();
List<Element> list = rootElement.selectNodes("//bean");
//先将所有的bean 给解析完成
for (Element element : list) {
String id = element.attributeValue("id");
String className = element.attributeValue("class");
Class<?> clazz = Class.forName(className);
Object obj = clazz.newInstance();
classMap.put(id,obj);
}
//解析所有的property,这里只配置了ref类型
List<Element> propertyList = rootElement.selectNodes("//property");
for (Element element : propertyList) {
Element parent = element.getParent();
String parentId = parent.attributeValue("id");
String elementId = element.attributeValue("id");
String ref = element.attributeValue("ref");
Object object = classMap.get(parentId);
Object refObj = classMap.get(ref);
Method[] declaredMethods = object.getClass().getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
//使用set 方法注入
if (declaredMethod.getName().equals("set" + elementId)) {
declaredMethod.invoke(object,refObj);
break;
}
}
classMap.put(parentId, object);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static Object getClass(String id){
return classMap.get(id);
}
}
```
**原有代码中使用set方法注入**:
```java
public class TransferServiceImpl implements TransferService {
private AccountDao accountDao;
//使用set方法注入
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
@Override
public void transfer(String fromCardNo, String toCardNo, int money) throws Exception {
//省略相同代码
}
```
**测试类**:
```java
public class Test {
public static void main(String[] args) throws Exception {
TransferService transferService = (TransferService) BeansFactory.getClass("transferService");
transferService.transfer("111111","222222",100);
}
}
```
#### 新增同一线程内事务控制
首先需要将获取连接上移至Service层,其次需要service同一个线程中使用同一个连接,这里引入一个工具类,使用ThreadLocal存储相应线程的Connection
```java
public class ConnectionUtils {
private static ConnectionUtils connectionUtils = new ConnectionUtils();
private ThreadLocal<Connection> connectionThreadLocal = new ThreadLocal<>();
private ConnectionUtils(){
}
public static ConnectionUtils instance(){
return connectionUtils;
}
public Connection getConnection() throws SQLException {
Connection connection = connectionThreadLocal.get();
if (connection == null) {
connection = DruidUtils.getInstance().getConnection();
connectionThreadLocal.set(connection);
}
return connection;
}
}
```
将原有AccountDao代码中获取数据库连接逻辑进行修改
```java
Connection con = ConnectionUtils.instance().getConnection();
```
并且在service层代码上增加事务控制代码,引入事务控制类`TransactionManager`
```java
@Override
public void transfer(String fromCardNo, String toCardNo, int money) throws Exception {
try {
//开启事务(关闭事务的自动提交)
TransactionManager.instance().openTransaction();
Account from = accountDao.queryAccountByCardNo(fromCardNo);
Account to = accountDao.queryAccountByCardNo(toCardNo);
from.setMoney(from.getMoney() - money);
to.setMoney(to.getMoney() + money);
accountDao.updateAccountByCardNo(to);
int error = 1/0;
accountDao.updateAccountByCardNo(from);
//如果没有异常,正常提交事务
TransactionManager.instance().commit();
} catch (Exception e) {
TransactionManager.instance().rollBack();
throw e;
}
}
```
这样达到了所有事务在一起提交或者一起回滚的效果,但是有点问题的是,我们如果要针对每一个方法都增加这些代码,首先工作量很大并且是重复的代码,其次也和业务代码耦合在了一起,是不合适的,这里使用动态代理,为所有的方法自动添加上事务相应的代码
##### 动态代理的知识
###### 动态代理-接口代理
**基本介绍**:动态的在内存中生成对象,代理类无需实现接口或继承类,被代理类需要实现对应接口,通过JDK自带的API动态的生成相应对象
**角色**:
- 接口
- 实现类
- 动态代理类(使用JDK的java.lang.reflect.Proxy#newProxyInstance)
- 中介类,用于增强对应方法
```java
//接口
public interface ITeacher {
void teacher();
}
//实现类
public class TeacherImpl implements ITeacher {
@Override
public void teacher() {
System.out.println("动态代理开始上课!");
}
}
//中介类
public class DynamicProxyInvocationHandler implements InvocationHandler {
private Object target;
public DynamicProxyInvocationHandler(Object object){
target = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("上课前");
Object result = method.invoke(target, args);
System.out.println("上课后");
return result;
}
}
//代理类
public class TeacherDynamicProxy {
private Object target;
public TeacherDynamicProxy(Object target) {
this.target = target;
}
public Object getProxyInstance(){
//调用JDK的API
return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),
new DynamicProxyInvocationHandler(target));
}
}
//客户端类
public class Client {
public static void main(String[] args) {
ITeacher teacher = new TeacherImpl();
TeacherDynamicProxy dynamicProxy = new TeacherDynamicProxy(teacher);
ITeacher teacherProxy = (ITeacher) dynamicProxy.getProxyInstance();
teacherProxy.teacher();
}
}
```
###### 动态代理-CGLIB代理
**基本介绍**:
- 静态代理与动态代理-接口代理都需要对象实现相应的接口,但是如果对象没有实现相应接口而需要进行代理的话,那我们可以使用目标对象子类来实现代理,称为**CGLIB代理**
- Cglib也称为子类代理,在内存中构建一个子类对象实现对目标对象的功能扩展
- Cglib需要引入相应的jar包,Spring AOP就是使用CGLIB实现方法拦截的
- Cglib底层是通过字节码处理框架(ASM)转换相应字节码并生成新的类
**角色**:
- 被代理对象
- CGLIB代理创建器,实现MethodInterceptor 接口
```java
//被代理对象
public class TeacherDAO {
public void teacher(){
System.out.println("cglib上课!");
}
}
//代理创建器
public class DynamicCglibProxy implements MethodInterceptor {
private Enhancer enhancer = new Enhancer();
private Object target;
public DynamicCglibProxy(Object obj) {
target = obj;
}
public Object getProxy(){
//设置需要代理的父类
enhancer.setSuperclass(target.getClass());
enhancer.setCallback(this);
//动态创建子类实例
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("上课前!");
Object result = method.invoke(target, objects);
System.out.println("上课后!");
return result;
}
}
//客户端类
public class Client {
public static void main(String[] args) {
TeacherDAO teacherDAO = new TeacherDAO();
DynamicCglibProxy dynamicCglibProxy = new DynamicCglibProxy(teacherDAO);
TeacherDAO teacherProxy = (TeacherDAO) dynamicCglibProxy.getProxy();
teacherProxy.teacher();
}
}
```
#### 使用动态代理新增事务控制代码
首先需要确认是使用JDK动态代理还是Cglib动态代理,这里我们使用JDK动态代理,声明一个JDKProxy代理类,专门用于动态代理相应需要事务的实现类
```java
public class JDKProxy {
private static JDKProxy jdkProxy = new JDKProxy();
private static TransactionManager transactionManager = TransactionManager.instance();
private JDKProxy(){}
public static JDKProxy instance(){
return jdkProxy;
}
public Object getProxy(Object obj){
return Proxy.newProxyInstance(obj.getClass().getClassLoader()
, obj.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result;
try {
transactionManager.openTransaction();
result = method.invoke(obj, args);
transactionManager.commit();
} catch (Exception e) {
transactionManager.rollBack();
throw e;
}
return result;
}
});
}
}
```
测试类也进行相应修改:
```java
public static void main(String[] args) throws Exception {
TransferService transferService = (TransferService) JDKProxy.instance().getProxy(BeansFactory.getClass(
"transferService"));
transferService.transfer("111111","222222",100);
}
```
这样就达到了动态代理类中所有方法都受事务控制的目的,即AOP的思想
#### 代码优化,工具类等使用IOC容器进行管理
修正后的代码相比于原始代码,新增了三个类,可以用于IOC容器管理,分别是`TransactionManager`,`ConnectionUtils`,`JDKProxy,`在XML配置文件中增加三个相应的XML类
```java
<?xml version="1.0" encoding="utf-8" ?>
<beans>
<bean id = "accountDao" class ="com.lagou.edu.dao.impl.JdbcAccountDaoImpl">
<property id = "ConnectionUtils" ref = "connectionUtils"/>
</bean>
<bean id = "transferService" class ="com.lagou.edu.service.impl.TransferServiceImpl">
<property id = "AccountDao" ref = "accountDao"/>
</bean>
<bean id = "connectionUtils" class="com.lagou.edu.utils.ConnectionUtils"/>
<bean id = "transactionManager" class="com.lagou.edu.utils.TransactionManager">
<property id = "ConnectionUtils" ref = "connectionUtils"/>
</bean>
<bean id = "jdkProxy" class="com.lagou.edu.factory.JDKProxy">
<property id = "TransactionManager" ref = "transactionManager"/>
</bean>
</beans>
```
改造原有代码,将原有声明代码都改成从IOC容器中获取
ConnectionUtils类:直接使用IOC容器管理,不需要提供实例化对象的方法
```java
public class ConnectionUtils {
private ThreadLocal<Connection> connectionThreadLocal = new ThreadLocal<>();
public Connection getConnection() throws SQLException {
Connection connection = connectionThreadLocal.get();
if (connection == null) {
connection = DruidUtils.getInstance().getConnection();
connectionThreadLocal.set(connection);
}
return connection;
}
}
```
TransactionManager类:其中依赖的connectionUtils类使用set方法进行注入
```java
public class TransactionManager {
private static ConnectionUtils connectionUtils;
public static void setConnectionUtils(ConnectionUtils connectionUtils) {
TransactionManager.connectionUtils = connectionUtils;
}
public void openTransaction() throws SQLException {
connectionUtils.getConnection().setAutoCommit(false);
}
public void commit() throws SQLException {
connectionUtils.getConnection().commit();
}
public void rollBack() throws SQLException {
connectionUtils.getConnection().rollback();
}
}
```
JDKProxy类:其中TransactionManager类也使用set方法进行注入
```java
public class JDKProxy {
private TransactionManager transactionManager;
public void setTransactionManager(TransactionManager transactionManager) {
this.transactionManager = transactionManager;
}
public Object getProxy(Object obj){
return Proxy.newProxyInstance(obj.getClass().getClassLoader()
, obj.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result;
try {
transactionManager.openTransaction();
result = method.invoke(obj, args);
transactionManager.commit();
} catch (Exception e) {
transactionManager.rollBack();
throw e;
}
return result;
}
});
}
}
```
JdbcAccountDaoImpl类:其中的依赖的connectionUtils类使用set方法进行注入
```java
public class JdbcAccountDaoImpl implements AccountDao {
private ConnectionUtils connectionUtils;
public void setConnectionUtils(ConnectionUtils connectionUtils) {
this.connectionUtils = connectionUtils;
}
//省略
}
```
测试类修改:
```java
public static void main(String[] args) throws Exception {
JDKProxy jdkProxy = (JDKProxy) BeansFactory.getClass("jdkProxy");
TransferService transferService = (TransferService) jdkProxy.getProxy(BeansFactory.getClass(
"transferService"));
transferService.transfer("111111","222222",100);
}
```
这样就达到一个简单的IOC容器与使用AOP实现事务的基本雏形框架代码了
## Spring中的IOC容器学习与分析
### 什么是 BeanDefinition?
BeanDefinition 是 Spring Framework 中定义 Bean 的配置元信息接口,包含:
- Bean 的类名
- Bean 行为配置元素,如作用域、自动绑定的模式,生命周期回调等
- 其他 Bean 引用,又可称作合作者(collaborators)或者依赖(dependencies)
- 配置设置,比如 Bean 属性(Properties)
### spring中Bean 的生命周期(重要!)

1. Spring对Bean进行实例化
2. Spring将值和bean的引用注入到bean对应的属性中
3. 如果Bean实现了BeanNameAware接口,Spring将Bean的ID传递给setBeanName()方法
4. 如果Bean实现了BeanFactoryAware接口,Spring将调用setBeanFactory()方法,将BeanFactory实例传入
5. 如果Bean实现了ApplicationContextAware接口,Spring将调用setApplicationConext()方法,将bean所在的应用上下文引用传入进来
6. 如果bean实现了BeanPostProcessor接口,Spring将调用他们的post-ProcessBeforeInitialization()方法
7. 如果bean中有@PostConstruct注解的方法,会优先执行@PostConstruct注解的方法,如果bean实现了InitializingBean接口,Spring将调用他们的afterPropertiesSet()方法,如果bean也使用了init-method声明了初始化方法,此时也会调用
8. 如果bean实现了BeanPostProcessor接口,Spring将调用他们的post-ProcessAfterInitialization()方法
9. 此时bean已经就绪,如果是单例模式,则将该 Bean 放⼊ Spring IoC 的缓存池中,并从单例缓存池中交给用户,如果是多例模式,则直接交给用户
10. 如果 bean中有@PreDestroy注解标记的方法,会优先执行@PreDestroy注解实现的方法,如果Bean 实现了 DisposableBean 接口, Spring 将调用它的 destroy() 接口方法。同样,如果 bean 使用 destroy-method 声明了销毁方法,该方法也会被调用
### Spring中IOC容器的初始化流程
```java
public void refresh() throws BeansException, IllegalStateException {
// 对象锁加锁
synchronized (this.startupShutdownMonitor) {
/*
Prepare this context for refreshing.
刷新前的预处理
表示在真正做refresh操作之前需要准备做的事情:
设置Spring容器的启动时间,
开启活跃状态,撤销关闭状态
验证环境信息里一些必须存在的属性等
*/
prepareRefresh();
/*
Tell the subclass to refresh the internal bean factory.
获取BeanFactory;默认实现是DefaultListableBeanFactory
加载BeanDefition 并注册到 BeanDefitionRegistry
*/
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
/*
Prepare the bean factory for use in this context.
BeanFactory的预准备工作(BeanFactory进行一些设置,比如context的类加载器等)
*/
prepareBeanFactory(beanFactory);
try {
/*
Allows post-processing of the bean factory in context subclasses.
BeanFactory准备工作完成后进行的后置处理工作
*/
postProcessBeanFactory(beanFactory);
/*
Invoke factory processors registered as beans in the context.
实例化实现了BeanFactoryPostProcessor接口的Bean,并调用接口方法
*/
invokeBeanFactoryPostProcessors(beanFactory);
/*
Register bean processors that intercept bean creation.
注册BeanPostProcessor(Bean的后置处理器),在创建bean的前后等执行
*/
registerBeanPostProcessors(beanFactory);
/*
Initialize message source for this context.
初始化MessageSource组件(做国际化功能;消息绑定,消息解析);
*/
initMessageSource();
/*
Initialize event multicaster for this context.
初始化事件派发器
*/
initApplicationEventMulticaster();
/*
Initialize other special beans in specific context subclasses.
子类重写这个方法,在容器刷新的时候可以自定义逻辑;如创建Tomcat,Jetty等WEB服务器
*/
onRefresh();
/*
Check for listener beans and register them.
注册应用的监听器。就是注册实现了ApplicationListener接口的监听器bean
*/
registerListeners();
/*
Instantiate all remaining (non-lazy-init) singletons.
初始化所有剩下的非懒加载的单例bean
初始化创建非懒加载方式的单例Bean实例(未设置属性)
填充属性
初始化方法调用(比如调用afterPropertiesSet方法、init-method方法)
调用BeanPostProcessor(后置处理器)对实例bean进行后置处理
*/
finishBeanFactoryInitialization(beanFactory);
/*
Last step: publish corresponding event.
完成context的刷新。主要是调用LifecycleProcessor的onRefresh()方法,并且发布事件(ContextRefreshedEvent)
*/
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}
```
### //todo BeanFactory的创建流程
### //todo Bean的创建流程
### //todoSpring IOC的循环依赖问题解决
## AOP的源码解析
### //todo Spring如何解析AOP与代理AOP
### //todo Spring声明式事务控制执行原理
Spring学习笔记