本文通过一个简单的A向B转账的业务,简单实现Spring中的核心思想,IOC及AOP;
IOC:控制反转,将对象创建(实例化、管理)的权利交给了IOC容器;
AOP: 面向切面编程,通过横向抽取机制,将业务代码与横切逻辑分离
1. IOC思想实现
1.1 定义一个XML文件,在文件中,定义需要实例化的类全限定名、ID及类中其他属性
1.2 定义BeanFactory工厂类
作用
A. 读取XML中类全限定名,通过反射机制生成实例化对象,通过获取对象中的set方法,注入所需属性对象(eg: TransferService中的TransferDao),存放在HashMap集合中
B.提供一个getBean(id)方法,返回实例化对象
<bean id="accountDao" class="com.kay.dao.impl.JdbcAccountDaoImpl">
<property name="ConnectionUtils" ref="connectionUtils"></property>
</bean>
<bean id="transferService" class="com.kay.service.impl.TransferServiceImpl">
<!-- TransferServiceImpl对象中需要使用到accountDao对象 ref 中配置引用的 bean标签对应的id-->
<property name="AccountDao" ref="accountDao" ></property>
</bean>
<bean id="connectionUtils" class="com.kay.utils.ConnectionUtils"></bean>
<bean id="transactionManager" class="com.kay.utils.TransactionManager">
<property name="ConnectionUtils" ref="connectionUtils"></property>
</bean>
<bean id="proxyFactory" class="com.kay.factory.ProxyFactory">
<property name="TransactionManager" ref="transactionManager"></property>
</bean>
</beans>
private static Map<String, Object> beanMap = new HashMap<>();
/**
* 读取bean.xml中的标签,解析成对象存放到 beanMap 集合中
*/
static {
SAXReader saxReader = new SAXReader();
try {
Document document = saxReader.read(BeanFactory.class.getClassLoader().getResourceAsStream("beans.xml"));
Element rootElement = document.getRootElement();
List<Element> beanList = rootElement.selectNodes("//bean");
if (beanList != null) {
for (Element element : beanList) {
// 获取 <bean> 标签中对应的 id 及 实现类类路径
String id = element.attributeValue("id"); // transferService
String classPath = element.attributeValue("class"); //com.kay.service.impl.TransferServiceImpl
// 反射实例化类对象
Object object = Class.forName(classPath).newInstance();
// 将对象存放到map集合中
beanMap.put(id, object);
}
}
// 处理 属性标签 <property> 为需要配置的对象注入对象(即 TransferServiceImpl对象中,需要注入 AccountDao )
List<Element> propertyList = rootElement.selectNodes("//property");
if (propertyList != null) {
for (Element element : propertyList) {
// 通过set+Id注入对象里面的引用
String methodName = "set" + element.attributeValue("name");//set + AccountDao
String ref = element.attributeValue("ref");// accountDao
Object obj = beanMap.get(ref); //需要被注入的对象 AccountDao
// 获取当前element的父级节点
Element parentElement = element.getParent();
String parentId = parentElement.attributeValue("id"); // transferService
Object parentObject = beanMap.get(parentId);
// 获取父节点所有方法,符合 methodName 的注入对应的属性对象
Method[] methods = parentObject.getClass().getMethods();
for (Method method : methods) {
if (method.getName().equals(methodName)){ // setAccountDao
// 调用该方法,将 需要被注入的对象(AccountDao)传入
method.invoke(parentObject, obj);
}
}
// parentObject被重新赋值了,需要覆盖beanMap中原值
beanMap.put(parentId, parentObject);
}
}
} catch (DocumentException | ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
/**
* 根据传入的类ID 返回实体类
* @param id
* @return
*/
public static Object getBean(String id) {
return beanMap.get(id);
}
2.AOP思想实现 (实现service层的事务管理) 动态代理实现
2.1.定义 ConnectionUtils获取当前线程Connection实例 (事务管理需要放在Service层,JDBC自带的事务管理只对一个Connection请求有用,一个业务代码涉及多个JDBC的执行请求,必须保证同一个业务代码,使用的是同一个Connection对象;此处通过同一个线程限定一个Connection对象)
2.2.定义TransactionManager操作事务的管理(事务开启、提交、回滚等)
public class ConnectionUtils {
private ThreadLocal<Connection> threadLocal = new ThreadLocal<>();
/**
* 从当前线程获取连接
* @return
* @throws SQLException
*/
public Connection getCurrentThreadCon() throws SQLException {
Connection connection = threadLocal.get();
if (connection == null){
// 连接为空,从连接池获取一个连接,并绑定到当前连接池
connection = DruidUtils.getInstance().getConnection();
threadLocal.set(connection);
}
return connection;
}
}
public class TransactionManager {
private ConnectionUtils connectionUtils;
public void setConnectionUtils(ConnectionUtils connectionUtils) {
this.connectionUtils = connectionUtils;
}
/**
* 开启事务
*/
public void beginTransaction() throws SQLException {
connectionUtils.getCurrentThreadCon().setAutoCommit(false);
}
/**
* 提交事务
* @throws SQLException
*/
public void commitTransaction() throws SQLException {
connectionUtils.getCurrentThreadCon().commit();
}
/**
* 回滚事务
*/
public void rollBack() throws SQLException {
connectionUtils.getCurrentThreadCon().rollback();
}
}
通过上面两步,初步实现service的事务管理,如下代码,在service接口中,在执行业务代码前开启事务,业务代码后提交事务,出现异常异常时,回滚事务;
但是存在以下问题:除业务代码外的事务代码和业务代码混在一起 且存在多个业务代码就得写对应个数的事务代码;属于代码重复
@Override
public void transfer(String fromCardNo, String toCardNo, int money) throws Exception {
try {
// 开启事务即:设置不自动提交
TransactionManager.getInstance().beginTransaction();
Account from = accountDao.queryAccountByCardNo(fromCardNo);
Account to = accountDao.queryAccountByCardNo(toCardNo);
from.setMoney(from.getMoney() - money);
to.setMoney(to.getMoney() + money);
accountDao.updateAccountByCardNo(to);
// int i = 1 / 0; // 制造异常
accountDao.updateAccountByCardNo(from);
// 提交
TransactionManager.getInstance().commitTransaction();
}catch (Exception e){
e.printStackTrace();
// 异常回滚
TransactionManager.getInstance().rollBack();
throw e;
}
}
通过动态代理避免以上问题,对service对象进行动态代理,在service中方法执行前开启事务,执行后提交事务,出现异常时回滚事务
2.3.定义 ProxyFactory 代理对象工程,用于生产代理对象
public class ProxyFactory {
private TransactionManager transactionManager;
public void setTransactionManager(TransactionManager transactionManager) {
this.transactionManager = transactionManager;
}
public Object getProxyObject(Object object){
return Proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
try {
// 开启事务
transactionManager.beginTransaction();
// 执行原方法
result = method.invoke(object, args);
// 提交事务
transactionManager.commitTransaction();
}catch (Exception e){
transactionManager.rollBack();
// 向上抛出异常
throw e;
}
return result;
}
});
}
}
2.4. 在Servlet层调用Service对象时,将从BeanFactory获得到的对象实例,传入到ProxyFactory中进行增强
private ProxyFactory proxyFactory = (ProxyFactory) BeanFactory.getBean("proxyFactory");
// 1. 获取TransferService实例化对象 并通过 ProxyFactory 动态代理类工厂将实例化后的TransferService进行增强
private TransferService transferService = (TransferService) proxyFactory.getProxyObject(BeanFactory.getBean("transferService"));
至此完成spring 的基本思想IOC及AOP思想