1. 手动编写注解事务
1.1 思路
1.2
//编程事务(需要手动begin 手动回滚 手都提交)
@Component()
@Scope("prototype") // 设置成原型解决线程安全
public class TransactionUtils {
private TransactionStatus transactionStatus;
// 获取事务源
@Autowired
private DataSourceTransactionManager dataSourceTransactionManager;
// 开启事务
public TransactionStatus begin() {
transactionStatus = dataSourceTransactionManager.getTransaction(new DefaultTransactionAttribute());
return transactionStatus;
}
// 提交事务
public void commit(TransactionStatus transaction) {
dataSourceTransactionManager.commit(transaction);
}
// 回滚事务
public void rollback() {
System.out.println("rollback");
dataSourceTransactionManager.rollback(transactionStatus);
}
}
注解类
@Autowired
private TransactionUtils transactionUtils;
@AfterThrowing("execution(* com.itmayiedu.service.*.*.*(..))")
public void afterThrowing() throws NoSuchMethodException, SecurityException {
// isRollback(proceedingJoinPoint);
System.out.println("程序发生异常");
// TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
// TransactionStatus currentTransactionStatus =
// TransactionAspectSupport.currentTransactionStatus();
// System.out.println("currentTransactionStatus:" +
// currentTransactionStatus);
transactionUtils.rollback();
}
// // 环绕通知 在方法之前和之后处理事情
@Around("execution(* com.itmayiedu.service.*.*.*(..))")
public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
// 调用方法之前执行
TransactionStatus transactionStatus = begin(proceedingJoinPoint);
proceedingJoinPoint.proceed();// 代理调用方法 注意点: 如果调用方法抛出异常不会执行后面代码
// 调用方法之后执行
commit(transactionStatus);
}
public TransactionStatus begin(ProceedingJoinPoint pjp) throws NoSuchMethodException, SecurityException {
// // 判断是否有自定义事务注解
ExtTransaction declaredAnnotation = getExtTransaction(pjp);
if (declaredAnnotation == null) {
return null;
}
// 如果有自定义事务注解,开启事务
System.out.println("开启事务");
TransactionStatus transactionStatu = transactionUtils.begin();
return transactionStatu;
}
public void commit(TransactionStatus transactionStatu) {
if (transactionStatu != null) {
// 提交事务
System.out.println("提交事务");
transactionUtils.commit(transactionStatu);
}
}
public ExtTransaction getExtTransaction(ProceedingJoinPoint pjp) throws NoSuchMethodException, SecurityException {
// 获取方法名称
String methodName = pjp.getSignature().getName();
// 获取目标对象
Class<?> classTarget = pjp.getTarget().getClass();
// 获取目标对象类型
Class<?>[] par = ((MethodSignature) pjp.getSignature()).getParameterTypes();
// 获取目标对象方法
Method objMethod = classTarget.getMethod(methodName, par);
// // 判断是否有自定义事务注解
ExtTransaction declaredAnnotation = objMethod.getDeclaredAnnotation(ExtTransaction.class);
if (declaredAnnotation == null) {
System.out.println("您的方法上,没有加入注解!");
return null;
}
return declaredAnnotation;
}
// 回滚事务
public void isRollback(ProceedingJoinPoint pjp) throws NoSuchMethodException, SecurityException {
// // 判断是否有自定义事务注解
ExtTransaction declaredAnnotation = getExtTransaction(pjp);
if (declaredAnnotation != null) {
System.out.println("已经开始回滚事务");
// 获取当前事务 直接回滚
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
return;
}
}
使用自定义注解
@ExtTransaction
public void add() {
userDao.add("test001", 20);
int i = 1 / 0;
System.out.println("################");
userDao.add("test002", 21);
}
2. 手写springmvc
2.1 SpringMVC的运行流程
⑴ 用户发送请求至前端控制器DispatcherServlet
⑵ DispatcherServlet收到请求调用HandlerMapping处理器映射器。
⑶ 处理器映射器根据请求url找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet。
⑷ DispatcherServlet通过HandlerAdapter处理器适配器调用处理器
⑸ 执行处理器(Controller,也叫后端控制器)。
⑹ Controller执行完成返回ModelAndView
⑺ HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet
⑻ DispatcherServlet将ModelAndView传给ViewReslover视图解析器
⑼ ViewReslover解析后返回具体View
⑽ DispatcherServlet对View进行渲染视图(即将模型数据填充至视图中)。
⑾ DispatcherServlet响应用户。
2.2 Servlet 生命周期
Servlet 加载—>实例化—>服务—>销毁。
init():
在Servlet的生命周期中,仅执行一次init()方法。它是在服务器装入Servlet时执行的,负责初始化Servlet对象。可以配置服务器,以在启动服务器或客户机首次访问Servlet时装入Servlet。无论有多少客户机访问Servlet,都不会重复执行init()。
service():
它是Servlet的核心,负责响应客户的请求。每当一个客户请求一个HttpServlet对象,该对象的Service()方法就要调用,而且传递给这个方法一个“请求”(ServletRequest)对象和一个“响应”(ServletResponse)对象作为参数。在HttpServlet中已存在Service()方法。默认的服务功能是调用与HTTP请求的方法相应的do功能。
destroy():
仅执行一次,在服务器端停止且卸载Servlet时执行该方法。当Servlet对象退出生命周期时,负责释放占用的资源。一个Servlet在运行service()方法时可能会产生其他的线程,因此需要确认在调用destroy()方法时,这些线程已经终止或完成。
1.3 手写SpringMVC思路
1.web.xml加载
为了读取web.xml中的配置,我们用到ServletConfig这个类,它代表当前Servlet在web.xml中的配置信息。通过web.xml中加载我们自己写的MyDispatcherServlet和读取配置文件。
2、初始化阶段
在前面我们提到DispatcherServlet的initStrategies方法会初始化9大组件,但是这里将实现一些SpringMVC的最基本的组件而不是全部,按顺序包括:
加载配置文件
扫描用户配置包下面所有的类
拿到扫描到的类,通过反射机制,实例化。并且放到ioc容器中(Map的键值对 beanName-bean) beanName默认是首字母小写
初始化HandlerMapping,这里其实就是把url和method对应起来放在一个k-v的Map中,在运行阶段取出
3、运行阶段
每一次请求将会调用doGet或doPost方法,所以统一运行阶段都放在doDispatch方法里处理,它会根据url请求去HandlerMapping中匹配到对应的Method,然后利用反射机制调用Controller中的url对应的方法,并得到结果返回。按顺序包括以下功能:
异常的拦截
获取请求传入的参数并处理参数
通过初始化好的handlerMapping中拿出url对应的方法名,反射调用
/**
* @DESC: 自定义DispatchServlet
* ### 1.创建一个前端控制器 ExtDispatcherServlet 拦截所有请求(springMvc 基于servlet实现)<br/>
* ### 2.出啊实话操作重写servlet init方法<br/>
* ###### 2.1 将扫包范围所有的类,注入到SpringMvc容器里,存放在Map集合中key为默认名小写,value对象<br/>
* ###### 2.2 将url映射和方法进行关联
* ########## 2.2.1 判断类上是否有注解,使用java反射机制循环遍历方法,判断方法上是否存在注解,进行封装url和方法对应存入集合中
* ### 3. 处理请求 重写get或post方法
* ###### 3.1 获取请求url,从urlBeans集合获取实例对象,获取成功实例对象后,调用urlMethods集合获取方法名称,使用反射执行
* @author: zhouben
* @date: 2019/10/31 0031 10:23
*/
public class ExtDispatchServlet extends HttpServlet {
// springmvc 容器对象 key:类名id ,value 对象
private ConcurrentHashMap<String, Object> beans = new ConcurrentHashMap<>();
// springmvc 容器对象 keya:请求地址 ,vlue类
private ConcurrentHashMap<String, Object> urlBeans = new ConcurrentHashMap<>();
// springmvc 容器对象 key:请求地址 ,value 方法名称
private ConcurrentHashMap<String, String> methodBeans = new ConcurrentHashMap<>();
@Override
public void init() throws ServletException {
//1.将扫包范围所有的类,注入到SpringMvc容器里,存放在Map集合中key为默认名小写,value对象
List<Class<?>> classes = ClassUtil.getClasses("com.zben.design.controller");
try {
findClassMVCAnnotation(classes);
//映射方法进行关联
handlerMapping();
} catch (Exception e) {
e.printStackTrace();
}
}
@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 {
// #################处理请求####################
//1.获取请求路径
String requestURI = req.getRequestURI();
//2.从Map集合中获取控制对象
Object obj = urlBeans.get(requestURI);
if (obj == null) {
resp.getWriter().print("not found 404 url");
return;
}
//3.使用url获取方法
String methodName = methodBeans.get(requestURI);
if (StringUtils.isEmpty(methodName)) {
resp.getWriter().print("not found method");
return;
}
//4. 通过反射机制调用方法
String resultPage = (String) invoke(obj, methodName);
//5. 调用视图解析器转发
extViewResovler(resultPage, req, resp);
}
private void extViewResovler(String pageName, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String prefix = "/";
String suffix = ".jsp";
request.getRequestDispatcher(prefix + pageName +suffix).forward(request, response);
}
private Object invoke(Object obj, String methodName) {
Class<?> clazz = obj.getClass();
try {
Method method = clazz.getDeclaredMethod(methodName);
Object result = method.invoke(obj);
return result;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
//将url映射和方法进行关联
private void handlerMapping() {
if (!beans.isEmpty()) {
for (Map.Entry<String, Object> entity : beans.entrySet()) {
Object value = entity.getValue();
Class<?> classes = value.getClass();
//判断类上是否有注解
ExtRequestMapping extRequestMapping = value.getClass().getDeclaredAnnotation(ExtRequestMapping.class);
String baseUrl = "";
if (extRequestMapping != null) {
//获取注解上的url
baseUrl = extRequestMapping.value();
}
//判断方法上是否有加url映射地址
Method[] declaredMethods = classes.getDeclaredMethods();
for (Method method : declaredMethods) {
//获取方法上是否有注解
ExtRequestMapping annotation = method.getDeclaredAnnotation(ExtRequestMapping.class);
if (annotation != null) {
String url = baseUrl + annotation.value();
// springmvc 容器对象 keya:请求地址 ,value类
urlBeans.put(url, value);
// springmvc 容器对象 key:请求地址 ,value 方法名称
methodBeans.put(url, method.getName());
}
}
}
}
}
//注入到SpringMvc容器里
private void findClassMVCAnnotation(List<Class<?>> classes) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
for (Class<?> classInfo : classes) {
ExtController extController = classInfo.getDeclaredAnnotation(ExtController.class);
//判断类上是否有次注解
if (extController != null) {
//注入到springmvc容器中
//转换成类名第一个字母小写
String beanId = ClassUtil.toLowerCaseFirstOne(classInfo.getSimpleName());
beans.put(beanId, ClassUtil.newInstance(classInfo));
}
}
}
}