要求
自定义@Service、@Autowired、@Transactional注解类,完成基于注解的IOC容器(Bean对象创建及依赖注入维护)和声明式事务控制,写到转账工程中,并且可以实现转账成功和转账异常时事务回滚
注意考虑以下情况:
1)注解有无value属性值【@service(value="") @Repository(value="")】
2)service层是否实现接口的情况【jdk还是cglib】
实现
1. 依赖配置
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<dependencies>
<!-- 单元测试Junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!-- mysql数据库驱动包 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.35</version>
</dependency>
<!--druid连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.21</version>
</dependency>
<!-- servlet -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<!-- jackson依赖 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.6</version>
</dependency>
<!--dom4j依赖-->
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>1.6.1</version>
</dependency>
<!--xpath表达式依赖-->
<dependency>
<groupId>jaxen</groupId>
<artifactId>jaxen</artifactId>
<version>1.1.6</version>
</dependency>
<!--引入cglib依赖包-->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.1_2</version>
</dependency>
<dependency>
<groupId>org.reflections</groupId>
<artifactId>reflections</artifactId>
<version>0.9.11</version>
</dependency>
</dependencies>
<build>
<plugins>
<!-- 配置Maven的JDK编译级别 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.2</version>
<configuration>
<source>11</source>
<target>11</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<!-- tomcat7插件 -->
<!-- 注意:目前来说,maven中央仓库还没有tomcat8的插件 -->
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<port>8080</port>
<path>/</path>
</configuration>
</plugin>
</plugins>
</build>
</project>
2. 自定义注解
/**
* 自定义注解@Autowired
* @author lane
* @date 2021年03月28日 下午6:37
*/
@Target(ElementType.FIELD) //作用于字段
@Retention(RetentionPolicy.RUNTIME)
public @interface Autowired {
boolean required() default true;
}
/**
* 自定义注解@Componet
* @author lane
* @date 2021年03月28日 下午6:37
*/
@Target(ElementType.TYPE) //用于描述类、接口(包括注解类型) 或enum声明
@Retention(RetentionPolicy.RUNTIME) //运行时有效
public @interface Componet {
String value() default "";
}
/**
* 自定义注解@Repository
* @author lane
* @date 2021年03月28日 下午6:37
*/
@Target(ElementType.TYPE) //用于描述类、接口(包括注解类型) 或enum声明
@Retention(RetentionPolicy.RUNTIME) //运行时有效
public @interface Repository {
String value() default "";
}
/**
* 自定义注解@Service
* @author lane
* @date 2021年03月28日 下午6:37
*/
@Target(ElementType.TYPE) //用于描述类、接口(包括注解类型) 或enum声明
@Retention(RetentionPolicy.RUNTIME) //运行时有效
public @interface Service {
String value() default "";
}
/**
* 自定义注解@Transactional
* @author lane
* @date 2021年03月28日 下午6:37
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Transactional {
String value() default "TransactionManager";
}
3. 数据库连接工具类
3.1 连接池DruidUtils
package com.lagou.edu.utils;
import com.alibaba.druid.pool.DruidDataSource;
/**
* @author
*/
public class DruidUtils {
private DruidUtils(){
}
private static DruidDataSource druidDataSource = new DruidDataSource();
static {
druidDataSource.setDriverClassName("com.mysql.jdbc.Driver");
druidDataSource.setUrl("jdbc:mysql://localhost:3306/bank");
druidDataSource.setUsername("root");
druidDataSource.setPassword("root");
}
public static DruidDataSource getInstance() {
return druidDataSource;
}
}
3.2 连接ConnectionUtils
package com.lagou.edu.utils;
import com.lagou.edu.annotation.Service;
import java.sql.Connection;
import java.sql.SQLException;
/**
* @author
*/
@Service
public class ConnectionUtils {
/*private ConnectionUtils() {
}
private static ConnectionUtils connectionUtils = new ConnectionUtils();
public static ConnectionUtils getInstance() {
return 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;
}
}
3.3 事务管理器TransactionManager
package com.lagou.edu.utils;
import com.lagou.edu.annotation.Autowired;
import com.lagou.edu.annotation.Service;
import java.sql.SQLException;
/**
* @author
*
* 事务管理器类:负责手动事务的开启、提交、回滚
*/
@Service
public class TransactionManager {
@Autowired
private ConnectionUtils connectionUtils;
public void setConnectionUtils(ConnectionUtils connectionUtils) {
this.connectionUtils = connectionUtils;
}
/*private TransactionManager(){
}
private static TransactionManager transactionManager = new TransactionManager();
public static TransactionManager getInstance() {
return transactionManager;
}*/
// 开启手动事务控制
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();
}
}
3.4 JSON转换工具类
package com.lagou.edu.utils;
import java.util.List;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
* JSON工具类(使用的是jackson实现的)
* @author
*/
public class JsonUtils {
private static final ObjectMapper MAPPER = new ObjectMapper();
/**
* 将对象转换成json字符串。
* @param data
* @return
*/
public static String object2Json(Object data) {
try {
String string = MAPPER.writeValueAsString(data);
return string;
} catch (JsonProcessingException e) {
e.printStackTrace();
}
return null;
}
/**
* 将json结果集转化为对象
*
* @param jsonData json数据
* @param beanType 对象中的object类型
* @return
*/
public static <T> T json2Pojo(String jsonData, Class<T> beanType) {
try {
T t = MAPPER.readValue(jsonData, beanType);
return t;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 将json数据转换成pojo对象list
* @param jsonData
* @param beanType
* @return
*/
public static <T>List<T> json2List(String jsonData, Class<T> beanType) {
JavaType javaType = MAPPER.getTypeFactory().constructParametricType(List.class, beanType);
try {
List<T> list = MAPPER.readValue(jsonData, javaType);
return list;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
4. Pojo类
package com.lagou.edu.pojo;
/**
* @author
*/
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 +
'}';
}
}
package com.lagou.edu.pojo;
/**
* @author
*/
public class Result {
private String status;
private String message;
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
@Override
public String toString() {
return "Result{" +
"status='" + status + '\'' +
", message='" + message + '\'' +
'}';
}
}
5. 持久层Dao
5.1 接口
/**
* @author
*/
public interface AccountDao {
Account queryAccountByCardNo(String cardNo) throws Exception;
int updateAccountByCardNo(Account account) throws Exception;
}
5.2 实现类
package com.lagou.edu.dao.impl;
import com.lagou.edu.annotation.Autowired;
import com.lagou.edu.annotation.Repository;
import com.lagou.edu.annotation.Service;
import com.lagou.edu.pojo.Account;
import com.lagou.edu.dao.AccountDao;
import com.lagou.edu.utils.ConnectionUtils;
import com.lagou.edu.utils.DruidUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
/**
* @author
*/
@Service("AccountDao")
@Repository("AccountDao")
public class JdbcAccountDaoImpl implements AccountDao {
@Autowired
private ConnectionUtils connectionUtils;
public void setConnectionUtils(ConnectionUtils connectionUtils) {
this.connectionUtils = connectionUtils;
}
public void init() {
System.out.println("初始化方法.....");
}
public void destory() {
System.out.println("销毁方法......");
}
@Override
public Account queryAccountByCardNo(String cardNo) throws Exception {
//从连接池获取连接
// Connection con = DruidUtils.getInstance().getConnection();
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();
//con.close();
return account;
}
@Override
public int updateAccountByCardNo(Account account) throws Exception {
// 从连接池获取连接
// 改造为:从当前线程当中获取绑定的connection连接
//Connection con = DruidUtils.getInstance().getConnection();
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();
//con.close();
return i;
}
}
6. 业务层service
6.1 接口
public interface TransferService {
public void setAccountDao(AccountDao accountDao);
void transfer(String fromCardNo,String toCardNo,int money) throws Exception;
}
6.2 实现类
package com.lagou.edu.service.impl;
import com.lagou.edu.annotation.Autowired;
import com.lagou.edu.annotation.Service;
import com.lagou.edu.annotation.Transactional;
import com.lagou.edu.dao.AccountDao;
import com.lagou.edu.pojo.Account;
import com.lagou.edu.service.TransferService;
import com.lagou.edu.utils.ConnectionUtils;
import com.lagou.edu.utils.TransactionManager;
/**
* @author
*/
@Service("TransferService")
@Transactional
public class TransferServiceImpl implements TransferService {
//private AccountDao accountDao = new JdbcAccountDaoImpl();
// private AccountDao accountDao = (AccountDao) BeanFactory.getBean("accountDao");
// 最佳状态
@Autowired
private AccountDao accountDao;
// 构造函数传值/set方法传值
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
@Override
public void transfer(String fromCardNo, String toCardNo, int money) throws Exception {
/*try{
// 开启事务(关闭事务的自动提交)
TransactionManager.getInstance().beginTransaction();*/
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);
/* // 提交事务
TransactionManager.getInstance().commit();
}catch (Exception e) {
e.printStackTrace();
// 回滚事务
TransactionManager.getInstance().rollback();
// 抛出异常便于上层servlet捕获
throw e;
}*/
}
}
7. 核心工厂类
7.1 动态代理工厂类
package com.lagou.edu.factory;
import com.lagou.edu.annotation.Autowired;
import com.lagou.edu.annotation.Service;
import com.lagou.edu.pojo.Account;
import com.lagou.edu.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;
/**
* @author
*
*
* 代理对象工厂:生成代理对象的
*/
@Service
public class ProxyFactory {
@Autowired
private TransactionManager transactionManager;
public void setTransactionManager(TransactionManager transactionManager) {
this.transactionManager = transactionManager;
}
//
//
// private static ProxyFactory proxyFactory = new ProxyFactory();
//
// public static ProxyFactory getInstance() {
// return proxyFactory;
// }
/**
* 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;
}
});
}
/**
* 使用cglib动态代理生成代理对象
* @param obj 委托对象
* @return
*/
public Object getCglibProxy(Object obj) {
return Enhancer.create(obj.getClass(), new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
Object result = null;
try{
// 开启事务(关闭事务的自动提交)
transactionManager.beginTransaction();
System.out.println("开启事务");
result = method.invoke(obj,objects);
// 提交事务
System.out.println("提交事务");
transactionManager.commit();
}catch (Exception e) {
e.printStackTrace();
// 回滚事务
transactionManager.rollback();
System.out.println("回滚事务");
// 抛出异常便于上层servlet捕获
throw e;
}
return result;
}
});
}
}
7.2 核心注解解析工厂类
package com.lagou.edu.factory;
import com.alibaba.druid.util.StringUtils;
import com.lagou.edu.annotation.Autowired;
import com.lagou.edu.annotation.Service;
import com.lagou.edu.annotation.Transactional;
import com.lagou.edu.dao.AccountDao;
import com.lagou.edu.service.TransferService;
import org.reflections.Reflections;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/**
* 工厂类,生产对象(使用注解反射技术)
* @author lane
* @date 2021年03月28日 下午6:59
*/
public class BeanFactoryAnnotation {
/**
* 任务一:读取解析anno,通过反射技术实例化对象并且存储待用(map集合)
* 任务二:对外提供获取实例对象的接口(根据id获取)
*/
//仿照xml ioc思想存储对象的集合
private static Map<String,Object> map = new HashMap<>();
//解析anno,生成对象存入map当中
static{
try{
//反射扫描包下所有的对象
Reflections reflections = new Reflections("com.lagou.edu");
//获取所有的注解class对象
Set<Class<?>> clazzs = reflections.getTypesAnnotatedWith(Service.class);
//遍历生成每一个对象
for (Class clazz:clazzs ) {
Object obj = clazz.newInstance();
Service service = (Service) clazz.getAnnotation(Service.class);
//判断是否在注解上加上自己的value值
if (StringUtils.isEmpty(service.value())){
String[] className = clazz.getName().split("\\.");
map.put(className[className.length-1],obj);
}else{
//使用自己定义的ID值
map.put(service.value(),obj);
}
}
// 实例化完成之后维护对象的依赖关系,检查哪些对象需要传值进入,根据它的配置Autowired注解,我们传入相应的值
for (Map.Entry<String,Object> entry:map.entrySet()) {
Object object = entry.getValue();
Class clazz = object.getClass();
//获取每一个属性
Field[] declaredFields = clazz.getDeclaredFields();
//遍历每一个属性,判断是否有Autowired注解
for (Field field:declaredFields) {
//判断是否使用注解
if(field.isAnnotationPresent(Autowired.class) &&field.getAnnotation(Autowired.class).required()){
//获取要注入的类
String[] fieldNames = field.getType().getName().split("\\.");
String fieldName = fieldNames[fieldNames.length-1];
Method[] methods = clazz.getMethods();
//遍历查找该属性是否有set方法,若有则调用注入该属性对象
for (Method method:methods) {
if (method.getName().equalsIgnoreCase("set"+fieldName)){
Object o = map.get(fieldName);
System.out.println(o.toString());
method.invoke(object,o);
}
}
}
}
//判断该类是否有@Transactional注解,有则用代理对象替换掉原来的对象
if (clazz.isAnnotationPresent(Transactional.class)){
ProxyFactory proxyFactory = (ProxyFactory)map.get("ProxyFactory");
Class[] interfaces = clazz.getInterfaces();
if(interfaces!=null&interfaces.length>0){
//jdk动态代理
object = proxyFactory.getJdkProxy(object);
}else {
//cglib动态代理
object = proxyFactory.getCglibProxy(object);
}
}
//注入,代理之后对象重新存放入map内
map.put(entry.getKey(),object);
}
}catch (Exception e){
e.printStackTrace();
}
}
//对外提供根据名称获取bean对象方法
public static Object getBeanAnno(String beanName){
return map.get(beanName);
}
}
8 测试用servlet
package com.lagou.edu.servlet;
import com.lagou.edu.annotation.Autowired;
import com.lagou.edu.factory.BeanFactory;
import com.lagou.edu.factory.BeanFactoryAnnotation;
import com.lagou.edu.factory.ProxyFactory;
import com.lagou.edu.service.impl.TransferServiceImpl;
import com.lagou.edu.utils.JsonUtils;
import com.lagou.edu.pojo.Result;
import com.lagou.edu.service.TransferService;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @author
*/
@WebServlet(name="transferServlet",urlPatterns = "/transferServlet")
public class TransferServlet extends HttpServlet {
// 1. 实例化service层对象
//private TransferService transferService = new TransferServiceImpl();
//private TransferService transferService = (TransferService) BeanFactory.getBean("transferService");
// 从工厂获取委托对象(委托对象是增强了事务控制的功能)
// 首先从BeanFactory获取到proxyFactory代理工厂的实例化对象
//public ProxyFactory proxyFactory = (ProxyFactory) BeanFactory.getBean("proxyFactory");
// public TransferService transferService = (TransferService) proxyFactory.getJdkProxy(BeanFactory.getBean("transferService")) ;
//使用工厂方法获取对象
public TransferService transferService = (TransferService)BeanFactoryAnnotation.getBeanAnno("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 {
// 设置请求体的字符编码
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 {
// 2. 调用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));
}
}
前端html
<!doctype html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>转账汇款</title>
<script type="text/javascript" src="js/jquery-3.4.1.min.js"></script>
<style type="text/css">
body {
background-color:#00b38a;
text-align:center;
}
.lp-login {
position:absolute;
width:500px;
height:300px;
top:50%;
left:50%;
margin-top:-250px;
margin-left:-250px;
background: #fff;
border-radius: 4px;
box-shadow: 0 0 10px #12a591;
padding: 57px 50px 35px;
box-sizing: border-box
}
.lp-login .submitBtn {
display:block;
text-decoration:none;
height: 48px;
width: 150px;
line-height: 48px;
font-size: 16px;
color: #fff;
text-align: center;
background-image: -webkit-gradient(linear, left top, right top, from(#09cb9d), to(#02b389));
background-image: linear-gradient(90deg, #09cb9d, #02b389);
border-radius: 3px
}
input[type='text'] {
height:30px;
width:250px;
}
span {
font-style: normal;
font-variant-ligatures: normal;
font-variant-caps: normal;
font-variant-numeric: normal;
font-variant-east-asian: normal;
font-weight: normal;
font-stretch: normal;
font-size: 14px;
line-height: 22px;
font-family: "Hiragino Sans GB", "Microsoft Yahei", SimSun, Arial, "Helvetica Neue", Helvetica;
}
</style>
<script type="text/javascript">
$(function(){
$(".submitBtn").bind("click",function(){
var fromAccount = $("#fromAccount").val();
var toAccount = $("#toAccount").val();
var money = $("#money").val();
if(money == null || $.trim(money).length == 0){
alert("sorry,必须输入转账金额~");
return;
}
$.ajax({
url:'/transferServlet',
type:'POST', //GET
async:false, //或false,是否异步
data:{
fromCardNo:fromAccount.split(' ')[1],
toCardNo:toAccount.split(' ')[1],
money:money
},
timeout:5000, //超时时间
dataType:'json', //返回的数据格式:json/xml/html/script/jsonp/text
success:function(data){
if("200" == data.status){
alert("转账成功~~~");
}else{
alert("转账失败~~~,message:" + data.message);
}
}
})
})
})
//检查输入值是否为整数
function checkFormat(obj){
var reg = /^[0-9]+[0-9]*]*$/;
if($.trim($(obj).val()).length>0){
if(!reg.test($(obj).val())){
alert("输入格式错误!请输整数!");
$(obj).val("");
}else{
$(obj).val(parseInt($(obj).val()));
}
}
}
</script>
</head>
<body>
<form>
<table class="lp-login">
<tr>
<td align="right"><span>收款账户</span></td>
<td align="center">
<input type="text" id="toAccount" value="韩梅梅 6029621011001" disabled></input>
</td>
</tr>
<tr>
<td align="right"><span>付款账户</span></td>
<td align="center">
<input type="text" id="fromAccount" value="李大雷 6029621011000" disabled></input>
</td>
</tr>
<tr>
<td align="right"><span>转账金额</span></td>
<td align="center">
<input type="text" id="money" onblur="checkFormat(this)"></input>
</td>
</tr>
<tr align="center">
<td colspan="2">
<a href="javasrcipt:void(0)" class="submitBtn"><span>转 出</span></a>
</td>
</tr>
</table>
</form>
</body>
</html>
总结
注解实现IOC是在手写实现Spring的IOC基础上改编而成,有大量的代码被注释掉了