Spring笔记

Spring

1.Spring概述

1.1 spring是什么

Spring是分层的Java SE/EE应用full-stack轻量级开源框架,以IOC(Inverse Of Control:反转控制)和AOP(Aspect Oriented Programming:面向切面编程)为内核,提供了展示层Spring MVC和持久层 Spring JDBC以及业务层事务管理等众多的企业级应用技术,还能整合开源世界众多著名的第三方框架和类库,逐渐成为使用最多的Java EE企业级应用开源框架.

什么是一站式开发:

img

1.2 Spring的优势

1.方便解耦,简化开发(工厂模式)

​ 通过Spring提供的Ioc容器,可以将对象间的依赖关系交由spring进行控制,避免硬编码所造成的过度程序耦合.用户也不必再为单例模式类,属性文件解析等很底层的需求编写代码,可以更专注于上层的应用.

2.Aop编程的支持(jdk动态代理,cglib动态代理)

​ 通过Spring的AOP功能,方便进行面向切面的编程,许多不容易用传统OOP(面向对象编程)实现的功能可以通过AOP轻松应付.

​ 声明式事务的支持可以将我们从单调烦闷的事务管理代码中解脱出来,通过声明式方式灵活的进行事务的管理,提高开发效率和质量。

3.方便程序的测试(和Junit整合)

​ 可以用非容器依赖的编程方式进行几乎所有的测试工作,测试不再是昂贵的操作,而是随手可做的事情。

4.方便集成各种优秀框架

​ Spring可以降低各种框架的使用难度,提供了对各种优秀框架(Struts、Hibernate、Mybatis、Hessian、Quartz、ActivityMQ、Redis等)的直接支持。

5.降低JavaEE API的使用难度 (SSM整合)

​ Spring对JavaEE API(如JDBC、JavaMail、远程调用等)进行了薄薄的封装层,使这些API的使用难度大为降低。

1.3 Spring的体系结构

img

2.IOC

2.1 什么是程序的耦合

​ 耦合性(Coupling),也叫耦合度,是对模块间关联程度的度量。耦合的强弱取决于模块间接口的复杂性、调用模块的方式以及通过界面传送数据的多少。模块间的耦合度是指模块之间的依赖关系,包括控制关系、调用关系、数据传递关系。模块间联系越多,其耦合性越强,同时表明其独立性越差( 降低耦合性,可以提高其独立性)。

在软件工程中,耦合指的就是就是对象之间的依赖性。对象之间的耦合越高,维护成本越高。因此对象的设计应使类和构件之间的耦合最小。软件设计中通常用耦合度和内聚度作为衡量模块独立程度的标准。划分模块的一个准则就是高内聚低耦合

  • 解决程序耦合的思路一: 反射

    此时的好处是,我们的类中不再依赖具体的驱动类,此时就算删除mysql的驱动jar包,依然可以编译(运行就不要想了,没有驱动不可能运行成功的)。

    同时,也产生了一个新的问题,mysql驱动的全限定类名字符串是在java类中写死的,一旦要改还是要修改源码。

    解决这个问题也很简单,使用配置文件配置(xml或者是properties)。

  • 解决程序耦合的思路二: 工厂模式解耦

    在实际开发中我们可以把三层的对象都使用配置文件配置起来,当启动服务器应用加载的时候,让一个类中的方法通过读取配置文件,把这些对象同时创建出来并存起来。在接下来的使用的时候,直接拿过来用就好了。

    那么,这个读取配置文件,创建和获取三层对象的类就是工厂。

2.2 控制反转-Inversion Of Control

​ 原来: 我们在获取对象时,都是采用new的方式.是主动的

img

现在: 我们获取对象时,同时跟工厂要,有工厂为我们查找或者创建对象。是被动的。

img

​ 在应用加载时,创建一个Map,用于存放三层对象。 我们把这个map称之为容器。 工厂就是负责给我们从容器中获取指定对象的类。这时候我们获取对象的方式发生了改变。而不是我们在程序中去new对象,而是通过工厂去创建对象,并且通过工厂去获取对象。

这种被动接收的方式获取对象的思想就是控制反转(IOC),它是spring框架的核心之一。

img

明确ioc的作用: 削减计算机程序的耦合(解除我们代码中的依赖关系),将对象的创建和调用都交给spring容器去处理。

2.3 IOC读取配置文件

img

【BeanFactory 和ApplicationContext 的区别 】

​ BeanFactory 才是Spring 容器中的顶层接口。

​ ApplicationContext 是它的子接口。

​ BeanFactory 和ApplicationContext 的区别:

​ 创建的方式都表示单例对象。

​ 创建对象的时间点不一样。

​ ApplicationContext:只要一读取配置文件,默认情况下就会创建对象。(立即加载)

​ BeanFactory:什么时候使用什么时候创建对象。(延迟加载)

【ApplicationContext 接口的实现类】

(1)ClassPathXmlApplicationContext:它是从类的根路径下加载配置文件 推荐使用这种

(2)FileSystemXmlApplicationContext:

它是从磁盘路径上加载配置文件,配置文件可以在磁盘的任意位置。

(3)AnnotationConfigApplicationContext:

使用注解配置容器对象时,需要使用此类来创建spring 容器。它用来读取注解。

【id和name的配置】

id中不能出现特殊字符(容器中的唯一标识),name中可以出现特殊的字符(表示引用)。可以指定多个name,之间可以用分号(“;”)、空格(“ ”)或逗号(“,”)分隔开,如果没有指定id,那么第一个name为标识符,其余的为别名; 若指定了id属性,则id为标识符,所有的name均为别名。如:

1
<bean name="alias1 alias2;alias3,alias4" id="hello1" class="com.zyh.spring3.hello.HelloWorld"> </bean>

此时,hello1为标识符,而alias1,alias2,alias3,alias4为别名,它们都可以作为的键值;

【实例化Beand三种方式】

第一种:采用无参数的构造方法方式实例化(用的最多)

1
2
3
4
5
6
<!--创建Bean的三种方式 -->
<!-- 第一种方式:使用默认构造函数创建。
在spring的配置文件中使用bean标签,配以id和class属性之后,且没有其他属性和标签时。
采用的就是默认构造函数创建bean对象,此时如果类中没有默认构造函数,则对象无法创建。
-->
<bean id="accountService" class="com.cyannote.service.impl.AccountServiceImpl"></bean>

第二种:采用静态工厂实例化的方式

1
2
<!-- 第二种方式:使用工厂中的静态方法创建对象(使用某个类中的静态方法创建对象,并存入spring容器) -->
<bean id="accountService" class="com.cyannote.factory.StaticFactory" factory-method="getAccountService"></bean>

第三种:采用实例工厂(非静态的)实例化的方式

1
2
3
<!-- 第三种方式: 使用普通工厂中的方法创建对象(使用某个类中的方法创建对象,并存入spring容器) -->
<bean id="instanceFactory" class="com.cyannote.factory.InstanceFactory"></bean>
<bean id="accountService" factory-bean="instanceFactory" factory-method="getAccountService"></bean>

【Bean的作用访问的配置:scope的配置】

Spring创建这个类的时候,默认采用的单例的模式进行创建的。如果想去改变单例模式,需要通过scope进行配置。
Scope属性中有以下几个取值:
 singleton :默认值,单例的。
 prototype :多例的。
 request :应用在web应用中,将创建的对象存入到request域中。
 session :应用在web应用中,将创建的对象存入到session域中。
 globalsession :应用在集群环境下使用。将创建的对象存入到全局的session中。

总结:开发场景

单例(常用):一般创建对象单例(例如Service对象、Dao对象,数据源的对象…)

多例:如果spring创建数据库连接对象Connection(每个线程使用的数据库连接对象是不同的,保证线程安全)

​ Struts2(本身多实例,多线程),如果spring创建struts2的对象,一定是多例(了解)

2.4 Bean的生命周期

单例对象
出生:当容器创建时对象出生
活着:只要容器还在,对象一直活着
死亡:容器销毁,对象消亡
总结:单例对象的生命周期和容器相同
多例对象
出生:当我们使用对象时spring框架为我们创建
活着:对象只要是在使用过程中就一直活着。
死亡:当对象长时间不用,且没有别的对象引用时,由Java的垃圾回收器回收
总结:多例对象的生命周期和对象是否被使用有关。与容器是否被销毁无关。

2.5 Spring的依赖注入(DI)

依赖注入:Dependency Injection。它是spring框架核心ioc的具体实现。
我们的程序在编写时,通过控制反转,把对象的创建交给了spring,但是代码中不可能出现没有依赖的情况。ioc解耦只是降低他们的依赖关系,但不会消除。例如:我们的业务层仍会调用持久层的方法。

