0%

前言

最早接触红黑树是大一下学期的时候有个同学写了一篇相关的论文,现在打算花点时间好好整理一下。

介绍

Red-Black Tree,翻译成红黑树。红黑树本身是一种二叉搜索树,所以必然是任意一个节点大于左子树所有的节点,小于右子树的所有节点。除了满足二叉搜索树,它还有以下特点:

  1. 每个节点要么是黑色,要么是红色。
  2. 根节点和叶子节点必然是黑色的。[这里稍微注意下,每个叶子节点都是null,即和我们平时写的不太一样]
  3. 如果一个节点是红色的,则它的子节点必须是黑色的。
  4. 从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点。即黑色的高度是一样的。

显然第四点能够保证它的相对平衡性(只会因为红色节点的存在而稍微破坏一下平衡性)。因为之前学过二叉平衡树,靠的是旋转来达到高度的平衡的,但是二叉搜索树的效率低,所以才有了红黑树这种相对不那么平衡但是效率高的多的数据结构。

平衡

三个步骤确保红黑树的相对平衡:

  • 变色:很好理解,节点由红色变成黑色或者由黑色变成红色
  • 左旋:和AVL树一致
  • 右旋:和AVL树一致

查找

因为本质是二叉搜索树,所以没什么好讲的…

插入

Emmmm 插入就比较麻烦了…..首先插入必须是插入红色的节点(除了空树这种情况),因为如果是黑色的节点,就肯定会破坏“黑色节点高度一致”这一条,所以必须是插入红色的,那么接下来分类讨论即可。

空树

那不多说,这个要插入的节点必然是根节点,且根节点必须是黑色的。

节点已经存在

只需要把value替换一下,其它啥都不用做。

父节点为黑色

直接插入即可,并不会违反任何平衡性的东西。

父节点为红色

如果要插入到的节点的父节点是红色的,那么由于我们要插入的节点也是红色的,违反了“红色节点的子节点必须是黑色”这一原则,所以需要进一步推理:由于根节点是必然是黑色的,而现在的父节点是红色,所以必然有爷爷节点,且爷爷节点必然是黑色的,在此基础上进行分类讨论:

  • 如果叔叔节点存在且为红色:把父节点和叔叔节点改成黑色,把爷爷改成红色,并将爷爷节点设置为当前节点,然后重复新的判断。
  • 如果叔叔不存在,或者叔叔为黑色(这里似乎是不可能的):
    • 如果父节点为爷爷节点的左节点,同时要插入的点也是父亲节点的左节点,那么就是LL双红情况,处理的方法是:把父节点设成黑色,爷爷节点设成红色,并对爷爷节点进行右旋。
    • 如果父节点为爷爷节点的左节点,同时要插入的点是父亲节点的右节点,即LR双红的情况,处理的方法是:左旋父亲节点,就可以得到上面的LL双红情况,然后继续即可。
    • 如果父节点为爷爷节点的右节点,同时要插入的点是父亲节点的右节点,那么就是RR双红情况,处理的方法是:把父节点设成黑色,爷爷节点设成红色,并对爷爷节点进行左旋。
    • 如果父节点为爷爷节点的右节点,同时要插入的点是父亲节点的左节点,那么就是RL双红情况,处理的方法是:右旋父亲节点,得到RR双红,继续即可。

代码实现

首先是一课树的基本架构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class RBTree<K extends Comparable<K>, V> {

/**
* 根节点
*/
private RBNode<K, V> root;
private static final boolean RED = true;
private static final boolean BLACK = false;


/**
* 红黑树的节点
*/
public class RBNode<K extends Comparable<K>, V> {
private boolean color;
private K key;
private V value;
private RBNode<K, V> left;
private RBNode<K, V> right;
private RBNode<K, V> parent;

public RBNode(boolean color, K key, V value, RBNode<K, V> left, RBNode<K, V> right, RBNode<K, V> parent) {
this.color = color;
this.key = key;
this.value = value;
this.left = left;
this.right = right;
this.parent = parent;
}
}
}

接下来需要定义一下打印的方法,为了后续方便我选择的是中序遍历;然后为了方便还自己动手定义了如何左旋一个节点和如何右旋一个节点,最后就是查找节点并进行插入的过程,按照上面的进行判断非常之容易,代码这里先省略了。

前言

学习完了三大框架,但是始终没有做一个真正的整合,这次打算在这里记录下。

依赖

简单梳理下这三大框架和一些必须的依赖:

  • 单元测试 junit4,5的话不太了解,这个有机会再看看
  • web相关的包,分别是javax.servlet-apijsp-apijstl-api
  • 数据库相关的,我这里用的是mysql,所以驱动用的是mysql-connector-java,以及相关的连接池druid
  • mybatis框架需要的mybatismybatis-spring
  • spring需要的就…太多了

依赖搞定之后就可以上手了,其实三个框架先配哪一个我个人觉得问题不大,我个人习惯是先创建好数据库,然后在项目中写好对应的Controller、Dao、Service等基本骨架,然后开搞。

Spring

我首先选择配置spring,这样我只需要一个spring的配置文件,然后之后用注解的方式将所有的对象交给IOC容器来管理。所以首先在resource下面创建一个applicationContext.xml文件,写好约束并且配置好开启注解扫描。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?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:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
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
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">

<context:component-scan base-package="cn.chenlangping">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>

</beans>

然后随便找个服务类,进行测试,看是否可以使用IOC容器获取对象即可。

Spring到此就完成了。

SpringMVC

