1. 递归在项目中的应用
第一个递归目录,或者递归解析XML
在实际项目中,我们往往需要动态生成树形结构的菜单来实现数据的管理,如图1所示;或者是需要动态生成树形的图表结构,如图2所示。这些树形结构往往没有层级限制。
图1 树形菜单结构
图2 树形图表结构
在此,以图2为例提出问题及解决方案。
数据库设计:
列名 |
类型 |
是否为空 |
备注 |
Id |
bigint |
否 |
编号,主键 |
ItemName |
varchar(50) |
否 |
子项名称 |
StartTime |
datetime |
否 |
开始时间 |
EndTime |
datetime |
否 |
结束时间 |
FartherCode |
varchar(50) |
否 |
父节点编码,如果值为’0’代表该节点为一级节点 |
Code |
varchar(50) |
否 |
当前节点编码 |
实现:
(1)读取所有的一级节点,放入DataTable中;
(2)遍历DataTable的每一行(即DataRow);
(3)对于给定的DataRow,如果该节点没有根节点,返回对应的字符串,否则,获取该节点的的所有子节点得到DataTable,跳转到第(2)步,最终将所有得到的字符串叠加返回;
核心算法:
2. 2.io读写多级目录能用到么? 有那种统计文件个数的功能需要实现么?
io流读写文件
及内容
3. 3.多线程在项目中的应用,哪两个或多个线程需要同时运行.
线程在实际项目中简单的应用
前段时间开发的项目中有一个office在线预览的功能,我们知道需要实现这个功能一般是
后台把用户上传的txt啊excel啊word啊先转换成pdf格式,然后使用pdf.js进行前台预览(有的还
需要转换成swf文件),功能倒是实现了,只是客户反馈提交表单的时候速度比较慢,我试了一下
,确实比较慢,大概好几十秒,这个有点无法忍受,所以后台转pdf的那个过程就将其使用线程,
以异步方式去处理。原理就和如下类似
不使用线程:
例子很简单,控制台输出begin并且在五秒后打印end
使用线程
效果,执行后控制台立刻显示begin和end
因为目前我们不需要中间处理的结果,因此可以让它在后台执行,不阻塞主线程。
另外,也可以使用线程池实现上面的功能,代码如下:
结合实际项目中转pdf的例子
代码如下:
4. 4.你们的项目中 文件上传或者图片上传同的是TCP协议么
:
上传文件我们用的是hession
:
hession是tcp
tcp和udp是传输层的协议
5. 反射:用在封装baseServlet中的获取Class对象 解耦合 配合配置文件做到解耦合 还用在哪了?
反射用的注意事项
链接:https://www.zhihu.com/question/23739003/answer/25519537
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
通常来说,是的。因为反射破坏了程序的“可静态分析”能力。
举个例子,大部分ide都会提供对某语法结构find reference的功能 (eclips下ctrl-shift-g,intellij下alt-f7),有的甚至提供列出所有调用栈的功能。这就是一种对代码的静态分析。在我们的团队中,提倡对任何现有方法进行修改之前,都应该先使用find reference查看所有对这个方法的调用位置。保证所做的修改不会在修正某条调用路径上的bug时在其他路径上引入新的bug。
再者,ide提供的分析功能能帮助你在debug时顺藤摸瓜找到真正的出错位置,以及理解作者原来的意图。
况且,大部分自动重构的功能都是基于静态分析的。
我个人觉得,静态语言所以能在近来不断涌现的动态语言冲击下,在大型项目中保持不败之地,一个重要原因就是强大的可静态分析能力。动态语言在IDE的帮助下也提供了一定的静态分析能力,但总不如静态语言来得快捷准确。而使用了反射后,大部分依赖于静态分析的功能都不再可用了。 这就是弃长取短,放弃了静态分析能力,又保留了冗长复杂的语法,那为何不干脆用动态语言呢。
当然,对于一些知名的library,ide会提供特殊的支持,对反射部分做静态分析。但自己写的代码,一旦用了反射,顺藤摸瓜理解代码的线索就断了。
基于注解(annotation)的反射可以一定程度上缓解这个问题,因为你还是可以通过注解找到使用位置和处理位置。但是, 动态扫描注解的效率很低,在生产代码普遍的做法是分成“扫描,缓存,查找,调用”四步走,增加了开发和维护的复杂度。如果出现调用错误的bug,真正的错误位置却可能在扫描阶段,不容易定位。
总的来说,我们团队目前对反射的不成文规则是:1. 业务代码中基本不用基于名称的反射。除非经过团队讨论确认非用不可,否则基本上不可能通过代码走查。
2. 业务代码中使用注解相对宽松,但必须缓存,不能在调用时现场扫描。
3. 一些基本库中允许对Java Bean使用按名访问的反射调用,但不提倡针对某个或某些具体属性(如果属性集可以罗列出来,那么就说明可以改为直接调用),必须是将属性作为整体来循环处理。此类代码必须有85%以上单元测试覆盖率(原则上底层库都要求85%覆盖率)。
6. 用到xml配置文件解析器DOM,SAX和dom4j解析什么的了么?
7. 动态代理: 代理类和被代理类 实现同一接口 代理类对被代理类进行增强. 项目中哪用到了?
先看一下代理模式,这个应该是设计模式中最简单的一个了,类图
代理模式最大的特点就是代理类和实际业务类实现同一个接口(或继承同一父类),代理对象持有一个实际对象的引用,外部调用时操作的是代理对象,而在代理对象的内部实现中又会去调用实际对象的操作
Java动态代理其实内部也是通过Java反射机制来实现的,即已知的一个对象,然后在运行时动态调用其方法,这样在调用前后作一些相应的处理,这样说的比较笼统,举个简单的例子
比如我们在应用中有这样一个需求,在对某个类的一个方法的调用前和调用后都要做一下日志操作,
一个普通的接口
- public interface AppService {
- public boolean createApp(String name);
- }
该接口的默认实现类
- public class AppServiceImpl implements AppService {
- public boolean createApp(String name) {
- System.out.println("App["+name+"] has been created.");
- return true;
- }
- }
日志处理器
- public class LoggerInterceptor implements InvocationHandler {//注意实现这个Handler接口
- private Object target;//目标对象的引用,这里设计成Object类型,更具通用性
- public LoggerInterceptor(Object target){
- this.target = target;
- }
- public Object invoke(Object proxy, Method method, Object[] arg)
- throws Throwable {
- System.out.println("Entered "+target.getClass().getName()+"-"+method.getName()+",with arguments{"+arg[0]+"}");
- Object result = method.invoke(target, arg);//调用目标对象的方法
- System.out.println("Before return:"+result);
- return result;
- }
- }
外部调用
- public class Main {
- public static void main(String[] args) {
- AppService target = new AppServiceImpl();//生成目标对象
- //接下来创建代理对象
- AppService proxy = (AppService) Proxy.newProxyInstance(
- target.getClass().getClassLoader(),
- target.getClass().getInterfaces(), new LoggerInterceptor(target));
- proxy.createApp("Kevin Test");
- }
- }
此外,可以使用Spring AOP 。
8. 事务: 事务只是在删除和修改的时候用的时候用.么? 还有什么情况用事务?
添加事务在实际项目中是必不可少的,事务是用来实现要么全都成功,要么全都不成功, 而主要针对的就是要不成功就全都不成功的问题.
想象你给你女朋友转账,这边刚扣了钱,你女朋友账户还没收到钱呢,突然出故障了,比如停电了. 那怎么办. 这就涉及到一个关键的知识点:事务. 利用的事务的回滚,把你账户上扣的钱再回滚到你账户上.
本篇采用maven构建war工程涉及主要知识点:
spring+springMVC+mybatis如何整合,
mybatis在写mapper.xml文件时,如何解决参数类型只能传一个的问题
update语句怎么写
如何保证正确配置了事务,事务少配一个,就会失效
声明式事务介绍:
声明式事务管理建立在AOP之上的。其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。声明式事务最大的优点就是不需要通过编程的方式管理事务,这样就不需要在业务逻辑代码中掺杂事务管理的代码,只需在配置文件中做相关的事务规则声明(或通过基于@Transactional注解的方式),便可以将事务规则应用到业务逻辑中。
显然声明式事务管理要优于编程式事务管理,这正是spring倡导的非侵入式的开发方式。声明式事务管理使业务代码不受污染,一个普通的POJO对象,只要加上注解就可以获得完全的事务支持。和编程式事务相比,声明式事务唯一不足地方是,后者的最细粒度只能作用到方法级别,无法做到像编程式事务那样可以作用到代码块级别。但是即便有这样的需求,也存在很多变通的方法,比如,可以将需要进行事务管理的代码块独立为方法等等。
声明式事务管理也有两种常用的方式,一种是基于tx和aop名字空间的xml配置文件,另一种就是基于@Transactional注解。显然基于注解的方式更简单易用,更清爽。实际中两种方式常组合起来使用.本篇的案例项目就是组合使用实现事务功能.
项目结构:
数据库部分:
数据库中就一张表user
user表的结构
user表的数据
代码部分:
pojo 之 User:
mapper之UserMapper接口:
mapper之UserMapper.xml
添加的依赖:
pom.xml:
需要特别注意的是这4个,不要导错了.
配置文件:
db.properties
mybatis.xml
spring之applicationContext.xml
spring之applicationContext-tran.xml
spring之spring-MVC.xml:
配置时你也许见到有人在<tx:advice>标签内又配置了一遍transaction-manager,其实完全没必要,配置了也不会报错,要想事务生效的一个关键点是
必须要配置 事务的注解驱动 <tx:annotation-driven transaction-manager="transactionManager"/>
<tx:annotation-driven transaction-manager="transactionManager"/>
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!-- 约定优于编码 设置事务详情 -->
<tx:method name="get*" read-only="true" propagation="SUPPORTS"/>
<tx:method name="find*" read-only="true" propagation="SUPPORTS"/>
<tx:method name="select*" read-only="true" propagation="SUPPORTS"/>
<tx:method name="add*"/>
<tx:method name="save*"/>
<tx:method name="insert*"/>
<tx:method name="delete*"/>
<tx:method name="update*"/>
</tx:attributes>
</tx:advice>
web.xml中的配置:
页面部分(两个jsp页面):
index.jsp
userlist.jsp
业务层之UserService接口
业务层之UserServiceImpl
实现事务回滚,1.必须要加@Transactional注解 2. catch块中必须throw个异常出来,是触发事务生效的必要条件
控制层:UserController
向tomcat内添加项目,启动tomcat,地址类输入localhostL8080/TestTranSaction 请求结果为
点击新增可添加一个用户(代码中定义死了,添加的是郑爽),点击更改可更改一个用户(更改后的用户叫逗逼姚,只更改username,age,sex三个字段)
为了测试事务回滚,特意加了//int a=6/0; 这样的代码来人为制造异常,此时事务是否添加上了(看是否回滚).可根据需要自行注释或解除注释.
归纳点:
要想事务配置成功:
1, 要在applicationContext-tran.xml配置文件中 添加事务的注解驱动,必须要有.
这几项缺一不可.(提醒: 声明式事务是基于AOP的)
2. 在业务层业务实现类必须要加@Transaction注解
3. 在业务层业务实现类catch块中必须要throw一个异常,以便触发回滚(这是触发回滚的一个条件)
4. spring-MVC.xml中要扫具体包只扫到Controller, 因为spring-MVC.xml是输入dispatcherServlet的,而applicationContext.xml,applicationContext-tran.xml是输入contextLoaderListener的, 因此spring-MVC.xml的加载要晚于它俩,会造成本已增加过的方法被覆盖
以上四点缺一不可.
常见的误解:
显示有增加标记的就一定配置事务成功了.没显示增加比较的说明事务没配置成功.
这个思维完全是荒谬的.
比如我的spring,springdao(她俩合在一个配置文件applicationContext.xml里面了)和事务tran是分开配置的.(其实spring,springdao也能拆开配置).,事务的配置文件中就没显示增强标记,它深圳还报了一个警告,下图中黄色区域 说Multiple annotations found at this line:- Referenced bean 'dataSource' not 其实这个警告说dataSource未找到,完全不靠谱(我其实在springContext.xml文件中配置过了),就像增加标记不靠谱一样
这个增加标记长什么样呢?在上面这个配置文件中加一行代码就会出现了
实际上<context:component-scan base-package="xxxx"/>并不是随便就能写的, 只有满足了前面提到的4点,声明式事务才能生效
扩展知识:
spring事务回滚规则
指示spring事务管理器回滚一个事务的推荐方法是在当前事务的上下文内抛出异常。spring事务管理器会捕捉任何未处理的异常,然后依据规则决定是否回滚抛出异常的事务。
默认配置下,spring只有在抛出的异常为运行时unchecked异常时才回滚该事务,也就是抛出的异常为RuntimeException的子类(Errors也会导致事务回滚),而抛出checked异常则不会导致事务回滚。可以明确的配置在抛出那些异常时回滚事务,包括checked异常。也可以明确定义那些异常抛出时不回滚事务。还可以编程性的通过setRollbackOnly()方法来指示一个事务必须回滚,在调用完setRollbackOnly()后你所能执行的唯一操作就是回滚。
事务只读属性
“只读事务”并不是一个强制选项,它只是一个“暗示”,提示数据库驱动程序和数据库系统,这个事务并不包含更改数据的操作,那么JDBC驱动程序和数据库就有可能根据这种情况对该事务进行一些特定的优化,比方说不安排相应的数据库锁,以减轻事务对数据库的压力,毕竟事务也是要消耗数据库的资源的。
但是你非要在“只读事务”里面修改数据,也并非不可以,只不过对于数据一致性的保护不像“读写事务”那样保险而已。
因此,“只读事务”仅仅是一个性能优化的推荐配置而已,并非强制你要这样做不可
mybatis在写mapper.xml文件时,如何解决参数类型只能传一个的问题?update语句怎么写?
这两个问题,见mapper接口文件和mapper.xml文件中的写法或说明.
还是写在这吧:
mysql删除末尾数据后,再插入新数据id不连续解决方案
MySQL的user表中本来15条数据,我把后5条给删除了,再插入新用户后id会从16开始计数, 导致重新插入值,字段id取值不连续.
ALTER TABLE USER AUTO_INCREMENT=10; (此处10改为自己的断点即可)
代码示例
首先是数据库表:
包括book(isbn, book_name, price),account(username, balance),book_stock(isbn, stock)
然后是使用的类:
BookShopDao
package com.yl.spring.tx;
public interface BookShopDao {
//根据书号获取书的单价
public int findBookPriceByIsbn(String isbn);
//更新书的库存,使书号对应的库存-1
public void updateBookStock(String isbn);
//更新用户的账户余额:使username的balcance-price
public void updateUserAccount(String username, int price);
}
BookShopDaoImpl
package com.yl.spring.tx;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
@Repository("bookShopDao")
public class BookShopDaoImpl implements BookShopDao {
@Autowired
private JdbcTemplate JdbcTemplate;
@Override
public int findBookPriceByIsbn(String isbn) {
String sql = "SELECT price FROM book WHERE isbn = ?";
return JdbcTemplate.queryForObject(sql, Integer.class, isbn);
}
@Override
public void updateBookStock(String isbn) {
//检查书的库存是否足够,若不够,则抛出异常
String sql2 = "SELECT stock FROM book_stock WHERE isbn = ?";
int stock = JdbcTemplate.queryForObject(sql2, Integer.class, isbn);
if (stock == 0) {
throw new BookStockException("库存不足!");
}
String sql = "UPDATE book_stock SET stock = stock - 1 WHERE isbn = ?";
JdbcTemplate.update(sql, isbn);
}
@Override
public void updateUserAccount(String username, int price) {
//检查余额是否不足,若不足,则抛出异常
String sql2 = "SELECT balance FROM account WHERE username = ?";
int balance = JdbcTemplate.queryForObject(sql2, Integer.class, username);
if (balance < price) {
throw new UserAccountException("余额不足!");
}
String sql = "UPDATE account SET balance = balance - ? WHERE username = ?";
JdbcTemplate.update(sql, price, username);
}
}
BookShopService
public interface BookShopService {
public void purchase(String username, String isbn);
}
BookShopServiceImpl
package com.yl.spring.tx;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@Service("bookShopService")
public class BookShopServiceImpl implements BookShopService {
@Autowired
private BookShopDao bookShopDao;
/**
* 1.添加事务注解
* 使用propagation 指定事务的传播行为,即当前的事务方法被另外一个事务方法调用时如何使用事务。
* 默认取值为REQUIRED,即使用调用方法的事务
* REQUIRES_NEW:使用自己的事务,调用的事务方法的事务被挂起。
*
* 2.使用isolation 指定事务的隔离级别,最常用的取值为READ_COMMITTED
* 3.默认情况下 Spring 的声明式事务对所有的运行时异常进行回滚,也可以通过对应的属性进行设置。通常情况下,默认值即可。
* 4.使用readOnly 指定事务是否为只读。 表示这个事务只读取数据但不更新数据,这样可以帮助数据库引擎优化事务。若真的是一个只读取数据库值得方法,应设置readOnly=true
* 5.使用timeOut 指定强制回滚之前事务可以占用的时间。
*/
@Transactional(propagation=Propagation.REQUIRES_NEW,
isolation=Isolation.READ_COMMITTED,
noRollbackFor={UserAccountException.class},
readOnly=true, timeout=3)
@Override
public void purchase(String username, String isbn) {
//1.获取书的单价
int price = bookShopDao.findBookPriceByIsbn(isbn);
//2.更新书的库存
bookShopDao.updateBookStock(isbn);
//3.更新用户余额
bookShopDao.updateUserAccount(username, price);;
}
}
BookStockException
package com.yl.spring.tx;
public class BookStockException extends RuntimeException {
/**
*
*/
private static final long serialVersionUID = 1L;
public BookStockException() {
super();
// TODO Auto-generated constructor stub
}
public BookStockException(String arg0, Throwable arg1, boolean arg2,
boolean arg3) {
super(arg0, arg1, arg2, arg3);
// TODO Auto-generated constructor stub
}
public BookStockException(String arg0, Throwable arg1) {
super(arg0, arg1);
// TODO Auto-generated constructor stub
}
public BookStockException(String arg0) {
super(arg0);
// TODO Auto-generated constructor stub
}
public BookStockException(Throwable arg0) {
super(arg0);
// TODO Auto-generated constructor stub
}
}
UserAccountException
package com.yl.spring.tx;
public class UserAccountException extends RuntimeException {
/**
*
*/
private static final long serialVersionUID = 1L;
public UserAccountException() {
super();
// TODO Auto-generated constructor stub
}
public UserAccountException(String arg0, Throwable arg1, boolean arg2,
boolean arg3) {
super(arg0, arg1, arg2, arg3);
// TODO Auto-generated constructor stub
}
public UserAccountException(String arg0, Throwable arg1) {
super(arg0, arg1);
// TODO Auto-generated constructor stub
}
public UserAccountException(String arg0) {
super(arg0);
// TODO Auto-generated constructor stub
}
public UserAccountException(Throwable arg0) {
super(arg0);
// TODO Auto-generated constructor stub
}
}
Cashier
package com.yl.spring.tx;
import java.util.List;
public interface Cashier {
public void checkout(String username, List<String>isbns);
}
CashierImpl。CashierImpl.checkout和bookShopService.purchase联合测试了事务的传播行为
package com.yl.spring.tx;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service("cashier")
public class CashierImpl implements Cashier {
@Autowired
private BookShopService bookShopService;
@Transactional
@Override
public void checkout(String username, List<String> isbns) {
for(String isbn : isbns) {
bookShopService.purchase(username, isbn);
}
}
}
测试类:
package com.yl.spring.tx;
import java.util.Arrays;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringTransitionTest {
private ApplicationContext ctx = null;
private BookShopDao bookShopDao = null;
private BookShopService bookShopService = null;
private Cashier cashier = null;
{
ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
bookShopDao = ctx.getBean(BookShopDao.class);
bookShopService = ctx.getBean(BookShopService.class);
cashier = ctx.getBean(Cashier.class);
}
@Test
public void testBookShopDaoFindPriceByIsbn() {
System.out.println(bookShopDao.findBookPriceByIsbn("1001"));
}
@Test
public void testBookShopDaoUpdateBookStock(){
bookShopDao.updateBookStock("1001");
}
@Test
public void testBookShopDaoUpdateUserAccount(){
bookShopDao.updateUserAccount("AA", 100);
}
@Test
public void testBookShopService(){
bookShopService.purchase("AA", "1001");
}
@Test
public void testTransactionPropagation(){
cashier.checkout("AA", Arrays.asList("1001", "1002"));
}
}
9. Tomcat: 项目中对tomcat进行优化么. 怎么优化?
10. 项目中怎么处理的请求和响应乱码问题?
11. Cook和session 在项目中怎么用的?
12. Filter过滤器学习的时候用到过滤get和post编码, 自动登录. 项目中用在哪了
13. Listener监听器学习的时候用来查询数据库监听过生日的人发送邮件. 项目中用在哪了?
14. Redis用到了么 用于缓存什么信息?
Tomcat 底层是serverSocket 自己封装了request 和reponse 调用了我们的servelt传入request,response 做了请求和响应内容
在实际项目中,我们往往需要动态生成树形结构的菜单来实现数据的管理,如图1所示;或者是需要动态生成树形的图表结构,如图2所示。这些树形结构往往没有层级限制。
图1 树形菜单结构
图2 树形图表结构
在此,以图2为例提出问题及解决方案。
数据库设计:
列名 |
类型 |
是否为空 |
备注 |
Id |
bigint |
否 |
编号,主键 |
ItemName |
varchar(50) |
否 |
子项名称 |
StartTime |
datetime |
否 |
开始时间 |
EndTime |
datetime |
否 |
结束时间 |
FartherCode |
varchar(50) |
否 |
父节点编码,如果值为’0’代表该节点为一级节点 |
Code |
varchar(50) |
否 |
当前节点编码 |
实现:
(1)读取所有的一级节点,放入DataTable中;
(2)遍历DataTable的每一行(即DataRow);
(3)对于给定的DataRow,如果该节点没有根节点,返回对应的字符串,否则,获取该节点的的所有子节点得到DataTable,跳转到第(2)步,最终将所有得到的字符串叠加返回;
核心算法: