手写spring事务框架-蚂蚁课堂
1、视频参加C:\Users\Administrator\Desktop\蚂蚁3期\【www.zxit8.com】 0017-(每特教育&每特学院&蚂蚁课堂)-3期-源码分析-手写Spring事务框架\0017-(每特教育&每特学院&蚂蚁课堂)-3期-源码分析-手写Spring事务框架
aop最主要的目的是解决 代码复用问题,例如现在一个controller类中存在1000个方法,我们要统计每个方法的执行时间,如果给每个方法都编写代码,代码就要重复写1000次,使用aop就只需要写一次就可以了
spring事务是基于aop的@rouand环绕通知实现的,在实际执行方法之前开启事务,在实际方法执行之后提交事务,本质是就是环绕通知的另外一种实现形式
AOP是OOP的延续,是(Aspect Oriented Programming)的缩写,意思是面向切面(方面)编程。
主要的功能是:日志记录,性能统计,安全控制,事务处理,异常处理等等。
主要的意图是:将日志记录,性能统计,安全控制,事务处理,异常处理等代码从业务逻辑代码中划分出来,通过对这些行为的分离,我们希望可以将它们独立到非指导业务逻辑的方法中,进而改 变这些行为的时候不影响业务逻辑的代码。
可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术。AOP实际是GoF设计模式的延续,设计模式孜孜不倦追求的是调用者和被调用者之间的解耦,AOP可以说也是这种目标的一种实现。
假设把应用程序想成一个立体结构的话,OOP的利刃是纵向切入系统,把系统划分为很多个模块(如:用户模块,文章模块等等),而AOP的利刃是横向切入系统,提取各个模块可能都要重复操作的部分(如:权限检查,日志记录等等)。由此可见,AOP是OOP的一个有效补充。
注意:AOP不是一种技术,实际上是编程思想。凡是符合AOP思想的技术,都可以看成是AOP的实现。
Aop, aspect object programming 面向切面编程
功能: 让关注点代码与业务代码分离!
Aop本质是就是解决代码复用的问题,代码不用重复写多次
spring是基于aop实现的,aop底层是通过动态代码实现的
动态代理分为jdk动态代理和cGlib代理,不清楚的看自己的博客,默认情况下推荐使用cGLIB的方法效率高,jdk、动态底层基于反射,性能测试的时候cpu会出现占用率过大的性能问题
spring框架中JDK和CGLIB动态代理区别
代理模式应用场景
SpringAOP、事物原理、日志打印、权限控制、远程调用、安全代理 可以隐蔽真实角色
代理的分类
静态代理(静态定义代理类)
动态代理(动态生成代理类)
Jdk自带动态代理
Cglib 、javaassist(字节码操作库)
静态代理
什么是静态代理
由程序员创建或工具生成代理类的源码,再编译代理类。所谓静态也就是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了。
静态代理代码
public interface IUserDao { void save(); } public class UserDao implements IUserDao { public void save() { System.out.println("已经保存数据..."); } } 代理类 public class UserDaoProxy implements IUserDao { private IUserDao target; public UserDaoProxy(IUserDao iuserDao) { this.target = iuserDao; } public void save() { System.out.println("开启事物..."); target.save(); System.out.println("关闭事物..."); } } |
动态代理
什么是动态代理
1.代理对象,不需要实现接口
2.代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象(需要我们指定创建代理对象/目标对象实现的接口的类型)
3.动态代理也叫做:JDK代理,接口代理
JDK动态代理
1)原理:是根据类加载器和接口创建代理类(此代理类是接口的实现类,所以必须使用接口 面向接口生成代理,位于java.lang.reflect包下)
2)实现方式:
1. 通过实现InvocationHandler接口创建自己的调用处理器 IvocationHandler handler = new InvocationHandlerImpl(…);
2. 通过为Proxy类指定ClassLoader对象和一组interface创建动态代理类Class clazz = Proxy.getProxyClass(classLoader,new Class[]{…});
3. 通过反射机制获取动态代理类的构造函数,其参数类型是调用处理器接口类型Constructor constructor = clazz.getConstructor(new Class[]{InvocationHandler.class});
4. 通过构造函数创建代理类实例,此时需将调用处理器对象作为参数被传入Interface Proxy = (Interface)constructor.newInstance(new Object[] (handler));
缺点:jdk动态代理,必须是面向接口,目标业务类必须实现接口
package com.itmayiedu.service; //user 服务层
public interface UserService { public void add();
}
package com.itmayiedu.service.impl; import com.itmayiedu.service.UserService; //user 服务层public class UserServiceImpl implements UserService { // spring 事务封装呢? aop技术
public void add() {
System.out.println("往数据库添加数据...");
} }
package com.itmayiedu.proxy001; import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy; import com.itmayiedu.service.MemberService;
import com.itmayiedu.service.impl.MemberServiceImpl; // 每次生成动态代理类对象时,实现了InvocationHandler接口的调用处理器对象
public class InvocationHandlerImpl implements InvocationHandler {
private Object target;// 这其实业务实现类对象,用来调用具体的业务方法
// 通过构造函数传入目标对象 public InvocationHandlerImpl(Object target) {
this.target = target;
} public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
System.out.println("使用jdk动态代理 开启事务");
result = method.invoke(target, args);
System.out.println("使用jdk动态代理 提交事务");
return result;
} public static void main(String[] args) throws NoSuchMethodException, SecurityException, InstantiationException,
IllegalAccessException, IllegalArgumentException, InvocationTargetException {
// 被代理对象
// UserService userService = new UserServiceImpl();
MemberService memberServiceImpl = new MemberServiceImpl();
InvocationHandlerImpl invocationHandlerImpl = new InvocationHandlerImpl(memberServiceImpl);
ClassLoader loader = memberServiceImpl.getClass().getClassLoader();
Class<?>[] interfaces = memberServiceImpl.getClass().getInterfaces();
// 主要装载器、一组接口及调用处理动态代理实例
MemberService newProxyInstance = (MemberService) Proxy.newProxyInstance(loader, interfaces,
invocationHandlerImpl);
newProxyInstance.memberAdd();
} }
日志打印输出为
动态代理与静态代理的区别,静态代理需要编写UserDaoProxy代理类对象,动态代理不用编写,上面jdk动态代理必须面向接口要先定义接口,然后编写接口的实现子类,才能使用jdk的动态代理
CGLIB动态代理
原理:利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。
什么是CGLIB动态代理
使用cglib[Code Generation Library]实现动态代理,并不要求委托类必须实现接口,底层采用asm字节码生成框架生成代理类的字节码
CGLIB动态代理相关代码
public class CglibProxy implements MethodInterceptor {
private Object targetObject;
// 这里的目标类型为Object,则可以接受任意一种参数作为被代理类,实现了动态代理
public Object getInstance(Object target) {
// 设置需要创建子类的类
this.targetObject = target;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(target.getClass());
enhancer.setCallback(this);
return enhancer.create();
} public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("开启事物");
Object result = proxy.invoke(targetObject, args);
System.out.println("关闭事物");
// 返回代理对象
return result;
}
public static void main(String[] args) {
CglibProxy cglibProxy = new CglibProxy();
UserDao userDao = (UserDao) cglibProxy.getInstance(new UserDao());
userDao.save();
}
}
CGLIB动态代理与JDK动态区别
java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。
而cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。
Spring中。
1、如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP
2、如果目标对象实现了接口,可以强制使用CGLIB实现AOP
3、如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换
JDK动态代理只能对实现了接口的类生成代理,而不能针对类 。
CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法 。
因为是继承,所以该类或方法最好不要声明成final ,final可以阻止继承和多态。
@Component
@Aspect
public class AopLog { // 前置通知
@Before("execution(* com.itmayiedu.service.UserService.add(..))")
public void begin() {
System.out.println("前置通知");
} //
// 后置通知
@After("execution(* com.itmayiedu.service.UserService.add(..))")
public void commit() {
System.out.println("后置通知");
} // 运行通知
@AfterReturning("execution(* com.itmayiedu.service.UserService.add(..))")
public void returning() {
System.out.println("运行通知");
} // 异常通知
@AfterThrowing("execution(* com.itmayiedu.service.UserService.add(..))")
public void afterThrowing() {
System.out.println("异常通知");
} // 环绕通知
@Around("execution(* com.itmayiedu.service.UserService.add(..))")
public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("环绕通知开始");
proceedingJoinPoint.proceed();
System.out.println("环绕通知结束");
}
}
这里重点要强调一个知识点
对于环绕通知,如果被执行的目标方法抛出了异常,然后目标方法没有使用try catch对异常进行处理,下来会执行
@AfterThrowing标注的异常处理方法。环绕通知中的 System.out.println("环绕通知结束");就这句不再会被执行,这里一定要注意
proceedingJoinPoint.proceed();
System.out.println("环绕通知结束");
如果目标方法出现了异常,手动使用了try catch对异常进行了处理,没有把异常抛出去给aop框架进行处理,那么就不会执行
@AfterThrowing标注的异常处理方法,
环绕通知中的 System.out.println("环绕通知结束");就这句会再会被执行,这里一定要注意
得出的结论:在业务层中编写代码的时候,如果使用了aop切面编程,业务层中的代码千万不要自己手动使用try catch 进行处理,这里会导致spring 的事务实效,当方法中的方法产生异常的
时候不会钓鱼@AfterThrowing的方法,导致事务失败的时候不能rollBack。这里一定要注意呀 接下来我们自己手动实效spring 事务 第一种方法编程方式实现的事务
spring.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<context:component-scan base-package="com.itmayiedu"></context:component-scan>
<aop:aspectj-autoproxy></aop:aspectj-autoproxy> <!-- 开启事物注解 --> <!-- 1. 数据源对象: C3P0连接池 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test"></property>
<property name="user" value="root"></property>
<property name="password" value="root"></property>
</bean> <!-- 2. JdbcTemplate工具类实例 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean> <!-- 3.配置事务 -->
<bean id="dataSourceTransactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean> </beans>
在spring中使用具有@Transactional 需要在在配置文件中开启对事务注解的支持,上面我们没有配置自己对事务的支持,我们要手写一个事务框架来实现对事务的支持 <!-- 开启spring对事务的注解支持 --> <tx:annotation-driven/>
上面我们没有配置自己对事务的支持,我们要手写一个事务框架来实现对事务的支持
我们使用c3p0的数据源
Spring声明式事务管理器类:
Jdbc技术:DataSourceTransactionManager
Hibernate技术:HibernateTransactionManager
手写Spring事务框架
编程事务实现
概述
所谓编程式事务指的是通过编码方式实现事务,即类似于JDBC编程实现事务管理。管理使用TransactionTemplate或者直接使用底层的PlatformTransactionManager。对于编程式事务管理,spring推荐使用TransactionTemplate。
案例
使用编程事务实现手动事务
使用编程事务实现,手动事务 begin、commit、rollback
package com.itmayiedu.transaction; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Component;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.interceptor.DefaultTransactionAttribute; //编程事务(需要手动begin 手动回滚 手都提交)
@Component
public class TransactionUtils { // 获取事务源
@Autowired
private DataSourceTransactionManager dataSourceTransactionManager; // 开启事务
public TransactionStatus begin() {
TransactionStatus transaction = dataSourceTransactionManager.getTransaction(new DefaultTransactionAttribute());
return transaction;
} // 提交事务
public void commit(TransactionStatus transaction) {
dataSourceTransactionManager.commit(transaction);
} // 回滚事务
public void rollback(TransactionStatus transaction) {
dataSourceTransactionManager.rollback(transaction);
} }
dataSourceTransactionManager就是上面c3p0对应的jdbc数据源的事务管理器,
DefaultTransactionAttribute采用数据库默认的隔离级别
接下来我们看如何在业务层中使用编程方式的事务
package com.itmayiedu.service.impl; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.interceptor.TransactionAspectSupport; import com.itmayiedu.dao.UserDao;
import com.itmayiedu.service.UserService;
import com.itmayiedu.transaction.TransactionUtils; //user 服务层
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
@Autowired
private TransactionUtils transactionUtils; // spring 事务封装呢? aop技术
public void add() {
TransactionStatus transactionStatus = null;
try {
// 开启事务
transactionStatus = transactionUtils.begin();
userDao.add("test001", 20);
System.out.println("开始报错啦!@!!");
int i = 1 / 0;
System.out.println("################");
userDao.add("test002", 21);
// 提交事务
if (transactionStatus != null)
ransactionUtils.commit(transactionStatus);
} catch (Exception e) {
e.getMessage();
// 回滚事务
if (transactionStatus != null)
transactionUtils.rollback(transactionStatus);
}
} }
在上面的方法中例如add方法实现事务我们手动使用transactionStatus = transactionUtils.begin();实现事务,如果存在1000个业务方法,我们就要手动写1000次
transactionStatus = transactionUtils.begin();,我们可以使用aop的方法来实现上面的事务
我们来看下实际的代码
package com.itmayiedu.aop; import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.interceptor.TransactionAspectSupport; import com.itmayiedu.transaction.TransactionUtils; //切面类 基于手手动事务封装
@Component
@Aspect
public class AopTransaction {
@Autowired
private TransactionUtils transactionUtils; // TransactionUtils 不要实现为单例子: 如果为单例子的话可能会发生线程安全问题
// // 异常通知
@AfterThrowing("execution(* com.itmayiedu.service.UserService.add(..))")
public void afterThrowing() {
System.out.println("回滚事务");
// 获取当前事务 直接回滚
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
} // 环绕通知 在方法之前和之后处理事情
@Around("execution(* com.itmayiedu.service.UserService.add(..))")
public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { // 调用方法之前执行
System.out.println("开启事务");
TransactionStatus transactionStatus = transactionUtils.begin();
proceedingJoinPoint.proceed();// 代理调用方法 注意点: 如果调用方法抛出溢出不会执行后面代码
// 调用方法之后执行
System.out.println("提交事务");
transactionUtils.commit(transactionStatus);
}
}
第一使用aop的@Around的环绕通知,在方法执行之前添加事务TransactionStatus transactionStatus = transactionUtils.begin();,在方法 proceedingJoinPoint.proceed();执行完成之后提交事务 transactionUtils.commit(transactionStatus); 然后时候aop的@AfterThrowing来处理方法抛出异常之后,对事务进行回滚,对事务进行回滚一般使用 transactionUtils.rollback(transactionStatus),需要知道当前事务的状态transactionStatus,这里如何获得当前事务的状态了,很多人在TransactionStatus transactionStatus = transactionUtils.begin()
将TransactionStatus transactionStatus当前事务定义为类AopTransaction的成员变量,这里有问题,因为AopTransaction 类是线程不安全的,可能在多线程会引起问题,可以使用下面的方法 TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();获得当前线程下事务的状态,并且将当前事务进行回滚
package com.itmayiedu.service.impl; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.interceptor.TransactionAspectSupport; import com.itmayiedu.dao.UserDao;
import com.itmayiedu.service.UserService;
import com.itmayiedu.transaction.TransactionUtils; //user 服务层
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
@Autowired
private TransactionUtils transactionUtils; public void add() {
// 注意事项: 在使用spring事务的时候,service 不要try 最将异常抛出给外层aop 异常通知接受回滚 userDao.add("test001", 20);
int i = 1 / 0;
System.out.println("################");
userDao.add("test002", 21); } }
执行上面的方法,AopTransaction 就实现了对add方法进行了事务的管理了。特别要注意的是在业务层中编写代码的时候,如果使用了aop切面编程,业务层中的代码千万不要自己手动使用try catch 进行处理,这里会导致spring 的事务实效,当方法中的方法产生异常的
时候,因为自己定义了try catch拦截了异常,异常不会抛出给aop框架处理,就不会调用@AfterThrowing的方法,而是会继续调用transactionUtils.commit(transactionStatus)提交事务,不会执行@AfterThrowing的方法中的 TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();、
所以在业务层代码中,千万不要编写下面的代码:
package com.itmayiedu.service.impl; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.interceptor.TransactionAspectSupport; import com.itmayiedu.dao.UserDao;
import com.itmayiedu.service.UserService;
import com.itmayiedu.transaction.TransactionUtils; //user 服务层
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
@Autowired
private TransactionUtils transactionUtils; public void add() {
// 注意事项: 在使用spring事务的时候,service 不要try 最将异常抛出给外层aop 异常通知接受回滚
try {
userDao.add("test001", 20);
int i = 1 / 0;
System.out.println("################");
userDao.add("test002", 21);
} catch (Exception e) {
e.printStackTrace();
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
} }
这种写法在spring框架使用事务管理的时候会导致事务失败,无法保证一致性,上面代码执行异常的时候,应该讲 userDao.add("test001", 20);从数据库中回滚,但是因为自己使用了try catch处理了异常
,不会执行不会执行@AfterThrowing的方法中的 TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();、而是会继续执行aop环绕通知中的transactionUtils.commit(transactionStatus)提交事务,导致userDao.add("test001", 20)保存在数据库中。
package com.itmayiedu.aop;
import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.annotation.AfterThrowing;import org.aspectj.lang.annotation.Around;import org.aspectj.lang.annotation.Aspect;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Component;import org.springframework.transaction.TransactionStatus;import org.springframework.transaction.interceptor.TransactionAspectSupport;
import com.itmayiedu.transaction.TransactionUtils;
//切面类 基于手手动事务封装@Component@Aspectpublic class AopTransaction {@Autowiredprivate TransactionUtils transactionUtils;
// TransactionUtils 不要实现为单例子: 如果为单例子的话可能会发生线程安全问题// // 异常通知@AfterThrowing("execution(* com.itmayiedu.service.UserService.add(..))")public void afterThrowing() {System.out.println("回滚事务");// 获取当前事务 直接回滚TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();}
// 环绕通知 在方法之前和之后处理事情@Around("execution(* com.itmayiedu.service.UserService.add(..))")public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
// 调用方法之前执行System.out.println("开启事务");TransactionStatus transactionStatus = transactionUtils.begin();proceedingJoinPoint.proceed();// 代理调用方法 注意点: 如果调用方法抛出溢出不会执行后面代码// 调用方法之后执行System.out.println("提交事务");transactionUtils.commit(transactionStatus);}}
手写spring事务框架-蚂蚁课堂的更多相关文章
- 手写Spring事务框架
Spring事务基于AOP环绕通知和异常通知 编程事务 声明事务 Spring事务底层使用编程事务+AOP进行包装的 = 声明事务 AOP应用场景: 事务 权限 参数验证 什么是AOP技术 AO ...
- 手写spring事务框架, 揭秘AOP实现原理。
AOP面向切面编程:主要是通过切面类来提高代码的复用,降低业务代码的耦合性,从而提高开发效率.主要的功能是:日志记录,性能统计,安全控制,事务处理,异常处理等等. AOP实现原理:aop是通过cgli ...
- Spring事务原理分析--手写Spring事务
一.基本概念和原理 1.Spring事务 基于AOP环绕通知和异常通知的 2.Spring事务分为编程式事务.声明事务.编程事务包括注解方式和扫包方式(xml) Spring事务底层使用编程事务(自己 ...
- 手写Spring MVC框架(二) 实现访问拦截功能
前言 在上一篇文章中,我们手写了一个简单的mvc框架,今天我们要实现的功能点是:在Spring MVC框架基础上实现访问拦截功能. 先梳理一下需要实现的功能点: 搭建好Spring MVC基本框架: ...
- 手写Spring MVC框架(一) 实现简易版mvc框架
前言 前面几篇文章中,我们讲解了Spring MVC执⾏的⼤致原理及关键组件的源码解析,今天,我们来模仿它⼿写⾃⼰的mvc框架. 先梳理一下需要实现的功能点: tomcat加载配置文件web.xml: ...
- 从零开始手写 spring ioc 框架,深入学习 spring 源码
IoC Ioc 是一款 spring ioc 核心功能简化实现版本,便于学习和理解原理. 创作目的 使用 spring 很长时间,对于 spring 使用非常频繁,实际上对于源码一直没有静下心来学习过 ...
- 《四 spring源码》利用TransactionManager手写spring的aop
事务控制分类 编程式事务控制 自己手动控制事务,就叫做编程式事务控制. Jdbc代码: Conn.setAutoCommite(false); // 设置手动控制事务 Hibern ...
- -手写Spring注解版本&事务传播行为
视频参考C:\Users\Administrator\Desktop\蚂蚁3期\[www.zxit8.com] 0018-(每特教育&每特学院&蚂蚁课堂)-3期-源码分析-手写Spri ...
- 手写Spring框架,加深对Spring工作机制的理解!
在我们的日常工作中,经常会用到Spring.Spring Boot.Spring Cloud.Struts.Mybatis.Hibernate等开源框架,有了这些框架的诞生,平时的开发工作量也是变得越 ...
随机推荐
- Linux 任务后台运行软件【即:终端复用器】之---screen
会话: 命令行的典型使用方式是,打开一个终端窗口(terminal window,以下简称"窗口"),在里面输入命令. 用户与计算机的这种临时的交互,称为一次"会话&qu ...
- Java IO(十六)InputStreamReader 和 InputStreamWriter
Java IO(十六)InputStreamReader 和 InputStreamWriter 一.介绍 InputStreamReader 和 OutputStreamWriter 是字节流通向字 ...
- Android简单应用程序破解——runtime.apk
对于<Debugging Android Application>一文中最后附上的练习,我采用了另一种静态方法绕开原有的逻辑去破解.主要的过程如下: 利用apktool将练习的runtim ...
- 相邻元素margin的自动合并与float的坑
css中相邻元素的margin其实是会自动合并的,且取较大值. <!DOCTYPE html> <html lang="en"> <head> ...
- Rocket - regmapper - RegField
https://mp.weixin.qq.com/s/7WKB1QxcVzqm2Q7bWcKHzA 简单介绍RegField的实现. 1. 简单介绍 定义寄存器域相关的参数类型. 2. RegFiel ...
- 【Spring】JdbcTemplate的使用,查询,增、删、改
数据库名:taobaodb 配置文件: JdbcTemplate主要提供以下五类方法: execute方法:可以用于执行任何SQL语句,一般用于执行DDL语句: update方法及batchUpdat ...
- Java实现 蓝桥杯 基础练习 字母图形
基础练习 字母图形 时间限制:1.0s 内存限制:256.0MB 提交此题 锦囊1 锦囊2 问题描述 利用字母可以组成一些美丽的图形,下面给出了一个例子: ABCDEFG BABCDEF CBABCD ...
- Java实现 蓝桥杯VIP 算法训练 s01串
题目描述 s01串初始为" 0" 按以下方式变换 0变1,1变01 数据规模和约定 0~19 输入 1个整数(0~19) 输出 n次变换后s01串 样例输入 3 样例输出 101 ...
- Java实现 LeetCode 119 杨辉三角 II
119. 杨辉三角 II 给定一个非负索引 k,其中 k ≤ 33,返回杨辉三角的第 k 行. 在杨辉三角中,每个数是它左上方和右上方的数的和. 示例: 输入: 3 输出: [1,3,3,1] 进阶: ...
- CSS清除浮动&内容居中&文字溢出
学习! 1.CSS清除浮动的方法 (1)添加标签清除浮动: 在浮动元素结尾处,并列的添加标签<div style="clear:both;"></div>. ...