那这种业务层和持久层的依赖关系,在使用spring之后,就让spring来维护了。
简单的说,就是坐等框架把持久层对象传入业务层,而不用我们自己去获取。 这就是依赖注入。


能注入的数据:有三类

(1)基本类型和String类型(值的注入)

(2)其他bean对象类型(在配置文件中或注解配置过的bean)[对象的注入]

(3)复杂类型/集合类型(集合的注入)

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
<!-- 复杂类型的注入/集合类型的注入
用于给List结构集合注入的标签:
list array set
用于个Map结构集合注入的标签:
map props
结构相同,标签可以互换
-->
<bean id="accountService3" class="com.cyannote.service.impl.AccountServiceImpl3">
<!--数组-->
<!--在spring的集合注入中,array,list,set是可以通用的-->
<property name="arrays">
<set>
<value>张三</value>
<value>22</value>
<ref bean="date"></ref>
</set>
</property>
<!--map集合-->
<property name="map">
<map>
<entry key="key001">
<value>赵六</value>
</entry>
<entry key="key002" value="23"></entry>
<entry key="key003">
<ref bean="date"></ref>
</entry>
</map>
</property>
<!--properties集合,和map集合很相似,也是键值对,键和值只能是String-->
<!--集合属性的应用场景:初始化系统中使用常量-->
<property name="properties">
<props>
<prop key="driver">com.mysql.jdbc.Driver</prop>
<prop key="url">jdbc:mysql:///itcastspring</prop>
<prop key="username">root</prop>
<prop key="password">root</prop>
</props>
</property>
</bean>

能注入的方式:有三种

构造函数注入:使用的标签:constructor-arg标签出现的位置:bean标签的内部

set方法注入 :涉及的标签:property 出现的位置:bean标签的内部*【推荐使用】***

