闭关修炼180天--手写IOC和AOP(xml篇)
闭关修炼180天--手写IOC和AOP(xml篇)
帝莘
首先先分享一波思维导图,涵盖了一些Spring的知识点,当然这里并不全面,后期我会持续更新知识点。
在手写实现IOC和AOP之前(也就是打造一个简单的Spring框架),先简单的了解一些Spring以及它的两个核心思想IOC和AOP的相关概念。
Spring:
概述:spring是分层的全栈轻量级开源框架,以ioc和AOP为内核,提供了展现层spring mvc和业务层管理等众多的企业应用技术,还能整合众多开源的第三方框架。
优势:1.方便解耦,简化开发 2.AOP编程的支持 3.声明式事务的支持。4.方便程序的测试。5.方便集成各种优秀框架 6.降低javaEE API的使用难度。6.源码是经典的java学习案例。
Spring 的核⼼结构:Spring是⼀个分层⾮常清晰并且依赖关系、职责定位⾮常明确的轻量级框架,主要包括⼏个⼤模块:数据处理模块、Web模块、AOP(Aspect Oriented Programming)/Aspects模块、
Core Container模块和 Test 模块,如下图所示,Spring依靠这些基本模块,实现了⼀个令⼈愉悦的融合了现有解决⽅案的零侵⼊的轻量级框架。
IOC:
IOC概念:控制反转,它是一个技术思想,不是一个技术实现,描述的事情是:java开发领域对象的创建,管理的问题。
传统开发方式:比如A依赖于B,往往会在A中new一个B的对象。
IOC思想下的开发方式:我们不用自己去new对象了,而是由IOC容器(Spring框架)去帮助我们实例化对象并且管理它,我们需要使用哪个对象,去问IOC容器要即可。我们丧失了一个权力(创建、管理对
象的权力),得到了一个福利(不用考虑对象的创建、管理一系列事情)
为什么叫控制反转?控制:指的是对象创建(实例化、管理)的权力;反转:控制权交给了外部环境(spring框架)
IOC解决了什么问题:IOC解决了对象之间的耦合问题
IOC和DI的区别?IOC是控制反转,DI是依赖注入。
怎么理解:IOC和DI描述的是一件事情(对象实例化及依赖关系维护这件事),不过角度不同。
IOC是站在对象的角度,对象实例化及其管理的权力交给了(反转)容器。
DI是站在容器的角度,容器会把对象依赖的其他对象注入(送过去),比如A对象实例化过程中因为声明了一个B类型的属性,那么就需要容器把B对象注入给A。
手写Spring框架流程:
实现思路:
- 编写xml文件,在根标签beans下条件bean,每一组bean对应一个需要加入容器管理的对象。
- 编写BeanFactory:解析xml文件,将解析出的一个个bean装载进map集合中,其中id为bean标签内id属性的值,value为根据bean标签内class属性的全路径值反射得到的目标类的实例对象。
- BeanFactory还要提供一个获取实例的方法getBean(String id)
- 为了添加不同类的依赖关系,在xml文件里面的bean标签组内添加property标签,其中property内的name属性的值是父标签所代表的类的set方法后面的名字,ref属性的值是它所依赖的对象的bean-id。在BeanFactory中进一步解析,把对应依赖关系添加进对象中,更新map。
- 配置事务,保证一个线程使用一个数据库连接,根据代理模式生产代理对象,根据代理对象回调用invoke方法的特性,在原方法前后增加事务增强,在这里,具体的事务管理我们交给TransactionManager类进行具体的管理,配置好代理类和事务管理类之间的依赖关系。
代码实现:
先看一下整体的项目结构:
sql建表语句:
SET FOREIGN_KEY_CHECKS=0; -- ----------------------------
-- Table structure for users
-- ----------------------------
DROP TABLE IF EXISTS `users`;
CREATE TABLE `users` (
`id` int(11) NOT NULL,
`username` varchar(255) COLLATE utf8_bin DEFAULT NULL,
`balance` double(11,0) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin; -- ----------------------------
-- Records of users
-- ----------------------------
INSERT INTO `users` VALUES ('1', '汪苏泷', '1000');
INSERT INTO `users` VALUES ('2', '薛之谦', '1000');
几个pom依赖
<!--dom4j-->
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>1.6.1</version>
</dependency>
<dependency>
<groupId>jaxen</groupId>
<artifactId>jaxen</artifactId>
<version>1.1.6</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.17</version>
</dependency>
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
</dependency>
<!--引入cglib依赖-->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.1_2</version>
</dependency>
xml文件代码:bean.xml
<?xml version="1.0" encoding="UTF-8" ?>
<beans>
<bean id="UserDao" class="com.radorm.spring.dao.impl.UserDaoImpl">
<property name="ConnectionUtils" ref="ConnectionUtils"></property>
</bean>
<bean id="UserService" class="com.radorm.spring.service.impl.UserServiceImpl">
<property name="UserDao" ref="UserDao"></property>
</bean> <bean id="ConnectionUtils" class="com.radorm.spring.utils.ConnectionUtils"></bean> <bean id="TransactionManager" class="com.radorm.spring.utils.TransactionManager">
<property name="ConnectionUtils" ref="ConnectionUtils"></property>
</bean> <bean id="ProxyFactory" class="com.radorm.spring.factory.ProxyFactory">
<property name="TransactionManager" ref="TransactionManager"></property>
</bean>
</beans>
三个工具类:DataSourceUtils ConnectionUtils ,TransactionManager
import com.mchange.v2.c3p0.ComboPooledDataSource;
import javax.sql.DataSource;
import java.beans.PropertyVetoException;
import java.sql.Connection;
import java.sql.SQLException; public class DataSourceUtils { private DataSourceUtils(){} private static DataSourceUtils dataSourceUtils = new DataSourceUtils(); public static DataSourceUtils getInstance(){
return dataSourceUtils;
} public DataSource createDataSource(){
//创建连接池
ComboPooledDataSource dataSource = new ComboPooledDataSource();
try {
dataSource.setDriverClass("com.mysql.jdbc.Driver");
dataSource.setJdbcUrl("jdbc:mysql:///mybatis");
dataSource.setUser("root");
dataSource.setPassword("root");
} catch (PropertyVetoException e) {
e.printStackTrace();
}
return dataSource;
} public Connection getConnection(){
DataSource dataSource = createDataSource();
try {
return dataSource.getConnection();
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
}
import java.sql.Connection; public class ConnectionUtils {
/**
* 声明当前线程
*/
private ThreadLocal<Connection> threadLocal = new ThreadLocal<>(); /**
* 获取当前线程里面的连接
* @return
*/
public Connection getLocalConnection(){
Connection connection = threadLocal.get();
if(connection == null){
//如果在当前线程内获取不到连接,那就在线程池中获取,并且放到当前线程中
connection = DataSourceUtils.getInstance().getConnection();
threadLocal.set(connection);
}
return connection;
}
}
import java.sql.SQLException; /**
* 事务管理工厂
*/
public class TransactionManager { private ConnectionUtils connectionUtils; public void setConnectionUtils(ConnectionUtils connectionUtils) {
this.connectionUtils = connectionUtils;
} public void beginTransaction() throws SQLException {
connectionUtils.getLocalConnection().setAutoCommit(false);
} public void commitTransaction() throws SQLException{
connectionUtils.getLocalConnection().commit();
} public void rollbackTransaction(){
try {
connectionUtils.getLocalConnection().rollback();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
两个工厂类:BeanFactory,ProxyFactory
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader; import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map; public class BeanFactory { private static Map<String,Object> map = new HashMap<>(); /**
* 1.使用dom4j技术读取配置文件信息
* 2.将读取到的对象以及key封装进map中
* key = id,value = class反射产生的实例对象
*/
static {
//根据xml的路径加载为字节流到内存中
InputStream inputStream = BeanFactory.class.getClassLoader().getResourceAsStream("beans.xml");
SAXReader saxReader = new SAXReader();
try {
//读取该字节流为document
Document document = saxReader.read(inputStream);
//获取根标签下的元素
Element rootElement = document.getRootElement();
//获取根标签内部所有的bean标签里面的内容
List<Element> beanList = rootElement.selectNodes("//bean");
for (Element element : beanList) {
//根据属性名获取bean标签里面的内容
String id = element.attributeValue("id");
String aClass = element.attributeValue("class");
//利用反射技术根据全路径获取类对象
Class<?> aClass1 = Class.forName(aClass);
//根据类对象获取实例
Object o = aClass1.newInstance();
//组装map
map.put(id,o);
}
//获取所有的property
List<Element> propertyList = rootElement.selectNodes("//property");
for (Element element : propertyList) {
//获取property标签里面的属性值
String name = element.attributeValue("name");
String ref = element.attributeValue("ref"); //获取父标签
Element parentElement = element.getParent();
//获取父标签的id
String parentId = parentElement.attributeValue("id");
//根据父标签的id获取出父标签的实例化对象
Object parentObj = map.get(parentId);
//获取父对象想要添加依赖的实例化对象
Object refObj = map.get(ref);
//获取父对象的所有方法
Method[] methods = parentObj.getClass().getMethods();
for (Method method : methods) {
String methodName = method.getName();
//判断是否是set方法
if(methodName.equalsIgnoreCase("set"+name)){
//往方法中传入参数
method.invoke(parentObj,refObj);
}
}
//重新将父对象加入到map中
map.put(parentId,parentObj);
}
} catch (DocumentException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
} public static <T> T getBean(String id){
return (T) map.get(id);
}
}
import com.radorm.spring.utils.TransactionManager;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy; /**
* JDK动态代理工厂
*/
public class ProxyFactory { private TransactionManager transactionManager; public void setTransactionManager(TransactionManager transactionManager) {
this.transactionManager = transactionManager;
} /**
* jdk动态代理:要求被代理的对象实现了接口
* @param object 委托对象
* @param <T> 代理对象
* @return
*/
public <T> T getProxyJDK(Object object){
Object result = Proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object re = null;
try {
//开启事务
transactionManager.beginTransaction();
re = method.invoke(object,args);
//提交事务
transactionManager.commitTransaction();
} catch (Exception e) {
e.printStackTrace();
//回滚事务
transactionManager.rollbackTransaction();
throw e;
}
return re;
}
}); return (T) result;
} /**
* cglib动态代理
* @param obj 委托对象
* @param <T> 代理对象
* @return
*/
public <T> T getProxyCglib(Object obj){
Object result = Enhancer.create(obj.getClass(), new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
Object od = null;
try {
//开启事务
transactionManager.beginTransaction();
od = method.invoke(obj,objects);
//提交事务
transactionManager.commitTransaction();
} catch (Exception e) {
e.printStackTrace();
//回滚事务
transactionManager.rollbackTransaction();
throw e;
}
return od;
}
});
return (T) result;
}
}
一个pojo:Users
public class Users { private Integer id; private String username; private Double balance; public Integer getId() {
return id;
} public void setId(Integer id) {
this.id = id;
} public String getUsername() {
return username;
} public void setUsername(String username) {
this.username = username;
} public Double getBalance() {
return balance;
} public void setBalance(Double balance) {
this.balance = balance;
} public Users(Integer id, String username, Double balance) {
this.id = id;
this.username = username;
this.balance = balance;
}
}
一个dao + 它的impl:UserDao + UserDaoImpl
import com.radorm.spring.pojo.Users; public interface UserDao { void draw(); Users select(Integer id); void updateUsers(Users users);
}
import com.radorm.spring.dao.UserDao;
import com.radorm.spring.pojo.Users;
import com.radorm.spring.utils.ConnectionUtils; import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException; public class UserDaoImpl implements UserDao { private ConnectionUtils connectionUtils; public void setConnectionUtils(ConnectionUtils connectionUtils) {
this.connectionUtils = connectionUtils;
} @Override
public void draw() {
System.out.println("11111111");
} @Override
public Users select(Integer id) {
Connection connection = connectionUtils.getLocalConnection();
try {
PreparedStatement preparedStatement = connection.prepareStatement("select * from users where id = ?");
preparedStatement.setObject(1,id);
ResultSet resultSet = preparedStatement.executeQuery();
while (resultSet.next()){
int id1 = resultSet.getInt("id");
String username = resultSet.getString("username");
double balance = resultSet.getDouble("balance");
Users users = new Users(id1,username,balance);
return users;
} } catch (SQLException e) {
e.printStackTrace();
}
return null;
} @Override
public void updateUsers(Users users) {
Connection localConnection = connectionUtils.getLocalConnection();
try {
PreparedStatement preparedStatement = localConnection.prepareStatement("update users set username = ?,balance = ? where id = ?");
preparedStatement.setString(1,users.getUsername());
preparedStatement.setDouble(2,users.getBalance());
preparedStatement.setInt(3,users.getId());
preparedStatement.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} }
}
一个service+它的impl:UserService,UserServiceImpl
public interface UserService { void transfer(Integer fromId,Integer toId,Double money) throws Exception; }
import com.radorm.spring.dao.UserDao;
import com.radorm.spring.pojo.Users;
import com.radorm.spring.service.UserService; public class UserServiceImpl implements UserService { private UserDao userDao; public void setUserDao(UserDao userDao) {
this.userDao = userDao;
} @Override
public void transfer(Integer fromId, Integer toId, Double money) throws Exception { Users from = userDao.select(fromId); Users to = userDao.select(toId); from.setBalance(from.getBalance() - money); to.setBalance(to.getBalance() + money); userDao.updateUsers(from);
//该异常代码可以选择打开或者关闭,检验事务的作用
//int i = 1/0 userDao.updateUsers(to); }
}
test:BeanTest
import com.radorm.spring.factory.ProxyFactory;
import com.radorm.spring.service.UserService;
import com.radorm.spring.factory.BeanFactory;
import org.junit.Test; public class BeanTest { @Test
public void test_bean(){
ProxyFactory proxyFactory = BeanFactory.getBean("ProxyFactory");
UserService userService = proxyFactory.getProxyJDK(BeanFactory.getBean("UserService"));
try {
userService.transfer(1,2,100d);
System.out.println("转账成功");
} catch (Exception e) {
System.out.println("转账失败");
}
}
}
到此代码完成!
我是Slience帝莘,期待与你的技术交流和思想碰撞。
闭关修炼180天--手写IOC和AOP(xml篇)的更多相关文章
- 闭关修炼180天--手写持久层框架(mybatis简易版)
闭关修炼180天--手写持久层框架(mybatis简易版) 抛砖引玉 首先先看一段传统的JDBC编码的代码实现: //传统的JDBC实现 public static void main(String[ ...
- 初学源码之——银行案例手写IOC和AOP
手写实现lOC和AOP 上一部分我们理解了loC和AOP思想,我们先不考虑Spring是如何实现这两个思想的,此处准备了一个『银行转账」的案例,请分析该案例在代码层次有什么问题?分析之后使用我们已有知 ...
- 手写IOC实现过程
一.手写ioc前基础知识 1.什么是IOC(Inversion of Control 控制反转)? IoC不是一种技术,只是一种思想,一个重要的面向对象编程的法则,它能指导我们如何设计出松耦合.更优良 ...
- SpringBoot项目里,让TKmybatis支持可以手写sql的Mapper.xml文件
SpringBoot项目通常配合TKMybatis或MyBatis-Plus来做数据的持久化. 对于单表的增删改查,TKMybatis优雅简洁,无需像传统mybatis那样在mapper.xml文件里 ...
- 手写IOC实践
一.IOC 1.什么是IOC? 控制反转(英语:Inversion of Control,缩写为IoC),是[面向对象编程]中的一种设计原则,可以用来减低计算机代码之间的[耦合度]其中最常见的方式叫做 ...
- 一文全解:利用谷歌深度学习框架Tensorflow识别手写数字图片(初学者篇)
笔记整理者:王小草 笔记整理时间2017年2月24日 原文地址 http://blog.csdn.net/sinat_33761963/article/details/56837466?fps=1&a ...
- 《四 spring源码》利用TransactionManager手写spring的aop
事务控制分类 编程式事务控制 自己手动控制事务,就叫做编程式事务控制. Jdbc代码: Conn.setAutoCommite(false); // 设置手动控制事务 Hibern ...
- 手写Promise看着一篇就足够了
目录 概要 博客思路 API的特性与手写源码 构造函数 then catch Promise.resolved Promise.rejected Promise.all Promise.race 概要 ...
- 第三节:工厂+反射+配置文件(手写IOC)对缓存进行管理。
一. 章前小节 在前面的两个章节,我们运用依赖倒置原则,分别对 System.Web.Caching.Cache和 System.Runtime.Cacheing两类缓存进行了封装,并形成了ICach ...
随机推荐
- 带你入门Camtasia Studio录像机软件
Camtasia软件和其他录制软件不同,不论是编辑功能还是制作功能还是其他功能方面都远远高于其他录制软件.那这边我们可以一起了解一下基础软件功能. 首先,我们在电脑端安装了软件以后,进行实际操作.在操 ...
- PowerPoint无法正常加载MathType的解决方法
MathType是一款十分便捷的数学公式编辑器,可以和很多办公软件和网站兼容使用,我们日常用的比较多的也就是Office和WPS,更具体的说是Word\Excel\PPT等等一系列办公常用软件. 不过 ...
- 【电子取证:FTK Imager篇】DD、E01系统镜像仿真
星河滚烫,人生有理想! ---[suy999] DD.E01系统镜像动态仿真 (一)使用到的软件 1.FTK Imager (v4.5.0.3) 2.VMware Workstation 15 P ...
- Mockito 结合 Springboot 进行应用测试
Spring Boot可以和大部分流行的测试框架协同工作:通过Spring JUnit创建单元测试:生成测试数据初始化数据库用于测试:Spring Boot可以跟BDD(Behavier Driven ...
- 【R语言入门】R语言中的变量与基本数据类型
说明 在前一篇中,我们介绍了 R 语言和 R Studio 的安装,并简单的介绍了一个示例,接下来让我们由浅入深的学习 R 语言的相关知识. 本篇将主要介绍 R 语言的基本操作.变量和几种基本数据类型 ...
- 推荐系统实践 0x0b 矩阵分解
前言 推荐系统实践那本书基本上就更新到上一篇了,之后的内容会把各个算法拿来当专题进行讲解.在这一篇,我们将会介绍矩阵分解这一方法.一般来说,协同过滤算法(基于用户.基于物品)会有一个比较严重的问题,那 ...
- 第10.5节 使用__all__定义Python模块导入白名单
一. 引言 <第10.4节 Python模块的弱封装机制>介绍了Python模块的的弱封装机制,除了使用弱封装机制来从一定程度上防止导入特定成员外,Python模块中还提供可另外一种类似白 ...
- PyQt学习随笔:Model/View开发时从Model相关类派生自定义类需要注意的问题
在<PyQt学习随笔:重写setData方法截获Model/View中视图数据项编辑的注意事项>介绍的方法,从Model相关类派生自定义类,通过重写setData方法以获取View中数据的 ...
- BlueCMS代码审计
BlueCMS版本号为:bluecms_v1.6_sp1 本地搭建环境后将源代码丢进seay源代码审计系统,开启本地web服务页面访问,大部分白盒+小部分黑盒审计 搭建好环境后第一步先检查是否有重装漏 ...
- 团队作业3_需求改进&系统设计
一.需求&原型改进 1.需求改进: (1)发现问题:通过发布问卷调查及收集整理的形式发现用户的新需求: (2)修改需求:考虑新增提醒用户未完成事件的功能. 附:用户调查问卷(如下) 调研途 ...