自定义@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事务的更多相关文章

  1. spring揭密学习笔记(3)-spring ioc容器:Spring的IoC容器之BeanFactory

    1. Spring的IoC容器和IoC Service Provider的关系 Spring的IoC容器和IoC Service Provider所提供的服务之间存在一定的交集,二者的关系如图4-1所 ...

  2. Spring IOC和Spring AOP的实现原理(源码主线流程)

    写在前面 正本文参考了<spring技术内幕>和spring 4.0.5源码.本文只描述原理流程的主线部分,其他比如验证,缓存什么可以具体参考源码理解. Spring IOC 一.容器初始 ...

  3. 【Spring IoC】Spring Bean(三)

    一.Spring Bean的定义 被称作 bean 的对象是构成应用程序的支柱也是由 Spring IoC 容器管理的.bean 是一个被实例化,组装,并通过 Spring IoC 容器所管理的对象. ...

  4. Spring Boot中Service用@Transactional 注解

    一般来说function2和function1用的是同一个Transaction. 这个取决于@Transactional 的 propagation设置(事务的传播性) 默认的是 1 @Transa ...

  5. spring ioc 原理 spring aop原理

    大家一直都说spring的IOC如何如何的强大,其实我倒觉得不是IOC如何的强大,说白了IOC其实也非常的简单.我们先从IOC说起,这个概念其实是从我们平常new一个对象的对立面来说的,我们平常使用对 ...

  6. Spring - IoC(1): Spring 容器

    BeanFactory & ApplicationContext org.springframework.beans.factory.BeanFactory 是最基本的 Spring 容器接口 ...

  7. spring IOC(Spring 生命周期,先1.构造方式,2,初始化方法,3,目标方法,4,销毁方法)

  8. Spring Boot 中使用 @Transactional 注解配置事务管理

    事务管理是应用系统开发中必不可少的一部分.Spring 为事务管理提供了丰富的功能支持.Spring 事务管理分为编程式和声明式的两种方式.编程式事务指的是通过编码方式实现事务:声明式事务基于 AOP ...

  9. Spring对于事务的控制@Transactional注解详解

    引用自:https://blog.csdn.net/fanxb92/article/details/81296005 先简单介绍一下Spring事务的传播行为: 所谓事务的传播行为是指,如果在开始当前 ...

随机推荐

  1. PHP date_timezone_get() 函数

    ------------恢复内容开始------------ 实例 返回给定 DateTime 对象的时区: <?php$date=date_create(null,timezone_open( ...

  2. PHP str_getcsv() 函数

    定义和用法 str_getcsv() 函数解析 CSV 格式字段的字符串,并返回一个包含所读取字段的数组. 语法 str_getcsv(string,separator,enclosure,escap ...

  3. 构建自己的js库

    一.背景 web前端开发人员经常会用到一些现成的js库(框架).框架的使用增加了代码的模块化和可复用性,最主要的是屏蔽了浏览器之间差异性的实现,使得代码更加简洁,框架使用者只需要将注意力放在业务的实现 ...

  4. .Net Core中简单使用MongoDB

    MongoDB 是由C++语言编写的,是一个基于分布式且面向文档存储的开源数据库系统. 下载地址: https://www.mongodb.com/download-center/community ...

  5. 您能解决这3个(看似)简单的Python问题吗?

    尝试解决以下问题,然后检查以下答案. 很多人学习python,不知道从何学起.很多人学习python,掌握了基本语法过后,不知道在哪里寻找案例上手.很多已经做案例的人,却不知道如何去学习更加高深的知识 ...

  6. C#LeetCode刷题之#342-4的幂(Power of Four)

    问题 该文章的最新版本已迁移至个人博客[比特飞],单击链接 https://www.byteflying.com/archives/4058 访问. 给定一个整数 (32 位有符号整数),请编写一个函 ...

  7. golang bool值

    目录 前言 1.基本介绍 2.类型转换 3.使用: 跳转 前言 不做文字的搬运工,多做灵感性记录 这是平时学习总结的地方,用做知识库 平时看到其他文章的相关知识,也会增加到这里 随着学习深入,会进行知 ...

  8. 使用 .NET Core 3.x 构建 RESTFUL Api (续)

    关于Entity Model vs 面向外部的Model Entity Framework Core 使用 Entity Model 用来表示数据库里面的记录. 面向外部的Model 则表示要传输的东 ...

  9. 图的DFS与BFS

    图的DFS与BFS(C++) 概述 大一学生,作为我的第一篇Blog,准备记录一下图的基本操作:图的创建与遍历.请大佬多多包涵勿喷. 图可以采用邻接表,邻接矩阵,十字链表等多种储存结构进行储存,这里为 ...

  10. 调试备忘录-nRF24L01P的使用(教程 + 源码)

    目录--点击可快速直达 MCU:KEAZ64A MDK:CodeWarrior 11.0 目录 写在前面 什么是nRF24L01P? nRF24L01P模块的简单介绍 nRF24L01P的工作模式 n ...