使用p名称空间注入数据(本质还是调用set方法

引入名称空间

1
2
3
4
5
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p"
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">

用p名称空间完成属性注入

语法:普通属性 p:属性名=”” 对象类型 p:属性名-ref=””

1
2
3
4
5
6
7
<!--
使用p空间完成注入
语法:普通属性 p:属性名=”” 对象类型 p:属性名-ref=””
-->
<bean id="accountService2" class="com.cyannote.service.impl.AccountServiceImpl2" p:name="小刚" p:age="25" p:birthday-ref="now"></bean>
<!-- 配置一个日期对象 -->
<bean id="now" class="java.util.Date"></bean>

这里注意:需要对属性提供set方法,方可实现注入。

2.6 基于注解的ioc配置

2.6.1 注解配置-控制反转ioc

用于创建对象的

​ 他们的作用就和在XML配置文件中编写一个标签实现的功能是一样的.

(1)@Component:
作用:用于把当前类对象存入spring容器中
属性:

​ value:用于指定bean的id。当我们不写时,它的默认值是当前类名,且首字母改小写。
(2)Spring中提供了@Component的衍生注解:
​ @Controller :用来修饰WEB层类(控制层)(springMVC延用了该注解)
​ @Service :用来修饰service层类(业务层)
​ @Repository :用来修饰DAO层类(持久层)
以上三个注解他们的作用和属性与Component是一模一样。
​ 他们三个是spring框架为我们提供明确的三层使用的注解,使我们的三层对象更加清晰。

配置创建容器时要扫描的包

1
<context:component-scan base-package="com.cyannote"></context:component-scan>

2.6.2 注解配置-依赖注入di

用于注入数据的

​ 它们的作用就和在xml配置文件中的bean标签中写一个标签的作用是一样的

@Autowired:
基于spring

作用:自动按照类型注入。只要容器中有唯一的一个bean对象类型和要注入的变量类型匹配,就可以注入成功
如果ioc容器中没有任何bean的类型和要注入的变量类型匹配,则报错。
如果Ioc容器中有多个类型匹配时:
先按照类型匹配,如果不能匹配上,会按照属性的名称进行匹配
出现位置:
可以是变量上,也可以是set方法上
细节:
在使用注解注入时,set方法就不是必须的了。
@Qualifier:
配合@Autowired注解一起使用
作用:在按照类型注入的基础之上再按照名称注入。
属性:
value:用于指定注入bean的id。
@Resource
JSR-250标准(基于jdk)
作用:直接按照bean的id名称注入。如果id属性不存在,可以再按照类型注入。它可以独立使用
属性:
name:用于指定bean的id,如果指定name,只能按照bean的id注入,不能按照类型注入。
注意:
以上三个注入都只能注入其他bean类型的数据,而基本类型和String类型无法使用上述注解实现(使用@Value)。

【原理图】

img

@Value
作用:用于注入基本类型和String类型的数据
属性:
value:用于指定数据的值。使用${表达式}可以读取配置文件(.properties文件)的信息(一会讲…)
在AccountServiceImpl.java中配置:

2.6.3注解配置-作用域

他们的作用就和在bean标签中使用scope属性实现的功能是一样的
@Scope
作用:用于指定bean的作用范围
属性:
value:指定范围的取值。常用取值:singleton prototype

3.AOP

3.1 什么是AOP

img

AOP (Aspect Oriented Programing) 称为:面向切面编程,它是一种编程思想。

AOP采取横向抽取机制,取代了传统纵向继承体系重复性代码的编写方式(应用场景:例如性能监视、事务管理、安全检查、缓存、日志记录等)。

img

3.2 AOP的作用

l 权限校验

l 日志记录

l 性能检测

l 缓存技术

l 事务管理

3.3 AOP底层实现

代理机制。
2个:spring的aop的底层原理
1:JDK代理(要求目标对象面向接口)(spring默认的代理方式是JDK代理)
2:CGLIB代理(面向接口、面向类)

3.4 AOP的相关术语

Joinpoint(连接点): (方法)
所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点。
Pointcut(切入点): (方法)
所谓切入点是指我们要对哪些Joinpoint进行拦截的定义。
Advice(通知/增强): (方法)
所谓通知是指拦截到Joinpoint之后所要做的事情就是通知。
通知的类型:前置通知,后置通知,异常通知,最终通知,环绕通知。
Target(目标对象):
代理的目标对象。
Weaving(织入): (了解)
是指把增强应用到目标对象来创建新的代理对象的过程。
spring采用动态代理织入,而AspectJ采用编译期织入和类装载期织入。
Proxy(代理):
一个类被AOP织入增强后,就产生一个结果代理类。
Aspect(切面): (类)
是切入点和通知(引介)的结合。
Introduction(引介): (不用了解)
引介是一种特殊的通知在不修改类代码的前提下, Introduction可以在运行期为类动态地添加一些方法或Field。

3.5 Aop的五种通知类型

前置通知、后置通知、异常通知、最终通知、环绕通知。

img

4.Spring中的事务控制

4.1 Spring事务控制我们要明确

第一:JavaEE体系进行分层开发,事务处理位于业务层,Spring提供了分层设计业务层的事务处理解决方案。

第二:spring框架为我们提供了一组事务控制的接口 。具体在后面的第二小节介绍。这组接口是在spring-tx-5.0.2.RELEASE.jar中。

第三:spring的事务控制都是基于AOP的,它既可以使用编程的方式实现,也可以使用配置的方式(声明式)实现。重点是使用配置(声明式事务处理)的方式实现。

4.2 编程式事务控制

xml配置文件:

配置事务管理器

1
2
3
4
<!-- 一:配置事务管理器=============== -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>

配置事务管理模板

1
2
3
4
<!-- 二:配置事务管理的模板 -->
<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager" ref="transactionManager"/>
</bean>

在业务层注入事务管理模板

1
2
3
4
5
<!-- 配置账户的业务层-->
<bean id="accountService" class="com.cyannote.service.impl.AccountServiceImpl">
<property name="accountDao" ref="accountDao"></property>
<property name="transactionTemplate" ref="transactionTemplate"/>
</bean>

改写业务层的代码

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
48
public class AccountServiceImpl implements AccountService {

// 注入事务管理的模板
private TransactionTemplate transactionTemplate;

public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
this.transactionTemplate = transactionTemplate;
}

private AccountDao accountDao;

public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}



public Account findAccountById(Integer accountId) {
return accountDao.findAccountById(accountId);

}

