自定義@Service註解java
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface CustomService { String value() default ""; }
自定義@Autowired註解mysql
@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface CustomAutowired { }
自定義@Transactional註解spring
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface CustomTransactional { }
接下來,咱們看一下controller層、service層、dao層 代碼sql
- 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 + '}'; } }
註解解析類json
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容器功能;加上自定義事務註解,實現了事務功能。
- 若是須要視頻講解,私聊我,本人錄製了運行效果展現和代碼講解視頻。