Spring基础、IOC(控制反转)、AOP(面向切面编程)、Log4j、注解配置
来源:互联网 发布:模拟量子编程 编辑:程序博客网 时间:2024/06/16 11:18
学习示例代码,包含本篇介绍的spring常用操作示例和所有所需jar文件下载地址:http://download.csdn.net/detail/daijin888888/9556697
1.什么是Spring,有什么作用
--Spring框架属于一个解决方案框架,可以对其他技术和框架进行整合应用。--*好处是:将程序中的各个组件和框架技术进行解耦,便于日后系统维护,升级和扩展操作。
--在SSH中,会将Action,DAO组件都交给Spring框架管理,由Spring框架创建这些对象,建立这些对象关联。
*2.Spring都含有哪些功能
--Spring框架提供了一个核心容器,该容器可以负责管理程序中的DAO、Action等组件(相当于工厂的作用)--提供了对其他技术,例如JDBC,Hibernate,Struts等框架整合API
--提供了IOC机制,实现组件对象关系的解耦
--提供了AOP机制,可以降低共通组件和一批目标组件的耦合度
--提供了事务管理功能
--提供了一个Spring MVC框架实现
*3.Spring框架容器
Spring框架容器可以管理DAO、Action等Bean组件。该容器具有以下功能:--可以创建Bean组件对象
--可以初始化Bean组件对象
--可以销毁Bean组件对象
--容器具有IOC和AOP机制
*4.Spring基本使用
1)创建和管理程序组件对象--引入Spring IOC开发包(commons-logging.jar、spring.jar)
--在src下添加Spring配置文件applicationContext.xml
--将Bean组件定义到Spring配置文件applicationContext.xml中
<bean id="标识符" class="包名.类名"></bean>
--(编码)实例化Spring容器对象利用getBean获取Bean对象实例
// 实例化Spring容器对象
String conf = "/applicationContext.xml";
ApplicationContext ac = new ClassPathXmlApplicationContext(conf);
// 从容器中获取Bean
类名 对象名 = (类名) ac.getBean("标识符");
示例:
- public class JdbcCostDao implements CostDao {
- public JdbcCostDao() {
- System.out.println("JdbcCostDao构造");
- }
- public void init() {
- System.out.println("初始化JdbcCostDao");
- }
- @Override
- public void save() {
- System.out.println("采用jdbc保存对象");
- }
- @Override
- public void delete() {
- String s = null;
- s.length();// 模拟异常
- System.out.println("采用jdbc删除对象");
- }
- public void destory() {
- System.out.println("释放资源");
- }
- }
- <bean id="jdbcCostDao" scope="singleton" init-method="init"
- destroy-method="destory" class="com.test.dao.JdbcCostDao"></bean>
- // 测试回收
- @Test
- public void test2() {
- // 实例化Spring容器对象
- String conf = "/applicationContext.xml";
- AbstractApplicationContext ac = new ClassPathXmlApplicationContext(conf);
- // 从容器中获取Bean
- CostDao costDao = (CostDao) ac.getBean("jdbcCostDao");
- costDao.save();
- ac.close();// 释放容器对象-->触发释放Bean对象-->触发destory-method方法
- }
- 采用jdbc保存对象
- 释放资源
在使用时,可以在<bean>定义部分利用scope属性指定bean对象采用单例模式创建还是原型模式创建。
scope="singleton"表示单例模式(默认值)
scope="prototype"表示原型模式,每次调用getBean都返回一个新的Bean对象。
如果应用Web程序中,通过配置可以扩展出request,session等属性值
b.控制Bean对象创建时机
scope="singleton"时,Bean对象是在容器实例化时创建。在<bean>中使用lazy-init="true"可以将Bean创建推迟到getBean方法。
scope="prototype"时,Bean对象是在getBean方法时创建。
c.追加初始化和销毁方法
在<bean>中利用init-method指定一个初始化方法。可以在对象创建之后自动调用。
<bean init-method="myinit">创建Bean对象后,会自动执行myinit方法。
同理,可以通过destroy-method属性指定方法名。当对象被垃圾回收前自动调用该方法执行资源释放工作。该属性仅适用于singleton模式的Bean对象,当执行Spring容器close()时,容器会释放Bean单例对象,会触发destroy-method="mydestroy"中的mydesctory方法。
*5.Spring的核心IoC容器
a.IoC概念Inversion of Control 控制反转或反向控制(控制转移)
2004 Martin fowler提出的IoC思想。IOC解决两个Bean组件调用问题,可以降低两个Bean组件对象之间的耦合度。所谓控制指的是对象的创建、初始化和销毁过程。当一个组件发生变更后,该逻辑需要响应修改,控制反转确切讲应该是控制的转移,意思是将控制逻辑由使用一方转移到第三框架或容器负责。当再发生组件变更后,只需要修改框架或容器配置,不需要修改关联组件。
IoC思想:两个组件之间调用(例如A调用B),原有方式是A负责创建B对象;现在变成了B对象由第三方框架或容器负责实例化,然后给A对象注入。即A对象获取B对象的方式发生了反转。
IoC技术方案有两种:依赖注入(DI:Dependency Injection)和依赖查找
Spring采用依赖注入技术实现IoC控制。
依赖技术主要以下两种方式:
--set方式注入(推荐)
在A中定义setB(B b),接收传进来的B对象
--构造方式注入
在A中定义构造方法 public A(B b){}
b.IoC使用方法(setter注入实现)
--编写Action,定义到Spring配置中
--编写DAO,定义到Spring配置中
--在Action中定义DAO接口类型变量和setter方法
--在Action的<bean>配置中使用下面配置
<bean id="addCostAction"...>
<property name="属性名" ref="要注入的Bean对象id值">
</property>
</bean>
示例:
- public class AddCostAction {
- // 属性(略)
- // 定义注入属性
- private CostDao costDao;
- public CostDao getCostDao() {
- return costDao;
- }
- // Spring容器会自动调用该方法注入costDao
- public void setCostDao(CostDao costDao) {
- this.costDao = costDao;
- }
- public String exeucte() {
- System.out.println("开始处理资费添加请求");
- // CostDao costDao = new JdbcCostDao();//采用注入的方式替代直接生成,以此解耦
- costDao.save();
- return "success";
- }
- }
- <bean id="hibernateCostDAO" class="com.test.dao.HibernateCostDAO"></bean>
- <bean id="addCostAction" scope="prototype" class="com.test.action.AddCostAction">
- <!-- setter注入配置,将hibernateCostDAO对象给action的setCostDao方法传入 -->
- <property name="costDao" ref="hibernateCostDAO"></property>
- </bean>
--编写Action组件
--编写DAO组件
--在Action中添加带有DAO参数的构造方法
--在Action的<bean>定义中,使用下面配置
<bean id="deleteCostAction" class="">
<constructor-arg
index="参数索引值,从0开始" ref="要注入的Bean对象id值">
</constructor-arg>
</bean>
示例:
- public class DeleteCostAction {
- private CostDao costDao;
- public DeleteCostAction() {
- }
- public DeleteCostAction(CostDao costDao) {
- this.costDao = costDao;
- }
- public String exeucte() {
- System.out.println("删除资费请求的处理");
- costDao.delete();
- return "success";
- }
- }
- <bean id="jdbcCostDao" scope="singleton" init-method="init"
- destroy-method="destory" class="com.test.dao.JdbcCostDao"></bean>
- <bean id="deleteCostAction" class="com.test.action.DeleteCostAction">
- <!-- 构造参数注入配置,将jdbcCostDao对象给action的构造方法的第一个参数传入 -->
- <constructor-arg index="0" ref="jdbcCostDao"></constructor-arg>
- </bean>
--注入Bean对象(重点)
<property name="属性名" ref="Bean对象id">
</property>
--注入基本类型
可以注入一个数值,一个字符串数据。
<property name="属性名" value="值">
</property>
--集合类型
注入List,Set,Map,Properties类型
示例:
- <bean id="messagebean" class="com.test.action.MessageBean">
- <property name="dir" value="D:\\images\"></property>
- <property name="size" value="10240"></property>
- <!-- 注入List -->
- <property name="types">
- <list>
- <value>jpg</value>
- <value>jpeg</value>
- <value>gif</value>
- </list>
- </property>
- <!-- 注入Set -->
- <property name="cites">
- <set>
- <value>北京</value>
- <value>上海</value>
- <value>广州</value>
- </set>
- </property>
- <!-- 注入Map -->
- <property name="books">
- <map>
- <entry key="1001" value="Java语言基础"></entry>
- <entry key="1002" value="JavaWeb基础"></entry>
- <entry key="1003" value="SSH框架技术"></entry>
- </map>
- </property>
- <!-- 注入Properties -->
- <property name="dbParams">
- <props>
- <prop key="username">root</prop>
- <prop key="password">123456</prop>
- <prop key="driverClassName">com.mysql.jdbc.Driver</prop>
- </props>
- </property>
- </bean>
- /**
- * @Description:测试注入数据类别
- *
- */
- public class MessageBean {
- private String dir;
- private int size;
- private List<String> types;
- private Set<String> cites;
- private Map<String, String> books;
- private Properties dbParams;
- public List<String> getTypes() {
- return types;
- }
- public void setTypes(List<String> types) {
- this.types = types;
- }
- public String getDir() {
- return dir;
- }
- public void setDir(String dir) {
- this.dir = dir;
- }
- public int getSize() {
- return size;
- }
- public void setSize(int size) {
- this.size = size;
- }
- public Set<String> getCites() {
- return cites;
- }
- public void setCites(Set<String> cites) {
- this.cites = cites;
- }
- public Map<String, String> getBooks() {
- return books;
- }
- public void setBooks(Map<String, String> books) {
- this.books = books;
- }
- public Properties getDbParams() {
- return dbParams;
- }
- public void setDbParams(Properties dbParams) {
- this.dbParams = dbParams;
- }
- public void show() {
- System.out.println("--显示注入参数--");
- System.out.println("dir:" + dir);
- System.out.println("size:" + size);
- System.out.println("允许上传的类型如下:");
- for (String s : types) {
- System.out.println(s);
- }
- System.out.println("城市信息:");
- for (String s : cites) {
- System.out.println(s);
- }
- System.out.println("图书信息:");
- Set<Entry<String, String>> book = books.entrySet();
- for (Entry en : book) {
- System.out.println(en.getKey() + ":" + en.getValue());
- }
- System.out.println("显示数据库连接参数:");
- Set keys = dbParams.keySet();
- for (Object key : keys) {
- System.out
- .println(key + "=" + dbParams.getProperty(key.toString()));
- }
- }
- }
测试程序:
- public class TestMessageBean {
- @Test
- public void test1() {
- String conf = "/applicationContext.xml";
- ApplicationContext ac = new ClassPathXmlApplicationContext(conf);
- MessageBean bean = (MessageBean) ac.getBean("messagebean");
- bean.show();
- }
- }
- --显示注入参数--
- dir:D:\\images\
- size:10240
- 允许上传的类型如下:
- jpg
- jpeg
- gif
- 城市信息:
- 北京
- 上海
- 广州
- 图书信息:
- 1001:Java语言基础
- 1002:JavaWeb基础
- 1003:SSH框架技术
- 显示数据库连接参数:
- password=123456
- driverClassName=com.mysql.jdbc.Driver
- username=root
6.Spring的AOP机制
1)什么是AOP,解决什么问题--Aspect Oriented Programming被称为面向方面(切面)编程。
--AOP主要解决共通组件与某一批目标组件作用关系,以低耦合方式建立关联。
--AOP编程核心是对方面处理(共通功能)进行解耦。OOP编程核心是对象。
--AOP编程是以OOP为基础,在OOP基础之上改善程序中对象结构,使得对象关系更加灵活,易于维护和扩展。
2)AOP相关概念
a.方面(Aspect):
方面封装了共通处理,在AOP编程中,可以灵活作用到另外一批目标组件的方法上。
b.切入点(Pointcut)
切入点负责指定哪些组件和方法,是方面切入的目标。采用一个表达式指定
c.通知(Advice)
通知负责指定切入点和目标组件方法之间的作用时机。例如先执行目标方法,再执行方面处理。或者先执行方面处理,再执行目标方法。
Spring提供以下5种通知类型:
--前置通知(aop:before):先执行方面处理,再执行目标方法
--后置通知(aop:after-returning):先执行目标方法,未抛出异常,再执行方面处理;有异常则不会执行方面组件
--最终通知(aop:after):先执行目标方法,有无异常,都会再执行方面处理
--环绕通知(aop:around):先执行方面处理前置部分,接着执行目标方法,然后再回到方面处理的后置部分。
--异常通知(aop:after-throwing):先执行目标方法,发生异常再执行方面处理;没有抛出异常不会执行。
try{
//前置通知--执行方面处理
//环绕的前置部分
执行目标方法
//环绕的后置部分
//后置通知--执行方面处理
}catch(){
//异常通知--执行方面处理
}finally{
//最终通知--执行方面处理
}
d.目标组件(Target)
切入点表达式指定的组件就是目标组件。
e.连接点(JoinPoint)
连接点的集合是切入点。连接点指的是方面组件与某个目标组件相结合的信息。通过该对象可以获取目标组件类型和方法等信息。
f.动态代理(AutoProxy)
Spring框架采用动态代理技术实现了AOP机制。
当采用了AOP之后,Spring容器返回的目标Bean对象,是Spring动态生成的一个代理类型。该代理类负责去调用原目标组件方法和方面处理的功能。
Spring中提供两种生成动态代理类的方法。
--JDK Proxy API技术
针对有接口实现的目标组件。采用实现原目标接口的方法生成一个类型。
public class $Proxy4 implements CostDao{
public void save(){
//调用JdbcCostDao的save()
//调用方面LoggerAop的功能
}
public void findAll(){
//调用JdbcCostDao的findAll()
//调用方面LoggerAop的功能
}
}
--CGLIB技术
针对没有接口实现的目标组件。采用继承方法生成一个子类。
public class $$EnhancerByCGLIB$232 extends 原目标组件类型{
public void save(){
super.save();//调用原目标方法
//调用方面组件处理
}
}
3)AOP使用步骤
a.记录用户进入系统后执行的操作
--编写方面,将共通功能封装
--添加Spring配置,将组件定义成方面组件
--添加Spring配置,定义切入点
--添加Spring配置,定义通知
方面代码:
- public class OptLogger {
- /**
- * @param pjp
- * 连接点(需用环绕通知切入)
- * @throws Throwable
- */
- public Object log(ProceedingJoinPoint pjp) throws Throwable {
- Object obj = pjp.proceed();// 执行目标组件方法处理
- // 获取当前请求要执行的Action目标组件类型
- String className = pjp.getTarget().getClass().getName();
- // 获取当前请求要执行的方法名
- String methodName = pjp.getSignature().getName();
- // 根据类名和方法名形成key
- String key = className + "." + methodName;
- // 解析opt.properties,根据key获取操作动作描述
- String optName = PropertiesUtil.getProperty(key);
- System.out.println("XXX在" + new Date() + "时间做了" + optName + "操作");
- return obj;// 将proceed目标方法结果返回
- }
- }
- PropertiesUtil用于读取配置文件,可将特定操作(类+方法转换成中文描述)
- <pre name="code" class="java">public class PropertiesUtil {
- private static Properties props = new Properties();
- static{
- try{
- InputStream inStream = PropertiesUtil.class
- .getClassLoader().getResourceAsStream(
- "opt.properties");
- props.load(inStream);
- }catch(Exception ex){
- ex.printStackTrace();
- }
- }
- public static String getProperty(String key){
- String val = props.getProperty(key);
- return val;
- }
- }
#use jdk/bin/native2ascii tool
#以下分别对应:资费添加操作、资费删除操作
com.test.action.AddCostAction.exeucte=\u8d44\u8d39\u6dfb\u52a0\u64cd\u4f5c
com.test.action.DeleteCostAction.exeucte=\u8d44\u8d39\u5220\u9664\u64cd\u4f5c
配置代码:
- <!-- AOP配置 -->
- <bean id="optLogger" class="com.test.aspect.OptLogger">
- <aop:config>
- <!-- 指定切入点 -->
- <aop:pointcut id="actionPointcut" expression="within(com.test.action.*)" />
- <!-- 将id=optLogger的Bean指定为方面组件 -->
- <aop:aspect id="optLoggerAspect" ref="optLogger">
- <!-- 指定通知,控制方面和目标组件作用时机 -->
- <aop:around pointcut-ref="actionPointcut" method="log" />
- </aop:aspect>
- </aop:config>
4)切入点表达式
切入点是用一个表达式表示,用于指定目标及其方法。
*a.方法限定表达式
可以指定哪些方法启用方面组件功能
execution(修饰符? 返回类型 方法名(参数列表) throws异常?)
示例1:
//匹配容器中所有Bean对象的findAll方法
execution(* findAll(..))
//匹配容器中所有Bean对象的find开头方法
execution(* find*(..))
示例2:
//匹配JdbcCostDao所有方法
execution(* org.dao.JdbcCostDao.*(..))
示例3:
//匹配org.dao包下所有类的所有方法
execution(* org.dao.*.*(..))
//匹配org.dao包及其子包所有类所有方法
execution(* org.dao..*.*(..))
*b.类型限定表达式
可以限定某个类型的所有方法启用方面within(类型)
示例1:
//匹配JdbcCostDao所有方法
within(org.dao.JdbcCostDao)
示例2:
//匹配org.dao下所有类的所有方法
within(org.dao.*)
示例3:
//匹配org.dao下及其子包所有类所有方法
within(org.dao..*)
*c.Bean名称限定表达式
可以利用Bean对象的id进行限定
bean(id或name值)
<bean id="" name="">
name属性作用与id相同,id要求更严格,不允许使用/等特殊字符,name允许。
示例1:
//匹配容器中所有id以Dao结尾的Bean对象
bean(*Dao)
d.参数限定表达式
args(参数类型)
示例1:
//匹配所有Bean对象中有一个参数,而且是String类型的方法
args(java.lang.String)
提示:上述表达式可以利用&&,||连接在一起使用。
5)通知方法定义
//前置通知方法
public void xxx(){}
//后置通知方法
public void xxx(Object retVal){}
参数retVal可以接收目标方法返回值
//最终通知方法
public void xxx(){}
*//环绕通知方法
public Object xxx(ProceedingJoinPoint pjp){}
*//异常通知方法
public void xxx(Exception e){}
参数e可以接收目标方法抛出的异常对象
(示例:如6中用Log4j记录程序异常信息并保存下来)
7.Log4j
log4j.jar工具是Apache组织下的一个日志组件,可以将消息按不同级别不同输出方式记录下来。log4j工具主要有以下3部分构成。
--日志器Logger
负责提供输出不同级别消息的方法。消息可以分为调试、普通、警告、错误、致命级别,不同级别信息利用不同日志器的方法输出。
--输出器Appender
负责控制信息输出的方式,例如控制台输出、文件输出等。
--布局器Layout
负责将消息格式化。
对应关系:一个Logger可以指定多个不同的Appender,每个Appender可以指定一个Layout。
--引入log4j.jar开发包
--在Java代码中获取Logger对象,之后利用它的debug(),error()输出信息
--在src下添加log4j.properties配置文件指定消息输出的级别,输出的appender和输出的layout格式。
提示:项目中由于SSH框架利用Log4j工具输出调试和异常信息,因此遇到错误提示不明确时,可以添加log4j.properties配置将级别调到DEBUG查看。
如配置文件,log4j.properties:
- log4j.rootLogger=error,myconsole,myfile
- log4j.appender.myfile=org.apache.log4j.FileAppender
- log4j.appender.myfile.File=D:\\log4j.html
- log4j.appender.myfile.layout=org.apache.log4j.HTMLLayout
- log4j.appender.myconsole=org.apache.log4j.ConsoleAppender
- log4j.appender.myconsole.layout=org.apache.log4j.SimpleLayout
- /**
- * @Description:异常记录方面组件
- */
- public class ExceptionLogger {
- // 获取日志器
- private static Logger logger = Logger.getLogger(ExceptionLogger.class);
- // ex就是目标方法抛出的异常对象
- public void logException(Exception ex) {
- // System.out.println("将异常写入文件" + ex);
- // 采用Log4j日志工具记录异常信息
- logger.error(ex);
- logger.error(ex.getStackTrace()[0]);
- }
- }
- <!-- AOP配置 -->
- <bean id="exceptionLogger" class="com.test.aspect.ExceptionLogger">
- </bean>
- <!-- 指定切入点 -->
- <aop:pointcut id="daoPointcut" expression="within(com.test.dao..*)" />
- <aop:config>
- <!-- 将id=exceptionLogger的Bean指定为方面组件 -->
- <!-- trowing指定logException方法的参数名,用于接收目标方法抛出的异常对象 -->
- <aop:aspect id="exceptionLoggerAspect" ref="exceptionLogger">
- <aop:after-throwing pointcut-ref="daoPointcut"
- method="logException" throwing="ex" />
- </aop:aspect>
- </aop:config>
- D:\\log4j.html
Log session start time Wed Jun 22 17:03:39 CST 2016
8.Spring注解配置
1)什么是注解
注解技术是JDK5.0开始提供的。
它的标记格式为"@名称",它可以定义在类前、方法前、成员变量前。
2)使用注解的好处
现在的框架都提供了注解配置,利用注解技术去简化或替代原有的XML配置。
3)Spring的组件扫描技术
Spring可以自动扫描指定包下的Class组件,如果发现类定义前面有以下几个标记
@Controller(Action组件应用)
@Service(Service业务组件应用)
@Repository(DAO组件应用)
@Component(其他组件应用)
就会将该组件扫描到Spring容器,由容器负责管理和对象的创建,等价于XML中<bean>定义。
使用方法:
--在applicationContext.xml中开启组件扫描功能 <context:component-scan base-package="指定包路径"/>
--在Action或DAO等组件类定义前使用上述标记。默认用类名首字母小写当id,也可以采用@Repository("指定id")格式
其他<bean>属性注解标记:
@Scope("prototype"或"singleton")等价于scope属性
@PreDestory等价于destory-method属性
@PostConstruct等价于init-method属性
4)注入的注解
@Resource
@Autowired
上面两种标记都可以实现类型匹配注入,但是如果遇到多个匹配对象时,会发生异常。这时需要指定注入哪个id的Bean
@Resource(name="指定id")
@Autowired()
@Qualifier("指定id")
上面标记可以写在变量定义前或set方法前。写在变量定义前时,set可省略。
5)AOP注解配置
使用方法:
--在applicationContext.xml中开启AOP的注解配置<aop:aspectj-autoproxy/>
--在方面组件中类定义前使用下面标记
@Component //将Bean扫描到容器
@Aspect //将Bean升级为方面组件
--在方面组件中定义一个空方法,利用@Point标记定义切入表达式
@Pointcut("切入表达式")
public void mypoint(){}
--在方面组件处理方法上,使用下面通知标记
@Before 前置通知标记
@Around 环绕通知标记
@AfterReturning 后置通知标记
@AfterThrowing 异常通知标记
@After 最终通知标记
源页:
本文转自-http://blog.csdn.net/daijin888888/article/details/51735291?locationNum=10&fps=1
- Spring基础、IOC(控制反转)、AOP(面向切面编程)、Log4j、注解配置
- Spring基础、IOC(控制反转)、AOP(面向切面编程)、Log4j、注解配置
- Spring学习一:IOC(控制反转)和AOP(面向切面)的xml配置和注解方式
- spring的IOC(控制反转)与AOP(面向切面编程)
- idea 实现Spring讲解(Ioc-控制反转)/Aop(面向切面的编程)
- Spring框架总结,控制反转(IOC),依赖注入(DI),面向切面编程(AOP)
- Spring AOP与AspectJ 面向切面编程配置与注解
- Java+spring切面编程(aop)spring控制反转(ioc)+hibenrate对象关系映射(ORM
- JavaEE框架——Spring入门基础(控制反转Ioc和切面技术Aop)
- Spring 面向切面(AOP)编程,注解
- Spring的控制反转(IoC)和面向切面编程(AOP)的概念浅析。
- Spring框架运行机制(控制反转,依赖注入,面向切面AOP)
- Spring AOP( 面向切面编程)配置
- 配置Spring AOP面向切面编程
- SPRING--Spring中IOC(反转控制) 和 AOP(面向方面编程)
- java+spring切面编程(aop)spring控制反转(ioc)+hibenrate对象关系映射(ORM) 学习网址
- Spring IOC 依赖倒置 AOP面向切面编程
- Spring面向切面编程--AOP,AspectJ,基于注解方式。
- C/C++基础整理(3)
- Nginx 配置 反向代理
- 第二周项目2
- WdatePicker日历控件使用方法
- 关于优酷开放SDk之setOnVideoIndexUpdateListener
- Spring基础、IOC(控制反转)、AOP(面向切面编程)、Log4j、注解配置
- Linux执行程序时缺少连接库的解决办法
- 安卓性能优化之界面UI优化——ViewStub
- Spring关于线程池和定时任务的英文官方文档摘录
- Date类型时间对比
- pdf加水印
- 第十五周 项目1-验证算法---哈希表及其运算的实现
- 第十五周-项目三 B-树的基本操作
- 第十五周项目1-验证算法(1-哈希表查找及冲突解决相关算法)