public void transfer(final String sourceName, final String targetName, final Float money) {
System.out.println("transfer....");
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
System.out.println(transactionStatus.isNewTransaction() + " "+transactionStatus.isCompleted());
//2.1根据名称查询转出账户
Account source = accountDao.findAccountByName(sourceName);
//2.2根据名称查询转入账户
Account target = accountDao.findAccountByName(targetName);
//2.3转出账户减钱
source.setMoney(source.getMoney()-money);
//2.4转入账户加钱
target.setMoney(target.getMoney()+money);
//2.5更新转出账户
accountDao.updateAccount(source);

// int i=1/0;

//2.6更新转入账户
accountDao.updateAccount(target);
}
});

}
}

4.3 声明式事务处理

配置文件

  • 配置事务管理器

    1
    2
    3
    4
    <!-- 一:配置事务管理器 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"></property>
    </bean>
  • 配置事务通知

    1
    2
    <!-- 二:配置事务的通知-->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
  • 配置aop

    1
    2
    3
    4
    5
    6
    7
    <!-- 三:配置aop-->
    <aop:config>
    <!-- 配置切入点表达式-->
    <aop:pointcut id="pc" expression="execution(* com.cyannote.service..*.*(..))"></aop:pointcut>
    <!--建立切入点表达式和事务通知的对应关系 -->
    <aop:advisor advice-ref="txAdvice" pointcut-ref="pc"></aop:advisor>
    </aop:config>

    注解方式

配置AccountServiceImpl.java

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
@Service
@Transactional(readOnly = true)
public class AccountServiceImpl implements AccountService {

@Autowired
private AccountDao accountDao;


public Account findAccountById(Integer accountId) {
return accountDao.findAccountById(accountId);

}
@Transactional(readOnly = false,propagation = Propagation.REQUIRED)
public void transfer( String sourceName, String targetName, Float money) {
System.out.println("transfer....");

//2.1根据名称查询转出账户
Account source = accountDao.findAccountByName(sourceName);
//2.2根据名称查询转入账户
Account target = accountDao.findAccountByName(targetName);
//2.3转出账户减钱
source.setMoney(source.getMoney() - money);
//2.4转入账户加钱
target.setMoney(target.getMoney() + money);
//2.5更新转出账户
accountDao.updateAccount(source);

int i=1/0;

//2.6更新转入账户
accountDao.updateAccount(target);

}
}

配置applicationContext.xml

1
2
3
4
5
6
<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>

<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>

总结

Spring整体的总结:

总结:
1:IOC DI(xml和注解)
2:AOP(5种通知)(xml和注解)
3:声明式事务处理(xml和注解)

  • DBUtils(第三方提供的)
  • JDBCTemplate(是spring提供的)
  • Mybatis(

4:spring3的新特性(纯注解开发)
@Configuration
@ConnectionScan
@Import
@Bean

自己创建的对象:使用注解
第三方创建的对象(数据源…):使用配置文件

文件上传和下载

文件上传和下载

1.文件上传

导包:commons-fileupload .jar commons-io .jar

1.1文件上传表单的三个要素

  1. 表单的提交的方式需要是POST
  2. 表单中需要有<input type=”file”素,需要有name属性和值。
  3. 表单enctype=”multipart/form-data”

1.2文件上传的步骤

  1. 创建磁盘文件项工厂

    DiskFileItemFactory df = new DiskFileItemFactory();

  2. 创建核心文件解析类

    ServletFileUpload sf = new ServletFileUpload(df);

  3. 解析请求

    list list = sf.parseRequest(request);

  4. 遍历集合,判断请求项 for(FileItem f:list)

    1. 若不是普通项 ! f.isFormItem()

      获取文件名, f.getName()

      获取文件内容(输入流)f.getInputStream()

      创建输出流 getServletContext.getRealPath(“存储路径”) ; OutPutStream os = new fileOutPutStream(realPath+”\“+filename);

      IO流写入文件

    2. 普通项

      获取参数名 f.getFileName()

      获取内容 f.getString();//可传入编码格式解决中文乱码问题

1.3 文件上传的Servlet

//1.创建磁盘文件项工厂DiskFileItemFactory

//创建一个核心的解析类ServletFileUpload

//利用核心类解析Request,解析后会得到多个部分,返回一个list集合 parseRequest

//遍历list集合,得到代表每个部分的文件项的对象.根据文件项判断是否为文件上传项

isFormItem()

文件上传的API

1.4 DiskFileItemFactory:磁盘文件项工厂

sizeThreshold 设置文件上传的缓冲区大小,默认10kb

Repository 临时文件存放路径

1.5 ServletFileUpload:核心解析类

FileItemFactory

isMutipartContent:判断恩仇type属性是否为multipart/form-data

parseRequest:解析request对象,返回一个list集合(每个部分的文件对象)

setFileSizeMax:设置单个文件的大小

setSizeMax:设置上传文件的总大小

setHeaderEncoding :设置中文文件名乱码的问题

setProgressListener:设置监听文件上传的进度

1.6 FileItem

isFormField:是否为普通项

getFieldName:获得普通项名字

getString:获得普通项内容

getName获得文件上传项名称

getInputStream:获得上传项内容

1.7 getSize获得上传文件大小

getContentType:获得上传文件格式

delete删除临时文件

//相同文件名文件上传解决办法

UUID.randomUUID().toString().relplace(“-“,””)+fliename.substring(filename.lastIndexOf(“.”));

//同一文件夹文件过多解决

文件上传代码

downloadServlet

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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;

import com.itheima.utils.UUIDUtils;

/**
* 上传Servlet
*/
public class uploadServlet extends HttpServlet {
protected void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
protected void doPost(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
try {
// request.setCharacterEncoding("UTF-8");
// 1.创建磁盘文件项工厂
DiskFileItemFactory fileitemfactory = new DiskFileItemFactory();
// 设置文件缓冲区大小
fileitemfactory.setSizeThreshold(1024 * 1024 * 3);
// 设置临时文件路径
String path = getServletContext().getRealPath("/temp");
fileitemfactory.setRepository(new File(path));
// 2.创建核心解析类
ServletFileUpload si = new ServletFileUpload(fileitemfactory);
// 解决中文文件名乱码问题
si.setHeaderEncoding("UTF-8");
// 3.解析请求
List<FileItem> parseRequest = si.parseRequest(request);
// 4. 判断文件上传项
for (FileItem fileItem : parseRequest) {
if (fileItem.isFormField()) {
// 基础项
String name = f.getFieldName();
// String content = f.getString();
// 解决获取普通项内容乱码的问题
String content = f.getString("UTF-8");
System.out.println(name + "---" + content);
} else {
// 文件上传项
// 获取文件名
String filename = fileItem.getName();
// 解决文件名重复的问题,UUID
filename = UUIDUtils.getUUID(filename);
// 处理ie浏览器文件名带路径的问题
if (filename.lastIndexOf("\\") != -1) {
filename = filename.substring(filename
.lastIndexOf("\\") + 1);
}
// 获取文件内容
InputStream is = fileItem.getInputStream();
// 获取上传路径
String realPath = getServletContext().getRealPath(
"/upload");
// 创建输出流
String path2 = UUIDUtils.getPath(filename);
String path3 = realPath + path2;
File file = new File(path3);
if (!file.exists()) {
file.mkdirs();
}
OutputStream os = new FileOutputStream(path3 + "\\"
+ filename);
// 上传
int len;
byte[] b = new byte[1024];
while ((len = is.read(b)) != -1) {
os.write(b, 0, len);
}
// 关闭流
is.close();
os.close();
// 删除临时文件
fileItem.delete();
}
}
} catch (FileUploadException e) {
e.printStackTrace();
}
}
}

upload.jsp

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
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
<script type="text/javascript">
function add() {
document.getElementById("div1").innerHTML += "<div><input type='file' name='updowm'><input type='button' value='删除' onclick='del(this)'></div>";
}
function del(who) {
who.parentNode.parentNode.removeChild(who.parentNode);
}
</script>
</head>
<body>
<h1>上传页面</h1>
<form action="${pageContext.request.contextPath }/uploadServlet"
method="post" enctype="multipart/form-data">
<input type="button" value="添加" onclick="add()"><input
type="submit" value="上传"><br>
<div id="div1"></div>
</form>
</body>
</html>

2.文件下载

2.1 文件下载流程

  1. 获取数据

    String filename = request.getParameter(“filename”);

  2. 设置两个头和一个输入流

    1. 设置两个头

      设置Content-Type

      response.setContentType(“getServletContext().getMimeType(filename)”);

      设置Content-Disposition

      response.setHeader(“Content-Disposition”,”attachment;filename=”+filename)

    2. 设置输入流

      String path=getServletContext().getRealPath(“/download”);

      InputStream is = new FileInputStream(path+”/“+filename);

  3. 获取输出流

    ​ OutPutStream os = response.getOutputStream();

2.2文件下载代码

downloadServlet

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
48
49
50
51
52
53
54
55
56
57
58
59
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;

import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.itheima.utils.DownloadEncodingUtils;

/**
* 文件下载Servlet
*/
public class downloadServlet extends HttpServlet {
private static final long serialVersionUID = 1L;

protected void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
// 获取数据
String filename = request.getParameter("filename");
// 解决文件名中文乱码问题
filename = new String(filename.getBytes("ISO-8859-1"), "UTF-8");
// 设置两个头,一个流
// 设置ContentType
response.setContentType(getServletContext().getMimeType(filename));
// 设置输入流
String realPath = getServletContext().getRealPath("/download");
InputStream is = new FileInputStream(realPath + "/" + filename);
System.out.println(filename);
// 解决不同浏览器文件名中文显示问题
String agent = request.getHeader("User-Agent");
if (agent.contains("Firefox")) {
// 火狐浏览器
filename = DownloadEncodingUtils.base64EncodeFileName(filename);
} else {
filename = URLEncoder.encode(filename, "UTF-8");
filename = filename.replace("+", " ");
}
// 设置Content-Disposition
System.out.println(filename);
response.setHeader("Content-Disposition", "attachment;filename="
+ filename);
// 获取输出流
ServletOutputStream os = response.getOutputStream();
int len = 0;
byte[] b = new byte[1024];
while ((len = is.read(b)) != -1) {
os.write(b, 0, len);
}
is.close();
}
protected void doPost(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}

download.jsp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>文件下载</h1>
<a href="${pageContext.request.contextPath }/download/psb.jpg">psb.jpg</a>
<h3>手动下载</h3>
<a
href="${pageContext.request.contextPath }/downloadServlet?filename=psb.jpg">psb.jpg</a>
<br>
<a
href="${pageContext.request.contextPath }/downloadServlet?filename=中文.jpg">中文.jpg</a>
</body>
</html>

2.3 文件列表下载

multiDownloadServlet

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
48
49
50
51
52
53
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;

import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.itheima.utils.DownloadEncodingUtils;

/**
* Servlet implementation class multidownloadServlet
*/
public class multidownloadServlet extends HttpServlet {
private static final long serialVersionUID = 1L;

protected void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
String path = request.getParameter("path");
path = new String(path.getBytes("ISO-8859-1"), "UTF-8");
File file = new File(path);
String filename = file.getName();
// 设置两个头,一个流
response.setContentType(getServletContext().getMimeType(filename));
InputStream is = new FileInputStream(file);
// 处理中文名
String agent = request.getHeader("User-Agent");
if (agent.contains("Firefox")) {
filename = DownloadEncodingUtils.base64EncodeFileName(filename);
} else {
filename = URLEncoder.encode(filename, "UTF-8");
filename = filename.replace("+", " ");
}
response.setHeader("Content-Disposition", "attach;filename=" + filename);
ServletOutputStream os = response.getOutputStream();
int len = 0;
byte[] b = new byte[1024];
while ((len = is.read(b)) != -1) {
os.write(b, 0, len);
}
//关闭流
is.close();
os.close();
}
protected void doPost(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}

multiDownload.jsp

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
<%@page import="java.io.File"%>
<%@page import="java.util.*"%>
<%@page import="java.util.LinkedList"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>多文件下载</h1>
<%
//创建队列
Queue<File> queue = new LinkedList<File>();
//创建根节点
File root = new File(
"F:\\java基础增强\\javaweb_day19(文件上传和下载)\\资料\\下载资料列表");
//根节点入队
queue.offer(root);
while (!queue.isEmpty()) {
//出队
File file = queue.poll();
//获取出队元素的所有子元素
File[] files = file.listFiles();
//遍历子元素
for (File f : files) {
if (f.isFile()) {
//为标准文件,则输出
%>
<h3>
<a
href="${pageContext.request.contextPath }/multidownloadServlet?path=<%=f.getCanonicalPath()%>"><%=f.getName()%></a>
</h3>
<%
} else {
//不是标准文件,则入队
queue.offer(f);
}
}
}
%>
</body>
</html>

日历对象 Calendar

1
Calendar cal = Calendar.getInstance();