第一步,配置web.xml,分别写好三大组件——filter、servlet和listener(listener是为了和spring框架进行整合,如果单独测试可以暂时不需要listener,我下面是带了listener的版本):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
<display-name>Archetype Created Web Application</display-name>

<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>
</filter>

<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

<!--默认加载WEB-INF下面的applicationContext.xml文件-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!--所以修改成resources下面的配置文件-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>

<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>

</web-app>

第二步,到对应的springmvc.xml文件中,配置好四步骤:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">

<!--开启注解扫描,只扫描Controller注解-->
<context:component-scan base-package="cn.chenlangping">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" />
</context:component-scan>

<!--配置的视图解析器对象-->
<bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/pages/"/>
<property name="suffix" value=".jsp"/>
</bean>

<!--过滤静态资源-->
<mvc:resources location="/css/" mapping="/css/**" />
<mvc:resources location="/images/" mapping="/images/**" />
<mvc:resources location="/js/" mapping="/js/**" />

<!--开启SpringMVC注解的支持-->
<mvc:annotation-driven/>

</beans>

第三步,如果你在第一步中没有配置监听器,就配置监听器,这样就能让servletContext在加载的时候加载一下applicationContext.xml,也就是能让ioc容器生效。到此就可以通过在controller里面进行配置来调用service对象了。

即SpringMVC和Spring的结合就此完成。

Mybatis

第一步,配置核心数据库的连接内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 配置连接数据库的信息 -->
<properties resource="jdbcConfig.properties"></properties>
<!-- 配置mybatis的环境 -->
<environments default="mysql">
<environment id="mysql">
<!-- 配置事务管理 -->
<transactionManager type="JDBC"/>
<dataSource type="pooled">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>

<!-- 指定映射文件位置 -->
<mappers>
<package name="cn.chenlangping.dao"/>
</mappers>
</configuration>

第二步,直接进行测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class TestMybatis {

@Test
public void testMybatis() throws IOException {
InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session = factory.openSession();
AccountDao mapper = session.getMapper(AccountDao.class);
List<Account> list = mapper.findAll();
for (Account account : list) {
System.out.println(account);
}
session.close();
inputStream.close();
}
}

如果能获得数据库的内容,说明连接成功。

最后只需要将mybatis和前面两者进行整合就可以了,思路是这样的,mybatis会帮我们生成代理对象,所以我们只需要让spring的IOC容器替我们管理这些代理对象即可完成整合了。核心思想是需要配置三件套:

  • 连接池,这个不多说,肯定需要
  • SqlSessionFactory,也是必须的,有了它就可以创建sqlSession
  • 配置接口所在的包。这里最难理解,是因为需要有了接口所在的包才能够生成代理对象。

上面的第一点和第三点,其实就是相当于之前单独的mybatis的主配置文件,这么一讲是不是更容易理解了呢?

所以我们只需要在applicationContext中加入三个对象,让它们收到IOC容器的管理即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!--数据库连接池-->
<bean id="datasource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="url" value="jdbc:mysql://....."/>
<property name="username" value="root"/>
<property name="password" value="......"/>
</bean>

<!--sqlSessionFactory对象-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="datasource"/>
</bean>

<!--要扫描的包-->
<bean id="mapperScanner" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="cn.chenlangping.dao"/>
</bean>

配置完这一步我们可以发现,其实之前的SqlMapConfig.xml已经完全没用了,删除即可。

到此,三大框架整合完毕。

https://blog.csdn.net/ncepu_Chen/article/details/91903396

解耦

从解耦开始聊,耦合就是程序的依赖关系。为什么需要解耦,因为实际中需求变化多端,你如果写死了你代码,将来要改会非常非常恶心。但是彻底的解除耦合是不可能的,因为就像这个社会一样,没有人能够不和其他人发生关系,程序中的类和方法也必然会和别的类、方法发生关系。

比如加载数据库驱动的代码:

1
2
3
4
5
// 最常用的
Class.forName("com.....");

// 其实也可以这么写
DriverManager.registerManager(new xxx.xxx.xxx.Driver());

我们为什么一般都是第一种而不是第二种呢,它们本质上是一样的,都是加载了jar包中的类,然后注册那个驱动。

第一,如果你的包不存在,那么反射的写法在编译的时候是不会报错的,但是第二种会报错。所以第一个原则就是:编译期间不去依赖,到运行的时候才去依赖,也就是少用new,而是使用反射来创建对象。

第二,将来你要是想要修改数据库驱动,那么你需要直接修改源代码,所以第二个原则就是使用配置文件

实际使用中,比如最常见的三层架构,表现层需要new一个业务层的对象来进行处理,业务层也需要new一个持久层的对象来进行数据库的处理,这就造成了耦合。所以由此获得灵感,我们可以使用工厂模式,配合上反射+使用配置文件的方式来完成解耦。

工厂模式还有一个小小的缺点,就是每次创建对象都是不同的,所以我们需要使用单例模式来让其在内存里只有一个对象,当然也可以用一个map来进行操作,在类加载的时候初始化这个map,之后要用拿出来就行了。

IOC

平时我们创建对象,都是new一个出来。相当于你需要直接和目标打交道;当有了工厂之后,你是直接去找工厂索要对象,工厂本身通过一些方式来控制对象(如上面的map)。所以,这就叫Inversion of Control,IOC控制反转。原来完全由你来主导的,变为了你传递一个字符串给工厂,由工厂来帮你创建对象,你再也无法决定来new谁了。

1
2
3
4
5
6
7
8
9
10
// 获取核心容器
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");

// 根据核心容器就可以获取对象了,分别是强转和直接传入类对象来构建
IAccountService as = (IAccountService) ac.getBean("accountService");
IAccountDao ad = ac.getBean("accountDao", AccountDaoImpl.class);

// 获取成功
System.out.println(as);
System.out.println(ad);

这里的核心容器即为ApplicationContext,它是一个接口,底下有三个实现类,分别是:

  • ClassPathXmlApplicationContext,加载类路径下面的配置文件。
  • FileSystemXmlApplicationContext,加载任意路径下的配置文件,但是不推荐使用
  • AnnotationConfigApplicationContext,加载注解的配置文件。

这个核心容器采用的思想是立即加载,即只要读取完配置文件,就会通过反射创建好对象放入到容器里。还有另外一个核心容器叫BeanFactory,只有当需要对象的时候才会new出来。

1
2
3
4
5
6
7
8
9
10
11
// 获取核心容器
Resource resource = new ClassPathResource("bean.xml");
BeanFactory factory = new XmlBeanFactory(resource);

// 根据核心容器就可以获取对象了,分别是强转和直接传入类对象来构建
IAccountService as = (IAccountService) factory.getBean("accountService");
IAccountDao ad = factory.getBean("accountDao", AccountDaoImpl.class);

// 获取成功
System.out.println(as);
System.out.println(ad);

由于service和dao本质上是单例,所以比较推荐第一种,即只要读取完配置文件就创建。

bean

首先说明,之前我在大学上课的时候,老师讲的是,javabean就是一个拥有getter、setter和类变量的类。但是其实不是的,bean表示的是可以重复使用的对象,当然之前的实体类要说是bean也是对的,只是实体类是bean的一种。

创建bean的三种方式

  1. 直接使用类的默认构造器来构造对象,这样只需要一个bean标签,里面写好id和全限定类名即可。如果没有默认构造函数,就会失败!
1
2
3
<bean id="accountDao" class="cn.chenlangping.dao.impl.AccountDaoImpl">
<!-- collaborators and configuration for this bean go here -->
</bean>
  1. 某个工厂类中的某个方法返回值是我们需要的对象。我们首先用一个bean对象指定工厂,然后再写一个bean,在标签里面写入对应的工厂名和工厂方法即可。
1
2
3
4
5
6
// 工厂类
public class MyFactory {
public AccountServiceImpl getAccountService() {
return new AccountServiceImpl();
}
}
1
2
<bean id="myFactory" class="cn.chenlangping.factory.MyFactory"></bean>
<bean id="accountService" factory-bean="myFactory" factory-method="getAccountService"></bean>
  1. 工厂类中的静态方法会返回一个我们需要的对象。
1
<bean id="accountService" class="cn.chenlangping.factory.MyFactory" factory-method="getAccountService"></bean>

2和3的区别也很好理解,因为2中的不是静态方法,所以需要通过反射来构建一个工厂,而3则不需要,直接就可以通过工厂来获取。

bean的作用范围

默认情况下,bean是一个单例。可以通过标签下的scope标签来进行调整。

  • singleton:默认值,单例
  • prototype:多例的。
  • request:作用于web应用的请求范围。
  • session:作用于Web应用的会话范围。
  • global-session:作用于集群的环境的范围。不是集群就是session。

bean的生命周期

不同类型的对象,生命周期不同。

  • 单例对象:随容器一起,容器创建它创建;容器存在它活着;容器销毁它死亡。
  • 多例对象:使用对象的时候容器为我们创建;对象只要使用的时候就一直活着;通过垃圾回收机制来回收。

依赖注入

通过控制反转,我们把创建对象的任务托管给了spring框架,但是代码中不可能消除所有依赖,例如:业务层仍然会调用持久层的方法,因此业务层类中应包含持久化层的实现类对象。
我们使用框架,通过配置的方式,将持久层对象传入业务层,而不是直接在代码中new某个具体的持久化层实现类,这种方式称为依赖注入

注入方式

使用构造函数注入

由于构造函数大部分都是会带参数的,这个时候我们就需要向spring框架提供信息来让其能够创建对象。<constructor-arg>标签就是用来做这个事情的,它有以下五个属性(我们需要确定参数的位置和参数的类型):

  1. index:指定参数在构造函数参数列表的索引位置,从0开始,用它可以唯一标识。
  2. type:指定数据的数据类型。但是很多情况下只靠参数的类型其实是无法确定是哪个参数的,所以一般要配合别的使用。
  3. name:指定参数的名字来找参数。一般都用它。
  4. value:参数的值,比如如果你要传入一个int,你需要写value="18",它是为了给基本类型和String类型赋值的。
  5. ref:对于不是基本类型的,比如date,我们如果要取它作为参数,那么需要首先写一个标签bean,然后定义好全限定类名和id,最后回到别的类里就可以用id使用了。

它的弊端就是,你必须提供构造函数的所有参数,一个都不能少。

使用setXxx函数注入

首先你的类必须为各种属性设立了各种setter方法,标签只剩下namevalueref三个了,name属性中写入你setXxx方法后面去掉set并且将首字母变为小写的值。

这个的好处就是解决了上面使用构造函数注入的缺点。但是缺点是无法保证某个属性必须有值。

集合类型注入

还有一些ListSet等集合类型的注入:

只有键的结构:

  • 数组字段: <array>标签表示集合,<value>标签表示集合内的成员。
  • List字段: <list>标签表示集合,<value>标签表示集合内的成员。
  • Set字段: <set>标签表示集合,<value>标签表示集合内的成员。
  • 其中<array><list><set>标签之间可以互相替换使用。

