Spring框架是个轻量级的Java EE框架。所谓轻量级,是指不依赖于容器就能运行的 .
轻量级框架是相对于重量级框架而言的,重量级框架必须依赖特定的容器,例如EJB框架就必须运行Glassfish、JBoss等支持EJB的容器中,而不能运行在Tomcat中
Spring以IOC、AOP为主要思想,其中IOC(Inversion of Control) 指控制反转或反向控制。在Spring框架中我们通过配置创建类对象,由Spring在运行阶段实例化、组装对象。AOP,Aspect Oriented Programming,面向切面编程,其思想是在执行某些代码前执行另外的代码,使程序更灵活、扩展性更好,可以随便地添加、删除某些功能。Servlet中的Filter便是一种AOP思想的实现。
Spring同时也是一个“一站式”框架,即Spring在JavaEE的三层架构[表现层(Web层)、业务逻辑层(Service层)、数据访问层(DAO层)]中,每一层均提供了不同的解决技术
- 表现层(Web层):Spring MVC
- 业务逻辑层(Service层):Spring的IoC
- 数据访问层(DAO层):Spring的jdbcTemplate
创建对象主要有三种方式:加载类,加载静态方法,加载动态方法
参数描述:
id:唯一标识符
class:全限定类名
factory-bean:类对象
factory-method:类方法(成员方法和静态方法)
<bean id="userService1" class="com.itheima.service.UserServiceimpl">bean>
<bean id="userService2" class="com.itheima.utils.FactoryUtils" factory-method="createOldUserService">bean>
<bean id="factoryUtils" class="com.itheima.utils.FactoryUtils">bean>
<bean id="userService3" factory-bean="factoryUtils" factory-method="createUserService">bean>
对象初始化方法和销毁方法
<bean id="userService4" class="com.itheima.service.UserServiceimpl" init-method="init" destroy-method="destory">bean>
对象创建采用单例或者多例
<bean id="userService11" class="com.itheima.service.UserServiceimpl" scope="singleton">bean>
<bean id="userService12" class="com.itheima.service.UserServiceimpl" scope="prototype">bean>
参数注入也有构造注入,set注入,p注入
方式1:构造注入
<bean id="userService_1" class="com.itheima.service.UserServiceimpl">
<constructor-arg index="0" value="张华">constructor-arg>
<constructor-arg index="1" value="11">constructor-arg>
bean>
<bean id="userService_2" class="com.itheima.service.UserServiceimpl">
<constructor-arg name="name" value="张华">constructor-arg>
<constructor-arg name="id" value="11">constructor-arg>
bean>
<bean id="userService_3" class="com.itheima.service.UserServiceimpl">
<constructor-arg name="name" value="张华">constructor-arg>
<constructor-arg name="id" value="11">constructor-arg>
<constructor-arg name="user" ref="user">constructor-arg>
bean>
<bean id="user" class="com.itheima.dao.User">
<constructor-arg name="username" value="宋祖儿">constructor-arg>
<constructor-arg name="id" value="11">constructor-arg>
bean>
set注入
set注入,可以注入复杂类型数据
<bean id="userService_4" class="com.itheima.service.UserServiceimpl">
<constructor-arg name="name" value="张华">constructor-arg>
<constructor-arg name="id" value="11">constructor-arg>
<constructor-arg name="user" ref="user1">constructor-arg>
bean>
<bean id="user1" class="com.itheima.dao.User">
<property name="username" value="宋祖儿">property>
bean>
<bean id="userService_5" class="com.itheima.service.UserServiceimpl1">
<constructor-arg name="name" value="张华">constructor-arg>
<constructor-arg name="id" value="11">constructor-arg>
<constructor-arg name="user" ref="user2">constructor-arg>
bean>
<bean id="user2" class="com.itheima.domain.User">
<property name="username" value="宋祖儿">property>
<property name="nickName" >
<array>
<value>1value>
<value>2value>
<value>1value>
array>
property>
bean>
也可以注入其他数据类型
<property name="myList">
<list>
<value>a1value>
<value>b2value>
<value>c3value>
<value>d4value>
list>
property>
<property name="mySet">
<array>
<value>a5value>
<value>a5value>
<value>b6value>
<value>c7value>
<value>d8value>
array>
property>
<property name="myMap">
<map>
<entry key="key1" value="value1">entry>
<entry key="key2" value="value2">entry>
<entry key="key3" value="value3">entry>
map>
property>
<property name="myProperties">
<map>
<entry key="keyxx" value="value1">entry>
<entry key="keyqqq" value="value2">entry>
<entry key="keyeree" value="value3">entry>
map>
property>
p注入,书写基本类型简单
<bean id="userService" class="com.itheima.service.impl.UserServiceImpl4" p:username="zhangsan" p:age="18" p:password="1234">bean>
Dao层,service层,其他层相应地配置xml文件,可以比较清晰地处理对象和方法
方式1:在xml文件中导入其他配置文件
<import resource="applicationContext-dao.xml">import>
方式2:在java代码中初始化时,可以加载到同一个容器中
ApplicationContext ac= new ClassPathXmlApplicationContext("applicationContext.xml","applicationContext-service.xml" ,"applicationContext-dao.xml");
1.Dbutils是什么?(当我们很难理解一个东西的官方解释的时候,就让我们记住它的作用)
Dbutils:主要是封装了JDBC的代码,简化dao层的操作。
作用:帮助java程序员,开发Dao层代码的简单框架。
框架的作用:帮助程序员,提高程序的开发效率。
出生:Dbutils是由Apache公司提供。2.为什么需要Dbutils ?
在使用Dbutils 之前,我们Dao层使用的技术是JDBC,那么分析一下JDBC的弊端:(1)数据库链接对象、sql语句操作对象,封装结果集对象,这三大对象会重复定义
(2)封装数据的代码重复,而且操作复杂,代码量大
(3)释放资源的代码重复
结果:(1)程序员在开发的时候,有大量的重复劳动。(2)开发的周期长,效率低3.Dbutils三个核心类介绍:
1:DbUtils:连接数据库对象----jdbc辅助方法的集合类,线程安全
构造方法:DbUtils()
作用:控制连接,控制书屋,控制驱动加载额一个类。 2:QueryRunner:SQL语句的操作对象,可以设置查询结果集的封装策略,线程安全。
构造方法:
(1)QueryRunner():创建一个与数据库无关的QueryRunner对象,后期再操作数据库的会后,需要手动给一个Connection对象,它可以手动控制事务。
Connection.setAutoCommit(false); 设置手动管理事务
Connection.commit(); 提交事务 (2)QueryRunner(DataSource ds):创建一个与数据库关联的queryRunner对象,后期再操作数据库的时候,不需要Connection对象,自动管理事务。
DataSource:数据库连接池对象。
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver">property>
<property name="username" value="root">property>
<property name="password" value="root">property>
<property name="url" value="jdbc:mysql:///ssm">property>
bean>
<bean id="dataSource2" class="org.apache.commons.dbcp2.BasicDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver">property>
<property name="username" value="root">property>
<property name="password" value="root">property>
<property name="url" value="jdbc:mysql:///ssm">property>
bean>
<bean id="dataSource3" class="com.zaxxer.hikari.HikariDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver">property>
<property name="username" value="root">property>
<property name="password" value="root">property>
<property name="jdbcUrl" value="jdbc:mysql:///ssm">property>
bean>
<bean id="dataSource4" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver">property>
<property name="username" value="root">property>
<property name="password" value="root">property>
<property name="url" value="jdbc:mysql:///ssm">property>
bean>
注解某些场合用起来比较方便,以下是一些常用注解
1.@Component//可以作用于任何类(考虑到语义,作用于web,service,dao三层除外的类)(基本注解)
2.@Controller //应用在web层(servlet) Controller 控制层 (语义注解)
3.@Service //应用在service Service 业务逻辑层 (语义注解)
4.@Repository //应用dao层 Repository 仓库 Repository 仓库层(数据库层) (语义注解)
5.@Scope("singleton") //单例
@Scope("prototype") //原型(域注解)
6.@PostConstruct //初始化(生命周期注解)
7.@PreDestroy //销毁(生命周期注解)
8.@Value("值") //注入普通属性
9.@AutoWired 自动装配 //@Resource jdk提供的注入注解
@Qualifier("id") //指明使用的id名称(当同一个接口有多个实现类)
配置方式:xml配置,l半注解配置,全注解配置
方式1:xml配置
<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl">
<property name="accountDao" ref="accountDao">property>
bean>
<bean id="accountDao" class="com.itheima.dao.impl.AccountDaoImpl">
<property name="queryRunner" ref="queryRunner">property>
bean>
<bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner">
<constructor-arg name="ds" ref="dataSource">constructor-arg>
bean>
<context:property-placeholder location="classpath:db.properties">context:property-placeholder>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}">property>
<property name="url" value="${jdbc.url}">property>
<property name="username" value="${jdbc.username}">property>
<property name="password" value="${jdbc.password}">property>
bean>
方式2:半注解配置
<context:component-scan base-package="com.itheima">context:component-scan>
方式3:全注解配置
就是对半注解进行改造,将数据库控制器和数据源写在类文件中,进而可以使用注解方式
配置类所需要用的注解
@Configuration //配置类
@ComponentScan(basePackages = "com.ithiema") //扫描包
@Autowired //自动装配
@PropertySource("classpath:jdbc.properties")//引入配置文件
@Test// 测试程序
@Bean(name="dataSource") //将方法返回值的对象 放入spring容器中
@Import //引入其他的配置类
配置类
@Configuration // 携带此注解表示 当前类就是一个配置类 作用跟xml一致
@ComponentScan(basePackages = "com.itheima") //扫描注解
@PropertySource("classpath:db.properties") //将db.properties 加载到程序内部
@Import(Config1.class) //引入其他的配置 等效
public class MyApplicationContext {
//@Value 支持 springEL的写法
@Value("${jdbc.driverClassName}")
private String driver;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
@Bean(name="dataSource")
public DataSource createDataSource(){
//流 加载 解析 赋值
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setDriverClassName(driver);
druidDataSource.setUrl(url);
druidDataSource.setUsername(username);
druidDataSource.setPassword(password);
return druidDataSource;
}
@Bean(name="queryRunner")
public QueryRunner createQueryRunner( DataSource dataSource){
QueryRunner queryRunner = new QueryRunner(dataSource);
return queryRunner;
}
}
事务模板
public void template(){
try {
connection.setAutoCommit(false);//1.开启事务
//逻辑操作2.0
connection.commit();//3.0提交事务
}catch (Exception e){
e.printStackTrace();
connection.rollback();//3.1回滚事务
}
}
}
因为需要使用事务,就需要从数据源中获取数据库连接,获取连接,由于数据源每次提供的连接可能是不一样的,所以需要手动传入一个Connection,或者用线程共享变量
首先将事务模板进行方法抽取,其次建立线程共享变量
//建立线程共享变量
private ThreadLocal local = new ThreadLocal();
//开启事务
public Connection openTx() throws SQLException {
Connection connection = dataSource.getConnection();
local.set(connection);
connection.setAutoCommit(false);
return connection;
}
//提交事务 并关闭事务
public void commitAndClose() throws SQLException {
Connection connection = this.getConnection()
DbUtils.commitAndClose(connection);
}
//回滚事务 并关闭事务
public void rollbackAndClose() throws SQLException {
Connection connection = this.getConnection()
DbUtils.rollbackAndClose(connection);
}
//获取数据源连接
public Connection getConnection(){
return local.get();
}
代理主要有动态代理和cglib代理
代理可以将于逻辑无关的代码进一步进行抽取,可以只关心逻辑代码,
参数说明:类加载器,接口数组,方法执行器
public Object proxy(Object target){
//代理对象
Object proxyTarget = Proxy.newProxyInstance(
MyJDKProxyTransactionManager.class.getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object returnObj = null; //service中的转账
try {
System.out.println("开启了事务");
//开启事务
jdbcConnectionUtils.openTx();
//放行 不能影响原来的业务逻辑
returnObj = method.invoke(target, args);
jdbcConnectionUtils.commitAndClose();
//提交事务
System.out.println("提交了事务");
} catch (Exception e) {
e.printStackTrace();
//回滚事务
jdbcConnectionUtils.rollbackAndClose();
System.out.println("回滚事务");
}
return returnObj;
}
}
);
//返回代理对象
return proxyTarget;
}
public Object proxy(Object target){
//创建增强器
Enhancer enhancer = new Enhancer();
//设置父类
enhancer.setSuperclass(target.getClass());
//设置回调函数 处理的方法
enhancer.setCallback(new MethodInterceptor(){
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
Object returnObj = null; //service中的转账
try {
System.out.println("cglib 开启了事务");
//开启事务
jdbcConnectionUtils.openTx();
//放行 不能影响原来的业务逻辑
returnObj = method.invoke(target, objects);
jdbcConnectionUtils.commitAndClose();
//提交事务
System.out.println("cglib 提交了事务");
} catch (Exception e) {
e.printStackTrace();
//回滚事务
jdbcConnectionUtils.rollbackAndClose();
System.out.println("cglib 回滚事务");
}
return returnObj;
}
});
//创建代理类对象
Object cglibProxy = enhancer.create();
//返回代理类对象
return cglibProxy;
}
通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。
AOP是一种编程范式,是OOP的延续,在OOP基础之上进行横向开发。
AOP研究的不是每层内部如何开发,而是同一层面上各个模块之间的共性功能。
AOP : 可以增强代码 , 将某一个层的公共代码抽取出来比如:事务、日志、统计。
相关专业术语如下
Joinpoint (连接点) 可以被增强的方法
:被代理对象的所有方法
Pointcut( 切入点)
:被增强的方法
Advice:通知/增强 增强方法
前置通知:进入被代理对象方法之前执行的增强业务逻辑
后置通知:执行被代理对象方法获取返回值之后执行的增强业务逻辑
异常通知:抛出异常执行的增强业务逻辑
最终通知:finally代码块中执行的增强业务逻辑
环绕通知
Target:被代理对象
Weaving:织入
技术手段:动态代理
Proxy:代理对象
Aspect:切面
是切入点和通知的结合
配置:被增强方法和增强业务之间的关联关系
<aop:config>
<aop:pointcut expression="execution(public void com.itheima.service.impl.AccountServiceImpl.save())" id="point1">aop:pointcut>
<aop:aspect ref="myEnhance">
<aop:before method="before" pointcut-ref="point1">aop:before>
<aop:after-returning method="afterReturning" pointcut-ref="point1">aop:after-returning>
<aop:after-throwing method="afterThrowing" pointcut-ref="point1">aop:after-throwing>
<aop:after method="after" pointcut-ref="point1">aop:after>
aop:aspect>
aop:config>
配置文件中要开启Aop注解支持
<aop:aspectj-autoproxy>aop:aspectj-autoproxy>
使用注解
@Aspect //切面类
@Before//前置事件
@AfterReturning//后置事件
@AfterThrowing//异常事件
@After//最终事件
@Around//环绕事件
xml配置
<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl">
<property name="accountDao" ref="accountDaoImpl">property>
bean>
<bean id="jdbcConnectionUtils" class="com.itheima.utils.JdbcConnectionUtils">
<property name="dataSource" ref="dataSource">property>
bean>
<aop:config>
<aop:aspect ref="jdbcConnectionUtils">
<aop:pointcut id="point" expression="execution(* com.itheima.service.impl.*.*(..))">aop:pointcut>
<aop:before method="openTx" pointcut-ref="point" >aop:before>
<aop:after-returning method="commitAndClose" pointcut-ref="point">aop:after-returning>
<aop:after-throwing method="rollbackAndClose" pointcut-ref="point">aop:after-throwing>
aop:aspect>
aop:config>
注解配置(半注解)
<aop:aspectj-autoproxy>aop:aspectj-autoproxy>
全注解
在配置类中开启AOP的注解支持
@EnableAspectJAutoProxy //注解的支持
JdbcTemplate是Spring对JDBC的封装,目的是使JDBC更加易于使用。JdbcTemplate是Spring的一部分。JdbcTemplate处理了资源的建立和释放
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<constructor-arg name="dataSource" ref="dataSource">constructor-arg>
bean>
在spring中提供了事务管理器(事务管理平台接口) , 定义了操作事务方法 , 使用不同的dao层技术 对这个接口进行实现, 方法实现即可
PlatformTransactionManager : 事务管理平台接口 定义了操作事务的方法spring 对接口已经进行了实现 , spring 为了每一个操作事务的技术,都可以提供实现提供的方法
TransactionStatus getTransaction(TransactionDefinition def) //获取事务的状态信息
void commit(TransactionStatus status) //提交事务
void rollback(TransactionStatus status) //回滚事务
事务管理器常用实现类 TransactionManager 事务
DataSourceTransactionManager//使用JDBC和iBatis 和 mybatis进行持久化数据时使用
HibernateTransactionManager//使用Hibernate进行持久化数据时使用
JpaTransactionManager//使用JPA进行持久化时使用(后期)
JtaTransactionManager //【分布式事务】时使用
事务的问题:
脏读 : 一个事务读到了另一个事务 没有提交的数据
不可重复读 : 一个事务读到了另一个事务 已经提交的数据 (泛指update)
虚读/幻读: 一个事务读到了另一个事务 已经提交的数据(泛指insert操作)
事务隔离级别 : (解决方案)
1.读未提交: 不解决任何问题 ( read uncommitted )
2.读已提交: 解决 脏读 read committed ( oracle 默认隔离级别)
3.可重复读: 解决 脏读和不可重复读 repeatable read ( mysql 默认)
4.序列化: 解决所有问题 serializable
性能: 1>2>3>4
安全: 1<2<3<4
一个方法调用另一个方法 此时 两个方法的事务状态如何
事务传播行为指的就是当一个业务方法被另一个业务方法调用时,应该如何进行事务控制。
A调用B的时候 B如何控制事务
PROPAGATION_REQUIRED(必须有事务,这是默认值) 配置增删改
如果A存在一个事务,则B加入到当前事务。如果A没有事务则B开启一个新的事务。(AB处于同一个事务中 , 不管A有没有事务 反正B需要有)
PROPAGATION_SUPPORTS(支持有事务) 配置查询
如果A存在一个事务,则B加入到当前事务。如果没有事务则非事务运行。 查询不一定非要事务PROPAGATION_REQUIRES_NEW(必须有新的)
总是开启一个新的事务。如果存在一个事务,则将这个存在的事务挂起,再来一个新的。PROPAGATION_NOT_SUPPORTED((不支持有事务)
总是非事务地执行,并挂起任何存在的事务。PROPAGATION_MANDATORY(强制有事务,自己还不负责创建)
如果存在一个事务,则加入到当前事务。如果没有事务,则抛出异常。PROPAGATION_NEVER(强制不要事务,自己还不负责挂起):
总是非事务地执行,如果存在一个活动事务,则抛出异常。PROPAGATION_NESTED(嵌套事务)
如果一个活动的事务存在,则运行在一个嵌套的事务中。如果没有活动事务, 则开启一个新的事
务。(取钱)
内层事务依赖于外层事务。外层事务失败时,会回滚内层事务所做的动作。
而内层事务操作失败并不会引起外层事务的回滚。
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource">property>
bean>
<tx:advice id="txAdvice">
<tx:attributes>
<tx:method name="find*" isolation="DEFAULT" propagation="SUPPORTS" read-only="true">tx:method>
<tx:method name="select*" isolation="DEFAULT" propagation="SUPPORTS" read-only="true">tx:method>
<tx:method name="query*" isolation="DEFAULT" propagation="SUPPORTS" read-only="true">tx:method>
<tx:method name="*">tx:method>
tx:attributes>
tx:advice>
<aop:config>
<aop:pointcut id="myPoint" expression="execution(* com.itheima.service.impl.*.*(..))">aop:pointcut>
<aop:advisor advice-ref="txAdvice" pointcut-ref="myPoint">aop:advisor>
aop:config>
xml配置
<context:component-scan base-package="com.itheima">context:component-scan>
<mvc:annotation-driven>mvc:annotation-driven>
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsps/">property>
<property name="suffix" value=".jsp">property>
bean>
核心控制器
<servlet>
<servlet-name>DispatcherServletservlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
<init-param>
<param-name>contextConfigLocationparam-name>
<param-value>classpath:spring-mvc.xmlparam-value>
init-param>
servlet>
<servlet-mapping>
<servlet-name>DispatcherServletservlet-name>
<url-pattern>/*url-pattern>
servlet-mapping>
<filter>
<filter-name>CharacterEncodingFilterfilter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilterfilter-class>
<init-param>
<param-name>encodingparam-name>
<param-value>utf-8param-value>
init-param>
filter>
<filter-mapping>
<filter-name>CharacterEncodingFilterfilter-name>
<url-pattern>/*url-pattern>
filter-mapping>
处理器映射器
RequestMappingHandlerMapping 映射器 根据路径找方法
处理器适配器
RequestMappingHandlerAdapter 适配器 帮助我们执行方法
<h1>特殊的数据类型h1>
<form action="${pageContext.request.contextPath}/params/demo3.do" method="get">
<input type="text" name="username" value="jack"><br/>
<input type="text" name="age" value="18"><br/>
<input type="text" name="password" value="1234"><br/>
<input type="text" name="nickname" value="rose"><br/>
<input type="checkbox" name="hobby" value="1">
<input type="checkbox" name="hobby" value="2">
<input type="checkbox" name="hobby" value="3">
<input type="checkbox" name="hobby" value="4">
<input type="submit">
form>
<h1>特殊的数据类型-对象嵌套对象h1>
<form action="${pageContext.request.contextPath}/params/demo4.do" method="get">
<input type="text" name="username" value="jack"><br/>
<input type="text" name="age" value="18"><br/>
<input type="text" name="password" value="1234"><br/>
<input type="text" name="nickname" value="rose"><br/>
<input type="checkbox" name="hobby" value="1">
<input type="checkbox" name="hobby" value="2">
<input type="checkbox" name="hobby" value="3">
<input type="checkbox" name="hobby" value="4">
<%--跟el类似--%>
<input type="text" name="address.city" value="beijing">
<input type="text" name="address.street" value="heima">
<input type="submit">
form>
<h1>特殊的数据类型-list-stringh1>
<form action="${pageContext.request.contextPath}/params/demo4.do" method="get">
<input type="text" name="username" value="jack"><br/>
<input type="text" name="age" value="18"><br/>
<input type="text" name="password" value="1234"><br/>
<input type="text" name="nickname" value="rose"><br/>
<input type="checkbox" name="hobby" value="1">
<input type="checkbox" name="hobby" value="2">
<input type="checkbox" name="hobby" value="3">
<input type="checkbox" name="hobby" value="4">
<%--跟el类似--%>
<input type="text" name="address.city" value="beijing">
<input type="text" name="address.street" value="heima">
<br/>
<%--list--%>
<%-- <input type="text" name="strList" value="a">
<input type="text" name="strList" value="b">
<input type="text" name="strList" value="c">
提交list数据的时候 建议 带索引 指定某个位置的数据是谁
--%>
<input type="text" name="strList[2]" value="a">
<input type="text" name="strList[1]" value="b">
<input type="text" name="strList[0]" value="c">
<br/>
<input type="submit">
form>
<h1>特殊的数据类型-list-对象h1>
<form action="${pageContext.request.contextPath}/params/demo4.do" method="get">
<input type="text" name="username" value="jack"><br/>
<input type="text" name="age" value="18"><br/>
<input type="text" name="password" value="1234"><br/>
<input type="text" name="nickname" value="rose"><br/>
<input type="checkbox" name="hobby" value="1">
<input type="checkbox" name="hobby" value="2">
<input type="checkbox" name="hobby" value="3">
<input type="checkbox" name="hobby" value="4">
<%--跟el类似--%>
<input type="text" name="address.city" value="beijing">
<input type="text" name="address.street" value="heima">
<br/>
<%--list--%>
<%--
addressList[0] 索引为1的address对象
--%>
<input type="text" name="addressList[0].city" value="a">
<input type="text" name="addressList[1].city" value="b">
<input type="text" name="addressList[2].city" value="c">
<br/>
<input type="submit">
form>
<h1>特殊的数据类型-map-string-stringh1>
<form action="${pageContext.request.contextPath}/params/demo4.do" method="get">
<input type="text" name="username" value="jack"><br/>
<input type="text" name="age" value="18"><br/>
<input type="text" name="password" value="1234"><br/>
<input type="text" name="nickname" value="rose"><br/>
<input type="checkbox" name="hobby" value="1">
<input type="checkbox" name="hobby" value="2">
<input type="checkbox" name="hobby" value="3">
<input type="checkbox" name="hobby" value="4">
<%--跟el类似--%>
<input type="text" name="address.city" value="beijing">
<input type="text" name="address.street" value="heima">
<br/>
<%--map 格式是key=value 封装到 strMap map中 并且 key的名称叫key1 的属性值--%>
<input type="text" name="strMap['key1']" value="beijing1">
<input type="text" name="strMap['key2']" value="beijing2">
<input type="text" name="strMap['key3']" value="beijing3">
<br/>
<input type="submit">
form>
<h1>特殊的数据类型-map-string-对象h1>
<form action="${pageContext.request.contextPath}/params/demo4.do" method="get">
<input type="text" name="username" value="jack"><br/>
<input type="text" name="age" value="18"><br/>
<input type="text" name="password" value="1234"><br/>
<input type="text" name="nickname" value="rose"><br/>
<input type="checkbox" name="hobby" value="1">
<input type="checkbox" name="hobby" value="2">
<input type="checkbox" name="hobby" value="3">
<input type="checkbox" name="hobby" value="4">
<%--跟el类似--%>
<input type="text" name="address.city" value="beijing">
<input type="text" name="address.street" value="heima">
<br/>
<%--addressMap['key1'] 表示map中的某一个address对象--%>
<input type="text" name="addressMap['key1'].city" value="beijing1">
<input type="text" name="addressMap['key2'].city" value="beijing2">
<input type="text" name="addressMap['key3'].city" value="beijing3">
<input type="text" name="addressMap['key3'].street" value="heima">
<br/>
<input type="submit">
form>
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="com.itheima.converter.MyStringToDateConverter">bean>
set>
property>
bean>
beans>
package com.itheima.converter;
import org.springframework.core.convert.converter.Converter;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* 日期转换器
* 自定义类型转换器(类) 浏览器传输的实际上是字符串 类型转换器中 将字符串转换成日期
*
* 在spring中提供了大量的转换器 , 我们抄即可
* 1.实现接口 implements Converter 传入的string 返回值是date
* 2.实现方法
*
*/
public class MyStringToDateConverter implements Converter<String, Date> {
/**
* convert 转换的方法
* @param source 数据的来源
* @return 转换后的结果
*/
@Override
public Date convert(String source) {
//字符串转日期 parse解析
Date date = null;
try {
//支持 斜杠 也要支持 横杠
boolean contains = source.contains("-");
if(contains){
date = new SimpleDateFormat("yyyy-MM-dd").parse(source);
}else{
date = new SimpleDateFormat("yyyy/MM/dd").parse(source);
}
} catch (ParseException e) {
System.out.println("解析日期异常");
e.printStackTrace();
}
return date;
}
}
<%--
文件上传三要素
1.表单必须是post提交
2.表单必须具有 enctype="multipart/form-data" 多部分表单上传属性
3.表单中必须有file组件 组件需要有name名称
--%>
<form action="${pageContext.request.contextPath}/param3/upload.do" method="post" enctype="multipart/form-data">
<input type="file" name="myFile"/>
<input type="submit">
form>
需要导入jar包
<dependency>
<groupId>commons-fileuploadgroupId>
<artifactId>commons-fileuploadartifactId>
<version>1.3.1version>
dependency>
浏览器提交给服务器的只是流数据 我们需要转换(springmvc 已经做了转换的处理 但是需要配置) 不配置 普通数据获得不了
java代码实现
package com.itheima.web.controlller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.multipart.MultipartFile;
import sun.misc.IOUtils;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.UUID;
@Controller
@RequestMapping("/param3")
public class Params3Controller {
/**
* 批量上传
* 开发中保证一件事 :
* 1.文件上传都要上传成功
* 2.保证后缀名一致
* 3.保证能够快速找到图片
* 4.保证不要重复图片即可
* @param username
* @param myFile
* @param request
* @return
* @throws IOException
*/
@RequestMapping("/upload")
public String upload(String username , MultipartFile[] myFile , HttpServletRequest request) throws IOException {
//获得每一个文件
for (MultipartFile multipartFile : myFile) {
//获得每一张图片并且上传 (批量上传 保证 每一张图片上传后的名称都是不一样的)
//不太好, 张三李四王五 同时上传100张图片 一共三百张个图片 同一个文件夹 不允许两张同名图片的
File file = new File("D:/"+ multipartFile.getOriginalFilename());
//不太好,同一秒中上传十个图片
//File file = new File("D:/"+ "时间戳毫秒级别" + multipartFile.getOriginalFilename());
//UUID 保证绝对不重复
//File file = new File("D:/"+ UUID.randomUUID().toString() +multipartFile.getOriginalFilename() );
multipartFile.transferTo(file);//挨个写入图片
}
return "success";
}
/**
* 简单文件上传
* @param username
* @param myFile
* @param request
* @return
* @throws IOException
*/
/*@RequestMapping("/upload")
public String upload(String username , MultipartFile myFile , HttpServletRequest request) throws IOException {
//文件写的路径 以及文件的名称
File file = new File("D:/" +myFile.getOriginalFilename() );
//上传
myFile.transferTo(file);
return "success";
}*/
/**
* enctype="multipart/form-data" 多部分表单上传属性有了以后
* 获得数据 全部失效
* 给spring-mvc.xml配置文件上传的组件
* @param username
* @return
*/
/*@RequestMapping("/upload")
public String upload(String username , MultipartFile myFile , HttpServletRequest request) throws IOException {
//MultipartFile 文件上传的类型 对象中封装了 所有的数据
String name = myFile.getName(); //表单提交的key名称
String originalFilename = myFile.getOriginalFilename(); //表单提交的value名称 不是文件内容 文件名称
//request.getContextPath();//项目在本地的物理位置(源码)
//项目在本地发布后 的路径(class的路径)
String realPath = request.getSession().getServletContext().getRealPath("/upload");
System.out.println(realPath);
//先查看文件夹 是否存在 如果不存在 创建文件夹 防止报错
File file = new File(realPath);
if(!file.exists()){
file.mkdirs();//创建文件夹
}
//文件的名称
String format = new SimpleDateFormat("yyyyMMdd").format(new Date());
String fileName = realPath + "/" + format + ".jpg";
//文件上传
myFile.transferTo( new File(fileName ));
*//*byte也可以书写*//*
*//*InputStream is = myFile.getInputStream();
FileOutputStream fos = new FileOutputStream("路径");
IOUtils.copy(is ,fos);*//*
return "success";
}*/
}
RequestParam
/**
* 参数列表的形参 必须 跟表单的name属性一致
* 如果不一致 可以使用@RequestParam
* @RequestParam(name = "username") String name
* 将提交的username赋值到 name属性中
* @RequestParam(defaultValue="1") 表示用户如果传入了数据 使用用户传递的数据 如果没有传递 默认值是1
* @RequestParam(required = true) 必须
*/
User-Agent
/**
* 获得请求头
* 形参的列表 指的是用户传递的数据 请求体
* 如果想获得请求头内容
* request.getHeader("key")
* @RequestHeader("headerName") 获得请求头 名称为headerName 的数据
* @return
*/
@RequestMapping("/demo2")
public String demo(@RequestHeader("User-Agent") String userAgent){
System.out.println(userAgent);
return "success";
}
Cookie
/**
* 获得请求头
* 形参的列表 指的是用户传递的数据 请求体
* 如果想获得请求头内容
* request.getHeader("key")
* @RequestHeader("headerName") 获得请求头 名称为headerName 的数据
*
* cookie的key是我们自己定义 取值 还是获得指定的key
* @return
*/
@RequestMapping("/demo2")
public String demo(@RequestHeader("User-Agent") String userAgent,
@RequestHeader("Cookie") String cookie,
@CookieValue("JSESSIONID") String cookieValue){
System.out.println(userAgent);
System.out.println(cookieValue);
System.out.println(cookie); //还需要解析 字符串截取
/**
* Cookies [] cookies = request.getCookies();
* 遍历数据
* if(cookie.getName().equals("last....")){
* 获得cookie值
* }
* */
return "success";
}
暂时省略
/**
* 环境搭建访问方法
* 1.页面代码没问题, 能够正常访问到服务器, 报错: 封装不上数据
* 解决:
* 1.1 导入jar包 springmvc才有处理json的类
* 1.2 加上一个注解 @RequestBody 加载参数之前
* @RequestBody 作用: 自动的将请求的数据 封装到对象中\
*
* 2. ajax不需要跳转页面
* String json = new ObjectMapper().writeValueAsString(对象);
* response.getWriter().print(json) 将对象转换成json 输出给浏览器
* 2.1 在方法上或者返回值之前加上注解 @ResponseBody
* 2.2 将方法的返回值修改成对象
* @ResponseBody: 将返回值 自动的转换成json
*/
方式一
/**
* 响应
* 返回值是字符串的情况下:
* 逻辑视图:(默认请求转发 , 没有重定向)
* 返回值和配置文件中的 视图解析器拼凑的路径
* 前缀+ 返回值 +后缀 => 逻辑视图
* 物理视图:
* forward: 请求转发(基本没用过)
* redirect: 重定向
* 只要携带了forward 或者 redirect 路径从webapp下开始描述(不会跟视图解析器拼凑路径)
* WEB-INF 只能请求转发进去 重定向 或者 地址栏直接访问进不去(受保护目录)
*/
@RequestMapping("/demo2")
public String demo2(){
System.out.println("服务器方法被访问到了");
//return "success";//响应到success页面
//return "forward:/WEB-INF/jsps/success.jsp";
//return "redirect:/WEB-INF/jsps/success.jsp";进不去
return "redirect:/redirectSuccess.jsp";
}
方式二
/**
* 原生方式返回 请求转发 重定向
* 先在方法中注入request和response 按照原来的内容操作即可
*/
@RequestMapping("/demo3")
public void demo3(HttpServletRequest request , HttpServletResponse response) throws ServletException, IOException {
System.out.println("服务器方法被访问到了");
//request.getRequestDispatcher("/WEB-INF/jsps/success.jsp").forward(request,response);
response.sendRedirect("/redirectSuccess.jsp");
}
方式三
/**
* spring mvc
* ModelAndView 模型和视图
* 模型: 返回的数据
* 视图: 返回的面
* 1.可以注入
* 2.可以自己new
* ModelAndView mv2 = new ModelAndView();
*/
@RequestMapping("/demo4")
public ModelAndView demo4(ModelAndView mv) throws ServletException, IOException {
//mv.addObject( key , value);//相当于 request.setAttribute(key, value)
//mv.setViewName("success");//设置视图名称 支持逻辑视图 也支持物理视图
mv.setViewName("redirect:/redirectSuccess.jsp");//设置视图名称 支持逻辑视图 也支持物理视图
return mv;
}
方式一
/**
* Model : 表示模型 , 存入的数据 和 以上的三种方式返回任意组合
*/
@RequestMapping("/demo5")
public String demo5(Model model ,HttpServletRequest request ,ModelAndView mv) throws ServletException, IOException {
//model 表示数据
model.addAttribute("msg" , "qwert") ; //等效mv.addObject(key, value)
return "success"; //view表示视图
}
方式二
/**
* spring mvc
* ModelAndView 模型和视图
* 模型: 返回的数据
* 视图: 返回的面
* 1.可以注入
* 2.可以自己new
* ModelAndView mv2 = new ModelAndView();
*
* springmvc为了防止数据丢失 将数据拼接到了地址栏中 以后重定向还可以拿到数据
*/
@RequestMapping("/demo4")
public ModelAndView demo4(ModelAndView mv) throws ServletException, IOException {
//mv.addObject( key , value);//相当于 request.setAttribute(key, value)
//mv.setViewName("success");//设置视图名称 支持逻辑视图 也支持物理视图
mv.addObject("msg" , "yyyyyyyy");
mv.addObject("msg2" , "yyyyyyyy");
mv.setViewName("redirect:/redirectSuccess.jsp");//设置视图名称 支持逻辑视图 也支持物理视图
//mv.setViewName("success");//设置视图名称 支持逻辑视图 也支持物理视图
return mv;
}
方式三
/**
* 响应
* 返回值是字符串的情况下:
* 逻辑视图:(默认请求转发 , 没有重定向)
* 返回值和配置文件中的 视图解析器拼凑的路径
* 前缀+ 返回值 +后缀 => 逻辑视图
* 物理视图:
* forward: 请求转发(基本没用过)
* redirect: 重定向
* 只要携带了forward 或者 redirect 路径从webapp下开始描述(不会跟视图解析器拼凑路径)
* WEB-INF 只能请求转发进去 重定向 或者 地址栏直接访问进不去(受保护目录)
*/
@RequestMapping("/demo2")
public String demo2(HttpServletRequest request){
System.out.println("服务器方法被访问到了");
//return "success";//响应到success页面
//return "forward:/WEB-INF/jsps/success.jsp";
//return "redirect:/WEB-INF/jsps/success.jsp";进不去
request.setAttribute("msg" , "我是消息"); //存入request
//return "redirect:/redirectSuccess.jsp";
return "success";
}
/**
* 原生方式返回 请求转发 重定向
* 先在方法中注入request和response 按照原来的内容操作即可
*/
@RequestMapping("/demo3")
public void demo3(HttpServletRequest request , HttpServletResponse response) throws ServletException, IOException {
System.out.println("服务器方法被访问到了");
request.setAttribute("msg", "xxxxx");
response.sendRedirect("/redirectSuccess.jsp");
//request.getRequestDispatcher("/WEB-INF/jsps/success.jsp").forward(request,response);
}
方式一
@Component
public class MyException implements HandlerExceptionResolver {
/**
* 只要程序发生了异常 此方法就可以捕获到异常
*/
@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
System.out.println("捕获到了程序的异常");
//模型视图
ModelAndView mv = new ModelAndView();
//
mv.addObject("code" , "10001");//返回错误码
mv.setViewName("forward:/WEB-INF/error/500.jsp"); //支持物理视图 支持逻辑视图
//不报 错误 自己处理跳转页面
return mv;
}
}
/**
* 1.创建类
*
* 2.实现接口
* 接口中的方法都有default关键字 可以选择自己实现某些方法
* 3.配置到springmvc的容器中
*
* 4.在springmvc中配置 拦截器的执行
*/
public class MyInterceptor1 implements HandlerInterceptor {
/**
* 放行前写的代码
* 返回值: boolean 返回true表示放行 返回false表示不放行
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("MyInterceptor1 preHandle 执行了 A");
return true;
}
/**
* 放行后写的代码
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("MyInterceptor1 postHandle 执行了 B");
}
/**
* 完成后?
* 指的是整个请求结束 渲染完视图 准备返回浏览器的时候
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("MyInterceptor1 afterCompletion 执行了 E");
}
}
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="com.itheima.web.interceptor.MyInterceptor1">bean>
mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="com.itheima.web.interceptor.MyInterceptor2">bean>
mvc:interceptor>
mvc:interceptors>
<properties>
<spring.version>5.0.2.RELEASEspring.version>
<slf4j.version>1.6.6slf4j.version>
<log4j.version>1.2.12log4j.version>
<mysql.version>5.1.6mysql.version>
<mybatis.version>3.4.5mybatis.version>
properties>
<dependencies>
<dependency>
<groupId>org.aspectjgroupId>
<artifactId>aspectjweaverartifactId>
<version>1.6.8version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-aopartifactId>
<version>${spring.version}version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-contextartifactId>
<version>${spring.version}version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-webartifactId>
<version>${spring.version}version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-webmvcartifactId>
<version>${spring.version}version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-txartifactId>
<version>${spring.version}version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-jdbcartifactId>
<version>${spring.version}version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-testartifactId>
<version>${spring.version}version>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.12version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>${mysql.version}version>
dependency>
<dependency>
<groupId>javax.servletgroupId>
<artifactId>servlet-apiartifactId>
<version>2.5version>
<scope>providedscope>
dependency>
<dependency>
<groupId>javax.servlet.jspgroupId>
<artifactId>jsp-apiartifactId>
<version>2.0version>
<scope>providedscope>
dependency>
<dependency>
<groupId>jstlgroupId>
<artifactId>jstlartifactId>
<version>1.2version>
dependency>
<dependency>
<groupId>log4jgroupId>
<artifactId>log4jartifactId>
<version>${log4j.version}version>
dependency>
<dependency>
<groupId>org.slf4jgroupId>
<artifactId>slf4j-apiartifactId>
<version>${slf4j.version}version>
dependency>
<dependency>
<groupId>org.slf4jgroupId>
<artifactId>slf4j-log4j12artifactId>
<version>${slf4j.version}version>
dependency>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatisartifactId>
<version>${mybatis.version}version>
dependency>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatis-springartifactId>
<version>1.3.0version>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druidartifactId>
<version>1.0.13version>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<version>1.18.6version>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-compiler-pluginartifactId>
<version>3.1version>
<configuration>
<source>1.8source>
<target>1.8target>
configuration>
plugin>
plugins>
build>
<context:component-scan base-package="com.itheima">context:component-scan>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource">property>
bean>
<context:property-placeholder location="classpath:db.properties">context:property-placeholder>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}">property>
<property name="url" value="${jdbc.url}">property>
<property name="username" value="${jdbc.username}">property>
<property name="password" value="${jdbc.password}">property>
bean>
<bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.itheima.dao">property>
bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource">property>
bean>
<tx:advice id="txAdvice">
<tx:attributes>
<tx:method name="select*" read-only="true" propagation="SUPPORTS"/>
<tx:method name="query*" read-only="true" propagation="SUPPORTS"/>
<tx:method name="find*" read-only="true" propagation="SUPPORTS"/>
<tx:method name="*" read-only="false" propagation="REQUIRED"/>
tx:attributes>
tx:advice>
<aop:config>
<aop:pointcut id="myPoint" expression="execution(* com.itheima.service.impl.*.*(..))">aop:pointcut>
<aop:advisor advice-ref="txAdvice" pointcut-ref="myPoint">aop:advisor>
aop:config>
<context:component-scan base-package="com.itheima.web">context:component-scan>
<mvc:annotation-driven>mvc:annotation-driven>
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsps/">property>
<property name="suffix" value=".jsp">property>
bean>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListenerlistener-class>
listener>
<context-param>
<param-name>contextConfigLocationparam-name>
<param-value>classpath:applicationContext.xmlparam-value>
context-param>
<servlet>
<servlet-name>DispatcherServletservlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
<init-param>
<param-name>contextConfigLocationparam-name>
<param-value>classpath:spring-mvc.xmlparam-value>
init-param>
<load-on-startup>1load-on-startup>
servlet>
<servlet-mapping>
<servlet-name>DispatcherServletservlet-name>
<url-pattern>*.dourl-pattern>
servlet-mapping>
<filter>
<filter-name>Filterfilter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilterfilter-class>
<init-param>
<param-name>encodingparam-name>
<param-value>utf8param-value>
init-param>
filter>
<filter-mapping>
<filter-name>Filterfilter-name>
<url-pattern>/*url-pattern>
filter-mapping>
db.properties共享代码
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/ssm
jdbc.username=root
jdbc.password=178121