手写@Service、@Autowired、@Transactional注解,实现spring ioc和spring事务
自定义@Service注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomService {
String value() default "";
}
自定义@Autowired注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomAutowired {
}
自定义@Transactional注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomTransactional {
}
接下来,我们看一下controller层、service层、dao层 代码
- controller层
@WebServlet(name="transferServlet",urlPatterns = "/transferServlet")
public class TransferServlet extends HttpServlet {
TransferService transferService;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
CustomApplicationContext customApplicationContext = new CustomApplicationContext("com.zhu.yuandi");
transferService = (TransferService) customApplicationContext.getBean("TransferService",customApplicationContext);
// 设置请求体的字符编码
req.setCharacterEncoding("UTF-8");
String fromCardNo = req.getParameter("fromCardNo");
String toCardNo = req.getParameter("toCardNo");
String moneyStr = req.getParameter("money");
int money = Integer.parseInt(moneyStr);
Result result = new Result();
try {
//调用service层方法
transferService.transfer(fromCardNo,toCardNo,money);
result.setStatus("200");
} catch (Exception e) {
e.printStackTrace();
result.setStatus("201");
result.setMessage(e.toString());
}
// 响应
resp.setContentType("application/json;charset=utf-8");
resp.getWriter().print(JsonUtils.object2Json(result));
}
}
- service层
public interface TransferService {
void transfer(String fromCardNo,String toCardNo,int money) throws Exception;
}
@CustomService(value = "transferService")
@CustomTransactional
public class TransferServiceImpl implements TransferService {
@CustomAutowired
private AccountDao accountDao;
@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);
//模拟异常
int c = 1/0;
accountDao.updateAccountByCardNo(from);
}
}
- dao层
public interface AccountDao {
Account queryAccountByCardNo(String cardNo) throws Exception;
int updateAccountByCardNo(Account account) throws Exception;
}
@CustomService
public class JdbcAccountDaoImpl implements AccountDao {
@CustomAutowired
private ConnectionUtils connectionUtils;
@Override
public Account queryAccountByCardNo(String cardNo) throws Exception {
Connection con = connectionUtils.getCurrentThreadConn();
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();
return account;
}
@Override
public int updateAccountByCardNo(Account account) throws Exception {
Connection con = connectionUtils.getCurrentThreadConn();
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();
return i;
}
}
开发中用到的数据库Utils、动态代理Utils等其他相关类
@CustomService
public class ConnectionUtils {
private ThreadLocal<Connection> threadLocal = new ThreadLocal<>(); // 存储当前线程的连接
/**
* 从当前线程获取连接
*/
public Connection getCurrentThreadConn() throws SQLException {
/**
* 判断当前线程中是否已经绑定连接,如果没有绑定,需要从连接池获取一个连接绑定到当前线程
*/
Connection connection = threadLocal.get();
if(connection == null) {
// 从连接池拿连接并绑定到线程
connection = DruidUtils.getInstance().getConnection();
// 绑定到当前线程
threadLocal.set(connection);
}
return connection;
}
}
public class DruidUtils {
private DruidUtils(){
}
private static DruidDataSource druidDataSource = new DruidDataSource();
static {
druidDataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
druidDataSource.setUrl("jdbc:mysql://localhost:3306/bank?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Hongkong");
druidDataSource.setUsername("root");
druidDataSource.setPassword("root");
}
public static DruidDataSource getInstance() {
return druidDataSource;
}
}
@CustomService
public class ProxyFactory {
@CustomAutowired
private TransactionManager transactionManager;
/**
* Jdk动态代理
* @param obj 委托对象
* @return 代理对象
*/
public Object getJdkProxy(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 = null;
try{
// 开启事务(关闭事务的自动提交)
transactionManager.beginTransaction();
result = method.invoke(obj,args);
// 提交事务
transactionManager.commit();
}catch (Exception e) {
e.printStackTrace();
// 回滚事务
transactionManager.rollback();
// 抛出异常便于上层servlet捕获
throw e;
}
return result;
}
});
}
}
@CustomService
public class TransactionManager {
@CustomAutowired
private ConnectionUtils connectionUtils;
// 开启手动事务控制
public void beginTransaction() throws SQLException {
connectionUtils.getCurrentThreadConn().setAutoCommit(false);
}
// 提交事务
public void commit() throws SQLException {
connectionUtils.getCurrentThreadConn().commit();
}
// 回滚事务
public void rollback() throws SQLException {
connectionUtils.getCurrentThreadConn().rollback();
}
}
public class Account {
private String cardNo;
private String name;
private int money;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getMoney() {
return money;
}
public void setMoney(int money) {
this.money = money;
}
public String getCardNo() { return cardNo; }
public void setCardNo(String cardNo) { this.cardNo = cardNo;}
@Override
public String toString() {
return "Account{" +
"cardNo='" + cardNo + '\'' +
", name='" + name + '\'' +
", money=" + money +
'}';
}
}
注解解析类
public class CustomApplicationContext {
//包名
private String packageName;
private ConcurrentHashMap<String, Object> beans = new ConcurrentHashMap<>();
public CustomApplicationContext(String packageName) {
this.packageName = packageName;
initBeans();
}
private void initBeans() {
List<Class> classList = getClasses(packageName);
findClassIsAddedCostomAnnotation(classList);
}
private List<Class> getClasses(String packageName) {
List<Class> classList = new ArrayList<>();
String packageDirName = packageName.replace(".", "/");
Enumeration<URL> dirs;
try {
dirs = Thread.currentThread().getContextClassLoader().getResources(packageDirName);
while (dirs.hasMoreElements()) {
URL url = dirs.nextElement();
String protocol = url.getProtocol();
if (protocol.equals("file")) {
String filePath = URLDecoder.decode(url.getFile(), "UTF-8");
findAndAddClassesInPackageByFile(packageName, filePath, classList);
}
}
} catch (Exception e) {
e.printStackTrace();
}
return classList;
}
private void findAndAddClassesInPackageByFile(String packageName, String filePath, List<Class> classList) {
File dir = new File(filePath);
//选出文件夹下面所有的文件
File[] files = dir.listFiles(new FileFilter() {
public boolean accept(File file) {
return (file.isDirectory() || file.getName().endsWith(".class"));
}
});
for (File file : files) {
if (file.isDirectory()) {
findAndAddClassesInPackageByFile(packageName + "." + file.getName(), file.getAbsolutePath(), classList);
} else {
String className = file.getName().substring(0, file.getName().length() - 6);
try {
classList.add(Class.forName(packageName + "." + className));
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
private void findClassIsAddedCostomAnnotation(List<Class> classList) {
try{
for (Class aClass : classList) {
classToObjectIntoBeans(aClass, classList);
}
}catch (Exception e){
e.printStackTrace();
}
}
private void classToObjectIntoBeans(Class aClass, List<Class> classList) {
Object obj = null;
try{
if(aClass.isInterface()){
for(Class implClass : classList) {
if (implClass.isInterface()) {
continue;
}
Class fieldClassCopy = implClass.getClassLoader().loadClass(aClass.getName());
if (fieldClassCopy.isAssignableFrom(implClass)) {
Constructor[] constructors = implClass.getConstructors();
for(Constructor constructor : constructors){
int parameterCount = constructor.getParameterCount();
if(parameterCount==0){
obj = constructor.newInstance();
}
}
break;
}
}
} else {
Constructor[] constructors = aClass.getConstructors();
for(Constructor constructor : constructors){
int parameterCount = constructor.getParameterCount();
if(parameterCount==0){
obj = constructor.newInstance();
}
}
}
if (obj != null) {
beans.put(aClass.getSimpleName(), obj);
}
}catch (Exception e){
e.printStackTrace();
}
}
public Object getBean(String beanName,CustomApplicationContext customApplicationContext) {
Object beanObject = beans.get(beanName);
try{
referenceBindObject(beanObject);
}catch (Exception e){
e.printStackTrace();
}
Class aClass = beanObject.getClass();
Annotation annotation = aClass.getAnnotation(CustomTransactional.class);
if (annotation != null) {
ProxyFactory proxyFactory = (ProxyFactory) customApplicationContext.getBean("ProxyFactory", customApplicationContext);
beanObject = proxyFactory.getJdkProxy(beanObject);
}
return beanObject;
}
private Object referenceBindObject(Object beanObject) {
Class beanClass = beanObject.getClass();
Field[] declaredFields = beanClass.getDeclaredFields();
try {
for (Field field : declaredFields) {
if (!field.isAccessible()) {
field.setAccessible(true);
}
CustomAutowired filedAnnotation = field.getAnnotation(CustomAutowired.class);
if (filedAnnotation == null) {
System.out.println(beanClass.getName() + "类中的" + field.getName() + "字段,该字段上没有加注解");
break;
}
Class fieldClass = field.getType();
String classSimpleName = fieldClass.getSimpleName();
Object fieldObject = beans.get(classSimpleName);
Object object = referenceBindObject(fieldObject);
field.set(beanObject, object);
}
} catch (Exception e) {
e.printStackTrace();
}
return beanObject;
}
}
- 以上,我们完成了自定义注解,实现了spring ioc容器功能;加上自定义事务注解,实现了事务功能。
- 如果需要视频讲解,私聊我,本人录制了运行效果展示和代码讲解视频。
手写@Service、@Autowired、@Transactional注解,实现spring ioc和spring事务的更多相关文章
- spring揭密学习笔记(3)-spring ioc容器:Spring的IoC容器之BeanFactory
1. Spring的IoC容器和IoC Service Provider的关系 Spring的IoC容器和IoC Service Provider所提供的服务之间存在一定的交集,二者的关系如图4-1所 ...
- Spring IOC和Spring AOP的实现原理(源码主线流程)
写在前面 正本文参考了<spring技术内幕>和spring 4.0.5源码.本文只描述原理流程的主线部分,其他比如验证,缓存什么可以具体参考源码理解. Spring IOC 一.容器初始 ...
- 【Spring IoC】Spring Bean(三)
一.Spring Bean的定义 被称作 bean 的对象是构成应用程序的支柱也是由 Spring IoC 容器管理的.bean 是一个被实例化,组装,并通过 Spring IoC 容器所管理的对象. ...
- Spring Boot中Service用@Transactional 注解
一般来说function2和function1用的是同一个Transaction. 这个取决于@Transactional 的 propagation设置(事务的传播性) 默认的是 1 @Transa ...
- spring ioc 原理 spring aop原理
大家一直都说spring的IOC如何如何的强大,其实我倒觉得不是IOC如何的强大,说白了IOC其实也非常的简单.我们先从IOC说起,这个概念其实是从我们平常new一个对象的对立面来说的,我们平常使用对 ...
- Spring - IoC(1): Spring 容器
BeanFactory & ApplicationContext org.springframework.beans.factory.BeanFactory 是最基本的 Spring 容器接口 ...
- spring IOC(Spring 生命周期,先1.构造方式,2,初始化方法,3,目标方法,4,销毁方法)
- Spring Boot 中使用 @Transactional 注解配置事务管理
事务管理是应用系统开发中必不可少的一部分.Spring 为事务管理提供了丰富的功能支持.Spring 事务管理分为编程式和声明式的两种方式.编程式事务指的是通过编码方式实现事务:声明式事务基于 AOP ...
- Spring对于事务的控制@Transactional注解详解
引用自:https://blog.csdn.net/fanxb92/article/details/81296005 先简单介绍一下Spring事务的传播行为: 所谓事务的传播行为是指,如果在开始当前 ...
随机推荐
- UOJ 422 [集训队作业2018] 小Z的礼物 min-max容斥 期望 轮廓线dp
LINK:小Z的礼物 太精髓了 我重学了一遍min-max容斥 重写了一遍按位或才写这道题的. 还是期望多少时间可以全部集齐. 相当于求出 \(E(max(S))\)表示最后一个出现的期望时间. 根据 ...
- bzoj 3790 神奇项链 回文串 manacher|PAM
LINK:神奇项链 存在两个操作:1. 一个操作可以生成所有形式的回文串 2.一个操作可以将两个串给合并起来 如果前缀和后缀相同还可以将其并起来. 多组询问 每次询问合成一个串所需最少多少次2操作. ...
- windows:shellcode 代码远程APC注入和加载
https://www.cnblogs.com/theseventhson/p/13197776.html 上一章介绍了通用的shellcode加载器,这个加载器自己调用virtualAlloc分配 ...
- 使用nexus搭建maven私库
什么是nexus? nexus是一个maven仓库管理器,使用nexus可以快速便捷的搭建自己的maven私有仓库. docker安装nexus 拉取镜像 docker pull sonatype/n ...
- 自定制格式化方式format
自定制格式化方式format # x='{0}{0}{0}'.format('dog') # # print(x) # class Date: # def __init__(self,year,mon ...
- 利用Python实现定时发送邮件,实现一款营销工具
说起自动化绝对算是茶余饭后最有显B格的谈资,毕竟解放双手是从老祖先那里就流传下来的基因,都2020了,你每天上班还要登录各个邮箱账号查收邮件?快来解锁本章内容 整体思路 很多人学习python,不知道 ...
- Ant Design Pro V5 从服务器请求菜单(typescript版)
[前言] 找了很多Admin模板,最后还是看中了AntDesignPro(下文简写antd pro)这个阿里巴巴开源的Admin框架,长这样(还行吧,目前挺主流的): 官网地址:https://pro ...
- C#LeetCode刷题之#59-螺旋矩阵 II(Spiral Matrix II)
目录 问题 示例 分析 问题 该文章的最新版本已迁移至个人博客[比特飞],单击链接 https://www.byteflying.com/archives/3678 访问. 给定一个正整数 n,生成一 ...
- SpringBoot整合Spring Security
好好学习,天天向上 本文已收录至我的Github仓库DayDayUP:github.com/RobodLee/DayDayUP,欢迎Star,更多文章请前往:目录导航 前言 Spring Securi ...
- (数据科学学习手札93)利用geopandas与PostGIS进行交互
本文完整代码及数据已上传至我的Github仓库https://github.com/CNFeffery/DataScienceStudyNotes 1 简介 PostGIS作为postgresql针对 ...