键值对的结构:

  • Map字段:<map>标签表示集合,<entry>标签表示集合内的键值对,其key属性表示键,value属性表示值。
  • Properties字段:<props>标签表示集合,<prop>标签表示键值对,其key属性表示键,标签内的内容表示值。
  • 其中<map><props>标签之间,<entry><prop>标签之间可以互相替换使用。

使用注解

之前一直使用配置文件,这里我们使用注解来简化操作。

创建对象

这些注解的作用相当于bean.xml中的<bean>标签。如果要使用注解,记得在bean.xml中加入扫描包的代码。

  • @Component:把当前类对象存入spring容器中,有一个属性value
    • value: 用于指定当前类的id。如果不写,则默认值是当前类名首字母改小写
  • @Controller:将当前表现层对象存入spring容器中
  • @Service:将当前业务层对象存入spring容器中
  • @Repository:将当前持久层对象存入spring容器中

上面这四个注解,除了名字以外其实完全一致,取不同的名字就是为了区别不同的作用层。也就是我们之后如果想创建一个对象并将其加入到容器中,那么只需要在定义类上面加个注解即可。

注入数据

这里我只想写一个:@Resource,直接按照bean的id注入。

改变作用域

@Scope,其值和之前作用域的五个值一样。

生命周期

  1. @PostConstruct:用于指定初始化方法
  2. @PreDestroy:用于指定销毁方法,别忘了如果是多例的话,是由虚拟机来决定是什么时候摧毁的。

其它部分

那么还有一些问题,有些类我们无法为其加入注解,那应该怎么办呢?比如jar包中的类,很显然我们无法修改其代码,自然也不能为其加入注解。还有一个问题是,上面也提到了,还是需要在bean.xml中加入扫描包的代码,虽然已经很简化了,那有没有办法甚至连bean.xml都不需要呢?

首先来解决第二个问题,我们希望用纯的注解来解决问题,即完完全全删除bean.xml。创建一个类,然后再这个类上面加上@Configuration注解,那么这个类就成为了一个配置类,之后spring会来找它创建容器。然后再这个类上面再加上@ComponentScan("包地址"),这样就完成了基础配置,就不需要bean.xml中扫描包的代码了。

1
2
3
4
@Configuration
@ComponentScan("cn.chenlangping")
public class SpringConfiguration {
}

接下来解决第一个问题,也就是我们需要为那些我们不能加注解的类来生成bean并且放入容器中,当然我们可以通过在之前那个配置类中创建方法,然后调用方法来创建新的对象来达成,但是这么做并不会将新创建的对象放入到容器中,我们需要在方法上加上@Bean这个注解,这样被它注解的方法的返回值就能够成功被放入容器中了。

和Junit整合

由于Junit并不知晓spring框架,所以我们需要加入spring-test来完成单元测试功能。

AOP

Aspect Oriented Programming,面向切面编程,通过使用动态代理的技术,来降低程序的耦合。所以有必要了解下动态代理技术。

动态代理技术

动态代理技术作为设计模式中的一种,它的主要目的是为了给函数增强它的功能,比如某个函数在写的时候没有加上日志的功能,这个时候就需要在运行的时候为它加上这个功能。目前主要的动态代理有两种,一种是JDK自带支持的,另外一种是cglib提供的。

JDK自带的

原理:客户端会调用接口的方法。运行时根据目标类动态创建代理类,代理类和目标类实现相同的接口。调用方调用代理类,代理类反射调用目标类。

优点:JDK自带,完全不需要导包。

缺点:被代理的类必须实现一个接口才可以。

示例代码:

1
2
3
4
// 一个普通的接口,里面就一个打印方法的定义
public interface IPrinter {
void print();
}
1
2
3
4
5
6
7
// 实现类
public class Printer implements IPrinter {
@Override
public void print() {
System.out.println("print");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 动态代理它
public static void main(String[] args) {
IPrinter iPrinter = new Printer();
iPrinter.print();

IPrinter iPrinter2 = (IPrinter) Proxy.newProxyInstance(Printer.class.getClassLoader(), Printer.class.getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before");
Object returnValue = method.invoke(iPrinter, args);
System.out.println("after");
return returnValue;
}
});
iPrinter2.print();
}

简单的来说就是通过Proxy的静态方法来创建一个对象,最后调用这个对象的方法即可。比较烦的是如何创建一个代理对象,首先传入被代理对象的类的类加载器、它们共通的接口以及一个InvocationHandler实例(上面使用了匿名内部类,实际上还可以用lambda表达式),分别有三个参数,是代理的对象、执行的方法和方法所带的函数。

cglib提供的

由于JDK自带的代理需要某个类实现了接口才可以动态代理,就有了这个第三方的jar实现。

原理:运行时根据目标类动态创建代理类,代理类是目标类的子类。然后通过这个子类来实现父类中的方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static void main(String[] args) {
Printer iPrinter = new Printer();
iPrinter.print();

Printer printer = (Printer) Enhancer.create(iPrinter.getClass(), new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("before");
Object returnValue = method.invoke(iPrinter, objects);
System.out.println("after");
return returnValue;
}
});
printer.print();
}

实现代码几乎和上面的JDK的一样,只是少了一个接口,但是因为它是通过子类实现的,所以实现类中独有的那些方法也可以被加强,这是接口所做不到的。

优点:只要一个类它不是final的就可以被加强。

Spring中的AOP

作用:之前也讲到了,在实际中遇到重复的代码,我们的做法是抽取出来并且封装成函数来调用,但是这么做会造成方法之间的依赖,于是就有了Spring的AOP帮我们解决这个问题。注意,需要依赖aspectjweaver,请不要忘记添加这个依赖。

