1.SpringMVC在项目中的应用?
1)service层,项目的业务逻辑层,一般先定义一个接口,在写一个实现类,实现所有的接口方法。service的实现类中要加注解@Service(用于标注业务层组件),@Resource 注入dao组件,方便在业务层中调用对用dao的方法.
@Service
public class ContentServiceImpl implements ContentService{
@Autowired
private ContentDao contentdao;
2)controller层,控制层它把用户请求的数据经过业务处理层处理之后封装成一个Model ,然后再把该Model 返回给对应的View 进行展示。controller要添加注解@Controller(用于标注控制层组件)),@Resource 注入service组件,方便调用业务层的方法.
@Controller
public class ContentController {
@Autowired
private ContentService contentService;
//用户评论 提交 按钮--insert
@RequestMapping("/ContentController")
@ResponseBody
public Object addByText( @ModelAttribute("content") Content content,HttpServletRequest request){
content.setUsername(((User)(request.getSession().getAttribute("sessionuser"))).getUsername());
contentService.addByText(content);
return new Gson().toJson(contentService.queryOneForJustNow());
}
2.Redis缓存数据存放的时间?
列:在实现积分项目业务中,对不同场景设置了不同的key-value缓存到了redis中。但是因为对不同业务的key需要缓存的时间不尽相同,这里自定义工具类来实现。
设置redis缓存key,截取部分代码:
try{ //cacheManager就相当从redis链接池获取一个连接,具体工厂类获取在后面备注 cacheManager = (RedisCacheManager) CacheManagerFactory.getCacheManager(); totalMonCount = Float.parseFloat(cacheManager.getString(monthKey)) + centCount; if (centLimitByMonth != -1){ if (totalMonCount > centLimitByMonth) { // 超出月上限不再累计 logger.error("exceeds the month limit cents! [" + totalMonCount + "] code:[" + code + "]"); return null; } } //当未超出月额度,此时要对日额度进行判断;只需判断其是否超出日上限积分 if (dayKey != null){ //累积积分;因为签到其实是没有每日积分的,是按次数规则累积的;但为了统一,直接用centCount代替(都是签一次1分) totalDayCount = Float.parseFloat(cacheManager.getString(dayKey)) + centCount; if (centLimitByDay != -1){ if (totalDayCount > centLimitByDay){ logger.info("[ERROR]teacher everyday assign cents > month limit! total: ["+totalDayCount+"]"); return null; } } cacheManager.set(dayKey,totalDayCount.toString(),DateUtil.getSecsToEndOfCurrentDay()); } //对月限制key进行积分累加 //每月1号凌晨1点启动脚本删除,同时设置了保存到月底缓存时间双重保障 cacheManager.set(monthKey, totalMonCount.toString(), DateUtil.getSecsToEndOfCurrentDay()); logger.info("==monthkey:"+monthKey+"---value:"+totalMonCount); } ... }catch(Exception e){ logger.error("===cache redis fail!"); e.printStackTrace(); }finally { if (cacheManager != null){ cacheManager.close(); } } //工厂类获取redis链接 public class CacheManagerFactory { private static ICacheManager cacheManager; private CacheManagerFactory(){ }; public static ICacheManager getCacheManager(){ if(cacheManager == null){ synchronized (CacheManagerFactory.class) { if(cacheManager == null){ JedisPooler jedisPooler = RedisPoolerFactory.getJedisPooler(); cacheManager = new RedisCacheManager(jedisPooler); } } } return cacheManager; } } //redis链接池工厂类获取链接 public class RedisPoolerFactory { private static JedisPooler jedisPooler; private RedisPoolerFactory(){ }; public static JedisPooler getJedisPooler(){ if(jedisPooler == null){ synchronized (RedisPoolerFactory.class) { if(jedisPooler == null){ jedisPooler = new JedisPooler(); } } } return jedisPooler; } } /** * * Redis 连接池实例 * * @author Ethan.Lam * @createTime 2011-12-3 * */ public class JedisPooler { private JedisPool pool; private String REDIS_HOST; private String REDIS_PSW; private int REDIS_PORT; private int REDIS_MaxActive; private int REDIS_MaxIdle; private int REDIS_MaxWait; public JedisPooler(String config) { __init(config); } public JedisPooler() { __init("/jedisPool.properties"); } private void __init(String conf) { // 完成初始化工作 Properties prop = new Properties(); try { InputStream _file = loadConfig(conf); prop.load(_file); REDIS_HOST = prop.getProperty("REDIS.HOST"); REDIS_PSW = prop.getProperty("REDIS.PSW"); REDIS_PORT = Integer.parseInt(prop.getProperty("REDIS.PORT").trim()); REDIS_MaxActive = Integer.parseInt(prop.getProperty("REDIS.MaxActive").trim()); REDIS_MaxIdle = Integer.parseInt(prop.getProperty("REDIS.MaxIdle").trim()); REDIS_MaxWait = Integer.parseInt(prop.getProperty("REDIS.MaxWait").trim()); } catch (Exception e) { e.printStackTrace(); REDIS_HOST = "localhost"; throw new NullPointerException(conf + " is not found !"); } JedisPoolConfig config = new JedisPoolConfig(); config.setMaxActive(REDIS_MaxActive); config.setMaxIdle(REDIS_MaxIdle); config.setMaxWait(REDIS_MaxWait); config.setTestOnBorrow(true); System.out.println("REDIS Cache服务信息: 当前连接的服务IP为:" + REDIS_HOST + ":" + REDIS_PORT); if (null == REDIS_PSW || "".equals(REDIS_PSW.trim())){ pool = new JedisPool(config, REDIS_HOST, REDIS_PORT, 5000); } else{ pool = new JedisPool(config, REDIS_HOST, REDIS_PORT, 5000, REDIS_PSW); } } public Jedis getJedis() { return pool.getResource(); } public void returnResource(Jedis jedis) { pool.returnResource(jedis); } public JedisPool getPool() { return pool; } InputStream loadConfig(String configPath) throws Exception { InputStream _file = null; try { String file = JedisPooler.class.getResource(configPath).getFile(); file = URLDecoder.decode(file); _file = new FileInputStream(file); } catch (Exception e) { System.out.println("读取jar中的配置文件...."); String currentJarPath = URLDecoder.decode(JedisPooler.class.getProtectionDomain() .getCodeSource().getLocation().getFile(), "UTF-8"); // 获取当前Jar文件名 System.out.println("currentJarPath:" + currentJarPath); java.util.jar.JarFile currentJar = new java.util.jar.JarFile(currentJarPath); java.util.jar.JarEntry dbEntry = currentJar.getJarEntry("jedisPool.properties"); InputStream in = currentJar.getInputStream(dbEntry); _file = in; } return _file; } }
对于redis这种希望指定缓存有效时间,现在提供3种方案:
1)自定义确切时间:
public static final long LoginCentTimeByDay = 86400;//s 未认证失效时间 1天
public static final long LoginCentTimeByMonth = 86400*30; //s 时效时间 30天
直接指定:
cacheManager.set(monthKey, totalMonCount.toString(),LoginCentTimeByDay)
2)自定义工具类,获取当前时间到第二天的零点、下个月1号零点的时间差(s):
cacheManager.set(monthKey, totalMonCount.toString(), DateUtil.getSecsToEndOfCurrentDay()); public class DateUtil { /** *获取每月最后一天时间 * @param sDate1 * @return */ public static Date getLastDayOfMonth(Date sDate1) { Calendar cDay1 = Calendar.getInstance(); cDay1.setTime(sDate1); final int lastDay = cDay1.getActualMaximum(Calendar.DAY_OF_MONTH); Date lastDate = cDay1.getTime(); lastDate.setDate(lastDay); return lastDate; } /* 获取下一个月第一天凌晨1点 */ public static Date nextMonthFirstDate() { Calendar calendar = Calendar.getInstance(); calendar.set(Calendar.HOUR_OF_DAY, 1); //设置为每月凌晨1点 calendar.set(Calendar.DAY_OF_MONTH, 1); //设置为每月1号 calendar.add(Calendar.MONTH, 1); // 月份加一,得到下个月的一号 // calendar.add(Calendar.DATE, -1); 下一个月减一为本月最后一天 return calendar.getTime(); } /** * 获取第二天凌晨0点毫秒数 * @return */ public static Date nextDayFirstDate() throws ParseException { Calendar cal = Calendar.getInstance(); cal.setTime(new Date()); cal.add(Calendar.DAY_OF_YEAR, 1); cal.set(Calendar.HOUR_OF_DAY, 00); cal.set(Calendar.MINUTE, 0); cal.set(Calendar.SECOND, 0); return cal.getTime(); } //********* /** * 获取当前时间到下个月凌晨1点相差秒数 * @return */ public static Long getSecsToEndOfCurrentMonth(){ Long secsOfNextMonth = nextMonthFirstDate().getTime(); //将当前时间转为毫秒数 Long secsOfCurrentTime = new Date().getTime(); //将时间转为秒数 Long distance = (secsOfNextMonth - secsOfCurrentTime)/1000; if (distance > 0 && distance != null){ return distance; } return new Long(0); } /** * 获取当前时间到明天凌晨0点相差秒数 * @return */ public static Long getSecsToEndOfCurrentDay() throws ParseException { Long secsOfNextDay = nextDayFirstDate().getTime(); //将当前时间转为毫秒数 Long secsOfCurrentTime = new Date().getTime(); //将时间转为秒数 Long distance = (secsOfNextDay - secsOfCurrentTime)/1000; if (distance > 0 && distance != null){ return distance; } return new Long(0); } }
3)使用定时任务定时清空redis缓存;避免出现定时任务异常,我的业务代码里都保障了两种方案都适用。
定时任务保证,到指定时间直接调用代码进行操作;代码里直接调用shell脚本直接删掉相关redis的缓存数据。
quartz定时任务就需要注意定义相应的cron时间:
我的定时任务的配置文件quartz.xml中定义:
<!--定时任务1--> <!-- 每天12点将当天用户积分行为缓存清掉 --> <bean id="deleteRedisCacheDayUsersJob" class="cn.qtone.xxt.cent.quartz.delRedisCacheCentUsers" /> <bean id="deleteRedisCacheDayUsersTask" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean"> <property name="targetObject" ref="deleteRedisCacheDayUsersJob" /> <property name="targetMethod" value="delCurrentDayCacheUsersByDay" /><!-- 定时执行 doItem 方法 --> <property name="concurrent" value="false" /> </bean> <bean id="deleteRedisCacheDayUsersTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean"> <property name="jobDetail" ref="deleteRedisCacheDayUsersTask" /> <property name="cronExpression" value="59 59 23 * * ?" /><!-- 每天凌晨23:59:59 点执行 --> <!--<property name="cronExpression" value="0 */1 * * * ?" /><!– 每隔1min执行一次 –>--> </bean> <bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean"> <property name="triggers"> <list> <ref bean="deleteRedisCacheDayUsersTrigger" /> <ref bean="deleteRedisCacheMonthUsersTrigger" /> <!--暂时不用--> <!--<ref bean="centUpdateByMonthTrigger" />--> </list> </property> </bean>
3.数据库中有一张表,表中有主键。现在后台要插入一条数据,但是这条数据会与主键冲突。项目已经开启了事物,要求这条数据不插入进数据库程序能正常运行。问解决的两个办法?
mysql忽略主键冲突、避免重复插入的几种方式
方案一:使用 ignore 关键字
如果是用主键primary或者唯一索引unique区分了记录的唯一性,避免重复插入记录可以使用:这样当有重复记录就会忽略,执行后返回数字0
insert
ignore
into
table_name(email,phone,user_id)
values
(
'test9@163.com'
,
'99999'
,
'9999'
)
还有个应用就是复制表,避免重复记录
insert
ignore
into
table
(
name
)
select
name
from
table2
方案二:使用 replace into
replace的语法格式为:
1.
replace
into
table_name(col_name, ...)
values
(...)
2.
replace
into
table_name(col_name, ...)
select
...
3.
replace
into
table_name
set
col_name=value, ...
replace
into
table_name(email,phone,user_id)
values
(
'test569'
,
'99999'
,
'123'
)
;方案三:ON DUPLICATE KEY UPDATE
INSERT
INTO
table_name1(title,first_name,last_name,email,phone,user_id
,role_id,status,campaign_id)
SELECT
''
,
''
,
''
,table_name2.email,table_name2.phone,
NULL
,
NULL
,
'pending'
,29
FROM
table_name2
WHERE
table_name2.status = 1
ON
DUPLICATE
KEY
UPDATE
table_name1.status =
'pending'
4.数据连接池:数据连接已经占满了,资源释放不掉该怎么处理(注意不是连接池配置问题)?
待解答:
5.编写一个队列,内部是用数组实现。最大长度为2000?
package JavaMianSiTest; public class QueueTest { public static void main(String[] args) { ArrayQueue queue = new ArrayQueue(100); System.out.println(queue.isEmpty()); for (int i = 0; i < 100; i++) { queue.insert(i); } System.out.println(queue.isFull()); while (!queue.isEmpty()) { System.out.println(queue.remove()); } } } class ArrayQueue { private int[] arrInt;// 内置数组 private int front;// 头指针 private int rear;// 尾指针 public ArrayQueue(int size) { this.arrInt = new int[size]; front = 0; rear = -1; } /** * 判断队列是否为空 * * @return */ public boolean isEmpty() { return front == arrInt.length; } /** * 判断队列是否已满 * * @return */ public boolean isFull() { return arrInt.length - 1 == rear; } /** * 向队列的队尾插入一个元素 */ public void insert(int item) { if (isFull()) { throw new RuntimeException("队列已满"); } arrInt[++rear] = item; } /** * 获得对头元素 * * @return */ public int peekFront() { return arrInt[front]; } /** * 获得队尾元素 * * @return */ public int peekRear() { return arrInt[rear]; } /** * 从队列的对头移除一个元素 * * @return */ public int remove() { if (isEmpty()) { throw new RuntimeException("队列为空"); } return arrInt[front++]; } }
6.Spring中bean的实现原理?
待解答:
7.SpringAOP的原理?
类别 |
机制 |
原理 |
优点 |
缺点 |
静态AOP |
静态织入 |
在编译期,切面直接以字节码的形式编译到目标字节码文件中。 |
对系统无性能影响。 |
灵活性不够。 |
动态AOP |
动态代理 |
在运行期,目标类加载后,为接口动态生成代理类,将切面植入到代理类中。 |
相对于静态AOP更加灵活。 |
切入的关注点需要实现接口。对系统有一点性能影响。 |
动态字节码生成 |
在运行期,目标类加载后,动态构建字节码文件生成目标类的子类,将切面逻辑加入到子类中。 |
没有接口也可以织入。 |
扩展类的实例方法为final时,则无法进行织入。 |
|
自定义类加载器 |
在运行期,目标加载前,将切面逻辑加到目标字节码里。 |
可以对绝大部分类进行织入。 |
代码中如果使用了其他类加载器,则这些类将不会被织入。 |
|
字节码转换 |
在运行期,所有类加载器加载字节码前,前进行拦截。 |
可以对所有类进行织入。 |
Spring AOP动态代理类:
1.使用CGlib动态代理:http://blog.csdn.net/yakoo5/article/details/9099133/
2.使用jdk实现动态代理:http://rejoy.iteye.com/blog/1627405
动态代理和静态代理的区别:
静态代理类优缺点
优点:业务类只需要关注业务逻辑本身,保证了业务类的重用性。这是代理的共有优点。
缺点:
1)代理对象的一个接口只服务于一种类型的对象,如果要代理的方法很多,势必要为每一种方法都进行代理,静态代理在程序规模稍大时就无法胜任了。
2)如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。
动态代理优缺点:
好处是接口中声明的所有方法都被转移到调用处理器一个集中的方法中处理(InvocationHandler.invoke)。
这样,在接口方法数量比较多的时候,我们可以进行灵活处理,而不需要像静态代理那样每一个方法进行中转
缺点:那就是它始终无法摆脱仅支持 interface 代理的桎梏,因为它的设计注定了这个遗憾。回想一下那些动态生成的代理类的继承关系图,它们已经注定有一个共同的父类叫 Proxy。
参考资料:http://layznet.iteye.com/blog/1182924
8.页面中出现乱码,怎么解决,在那几个地方解决?
1)在jsp 页面中的开头添加<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8" isELIgnored="false"%>
2)web.xml中加入
<!--form表单通过post传递的时候解决乱码问题 --> <filter> <filter-name>CharacterEncodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> <init-param> <param-name>forceEncoding</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>CharacterEncodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
3)在controller层中的注解加
@RequestMapping(value = "/ajaxContent", produces = "text/html;charset=UTF-8;")
4)在eclipse中对当前页面右键选择properties的other中选择utf-8(这个是解决Java代码中的注解乱码)
9.Stringbuffer 和stringbudier的区别?
1)在执行速度方面的比较:StringBuilder > StringBuffer > String
2)StringBuilder:线程非安全 StringBuffer:线程安全的
string 、stringbuffer和stringbudier三者使用的总结:
1.如果要操作少量的数据用 = String
2.单线程操作字符串缓冲区 下操作大量数据 = StringBuilder
3.多线程操作字符串缓冲区 下操作大量数据 = StringBuffer