自定义@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 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 beans = new ConcurrentHashMap<>();
public CustomApplicationContext(String packageName) {
this.packageName = packageName;
initBeans();
}
private void initBeans() {
List classList = getClasses(packageName);
findClassIsAddedCostomAnnotation(classList);
}
private List getClasses(String packageName) {
List classList = new ArrayList<>();
String packageDirName = packageName.replace(".", "/");
Enumeration 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 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 classList) {
try{
for (Class aClass : classList) {
classToObjectIntoBeans(aClass, classList);
}
}catch (Exception e){
e.printStackTrace();
}
}
private void classToObjectIntoBeans(Class aClass, List 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容器功能;加上自定义事务注解,实现了事务功能。
- 如果需要视频讲解,私聊我,本人录制了运行效果展示和代码讲解视频。