在spring中老生常谈的就是IOC与AOP。IOC即控制反转,AOP即面向切面编程。IOC与AOP这两种理念其实出现在spring出现之前,spring框架只是将其付之实现。
谈到IOC的同时也要引入另外一个概念DI,DI是依赖注入。在某些方面我们可以说IOC与DI是同一个东西,但是细分下来还有稍有区别。IOC和DI主要是讲述了一个对象创建和对象管理权利转移给IOC容器(即spring框架)。但是IOC是站在对象角度来说,把对象的创建管理权限交给了容器。而DI是从容器看向对象的角度。举个例子,加入A依赖于B,那么再DI角度来说,发现A依赖于B那么便会创建B,同时将B注入到A中。
控制反转之控制:主要是指对象的创建,管理的权利
控制反转之反转:主要是指将控制的权利反转给IOC容器
AOP 是面向切面编程。讲到AOP不免就要提起OOP。OOP是面向对象编程,三大特性是封装继承多态,已经解决了大部分的代码重复问题。但是也有OOP解决不了的部分,那么此时就需要借助AOP来实现。OOP是垂直继承体系,而AOP是横向抽取机制。
原生代码存在耦合问题,不符合面向接口开发的原则。
public class TransferServiceImpl implements TransferService {
private AccountDao accountDao = new JdbcAccountDaoImpl();
}
由上面的代码可以看出,TransferServiceImpl 依赖与AccountDao ,在调用AccountDao 的方法时,需要new该对象进行使用,这时就存在了类与类之间的耦合问题。
我们可以将AccountDao 使用java中的反射进行实例化,即Class.forName(“全限定类名”),由于需要全限定类名的字符串,如果直接写入代码中,又存在了硬编码问题,我们可以使用xml来存放字符串。同时在TransferServiceImpl 中列出AccountDao 的set方法,使用set方法为AccountDao 实例化。
beans.xml
创建BeanFactory类解析xml文件,并对TransferServiceImpl 中的AccountDao 进行实例化。
private static Map map=new HashMap<>();//用于存储对象
static{
//执行任务1
//加载xml
InputStream resourceAsStream = BeanFactory.class.getClassLoader().getResourceAsStream("beans.xml");
//通过dom4j解析
SAXReader saxReader=new SAXReader();
try {
Document read = saxReader.read(resourceAsStream);
Element rootElement = read.getRootElement();
List list = rootElement.selectNodes("//bean");
for (Element element : list) {
//处理每个bean元素,获取该元素的id和class属性
String id = element.attributeValue("id");//获取id
String clazz = element.attributeValue("class");//获取全限类名
//通过反射技术实例化对象
Class> aClass = Class.forName(clazz);
Object o = aClass.newInstance();
//放入map中
map.put(id,o);
}
//实例化完成之后,检查那些对象需要传值,根据配置进行传值
//实例化完成之后,维护对象依赖关系
//有property属性的元素,其父元素需要传值
List propertyList = rootElement.selectNodes("//property");
for (Element element : propertyList) {
//找到当前需要处理依赖关系的bean
Element parent = element.getParent();
String name = element.attributeValue("name");
String ref = element.attributeValue("ref");
//调用父元素的反射方法进行赋值
String parentId = parent.attributeValue("id");
Object parentObject = map.get(parentId);
Method[] methods = parentObject.getClass().getMethods();
for (Method method : methods) {
boolean equals = method.getName().equals("set" + name);
if(equals){
method.invoke(parentObject,map.get(ref));
}
}
//把处理好的parentElement重新放入map中
map.put(parentId,parentObject);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static Object getBean(String id){
return map.get(id);
}
通过该步骤之后,在TransferServiceImpl 中便可以直接通过其中的setAccountDao方法进行AccountDao 的实例化了。
关于一些项目中存在一个方法中有两个甚至更多操作数据库的代码,如果在执行中出现bug那么程序便会终止,此时如果操作数据库的代码没有执行完成,则会出现十分严重的问题。此时在多个操作数据库代码之上添加事务就十分有必要。
数据库事务归根结底是Connection的事务,多个操作数据库的代码使用的是多个Connection,且事务在dao层已经进行了提交。
在同一个方法执行过程中,多个操作数据库代码都属于同一线程,我们可以给当前线程绑定一个Connection,和当前线程有关的数据库操作都使用该Connection。
具体代码实现如下,使用创建单例模式的ConnectionUtils
public class ConnectionUtils {
private ConnectionUtils() {
}
private static ConnectionUtils connectionUtils=new ConnectionUtils();
public static ConnectionUtils getInstance(){
return connectionUtils;
}
private ThreadLocal threadLocal= new ThreadLocal<>();//当前线程连接
/**
* 从当前线程获取连接
* @return
*/
public Connection getCurrentThreadConn() throws SQLException {
//判断当前线程是否绑定连接,如果没有,则获取连接并绑定
Connection connection=threadLocal.get();
if(connection==null){
connection=DruidUtils.getInstance().getConnection();
threadLocal.set(connection);
}
return connection;
}
}
创建一个事务管理工具类TransactionManager,同样使用单例模式
public class TransactionManager {
private static TransactionManager transactionManager=new TransactionManager();
private TransactionManager() {
}
public static TransactionManager getInstance(){
return transactionManager;
}
/**
* 开启事务
*/
public void beginTransaction() throws SQLException {
ConnectionUtils.getInstance().getCurrentThreadConn().setAutoCommit(false);
}
/**
* 提交事务
*/
public void commit() throws SQLException {
ConnectionUtils.getInstance().getCurrentThreadConn().commit();
}
/**
*
* 回滚事务
*/
public void rollback() throws SQLException {
ConnectionUtils.getInstance().getCurrentThreadConn().rollback();
}
}
之后在service层使用事务控制
@Override
public void transfer(String fromCardNo, String toCardNo, int money) throws Exception {
try {
//开启事务
TransactionManager.getInstance().beginTransaction();
······(多个操作数据库代码)
//提交事务
TransactionManager.getInstance().commit();
} catch (Exception e) {
e.printStackTrace();
//回滚事务
TransactionManager.getInstance().rollback();
throw e;
}
}
通过以上代码可以进行手写AOP的实现,该代码可以进行优化,在proxyFactory中加入TransactionManager的实例化对象,以及在TransactionManager和dao层代码中添加ConnectionUtils 的实例化对象,以及修改beans.xml如下
至此,手写IOC和AOP以及完成