1.MyBatis
ORM框架,将数据库的数据行与对象进行映射
1.1 MyBatis的开发步骤
1.添加MyBatis和数据库驱动的jar包
2.创建pojo包,编写一个实体类Student
3.编写映射配置文件StudentMapper.xml
4.编写核心配置文件MyBatisConfig.xml
5.在测试类中调用Mybatis提供的API
1.2 映射配置文件(StudentMapper.xml)
<?xml version="1.0" encoding="UTF-8" ?>
<!--MyBatis的DTD约束-->
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- mapper:核心根标签 namespace属性:名称空间 -->
<mapper namespace="StudentMapper">
<!--
select标签:查询功能的标签
id属性:唯一标识,和名称空间一起确定sql语句
resultType属性:指定结果映射对象类型
parameterType属性:指定参数映射对象类型
通过#{}获得参数中的值
-->
<select id="selectAll" resultType="student">
SELECT * FROM student
</select>
<select id="selectById" resultType="student" parameterType="int">
SELECT * FROM student WHERE id = #{id}
</select>
<insert id="insert" parameterType="student">
INSERT INTO student VALUES (#{id},#{name},#{age})
</insert>
<update id="update" parameterType="student">
UPDATE student SET name = #{name},age = #{age} WHERE id = #{id}
</update>
<delete id="delete" parameterType="int">
DELETE FROM student WHERE id = #{id}
</delete>
</mapper>
1.3 核心配置文件(MyBatisConfig.xml)
<?xml version="1.0" encoding="UTF-8" ?>
<!-- MyBatis的DTD约束 -->
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<!-- configuration 核心根标签 -->
<configuration>
<!-- 引入数据库连接的配置文件 -->
<properties resource="jdbc.properties"/>
<!-- 起别名,package给指定包下所有的类起别名(类名),用于命名空间-->
<typeAliases>
<typeAlias type="com.itheima.bean.Student" alias="student"/>
<!--<package name="com.itheima.bean"/>-->
</typeAliases>
<!-- environments配置数据库环境,default属性指定使用的是哪个 -->
<environments default="mysql">
<environment id="mysql">
<!-- transactionManager事务管理,type属性,采用JDBC默认的事务 -->
<transactionManager type="JDBC"></transactionManager>
<!-- dataSource数据源信息 type属性 连接池 -->
<dataSource type="POOLED">
<!-- property获取数据库连接的配置信息 -->
<property name="driver" value="${driver}" />
<property name="url" value="${url}" />
<property name="username" value="${username}" />
<property name="password" value="${password}" />
</dataSource>
</environment>
</environments>
<!-- mappers引入映射配置文件,resource属性指定映射配置文件的名称 -->
<mappers>
<mapper resource="StudentMapper.xml"/>
</mappers>
</configuration>
1.3.1 数据库连接配置文件(jdbc.properties)
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://192.168.59.143:3306/db1
username=root
password=itheima
1.4 持久层代码
1.4.1 StudentMapperImpl
public class StudentMapperImpl implements StudentMapper {
@Override
public List<Student> selectAll() {
//1.加载核心配置文件
InputStream is = Resources.getResourceAsStream("MyBatisConfig.xml");
//2.通过获取SqlSessionFactoryBuilder获取SqlSession工厂对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
//3.通过SqlSession工厂对象获取SqlSession对象,如果参数为true,表示自动提交事务
SqlSession sqlSession = sqlSessionFactory.openSession(true);
//4.执行查询
List<Student> list = sqlSession.selectList("StudentMapper.selectAll");
//5.释放资源,返回数据
sqlSession.close();
is.close();
return list;
}
}
/*
SqlSession核心API
public List<E> selectList(String statement,Object paramter);
public T selectOne(String statement,Object paramter);
public int insert(String statement,Object paramter);
public int update(String statement,Object paramter);
public int delete(String statement,Object paramter);
public void commit();
public void rollback();
public T getMapper(Class<T> cls);//获取指定接口的代理实现类对象
publcc void close();
*/
1.4.2 StudentMapper
public interface StudentMapper {
//查询全部
public abstract List<Student> selectAll();
//根据id查询
public abstract Student selectById(Integer id);
//新增数据
public abstract Integer insert(Student stu);
//修改数据
public abstract Integer update(Student stu);
//删除数据
public abstract Integer delete(Integer id);
//对于多个参数来说,每个参数之前都要加上@Param注解,要不然会找不到对应的参数
@Select("select * from user where name = #{name} and pwd = #{pwd}")
public User login(@Param("name")String name, @Param("pwd")String pwd);
}
1.5 接口代理方式实现持久层
遵循以下规范:
1.映射配置文件中的namespace与mapper接口的全类名相同
2.mapper接口的方法名和映射配置文件中每个SQL语句的id相同
3.Mapper接口方法的参数类型和映射配置文件中定义的每个sql的parameterType的类型相同
4.Mapper接口方法的返回值类型和映射配置文件中定义的每个sql的resultType的类型相同
只要遵循了以上规范,就可以不用写接口的实现类,而是由mybatis通过动态代理的方式自动生成实现类对象,负责crud操作
//在service层中
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
stu = mapper.selectById(id);
1.6 动态SQL
1.6.1 <if>
<!-- <where>标签里面写动态条件,if标签的test属性中的条件判断为true则拼接 -->
<select id="selectByCondition" parameterType="student" resultType="student">
select * from student
<where>
<if test="id!=0">
and id=#{id}
</if>
<if test="username!=null">
and username=#{username}
</if>
</where>
</select>
1.6.2 <foreach>
select * from student where id in(1,2,3)
映射配置文件:
<!--collection:参数容器类型(list集合,array数组)
open:开始的SQL语句
close:结束的SQL语句
item:参数变量名
separator:分隔符。
-->
<select id="selectByIds" resultType="student" parameterType="list">
select * from student
<where>
<foreach collection="list" open="id in(" close=")" item="id" separator=",">
#{id}
</foreach>
</where>
</select>
测试代码:
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
List<Integer> ids = new ArrayList<>();
ids.add(1);
ids.add(2);
ids.add(3);
List<Student> sList = mapper.selectByIds(ids);
System.out.println(sList);
1.6.3 SQL片段抽取
<sql id="selectStudent" select * from student</sql>
<select id="findById" parameterType="int" resultType="student">
<include refid="selectStudent"></include> where id=#{id}
</select>
1.7 分页插件
<!-- 在核心配置文件中配置PageHelper插件 -->
<plugin interceptor="com.github.pagehelper.PageHelper">
<!-- 指定语言 -->
<property name="dialect" value="mysql"/>
</plugin>
@Test
public void testPageHelper(){
//设置分页参数,第几页,每页条数
PageHelper.startPage(1,2);
List<User> select = userMapper.selectAll();
for(User user : select){
System.out.println(user);
}
//其他分页数据
PageInfo<User> pageInfo = new PageInfo<User>(select);
System.out.println("总条数:"+pageInfo.getTotal());
System.out.println("总页数:"+pageInfo.getPages());
System.out.println("当前页:"+pageInfo.getPageNum());
System.out.println("每页显示长度:"+pageInfo.getPageSize());
System.out.println("是否第一页:"+pageInfo.isIsFirstPage());
System.out.println("是否最后一页:"+pageInfo.isIsLastPage());
}
1.8 多表操作
1.8.1 一对一
模型:人和身份证,card类中包含person属性
.<!--
<resultMap>:配置字段和对象属性的映射关系标签
id:唯一标识
type:实体对象类型
<id>:配置主键列的映射关系
column:列名
property:包含对象的变量名
<result>:配置非主键列映射关系
<association>:配置card对象中包含对象(person)的映射关系
-->
<resultMap id="oneToOne" type="card">
<id column="cid" property="id" />
<result column="number" property="number" />
<association property="p" javaType="person">
<id column="pid" property="id" />
<result column="name" property="name" />
<result column="age" property="age" />
</association>
</resultMap>
<select id="selectAll" resultMap="oneToOne">
SELECT c.id cid,number,pid,NAME,age FROM card c,person p WHERE c.pid=p.id
</select>
1.8.2 一对多
模型:班级和学生,class类中包含List<Student>属性
<resultMap id="oneToMany" type="classes">
<id column="cid" property="id" />
<result column="cname" property="name" />
<!-- <collection>:配置包含集合对象的映射关系 -->
<collection property="students" ofType="student">
<id column="sid" property="id" />
<result column="sname" property="name" />
<result column="sage" property="age" />
</collection>
</resultMap>
<select id="selectAll" resultMap="oneToMany">
SELECT c.id cid,c.name cname,s.id sid,s.name sname,s.age sage FROM classes c,student s WHERE c.id=s.cid
</select>
1.8.3 多对多
模型:学生和课程,中间表包含了学生的id和所选课程的id
<!-- 注意:mybatis会根据主键(id)自动去重 -->
<resultMap id="ManyToMany" type="student">
<id column="sid" property="id" />
<result column="sname" property="name" />
<result column="sage" property="age" />
<collection property="courses" ofType="course">
<id column="cid" property="id" />
<result column="cname" property="name" />
</collection>
</resultMap>
<select id="selectAll" resultMap="ManyToMany">
<!-- stu_cr作为中间表将有sid和cid字段,将学生id与课程id关联 -->
SELECT sc.sid,s.name sname,s.age sage,sc.cid,c.name cname FROM student s,course c,stu_cr sc WHERE sc.sid=s.id AND sc.cid=c.id
</select>
1.9 注解开发
省去了映射配置文件
1.9.1 单表CRUD
在核心配置文件中扫描使用注解的类:
<mappers>
<!--扫描使用注解的类-->
<mapper class="com.itheima.mapper.UserMapper"></mapper>
</mappers>
或者扫描包含映射关系的接口所在的包:
<mappers>
<package name="com.itheima.mapper"></package>
</mappers>
或者在接口上使用mapper注解
@Mapper
public interface StudentMapper {}
接口上使用CRUD注解:
public interface StudentMapper {
//查询全部
@Select("SELECT * FROM student")
public abstract List<Student> selectAll();
//新增操作
@Insert("INSERT INTO student VALUES (#{id},#{name},#{age})")
public abstract Integer insert(Student stu);
//修改操作
@Update("UPDATE student SET name=#{name},age=#{age} WHERE id=#{id}")
public abstract Integer update(Student stu);
//删除操作
@Delete("DELETE FROM student WHERE id=#{id}")
public abstract Integer delete(Integer id);
}
1.9.2 多表操作
1.9.2.1 一对一
模型:身份证和人,card类中包含person属性
public interface CardMapper {
//查询全部
@Select("SELECT * FROM card")
@Results({
//根据查询出的列名封装属性
@Result(column = "id",property = "id"),
@Result(column = "number",property = "number"),
@Result(
property = "p", // 包含对象的变量名
javaType = Person.class,// 包含对象的实际数据类型
column = "pid", // 根据查询出的card表中的pid字段来查询person表,作为PersonMapper接口中selectById方法的参数
/*
one、@One 一对一固定写法
select属性:指定调用哪个接口中的哪个查询方法
*/
one = @One(select = "com.itheima.one_to_one.PersonMapper.selectById")
)
})
public abstract List<Card> selectAll();
}
public interface PersonMapper {
//根据id查询
@Select("SELECT * FROM person WHERE id=#{id}")
public abstract Person selectById(Integer id);
}
1.9.2.2 一对多
模型:班级和学生,class类中包含List<Student>属性
public interface ClassesMapper {
@Select("SELECT * FROM classes")
@Results({
@Result(column = "id",property = "id"),
@Result(column = "name",property = "name"),
@Result(
property = "students",
javaType = List.class,
column = "id",
/*
many、@Many 一对多查询的固定写法
select属性:指定调用哪个接口中的哪个查询方法
*/
many = @Many(select = "com.itheima.one_to_many.StudentMapper.selectByCid")
)
})
public abstract List<Classes> selectAll();
}
public interface StudentMapper {
//根据cid查询student表
@Select("SELECT * FROM student WHERE cid=#{cid}")
public abstract List<Student> selectByCid(Integer cid);
}
1.9.2.2 多对多
模型:学生和课程,中间表包含了学生的id和所选课程的id
public interface StudentMapper {
//查询中间表有学生id(即选择了课程的)的全部学生数据
@Select("SELECT DISTINCT s.id,s.name,s.age FROM student s,stu_cr sc WHERE sc.sid=s.id")
@Results({
@Result(column = "id",property = "id"),
@Result(column = "name",property = "name"),
@Result(column = "age",property = "age"),
@Result(
property = "courses",
javaType = List.class,
column = "id", // 根据查询出student表的id来作为关联条件,去查询中间表和课程表
many = @Many(select = "com.itheima.many_to_many.CourseMapper.selectBySid")
)
})
public abstract List<Student> selectAll();
}
public interface CourseMapper {
//根据学生id查询所选课程
@Select("SELECT c.id,c.name FROM stu_cr sc,course c WHERE sc.cid=c.id AND sc.sid=#{id}")
public abstract List<Course> selectBySid(Integer id);
}
1.10 构建SQL
org.apache.ibatis.jdbc.SQL 功能类,专门用于构建 SQL 语句
//定义一个生成SQL语句的类,方法使用匿名内部类返回实现类对象,在代码块中调用父类的方法
public class ReturnSql {
//定义方法,返回查询的sql语句
public String getSelectAll() {
return new SQL() {
{
SELECT("*");
FROM("student");
}
}.toString();
}
//定义方法,返回新增的sql语句
public String getInsert(Student stu) {
return new SQL() {
{
INSERT_INTO("student");
INTO_VALUES("#{id},#{name},#{age}");
}
}.toString();
}
//定义方法,返回修改的sql语句
public String getUpdate(Student stu) {
return new SQL() {
{
UPDATE("student");
SET("name=#{name}","age=#{age}");
WHERE("id=#{id}");
}
}.toString();
}
//定义方法,返回删除的sql语句
public String getDelete(Integer id) {
return new SQL() {
{
DELETE_FROM("student");
WHERE("id=#{id}");
}
}.toString();
}
}
public interface StudentMapper {
/*
@xxxProvider:生成对应的SQL语句
type:生成SQL语句功能类对象
method:指定调用方法
*/
@SelectProvider(type = ReturnSql.class , method = "getSelectAll")
public abstract List<Student> selectAll();
@InsertProvider(type = ReturnSql.class , method = "getInsert")
public abstract Integer insert(Student stu);
@UpdateProvider(type = ReturnSql.class , method = "getUpdate")
public abstract Integer update(Student stu);
@DeleteProvider(type = ReturnSql.class , method = "getDelete")
public abstract Integer delete(Integer id);
}
1.11 Mybatis防止SQL注入
1.#{}将传入的数据都当成一个字符串,会对自动传入的数据加一个双引号,底层使用JDBC中的PreparedStatement,预编译SQL语句
2.${}将传入的数据直接显示生成在sql中,一般用于order by的后面,select * from 表名 order by ${age}
3.#{}能够很大程度防止SQL注入,${}无法防止SQL注入
1.12 MyBatisPlus
1.导入jar包
2.配置对象关系映射:
@TableName(“demo_user”)//与表绑定
public class User implements Serializable {
@TableId(type = IdType.AUTO)//主键自增
private Integer id;
@TableField(“name”)//同名可以省略
private String name;
}
3.Mapper接口继承BaseMapper<>接口
4.配置YML文件
mybatis-plus:
type-aliases-package: com.jt.pojo
mapper-locations: classpath:/mybatis/*.xml
configuration:
map-underscore-to-camel-case: true
条件构造器QueryWrapper
1.根据传入的对象的不为null的属性生成where条件,默认and连接
2.比较条件queryWrapper.gt().lt().eq().ge().le().ne()
3.like条件queryWrapper.like().likeLeft().likeRight().notLike()
4.in/or条件
5.orderby条件
2 Spring
Spring是一个完整的生态,核心SpringFramework是IOC和AOP的容器框架
2.0 IOC
IoC(Inversion Of Control)控制反转,是一种理论思想,在Spring中由IOC容器反向控制应用程序所需要使用的外部资源
作用:为了解决业务逻辑层和其他各层对象直接耦合的问题
2.1 IOC配置
2.1.1 入门案例
pom.xml导入依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
service层接口和实现类
public interface UserService {
void save();
}
public class UserServiceImpl implements UserService {
public void save() {
System.out.println("user service running...");
}
}
applicationContext配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="userService" class="com.itheima.service.impl.UserServiceImpl"/>
</beans>
UserApp启动类
public class UserApp {
public static void main(String[] args) {
//2.加载配置文件
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
//3.获取资源
UserService userService = (UserService) ctx.getBean("userService");
userService.save();
}
}
2.1.2 bean标签属性
<!-- id:bean的名称,可通过id获取bean name:同id,别名 class:bean的类型 -->
<bean id="beanId" name="beanName1,beanName2" class="ClassName"></bean>
<!-- singleton/prototype:设定创建出的对象保存在spring容器中,单例(默认)/原型 -->
<bean scope="singleton"></bean>
<!-- 定义bean在初始化/销毁时的方法,当scope=prototype时,对象的销毁由gc控制,destroy方法将不会被执行 -->
<bean init-method="init" destroy-method="destroy></bean>
2.1.3 DI
IoC:Spring反向控制应用程序所需的外部资源
DI:应用程序运行依赖的资源由Spring提供,资源进入应用程序的方式称为注入
IoC与DI是同一件事站在不同角度看待问题
2.1.3.1 set注入
使用set方法给bean提供资源,property标签
public class UserServiceImpl implements UserService {
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void save() {
System.out.println("user service running...");
}
}
<bean id="userService" class="com.itheima.service.impl.UserServiceImpl">
<!-- name:set方法后第一个字母小写的名称 ref:设定引用类型属性对应bean的id,value:设定基本数据类型和字符串类型属性对应的值 -->
<property name="userDao" ref="userDao"/>
</bean>
<!-- 将要注入的资源声明为bean -->
<bean id="userDao" class = "com.itheima.dao.impl.UserDaoImpl"/>
2.1.3.2 构造器注入
public class UserServiceImpl implements UserService {
private UserDao userDao;
private int num;
private String version;
public UserServiceImpl() {
}
public UserServiceImpl(UserDao userDao, int num, String version){
this.userDao = userDao;
this.num = num;
this.version = version;
}
<bean id="userService" class="com.itheima.service.impl.UserServiceImpl">
<!-- name:类名小写 ref:设定引用类型属性对应bean的id,value:设定基本数据类型和字符串类型属性对应的值 -->
<constructor-arg name="userDao" ref="userDao"/>
<constructor-arg name="num" value="666666"/>
<constructor-arg name="version" value="itcast"/>
</bean>
<!-- 将要注入的资源声明为bean -->
<bean id="userDao" class = "com.itheima.dao.impl.UserDaoImpl"/>
2.1.3.3 接口注入
不提倡,强制被注入对象实现不必要的接口,带有侵入性
2.1.3.4 集合类型数据注入
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
<!-- 注入array类型 -->
<property name="arr">
<array>
<value>123456</value>
<value>66666</value>
</array>
</property>
<!-- 注入list类型 -->
<property name="al">
<list>
<value>itheima</value>
<value>66666</value>
</list>
</property>
<!-- 注入set类型 -->
<property name="set">
<set>
<value>itheima</value>
<value>66666</value>
</set>
</property>
<!-- 注入properties类型 -->
<property name="properties">
<props>
<prop key="name">itheima666</prop>
<prop key="value">666666</prop>
</props>
</property>
<!-- 注入map类型 -->
<property name="map">
<map>
<entry key="name" value="itheima"/>
<entry key="value" value="666"/>
</map>
</property>
</bean>
2.1.4 导入外部资源
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!-- 导入properties文件 -->
<!-- 1.开启context命名空间支持 2.加载指定的properties文件3.读取配置文件中的属性 -->
<context:property-placeholder location="classpath:*.properties"/>
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl">
<property name="userName" value="${username}"/>
<property name="password" value="${pwd}"/>
</bean>
<!-- 导入applicationContext文件 -->
<import resource="applicationContext-user.xml"/>
<import resource="applicationContext-book.xml"/>
<!-- 导入第三方类库,查看类的set方法 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/spring_db"/>
<property name="username" value="root"/>
<property name="password" value="itheima"/>
</bean>
</beans>
2.2 IOC注解
加载纯注解格式上下文对象,需要使用AnnotationConfigApplicationContext
ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
2.2.1 替换bean配置
@Compent @Controller @Service @Repository @Bean(将方法的返回值交给Spirng容器管理,解决第三方bean的引入问题)
@Scope(标注Bean的作用范围) @PostConstruct(Bean的初始化方法) @PreDestory(Bean的销毁方法)
@Autowired(根据类型注入) @Qualifier(配合@Autowored根据名称注入)
@Resource(相当于@Autowired+@Qualifier根据名称注入)
@Value(注入普通属性,@Value(“${key}”) 从properties文件中取值)
@Repository
@PropertySource(value={"classpath:jdbc.properties"})
public class BookDaoImpl implements BookDao {
@Value("${jdbc.userName}")
private String userName;
@Value("${jdbc.password}")
private String password;
public void save() {
System.out.println("book dao running..." + userName + " " + password);
}
}
2.2.2 替换启动和导入配置
@Configuration(指定当前类是一个Spring配置类)
@CompentScan(指定Spring在初始化容器时要扫描的包)
@PropertySource(加载外部配置文件)
@Import(导入其他配置类)
2.3 AOP
作用:将外围业务代码与核心业务代码分离
使用注解功能时,需要导入aspectjweaver依赖,因为spring直接使用AspectJ的注解功能,但是Spring底层技术依然是jdk动态代理和cglib动态代理
2.3.1 AOP相关概念
Target:目标对象,即需要方法增强的对象
Proxy:目标对象被AOP织入增强后,产生一个代理类
Joinpoint(连接点):有机会被增强的方法
Pointcut(切入点):即将被增强的方法,也被称为目标方法
Advice(通知):增强的方法
Aspect(切面):切入点和通知的结合
Weaving(织入):把增强应用到目标对象来创建新的代理对象的过程
2.3.2 AOP的xml配置
maven坐标
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
</dependencies>
applicationContext.xml
<bean id="userService" class="com.itheima.service.impl.UserServiceImpl"/>
<bean id="myAdvice" class="com.itheima.aop.AOPAdvice"/>
<aop:config>
<!--抽取切点表达式,常用execution(* com.itheima.aop.*.*(..))-->
<aop:pointcut id="pointcut" expression="execution(* *..*.*(..))"/>
<!--声明切面,指定通知(method)和切入点(pointcut-ref)做织入-->
<aop:aspect ref="myAdvice">
<!--前置,后置,环绕,异常抛出,最终通知-->
<aop:before method="before" pointcut-ref="pointcut"/>
<aop:after-returning method="afterReturing" pointcut-ref="pt"/>
<aop:around method="around" pointcut-ref="pt"/>
<aop:after-throwing method="afterThrowing" pointcut-ref="pt"/>
<aop:after method="after" pointcut-ref="pt"/>
</aop:aspect>
</aop:config>
其中环绕通知这样写
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("around before...");
Object ret = pjp.proceed();//调用目标对象的切入点方法
System.out.println("around after...");
return ret;
}
2.3.3 AOP注解
@Aspect(标注切面类)
@Before @AfterReturning @Aroud @AfterThrowing @After (标注通知方法,里面写切点表示式或类名.切点表达式方法)
@Pointcut(标注切点表达式方法,里面写切点表示式)
@EnableAspectJAutoProxy(在配置类上配置aop自动代理)
2.3.4 AOP原理
在运行期间,Spring通过动态代理技术生成代理对象,在代理对象调用目标对象方法时进行功能增强
有接口时会自动选择jdk动态代理技术,如果没有则选择cglib技术
2.3.4.1 jdk动态代理
通过反射创建接口实现类实现
public interface TargetInterface {
void save();
}
public class Target implements TargetInterface{
@Override
public void save() {
System.out.println("save running...");
}
}
public class Advice {
public void before() {
System.out.println("前置增强");
}
public void afterReturning() {
System.out.println("后置增强");
}
}
public class ProxyTest {
public static void main(String[] args) {
final Target target = new Target();//目标对象
final Advice advice = new Advice();//封装增强方法的对象
//动态代理生成的对象是接口的实现类
TargetInterface proxy = (TargetInterface) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler() {
@Override
//调用代理对象的任意方法都会触发invoke方法,method是目标对象的方法对象
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
advice.before();
//调用目标对象的方法
Object res = method.invoke(target, args);
advice.afterReturning();
return res;
}
});
proxy.save();
}
}
2.3.4.2 cglib动态代理
目标对象不必带有接口,通过继承实现
public class Target {
public void save() {
System.out.println("save running...");
}
}
public class Advice {
public void before() {
System.out.println("前置增强");
}
public void afterReturning() {
System.out.println("后置增强");
}
}
public class ProxyTest {
public static void main(String[] args) {
final Target target = new Target();
final Advice advice = new Advice();
Enhancer enhancer = new Enhancer();//创建增强器
enhancer.setSuperclass(target.getClass());//设置父类(目标)对象
enhancer.setCallback(new MethodInterceptor() {
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
advice.before();
Object res = method.invoke(target, args);
advice.afterReturning();
return res;
}
});//设置回调,即拦截方法
Target proxy = (Target) enhancer.create();//创建代理对象
proxy.save();
}
}
2.4 事务管理
当业务中包含多个数据层的调用时,需要在业务层开启事务,对数据层中多个操作进行组合并归属于同一个事务进行处理
2.4.1 编程式事务
目标对象
public class AccountServiceImpl implements AccountService {
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
public void transfer(String outName, String inName, Double money) {
accountDao.inMoney(outName,money);
int i = 1/0;
accountDao.outMoney(inName,money);
}
}
通知对象
public class TxAdvice {
private DataSource dataSource;
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
public Object transactionManager(ProceedingJoinPoint pjp) throws Throwable {
//获取事务管理器,这个实现类适用于适用于Spring JDBC或MyBatis,同时为其设置相应数据源
PlatformTransactionManager ptm = new DataSourceTransactionManager(dataSource);
//创建默认事务定义,包含了事务的基本信息
TransactionDefinition td = new DefaultTransactionDefinition();
//获取事务状态,参数需要事务定义
TransactionStatus ts = ptm.getTransaction(td);
//调用目标方法
Object ret = pjp.proceed(pjp.getArgs());
//提交事务,参数需要事务状态
ptm.commit(ts);
return ret;
}
}
aop配置
<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl">
<property name="accountDao" ref="accountDao"/>
</bean>
<bean id="txAdvice" class="com.itheima.aop.TxAdvice">
<property name="dataSource" ref="dataSource"/>
</bean>
<aop:config>
<aop:pointcut id="pt" expression="execution(* *..transfer(..))"/>
<aop:aspect ref="txAdvice">
<aop:around method="transactionManager" pointcut-ref="pt"/>
</aop:aspect>
</aop:config>
2.4.2 声明式事务
2.4.2.1 事务传播机制
事务的传播性一般用在事务嵌套的场景,比如一个事务方法里面调用了另外一个事务方法,那么两个方法是各自作为独立的方法提交还是内层的事务合并到外层的事务一起提交,这就是需要事务传播机制的配置来确定怎么样执行
常用的事务传播机制如下:
- requied
Spring默认的传播机制,能满足绝大部分业务需求,如果外层有事务,则当前事务加入到外层事务,一块提交,一块回滚。如果外层没有事务,新建一个事务执行 - requires_new
该事务传播机制是每次都会新开启一个事务,同时把外层事务挂起,当当前事务执行完毕,恢复上层事务的执行。如果外层没有事务,执行当前新开启的事务即可 - supports
如果外层有事务,则加入外层事务,如果外层没有事务,则直接使用非事务方式执行。完全依赖外层的事务 - not_supported
该传播机制不支持事务,如果外层存在事务则挂起,执行完当前代码,则恢复外层事务,无论是否异常都不会回滚当前的代码 - mandatory
该传播机制不支持外层事务,即如果外层有事务就抛出异常 - never
与NEVER相反,如果外层没有事务,则抛出异常 - nested
该传播机制的特点是可以保存状态保存点,当前事务回滚到某一个点,从而避免所有的嵌套事务都回滚,即各自回滚各自的,如果子事务没有把异常吃掉,基本还是会引起全部回滚的。
2.4.2.2 xml方式
<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl">
<property name="accountDao" ref="accountDao"/>
</bean>
<!--配置事务管理器-->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--tx标签,用来替换通知类,指定id与事务管理器-->
<tx:advice id="txAdvice" transaction-manager="txManager">
<!--定义哪些切入点需要作为一个事务执行-->
<tx:attributes>
<tx:method name="*" read-only="false"/>
<tx:method name="get*" read-only="true"/>
<tx:method
name="transfer"
read-only="false"
timeout="-1"
isolation="DEFAULT"
no-rollback-for=""
rollback-for="RuntimeException.class"
propagation="REQUIRED" <!--事务传播行为-->
/>
</tx:attributes>
</tx:advice>
<aop:config>
<!--切入点为Service层的所有方法-->
<aop:pointcut id="pt" expression="execution(* com.itheima.service.*Service.*(..))"/>
<!--在进行事务管理时,使用<aop:advisor>标签,其中配置的通知类必须实现Advice接口-->
<aop:advisor advice-ref="txAdvice" pointcut-ref="pt"/>
</aop:config>
2.4.2.3 注解方式
Service层接口加@Transactional注解,表示对应的方法开启事务
public interface AccountService {
@Transactional
public void transfer(String outName, String inName, Double money);
}
在JDBC配置类中配置事务管理器
@Bean
public PlatformTransactionManager getTransactionManager(DataSource dataSource){
return new DataSourceTransactionManager(dataSource);
}
在Spirng配置类上开启注解驱动
@Configuration
@ComponentScan("com.itheima")
@PropertySource("classpath:jdbc.properties")
@Import({JDBCConfig.class,MyBatisConfig.class})
@EnableTransactionManagement
public class SpringConfig {
}
2.4.3 Spring事务的实现原理及问题
-
声明式事务的实现就是通过AOP生成动态代理,通过环绕增强的方式,在目标方法执行之前开启事务,在目标方法执行之后提交或者回滚事务
-
同一个类内某个方法调用各一个事务方法会失效,直接通过this对象调用,没有通过代理对象
解决办法 :
1.通过代理对象调用方法
// 1.引入aop依赖
// 2.启动类添加注解,开启aspect动态代理模式,对外暴露代理对象
@EnableAspectJAutoProxy(exposeProxy = true)
// 3.使用代理对象调用事务方法
StudentServiceImpl object = (StudentServiceImpl)AppContext.currentProxy();
object.method();
2.在service类中注入自己
@Servcie
public class ServiceA {
@Autowired
prvate ServiceA serviceA;
public void save(User user) {
queryData1();
serviceA.doSave(user);
}
@Transactional(rollbackFor=Exception.class)
public void doSave(User user) {
updateData();
}
}
2.5 Spring整合第三方技术
2.5.1 Spring整合MyBatis
2.5.1.1 xml配置方式
1.导入Spring整合MyBatis依赖
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.3</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.16</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.0</version>
</dependency>
</dependencies>
2.将SqlSessionFactory和映射文件配置成spring管理的bean
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!--加载JDBC的properties配置文件信息-->
<context:property-placeholder location="classpath:*.properties"/>
<!--配置druid数据源-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!--配置service作为spring的bean,注入Dao,这个Dao不需要配置,因为有了下面的配置,由Spring生成 -->
<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl">
<property name="accountDao" ref="accountDao"/>
</bean>
<!--spring整合mybatis后控制的创建连接用的对象-->
<bean class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="typeAliasesPackage" value="com.itheima.domain"/>
</bean>
<!--加载mybatis映射配置的扫描,将其作为spring的bean进行管理-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.itheima.dao"/>
</bean>
</beans>
3.在应用程序中使用
public class App {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
AccountService accountService = (AccountService) ctx.getBean("accountService");
Account ac = accountService.findById(1);
System.out.println(ac);
}
}
2.5.1.2 注解方式
核心配置类
@Configuration
@ComponentScan("com.itheima")
@PropertySource("classpath:jdbc.properties")
@Import({JDBCConfig.class,MyBatisConfig.class})
public class SpringConfig {
}
JDBC配置类
public class JDBCConfig {
@Value("${jdbc.driver}")
private String driver;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String userName;
@Value("${jdbc.password}")
private String password;
@Bean("dataSource")
public DataSource getDataSource(){
DruidDataSource ds = new DruidDataSource();
ds.setDriverClassName(driver);
ds.setUrl(url);
ds.setUsername(userName);
ds.setPassword(password);
return ds;
}
}
MyBatis配置类
public class MyBatisConfig {
@Bean
public SqlSessionFactoryBean getSqlSessionFactoryBean(@Autowired DataSource dataSource){
SqlSessionFactoryBean ssfb = new SqlSessionFactoryBean();
ssfb.setTypeAliasesPackage("com.itheima.pojo");
ssfb.setDataSource(dataSource);
return ssfb;
}
@Bean
//可在Spring的核心配置类上加@MapperScan代替
public MapperScannerConfigurer getMapperScannerConfigurer(){
MapperScannerConfigurer msc = new MapperScannerConfigurer();
msc.setBasePackage("com.itheima.dao");
return msc;
}
}
2.5.2 Spring整合Junit
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
//让Spring在测试环境下运行
@RunWith(SpringJUnit4ClassRunner.class)
//设定对应的spring容器
@ContextConfiguration(classes = SpringConfig.class)
public class UserServiceTest {
@Autowired
private AccountService accountService;
@Test
public void testFindById(){
Account ac = accountService.findById(2);
Assert.assertEquals("Jock",ac.getName());
}
}
2.6 Bean的生命周期
Spring容器启动进行扫描,根据扫描到的类名生成BeanName并实例化一个BeanDefinition对象,放入BeanDefinitionMap,遍历做一系列的验证(是否单例、是否延迟加载、是否抽象),去单例池中查是否存在,去二级缓存查是否存在,如果都没有,准备创建Bean
推断构造方法,通过反射实例化Java对象,进行属性填充(自动注入),进行初始化:调用Aware接口,调用BeanPostProcessor前置处理,调用初始化生命周期方法,进行BeanPostProcessor后置处理,容器关闭时调用destory方法
3 SpringMVC
MVC(Model View Controller),一种用于设计创建Web应用程序表现层的模式
- Model(模型):数据模型,用于封装数据
- View(视图):页面视图,用于展示数据
- Controller(控制器):处理用户交互的调度器,用于根据用户需求处理程序逻辑
SpringMVC技术架构:
请求->DispatcherServlet(拦截)->HandlerMapping返回处理器链给DS->HandlerAdapter->Handler返回ModelAndView给DS->ViewResolver返回View给DS->响应
- DispatcherServlet:前端控制器, 是整体流程控制的中心,由其调用其它组件处理用户的请求
- HandlerMapping:处理器映射器, 负责根据用户请求找到对应具体的Handler处理器
- Handler:处理器,业务处理的核心类,通常由开发者编写,描述具体的业务
- HandlAdapter:处理器适配器,通过它对处理器进行执行
- View Resolver:视图解析器, 将处理结果生成View视图
3.1 xml方式配置
这个web应用程序工作流程:
- 服务器启动
- 读取web.xml中的配置,在其中配置有DispatcherServlet和其拦截的请求路径
- 读取spring-mvc.xml中的配置,扫包加载bean,读取bean中方法标注的@RequestMapping
- 处理请求
- DispatcherServlet配置拦截所有请求
- 使用请求路径与所有加载的@RequestMapping的内容进行比对
- 执行对应的方法
- 根据方法的返回值在webapp目录中查找对应的页面并展示
maven坐标
<dependencies>
<!-- servlet3.1规范的坐标 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<!--jsp坐标-->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.1</version>
<scope>provided</scope>
</dependency>
<!--spring的坐标-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
<!--spring web的坐标-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
<!--springmvc的坐标-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
</dependencies>
<build>
<!--设置插件-->
<plugins>
<!--具体的插件配置-->
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.1</version>
<configuration>
<port>80</port>
<path>/</path>
</configuration>
</plugin>
</plugins>
</build>
web.xml
<servlet>
<servlet-name>DispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--加载springmvc的配置文件-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:spring-mvc.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!--处理中文乱码-->
<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>
spring-mvc.xml
<!--只加载controller层的bean-->
<context:component-scan base-package="com.itheima">
<context:include-filter
type="annotation"
expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!--对静态资源放行-->
<mvc:default-servlet-handler/>
UserController类
@Controller
public class UserController {
//设定当前方法的访问映射地址
@RequestMapping("/save")
public String save(){
System.out.println("user mvc controller is running ...");
//设定具体跳转的页面
return "success.jsp";
}
}
3.2 注解方式配置
ServletContainersInitConfig类替换web.xml
public class ServletContainersInitConfig extends AbstractDispatcherServletInitializer {
//初始化servlet容器并加载springmvc的配置类
@Override
protected WebApplicationContext createServletApplicationContext() {
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
ctx.register(SpringMVCConfiguration.class);
return ctx;
}
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
@Override
protected WebApplicationContext createRootApplicationContext() {
return null;
}
//处理中文乱码
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
super.onStartup(servletContext);
CharacterEncodingFilter cef = new CharacterEncodingFilter();
cef.setEncoding("UTF-8");
FilterRegistration.Dynamic registration = servletContext.addFilter("characterEncodingFilter", cef);
registration.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST,DispatcherType.FORWARD,DispatcherType.INCLUDE),false,"/*");
}
}
SpringMVCConfiguration类替换spring-mvc.xml
@Configuration
@ComponentScan(value = "com.itheima",includeFilters =
@ComponentScan.Filter(type=FilterType.ANNOTATION,classes = {Controller.class})
)
public class SpringMVCConfiguration implements WebMvcConfigurer{
//对静态资源放行
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();;
}
}
3.2 请求
3.2.1 常用注解
@RequestParam 接收请求头(即url中的参数)中的参数(post请求也可以用)
@RequestBody 将传递过来的Json串转成对象,写在形参前面,必须使用post请求
@RequestMapping 请求路径
@PathVariable 用于restful请求接收参数,从路径中取变量
@CrossOrigin 支持跨域访问,当一个请求url的协议、域名、端口三者之间任意一个与当前页面url不同即为跨域,是浏览器对javascript施加的安全限制
3.2.2 参数传递方式
1.简单参数传递,用于get方式提交
@RequestMapping("/getId")
public String getId(@RequestParam("id") Integer id) {
return id + "";
}
2.对象方式传递
@RequestMapping("/getUser")
public User getUser(@RequestBody User user) {
return user;
}
3.RestFul风格,将参数写在路径中,约定get(查),post(增),put(改),delete(删)
@GetMapping("/getId/{id}")
public String getId(@PathVariable Integer id){
return "restFul动态的获取参数:"+id;
}
@GetMapping("/getUser/{name}/{age}/{sex}")
public User getUser(User user) {
return user;
}
3.2.3 Get和Post请求区别
get和post是http协议发送请求的两种方式,而http协议是基于TCP/IP的,所以本质都是TCP连接并无区别
但是有规定:
1.get请求参数封装在URL中,长度有限制(浏览器限制),不安全
2.post请求参数封装在请求体重,长度无限制
3.get产生一个TCP数据包,post产生两个TCP数据包
3.3 响应
3.3.1 常用注解
@ResponseBody 指定方法返回的是String或json类型数据
@RestController = @Controller + @ResponseBody 指定该对象是组件,又指定方法返回的是String或json类型数据
3.3.2 页面跳转
1.转发(默认)
@RequestMapping("/showPage1")
public String showPage1() {
System.out.println("user mvc controller is running ...");
return "forward:page.jsp";
}
2.重定向
@RequestMapping("/showPage2")
public String showPage2() {
System.out.println("user mvc controller is running ...");
return "redirect:page.jsp";
}
3.3.3 返回JSON数据
@RequestMapping("/showData")
@ResponseBody
public Book showData4() {
Book book = new Book();
book.setName("SpringMVC入门");
book.setPrice(66.66d);
return book;
}
3.3.4 响应状态码
1xx 消息,一般是告诉客户端,请求已经收到了,正在处理,别急…
2xx 处理成功,一般表示:请求收悉、我明白你要的、请求已受理、已经处理完成等信息
3xx 重定向到其它地方。它让客户端再发起一个请求以完成整个处理
4xx 处理发生错误,责任在客户端,如客户端的请求一个不存在的资源,客户端未被授权,禁止访问等
5xx 处理发生错误,责任在服务端,如服务端抛出异常,路由出错,HTTP版本不支持等
3.4 拦截器
定义拦截器
public class MyInterceptor implements HandlerInterceptor {
//处理器运行之前执行
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
System.out.println("前置运行a1");
//返回值为false将拦截原始处理器的运行
//如果配置多拦截器,返回值为false将终止当前拦截器后面配置的拦截器的运行
return true;
}
//处理器运行之后执行
@Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println("后置运行b1");
}
//拦截器最后执行的方法,无论原始方法是否执行
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) throws Exception {
System.out.println("完成运行c1");
}
//如果配置多个拦截器,运行顺序为a1->a2->a3->处理器->b3->b2->b1->c3->c2->c1
}
配置
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/showPage"/>
<bean class="com.itheima.interceptor.MyInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
3.5 异常处理
自定义异常处理器,并进行分类异常管理
@Component
//@ControllerAdvice,可用此注解代替实现接口
public class ExceptionResolver implements HandlerExceptionResolver {
public ModelAndView resolveException(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) {
@ExceptionHandler(BusinessException.class)
@ResponseBody
//对出现异常的情况进行拦截,并将其处理成统一的页面数据结果格式
public Result doBusinessException(BusinessException e){
return new Result(e.getCode(),e.getMessage());
}
}
}
3.6 文件上传
前端页面
<form action="/fileupload" method="post" enctype="multipart/form-data">
上传LOGO: <input type="file" name="file"/><br/>
<input type="submit" value="上传"/>
</form>
添加依赖
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
SpringMVC配置MultipartResolver接口的实现类
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
</bean>
文件上传
@RequestMapping"/fileupload")
//参数中定义MultipartFile类型参数,用于接收页面提交的type=file类型的表单,要求表单名称与参数名相同
public String fileupload(MultipartFile file, HttpServletRequest request) throws IOException {
System.out.println("file upload is running ..."+file);
if(!file.isEmpty()){
//获取原始上传的文件名,可以作为当前文件的真实名称保存到数据库中备用
String fileName = file.getOriginalFilename();
//设置保存的路径
String realPath = request.getServletContext().getRealPath("/images");
//保存文件的方法,指定保存的位置和文件名即可,通常文件名使用随机生成策略产生,避免文件名冲突问题
String uuid = UUID.randomUUID().toStirng().replace("-","").toUpperCase();
file.transferTo(new File(realPath,uuid));
}
return "page.jsp";
}
3.7 SSM整合
项目整体结构:controller层,service层,dao层,pojo层,vo层
附加:自定义异常处理类,service接口事务处理
配置文件:
mybatis配置UserDao.xml,jdbc.properties
spring配置applicationContext.xml(扫包+整合mybatis+开启事务管理)
springmvc配置spring-mvc.xml(扫包+注解驱动),web.xml(通过监听器加载spring运行环境+配置servlet)
注解方式替换web.xml
public class ServletContainersInitConfig extends AbstractDispatcherServletInitializer {
//根据spring-mvc.xml的配置创建IOC容器
@Override
protected WebApplicationContext createServletApplicationContext() {
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
ctx.register(SpringMvcConfig.class);
return ctx;
}
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
//根据applicationContext.xml创建父容器
@Override
protected WebApplicationContext createRootApplicationContext() {
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
ctx.register(SpringConfig.class);
return ctx;
}
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
super.onStartup(servletContext);
CharacterEncodingFilter cef = new CharacterEncodingFilter();
cef.setEncoding("UTF-8");
FilterRegistration.Dynamic registration = servletContext.addFilter("characterEncodingFilter", cef);
registration.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD, DispatcherType.INCLUDE), false, "/*");
}
}
注解方式替换spring-mvc.xml
@Configuration
@ComponentScan("com.itheima.controller")
@EnableWebMvc
public class SpringMvcConfig {
}
ApplicationContext与ServletContext的区别:
ServletContext定义了一系列方法使得Servlet和所在的容器可以进行交互,所有Servlet都共享这个上下文信息
ApplicationContext是ServletContext中的一个属性值,这个属性值中存有程序运行的所有上下文信息(即IOC容器)
4 SpringBoot
提供了一种快速使用Spring的方式,主要功能有起步依赖,自动配置,监控等
4.1 SpringBoot工程结构
pom.xml中的起步依赖
<!--父工程中定义版本信息-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.8.RELEASE</version>
</parent>
<dependencies>
<!--web开发起步依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--测试依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
启动类
@SpringBootApplication
public class SpringBootRun {
public static void main(String[] args) {
SpringApplication.run(HelloApplication.class,args);
}
}
测试类
@RunWith(SpringRunner.class)
@SpringBootTest(classes = SpringbootJunitApplication.class )
public class UserServiceTest {
@Test
public void test() {}
}
4.2 配置文件
使用application.properties/yml/yaml进行自定义配置
内部配置文件:
不同目录优先级:file:/config/>file:/>classpath:/config/>classpath:/
同一目录下优先级:properties>yml > yaml
外部配置文件:
1.通过命令行指定
java -jar app.jar --name="Spring“ --server.port=9000
java -jar app2.jar --spring.config.location=e://application.properties
2.与工程的jar包同级的config目录下或者同目录下的配置文件自动读取
4.2.1 从配置文件中获取数据
//方式一
@Value("${name}")
private String name;
//方式二
@Autowired
private Environment env;
System.out.println(env.getProperty("name"));
//方式三,与Java对象进行关联
@Component
@ConfigurationProperties(prefix = "person")
public class Person {...}
4.2.2 profile
实现不同环境下配置文件的切换
多properties文件方式:
application-dev.properties/yml 开发环境
application-test.properties/yml 测试环境
application-pro.properties/yml 生产环境
yml多文档方式:
在yml中使用 — 分隔不同配置
profile激活方式
- 配置文件: 配置文件中配置spring.profiles.active=dev
- 虚拟机参数:在VM options指定-Dspring.profiles.active=dev
- 命令行参数:java –jar xxx.jar --spring.profiles.active=dev
4.3 SpringBoot整合
4.3.1 SpringBoot整合mybatis
起步依赖,创建工程时可指定
<dependencies>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<!--<scope>runtime</scope>-->
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
yml配置
#datasource
spring:
datasource:
url: jdbc:mysql:///springboot?serverTimezone=UTC
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
#mybatis
mybatis:
mapper-locations: classpath:mapper/*Mapper.xml #mapper映射文件,使用注解可省略
type-aliases-package: com.itheima.springbootmybatis.domain #别名
4.3.2 SpringBoot整合redis
起步依赖,创建工程时可指定
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
yml配置
spring:
redis:
host: 127.0.0.1
port: 6379
自动注入RedisTemplate
@Autowired
private RedisTemplate redisTemplate;