一、数据库优化
1.这也是查询中尽量避免使用SELECT *
2.加上LIMIT限制的原因之一。查一条limit 1。
3.left join 连接查询比子查询速度快
4.加索引,但建议大家经常把表中一些不常用的索引删掉
5.缓存
6.分库分表,读写分离
7.数据库集群
8.很多时候用 exists 代替 in 是一个好的选择
9.应尽量避免在where子句中对字段进行函数操作
二、SpringBoot启动流程解析
启动类上的注解@SpringBootApplication包括三个注解,功能如下:
@EnableAutoConfiguration:SpringBoot根据应用所声明的依赖来对Spring框架进行自动配置
@SpringBootConfiguration(内部为@Configuration):被标注的类等于在spring的XML配置文件中
(applicationContext.xml),装配所有bean事务,提供了一个spring的上下文环境
@ComponentScan:组件扫描,可自动发现和装配Bean,默认扫描SpringApplication的run方法里的Booter.class所在的包
路径下文件,所以最好将该启动类放到根包路径下
首先进入run方法,run方法中去创建了一个SpringApplication实例,在该构造方法内,我们可以发现其调用了一个初始化
的initialize方法,这里主要是为SpringApplication对象赋一些初值。构造函数执行完毕后,我们回到run方法
该方法中实现了如下几个关键步骤:
1.创建了应用的监听器SpringApplicationRunListeners并开始监听
2.加载SpringBoot配置环境(ConfigurableEnvironment),如果是通过web容器发布,会加载StandardEnvironment,其最终
也是继承了ConfigurableEnvironment,可以看出,*Environment最终都实现了PropertyResolver接口,我们平时通过
environment对象获取配置文件中指定Key对应的value方法时,就是调用了propertyResolver接口的getProperty方法
3.配置环境(Environment)加入到监听器对象中(SpringApplicationRunListeners)
4.创建run方法的返回对象:ConfigurableApplicationContext(应用配置上下文),方法会先获取显式设置的应用上下文
(applicationContextClass),如果不存在,再加载默认的环境配置(通过是否是web environment判断),默认选择
AnnotationConfigApplicationContext注解上下文(通过扫描所有注解类来加载bean),最后通过BeanUtils实例化上下文
对象,并返回,主要看其继承的两个方向:
LifeCycle:生命周期类,定义了start启动、stop结束、isRunning是否运行中等生命周期空值方法
ApplicationContext:应用上下文类,其主要继承了beanFactory(bean的工厂类)
5.回到run方法内,prepareContext方法将listeners、environment、applicationArguments、banner等重要组件与上下文
对象关联
6.接下来的refreshContext(context)方法(初始化方法如下)将是实现spring-boot-starter-*(mybatis、redis等)自动化
配置的关键,包括spring.factories的加载,bean的实例化等核心工作。
配置结束后,Springboot做了一些基本的收尾工作,返回了应用环境上下文。回顾整体流程,Springboot的启动,主要创
建了配置环境(environment)、事件监听(listeners)、应用上下文(applicationContext),并基于以上条件,在容器中开
始实例化我们需要的Bean,至此,通过SpringBoot启动的程序已经构造完成,接下来我们来探讨自动化配置是如何实现。
自动化配置: 之前的启动结构图中,我们注意到无论是应用初始化还是具体的执行过程,都调用了SpringBoot自动配置模块
该配置模块的主要使用到了SpringFactoriesLoader,即Spring工厂加载器,该对象提供了loadFactoryNames方法,入参为
factoryClass和classLoader,即需要传入上图中的工厂类名称和对应的类加载器,方法会根据指定的classLoader,加载
该类加器搜索路径下的指定文件,即spring.factories文件,传入的工厂类为接口,而文件中对应的类则是接口的实现类
,或最终作为实现类,所以文件中一般为如下图这种一对多的类名集合,获取到这些实现类的类名后,loadFactoryNames
方法返回类名集合,方法调用方得到这些集合后,再通过反射获取这些类的类对象、构造方法,最终生成实例
三、SpringMVC 处理器适配器详解
处理器适配器 HandlerAdapter:作用是根据映射器找到的处理器 Handler 信息,按照特定的规则去执行相关的处理器
Handler。
其配置方式有两种,一种是基于 xml 的资源配置,也就是非注解的配置方式。另外一种就是基于 Annotation
注解的配置。其注解在代码中做上特殊标记,这些标记就可以编译、类加载、运行时被读取,然后去执行相应的处理。
第一种
第二种mvc:annotation-driven</mvc:annotation-driven>
SpringMVC 中处理器映射器 HandlerMapping 根据配置找到相应的 Handler,返回给前端控制器 DispatcherServlet,前
端控制器再传给处理器适配器让它进行处理,处理器适配器会去找到对应的 Handler 去处理,处理后则就会返回一个
ModleAndView 对象。
映射器主要是跟我们在浏览器上输入的url来映射对应的Handle,而适配器主要是决定调用哪个Handler来实现具体的业务逻
辑,
四、Oracle
Oracle数据库分页的三种方法
– 不能对ROWNUM使用>(大于1的数值)、>=(大于或等于1的数值)、=(大于或等于1的数值),否则无结果
– 所以直接用只能从1开始
– rownum >10 没有记录,因为第一条不满足去掉的话,第二条的rownum又成了1,所以永远没有满足条件的记录。
select * from student where rownum>=1;
–如果想要用rownum不从1开始,需按下面方法使用
select a1.* from (select student.*,rownum rn from student) a1 where rn >5;
–分页查询一
select * from (select a1.*,rownum rn from (select * from student) a1 where rownum <=5) where rn>=2;
–分页查询二
select a1.* from (select student.*,rownum rn from student where rownum <=5) a1 where rn >=3;
–分页查询三
select a1.* from (select student.*,rownum rn from student) a1 where rn between 3 and 5;
五、redis例子
@Override
public List getContentList(long cid) {
// 查询数据库之前,先查询缓存,并且添加缓存不能影响正常业务逻辑
try {
String json = jedisClient.hget(CONTENT_KEY, cid + “”);
// 判断是否命中缓存,判断json字符串是否为null或""
if (StringUtils.isNotBlank(json)) {
// 把这个json转换成List集合
List list = JsonUtils.jsonToList(json, TbContent.class);
return list;
}
} catch (Exception e) {
e.printStackTrace();
}
// 根据内容分类id查询内容列表
TbContentExample example = new TbContentExample();
// 设置查询条件
Criteria criteria = example.createCriteria();
criteria.andCategoryIdEqualTo(cid);
// 执行查询
List list = contentMapper.selectByExample(example);
// 向缓存中保存结果,并且添加缓存不能影响正常业务逻辑
try {
jedisClient.hset(CONTENT_KEY, cid + “”, JsonUtils.objectToJson(list));
} catch (Exception e) {
e.printStackTrace();
}
return list;
}
缓存有个问题就是如果数据库表中的数据做了修改,缓存是需要同步的,否则查询的还是老数据,因此凡是涉及增、删、
改的操作都需要同步缓存。我们以添加内容为例,应将ContentServiceImpl类中的insertContent方法修改为:
public TaotaoResult insertContent(TbContent content) {
// 补全pojo的属性
content.setCreated(new Date());
content.setUpdated(new Date());
// 向内容表中插入数据
contentMapper.insert(content);
// 做缓存同步,清除redis中内容分类id对应的缓存信息
jedisClient.hdel(CONTENT_KEY, content.getCategoryId().toString());
return TaotaoResult.ok();
}
六、tomcat配置jvm内存
Tomcat默认可以使用的内存为128MB,在较大型的应用项目中,这点内存是不够的,需要调大。
Windows下,在文件/bin/catalina.bat,Unix下,在文件/bin/catalina.sh的前面,增加如下设置:
JAVA_OPTS=’-Xms【初始化内存大小】
-Xmx【可以使用的最大内存】’
需要把这个两个参数值调大。例如:
JAVA_OPTS’-Xms256m-Xmx512m’
表示初始化内存为256MB,可以使用的最大内存为512MB。
七、事务的传播行为
事务传播行为用来描述由某一个事务传播行为修饰的方法被嵌套进另一个方法的时事务如何传播。
用伪代码说明:
public void methodA(){
methodB();
//doSomething
}
@Transaction(Propagation=XXX)
public void methodB(){
//doSomething
}
代码中methodA()方法嵌套调用了methodB()方法,methodB()的事务传播行为由@Transaction(Propagation=XXX)设置决定
。这里需要注意的是methodA()并没有开启事务,某一个事务传播行为修饰的方法并不是必须要在开启事务的外围方法中调
用。
Spring中七种事务传播行为
事务传播行为类型 说明
PROPAGATION_REQUIRED 如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务 中。这是最常见的选择。
PROPAGATION_SUPPORTS 支持当前事务,如果当前没有事务,就以非事务方式执行。
PROPAGATION_MANDATORY 使用当前的事务,如果当前没有事务,就抛出异常。
PROPAGATION_REQUIRES_NEW 新建事务,如果当前存在事务,把当前事务挂起。
PROPAGATION_NOT_SUPPORTED 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
PROPAGATION_NEVER 以非事务方式执行,如果当前存在事务,则抛出异常。
PROPAGATION_NESTED 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与
PROPAGATION_REQUIRED类似的操作。
八、jsp
tomcat扫描的这个jsp文件,把jsp文件翻译成java源文件.java,这个过程就是翻译。
tomcat服务器把刚刚翻译得到的java源文件编译成class字节码文件.class,(编译过程)。
jsp间接继承了HttpServlet,所以jsp就是servlet.