假设需要为某个类中的某个方法进行增强,比如为这个方法加上一个日志记录的功能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 定义接口
public interface INormal {
void test1();

void test2(int i);

int test3();
}

// 实现类
public class NormalImpl implements INormal {
@Override
public void test1() {
System.out.println("test1");
}

@Override
public void test2(int i) {
System.out.println("test2");
}

@Override
public int test3() {
System.out.println("test3");
return 0;
}
}

现在需要为这里的三个方法增加一个日志记录的功能,所以首先需要去定义一个日志类,并在其内部完成功能:

1
2
3
4
5
public class Logger {
public void printLog(){
System.out.println("打印日志");
}
}

最后就是配置Spring,让它把我们定义的打印日志的功能,加入到上面三个方法中。

通过XML的方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?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:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">

<!-- 配置两个bean对象放入容器中 -->
<bean id="normal" class="cn.chenlangping.aop.NormalImpl"></bean>
<bean id="logger" class="cn.chenlangping.aop.Logger"></bean>

<!-- 配置AOP -->
<aop:config>
<!-- 配置切面 -->
<aop:aspect id="logAdvice" ref="logger">
<aop:before method="printLog" pointcut="execution(public void cn.chenlangping.aop.NormalImpl.test1())"/>
<aop:after method="printLog" pointcut="execution(public void cn.chenlangping.aop.NormalImpl.test2(int))"/>
</aop:aspect>
</aop:config>
</beans>

真心觉得特别简单,放入IOC容器这个是必须的,否则spring怎么帮你创建对象并管理呢?然后只要想好,哪个类的哪个函数,需要和另外一个类中的一个函数发生关系,是怎么样的关系即可。

通过注解的方式

首先是需要实现类成为一个组件(相当于声明为bean对象,此处略),然后需要修改下Logger类,为他加上几个注解即可:

1
2
3
4
5
6
7
8
@Component
@Aspect
public class Logger {
@Before("execution(public void cn.chenlangping.aop.NormalImpl.test1())")
public void printLog(){
System.out.println("打印日志");
}
}

xml文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?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:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">

<context:component-scan base-package="cn.chenlangping.aop"/>
<aop:aspectj-autoproxy/>

</beans>

最后再结合xml文件,只需要简单的定义扫描的包和开启AOP支持即可。虽然可以用类来使用纯注解,但是我个人认为还是xml结合注解会方便的多。

Spring JdbcTemplate

对JDBC进行了一些简单的封装。

框架

首先Mybatis是一套框架。框架是为了解决软件开发中遇到的一些问题而生的,框架一般会封装很多细节,能让开发者可以减少工作量、提高工作效率。阿里巴巴内部对这个框架用了很多。

  • 表现层:代表框架:SpringMVC,用以展示数据。
  • 业务逻辑层:主要靠servicejavabean来完成,用来完成业务的处理。
  • 持久层:mybatis,和数据库进行交互,向外提供一个DAO接口

目前在Java中,原生的技术有JDBC,包含了ConnectionPreparedStatement之类的,然后当然有一些简单封装的工具类,比如ApacheDBUtils等。

mybatis在内部封装了jdbc,使得开发者只需要关注于sql语句本身,其它的加载驱动、创建连接等,我们都可以通过注解或者配置xml文件来进行处理。还有一个就是能够直接返回一个对象,便于开发人员直接操作java对象。

之前的流程

我们连接数据库,大体可以分成这几个步骤:

  • 配置数据库的信息
  • 连接到数据库
  • 对数据库进行操作
  • 获取了对应的数据之后进行处理
  • 一些善后工作等

当然之前我们都是通过编写对应的代码,一些数据库的信息都是直接写死在了对应的代码里面,这样耦合很高,而且也不安全,需要切换的时候也非常不方便。

环境搭建

首先因为是和数据库相关的框架,相关的数据库必须得有,所以创建一个:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
-- 创建数据库
CREATE DATABASE IF NOT EXISTS db_mybatis CHARACTER SET utf8;
-- 使用数据库
USE db_mybatis;
-- 创建数据表
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` int(11) NOT NULL auto_increment,
`username` varchar(32) NOT NULL COMMENT '用户名称',
`birthday` datetime default NULL COMMENT '生日',
`sex` char(1) default NULL COMMENT '性别',
`address` varchar(256) default NULL COMMENT '地址',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- 添加记录
insert into `user`(`id`,`username`,`birthday`,`sex`,`address`) values
(41,'老王','2018-02-27 17:47:08','男','北京'),
(42,'小二王','2018-03-02 15:09:37','女','北京金燕龙'),
(43,'小二王','2018-03-04 11:34:34','女','北京金燕龙'),
(45,'传智播客','2018-03-04 12:04:06','男','北京金燕龙'),
(46,'老王','2018-03-07 17:37:26','男','北京'),
(48,'小马宝莉','2018-03-08 11:44:00','女','北京修正');

数据准备好了之后,就可以开始编写代码了,具体步骤如下:

创建项目并初始化

创建空maven工程并且写好数据库驱动依赖和mybatis的依赖。

首先的pom里面增加对应的依赖(还有mysql的驱动、Junit这些的自己添加):

1
2
3
4
5
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>x.x.x</version>
</dependency>

并且在类路径下(一般推荐在resource下,如果不是在resource下面,那么默认情况下是不会打包进项目中的,这点切记,当然可以通过配置maven来让对应的后缀的文件加入到类路径中)创建一个SqlMapConfig.xml(当然叫什么其实是随意的,反正代码能找到就行),然后在里面配置好对应的数据库相关信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<environments default="mysql">
<environment id="mysql">
<!-- 配置事务 -->
<transactionManager type="JDBC"/>
<!-- 连接池 -->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url"
value="jdbc:mysql://localhost/df?useUnicode=true&amp;characterEncoding=UTF-8&amp;serverTimezone=GMT%2B8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>

<!-- 对应的sql映射文件所在位置 -->
<mappers>
<mapper resource="com/df/dao/IDeviceInfoDao.xml"/>
</mappers>
<!-- 如果上面的内容没有的话,就会报Type interface cn.clp.mapper.UserMapper is not known to the MapperRegistry.-->

</configuration>

到这里准备工作就做好了。PS:上面那个<mappers>里面的映射关系是用来管理对应的sql语句的。

创建dao的接口和实体类

就是到项目下面,创建一个接口,这个接口后面就只需要定义对应的方法,比如查找的方法就可以了。不需要任何的实现方法,这里也是mybatis最与众不同的地方,它使用了配置文件来代替实现类完成对应的增删改查功能。

为对应的数据库封装对应的对象,因为我们最后肯定是要把数据库的内容读取出来到对象的嘛,那就先创建好这些对象,然后这些对象也可以在上面创建的接口中被使用。

创建对应的映射文件

这个文件就是上面初始化的配置里面mapper所对应的文件。在这个文件里面写好对应的sql语句,然后就可以执行里面的sql语句了。所以可以把这个文件当成是一座桥梁,把之前定义的接口和sql语句联系在了一起,而这个sql语句其实就是接口的实现函数。

一个demo如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.df.df_back.dao.IDeviceInfoDao">

<resultMap id="ApplicationResultMap" type="com.df.df_back.domain.Application">
<result property="id" column="id"/>
<result property="applicationInfo" column="application_info"/>
</resultMap>
<select id="findAllApplication" resultMap="ApplicationResultMap">
select id, application_info
from application;
</select>
</mapper>

可以看到上面我指定了namespace就是对应的接口,而里面的就是一个select语句。你看它为什么叫mapper,因为它就是连接了接口和对应的实现类。

到这里就完成了,值得注意的是:

  • Mybatis中,持久层的操作接口名称和映射文件也叫Mapper,所以UserMapperIUserDao是一样的
  • 映射配置文件的mapper标签namespace属性的取值必须是mapper接口(即DAO接口)的全限定类名
  • 映射配置文件的操作配置,id 属性的取值必须是mapper接口的方法名

看上去是不是一头雾水?什么破玩意儿啊,我接下来用我自己的语言组织下。

首先,你要连接数据库,那么必须要指定驱动、url、用户名和密码,这是肯定省不了的吧?这就对应了上面说的主配置文件。然后对于一个类的方法,你首先必须找到一条sql语句与之对应吧?这个就是第二个配置文件的作用,它让一条sql语句和一个接口(当然也可以是类)中的方法产生了联系。而我们之后会有许许多多的类,也就是会有很多的xml配置文件,这些配置文件需要将自己所在的位置写入到主配置文件中,这样就完成了。

还觉得麻烦吗?对,反正我觉得很麻烦,那怎么办呢?用注解来简化下操作呗。

分析和还原

首先这是主要的一些代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public static void main(String[] args) throws Exception {
// 1. 读取配置文件
InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
// 2. 通过配置文件创建SqlSessionFactory工厂
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(is);
// 3. 获取 SqlSession 对象
SqlSession sqlSession = factory.openSession();
// 4. 使用 SqlSession 创建 Mapper 的代理对象
IUserDao iUserDao = sqlSession.getMapper(IUserDao.class);
// 5. 使用代理对象执行查询
List<User> users = iUserDao.findAll();
for (User user : users) {
System.out.println(user.getUsername());
}
// 6. 释放资源
sqlSession.close();
is.close();
}

我们简单分析下,就可以自己来完成这个框架的简单替换了(即自己完成mybatis的核心功能)。一般来说,实际中我们推荐前三步使用工具类进行封装,这样需要的时候可以直接返回sqlSession进行使用。

整体流程是这样的:配置文件 -> SqlSessionFactoryBuilder -> SqlSessionFactory -> SqlSession ->操作数据库

  1. 读取xml配置我们可以用dom4j来完成。为了简便,可以直接硬编码….
  2. 创建工厂这步,我们只需要用到工厂模式就可以很轻松的完成啦
  3. 动态代理技术

首先,我们对主配置文件中的四个信息(driver、url、username和password)进行封装,构成一个configuration的类,然后构建一个工具类,能够获取connection对象:

1
2
3
4
5
6
7
public class DataSourceUtil {
public static Connection getConnection(Configuration configuration) throws ClassNotFoundException, SQLException {
Class.forName(configuration.getDriver());
Connection connection = DriverManager.getConnection(configuration.getUrl(), configuration.getUsername(), configuration.getPassword());
return connection;
}
}

本来是想用连接池的,但是这样会增加复杂度,后期可以修改下。

其次,我们封装一下Map<String, Mapper> mappers这个数据结构:它的键是全限定类名+方法名,然后Mapper里面是一个sql语句和一个返回类型(也就是数据库表中的数据所对应的类名)。这样我们就能知道每个方法所对应的sql语句以及应该给查询出来的语句赋值到哪个对象上去了。

然后,也是最最重要的一部分,就是使用动态代理的方法来加强函数的功能。这部分就是在getMapper那个方法中,为其加入数据库查询的部分,然后加入返回值的对象即可。

CURD

插入操作

定义一个函数来保存一个对象到数据库里,只需要在接口中创建一个方法,比如是void saveUser(User user);,然后在配置文件里面加入一个insert标签:

1
2
3
<insert id="saveUser" parameterType="cn.chenlangping.domain.User">
INSERT INTO user (username,address,sex,birthday) values( #{username},#{address},#{sex},#{birthday})
</insert>

其中id是方法名字,而第二个参数是真正参数的全限定类名。最后在#{}内的内容,就是属性的名字。最后使用的时候不要忘记,默认是关闭了自动提交事务的,所以记得手动提交下事务

更新操作

一样的方法,也是在接口中创建一个方法,然后是一样的,只是标签有点不同。

1
2
3
<update id="updateUser" parameterType="cn.chenlangping.domain.User">
UPDATE user SET username=#{username},address=#{address},sex=#{sex},birthday=#{birthday} where id = #{id}
</update>

删除操作

删除操作有点特殊,因为我们只需要知道id即可删除对象,而且仅仅需要一个参数,所以仅仅是一个占位符而已。

1
2
3
<delete id="deleteUser" parameterType="java.lang.Integer">
DELETE from user where id = #{id}
</delete>

map的妙用

上面的一些我们在查询的时候,需要自己构造一个对应的对象,如果我们仅仅需要这个对象的某一些属性来作为参数查询,而这个对象的属性非常多,那么就可以使用map来进行简化操作了。

配置文件详解

核心配置文件

环境配置(environments)

MyBatis 可以配置成适应多种环境,现实中最常用的就是测试环境和生产环境的一键切换。尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境。

属性(properties)

属性可以在很多地方定义,然后就可以被框架所使用。当然会存在多个属性之间相互覆盖的问题,它们之间的顺序是这样的:

  • 首先读取在 properties 元素体内指定的属性。
  • 然后根据 properties 元素中的 resource 属性读取类路径下属性文件,或根据 url 属性指定的路径读取属性文件,并覆盖之前读取过的同名属性。
  • 最后读取作为方法参数传递的属性,并覆盖之前读取过的同名属性。

设置(settings)

设置我们一般只会使用其中的缓存启用、延迟加载等少量设置,需要的时候再进行调试就可以了。

类型别名(typeAliases)

如果不想写很长的全限定类名,那么就可以使用它来简化操作。可以一个类一个类来自己定义;当然也可以对一个包下的所有都去掉它们的全限定类名。这里需要注意的是,本来不同的包下可以存在相同类名的类;但是由于去掉了包名,所以导致类名冲突,这样子mybatis的处理方式是抛出异常。

映射器(mappers)

我们可以指定对应的xml文件的位置来找到映射器,也可以通过使用映射器接口实现类的完全限定类名、还可以通过指定包名。

这里需要注意的是,如果通过类名来找到对应的映射器,那么xml文件和类名需要完全一致才可以。

属性名和字段名不一致

对象的对象

比如我现在用另外一个对象封装了user,然后想利用user的name来进行查询,那么可以使用一个简洁的语法,即OGNL(Object Graphic Navigation Language)来进行处理,简单来说就是使用对象名.属性即可访问成功。比如我要访问user的username属性,我不再需要user.getUsername()方法了,而是只需要输入user.username即可。

其实上面已经在用了,只不过由于只有一层,所以不明显。

如果实体类属性和数据库列名不一致呢?

前面我们都是让类的属性和数据库列名相同,这样是为了方便建立对象,那么如果就是不一样呢?有两种解决办法,第一种就是用数据库的别名,第二种当然是进行配置啦。

1
2
3
4
5
6
<resultMap id="whatever" type="cn.chenlangping.domain.User">
<!-- 主键 -->
<id property="java类中的名字" column="数据库中的列名"/>
<!-- 非主键 -->
<result property="java类中的名字" column="数据库中的列名"/>
</resultMap>

在select等语句上面写上这块,然后在update啊这种DML语句中,把resultType删了换成resultMap并且写上id,这样mybatis就知道了。

根据对象条件

假设我传入了一个user对象,而这个对象中我只设置了名字,也就是我希望能根据user中存在的信息来找用户,那么应该怎么做?显然上面的这些都不行了,需要引入特殊的标签<if>

1
2
3
4
5
6
<select id="findUserByCondition" parameterType="cn.chenlangping.domain.User" resultType="cn.chenlangping.domain.User">
SELECT * FROM user where 1=1
<if test="username!=null">
and username = #{username}
</if>
</select>

上面的例子就能很好的说明问题,如果传入的user对象的username不是空(按照java的语法),那么把下面if标签里面的内容拼接到原始的那句话上面。

仔细看上面的内容,你会发现有个1=1 看上去是不是贼不舒服?mybatis为我们提供了<where>标签来解决,所以修改一下就会这样:

1
2
3
4
5
6
7
8
9
<select id="findUserByCondition" parameterType="cn.chenlangping.domain.User"
resultType="cn.chenlangping.domain.User">
SELECT * FROM user
<where>
<if test="username!=null">
username = #{username}
</if>
</where>
</select>

实际中可能还会碰到这种需求,比如select * from user where id in (1,2,3);,这种sql语句在mybatis中应该怎么写呢?用<foreach>标签即可。

连接池

连接池首先它必须是线程安全的,不然会导致两个线程能够拿到同一个。而且它本身也是一个队列,这样就能够循环使用各个连接啦。而mybatis当然也提供了连接池的配置,在主配置文件里的datasource标签中就可以进行配置,type就是用来表示是采用什么方式来进行连接的,它可以有以下三种取值:

  • POOLED:采用传统Java datasource中的连接池。mybatis有针对这个规范的实现。
  • UNPOOLED:不用连接池,虽然它实现了javax.sql.datasource接口,但是实际上它每次都是单个连接,并没有使用所谓的连接池。
  • JNDI:采用服务器提供的JNDI技术实现,来获取datasource对象,不同服务器不同;如果不是web工程,将无法使用。比如tomcat使用的是叫dbcp连接池。

多表关联

假设现在有一张用户表,一张账户表;两者是多对一的关系,即一个用户可以有多个账户。显然根据数据库的知识我们可以知道,账户表中需要有一个外键来指向用户表。

1
2
3
4
5
6
7
8
9
10
11
12
-- 创建账户表,外键为uid,关联用户表的id
DROP TABLE IF EXISTS `account`;
CREATE TABLE `account` (
`id` int(11) NOT NULL COMMENT '编号',
`uid` int(11) default NULL COMMENT '用户编号',
`money` double default NULL COMMENT '金额',
PRIMARY KEY (`id`),
KEY `FK_Reference_8` (`uid`),
CONSTRAINT `FK_Reference_8` FOREIGN KEY (`uid`) REFERENCES `user` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- 导入账户数据
insert into `account`(`id`,`uid`,`money`) values (1,46,1000),(2,45,1000),(3,46,2000);

然后是在Java中,必然我们需要建立两个类来分别对应用户和账户。同时针对这两个类我们也需要有两个配置文件。

最后再新建一个类,能够存入查询出来的数据。

缓存

适用于缓存的数据:不经常改变、而且改变的话对最终结果影响不大的数据才使用用缓存。

一级缓存

sqlSession本身就提供了一块缓存区域,该区域本质上是一块Map。可以通过sqlSession.clearCache()方法来清除缓存。

二级缓存

sqlSessionFactory也提供了缓存,叫做二级缓存,但是默认是不支持的。需要在主配置文件中和对应的类配置文件中配置,然后还需要让语句支持。二级缓存中存放的是数据,而不是对象。

概念

Redis是一款nosql系列的非关系型数据库。nosql= not only sql,而不是not sql的意思。

关系型数据库的数据之间会存在关联关系,而且数据是存放在硬盘上的。而非关系型数据库之间的数据是没有关系的,而且数据存放在内存中。

显然数据存放在内存中可以带来更快的速度,但是缺点么,内存里的数据断电就没了,所以实际上我们需要把两者结合在一起使用。我们会把数据存在关系型数据库中,而使用nosql作为缓存。

在实际中我们经常碰到一些缓存的需求,比如某个数据我经常用到,每次一用到就去数据库中取的话显然性能不好,那么我就开辟一个内存空间来存放这些数据(比如实际中我们会定义一个map对象来使用),但是由于map是依赖于jvm的,而jvm分配的内存还是没那么大,所以需要像redis这样的nosql数据库出马来解决问题。

阅读全文 »

JSP

首先,我们可以直接在jsp页面中定义html代码,也可以直接定义java代码。

其次,现在几乎不怎么使用jsp技术了,你看看哪个网站的页面还有jsp结尾的?所以我认为对它来说,简单理解即可。

阅读全文 »

Tomcat

tomcat是一个web服务器软件,说到web服务器软件,肯定免不了要提到Apache/Nginx,那tomcat和它们之间的区别是什么呢?

这里有必要先科普一个概念:

  • 静态资源:白话点就是用html、css、javascript写的那些东西,所有用户看到的代码都是一样的。
  • 动态资源:用servlet/jsp、php写的,动态的内容;动态内容需要先转换成静态才可以使用。

既然都是服务器软件,那它们必然都是能够绑定ip地址、监听端口并处理来自浏览器的HTTP请求。tomcat不仅能够做到这些,它还能够支持servlet和jsp,即所谓的动态内容。它能够根据你所做的配置,当用户访问指定url的时候,它会去加载特定的java类,在特定的类中有代码来处理页面内容。简单点就是如果你希望能够运行用servlet编写的java程序,那么必然需要使用tomcat;而如果只是一些静态页面的话,直接使用Nginx效率会更好。

Apache Tomcat and Nginx server, were created for two different tasks. NGINX is a free, open-source, high-performance HTTP server and reverse proxy, as well as an IMAP/POP3 proxy server and Apache Tomcat is an open source implementation of the Java Servlet, JavaServer Pages, Java Expression Language and Java WebSocket technologies. The Java Servlet, JavaServer Pages.

阅读全文 »

安装使用(win)

通过这个网站可以下载安装包,记得下载database,下载完成之后根据提示一步一步安装即可。

不同版本的下载使用不同,都是很简单的英文,稍微花点时间就能看懂,这里不多赘述。

2681584243973_.pic_hd

安装的时候如果让你选择了安装位置,那么安装之后的位置就是你自己选择的,如果没有选择,那么默认是安装在C:\Program Files\Mysql下,配置文件在C:\Program Files\MySQL\MySQL Server 8.0\data(win7在C:\ProgramData\Mysql)。Mysql是作为一个服务在Windows下运行的,如果要停止它,可以直接停止服务。

如果要卸载的话,记得卸载完成之后,把数据库的数据也一并处理掉,否则下次会安装不成功。

阅读全文 »

前言

不论是自己的小项目也好,还是日常使用也好,都离不开Java使用数据库,自己之前也封装过一些使用JDBC库,今天打算花点时间好好梳理下,顺便手写一个简单的ORM框架,并且之后打算深入研究下mybatis框架。

阅读全文 »