SSM学习
框架 : 具有约束性支撑项目某一些功能的半成品的项目,需要按照框架的方式实现功能
定义项目的某一个标准
MVC框架:struts1,struts2,Mybatis 客户端发送请求,控制层进行处理,模型层装载并传输数据,在视图层进行展示
持久层框架 : hibernate 和 Mybatis ,处理数据
整合型框架(设计型框架):spring(用于整合 springMVC和hibernate 或者 springMVC和Mybatis )
struts1 :(封装 Servlet) MVC框架
struts2 : (封装过滤器) MVC框架
hibernate (封装 JDBC),只需要调用方法
spring
springMVC :MVC框架
Mybatis: (封装 JDBC) 半自动的持久层框架
Spring
是一个 IOC(依赖注入) 和 AOP(面向切面) 容器框架
1、Spring特性
-
非侵入性:
基于 Spring 开发的应用中的对象可以不依赖 Spring 的 API
-
依赖注入性:
DI(Dependecy Injection)反转控制(IOC)最经典的实现(DI是一种思想,IOC是这种实现的实现)
-
面向切面编程:
Aspect Oriented Programming -- AOP
Object Oriented Programming -- OOP
AOP是对OOP进行的补充
-
容器:
Spring 是一个容器,包含并且管理应用对象的生命周期
-
组件化:
Spring 实现了使用简单的组件配置组合成一个复杂的应用,可以使用 java 的注解和 xml 组合这些对象
组件:就是 Spring 管理的对象
-
一站式:
在 IOC 和 AOP 的基础上可以整合各种企业应用的开源框架和优秀的第三方类库(Spring 自身提供表述层的 SpringMVC和持久层的SpringJDBC)
2、Spring 模块
3、Spring 搭建
-
加入 jar 包
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>4.0.0.RELEASE</version>
</dependency>
<!--
核心jar包
-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.0.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.0.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>4.0.0.RELEASE</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.1.1</version>
</dependency>
</dependencies>
-
创建配置 spring 配置文件
-
通过 spring 获取对象
public void testPersionSpring(){
// 1、初始化容器 ClassPathXmlApplicationContext(配置文件的路径)
ApplicationContext applicationContext =
new ClassPathXmlApplicationContext("PersionBean.xml");
// 2、获取对象 通过 getBean 方法创建对象,通过 bean 的 id 获取
persion persion = (persion) applicationContext.getBean("persion");
System.out.println(persion.toString());
}
4、IOC 容器和 Bean 的配置
4.1、IOC和DI
4.1.1、IOC(Inversion of Contoral):控制反转
把原来对对象的控制权交个程序本身,我们只需要使用
如果不使用 Spring 则需要自己创建对象在进行使用,而使用 Spring 后只需要获取对象然后在使用,不需要自己创建对象
4.1.2、DI(Dependency Injection):依赖注入
依赖 : 对象和属性之间的关系
注入 : 给对象的属性进行赋值
依赖于谁,就对谁注入(定义bean的基本属性)
IOC的另一种表述方式,组件以一些预先定义好的方式(setter方法)接受来自于容器的资源注入
IOC是反转控制的思想,而DI是IOC的具体实现
4.2.3、IOC 容器在 Spring 中的实现
IOC容器在最底层实质上是一个对象工厂
-
在通过IOC容器读取 Bean 的实例之前,需要将 IOC 容器本身实例化
-
Spring 提供了 IOC 容器的两种实现方式
-
@BeanFactory :IOC容器的基本实现,是在 Spring 内部的基础设施,是面向Spring 本身的,不是提供给开发人员使用
-
@ApplicationContext :BeanFactory的子接口,提供了更高级的特性,面向Spring的使用者,所有场合都使用 ApplicationContext 而不是底层的 BeanFactory
-
4.2、ApplicationContext 主要实现类
-
ClassPathXmlApplicationContext : 对应类路径下的 XML 格式的配置文件
相对路径
-
FileSystemXmlApplicationContext:对应文件系统中的 xml 格式的配置文件
绝对路径
-
在初始化时候就创建单例的 bean ,也可以通过配置的方式指定创建的 bean 是多例的
4.3、ConfigurableApplicationContext
是 ApplicationContext 的值接口,包含一些扩展的方法 ConfigurableApplicationContext 中的 refresh() 和 close() 方法让 ApplicationContext 具有启动、关闭和刷新上下文的能力
4.4、webApplicationContext
为 web 应用准备的,允许从相对于 web 报目录的路径下完成初始化工作
4.5、获取bean
通过 java 的反射机制获取对象,创建的对象时对象的实体类必须有一个无参的构造器
使用 容器的 getBean() 方法获取 bean 的实例
// 2、获取bean
/**
* 2.1、通过 xml 的bean的Id 获取bean
* */
persion persionById = (persion) applicationContext.getBean("persion");
/**
* 2.2 、不通过id获取 bean 而实通过类型获取bean的过程中可以不设置id
* 如果获取bean时但是有多个类型相同的bean时会出错,所有如果使用类型获取bean时xml
* 中不能有多个bean使用同一类型
*/
persion persionByClass = applicationContext.getBean(com.lyy.mode.persion.class);
/**
* 2.3,通过 bean的id和bean的类型获取bean
* */
persion persionByIdAndClss =
applicationContext.getBean("persion",com.lyy.mode.persion.class);
System.out.println(persionByClass.toString());
5、给 Bean 的属性进行赋值
5.1、依赖注入的方式
5.1.1、setXXX注入
<bean id="student_01" class="com.lyy.mode.Student">
<!-- name 中的值,是对应的set方法名 -->
<property name="name" value="老王"/>
<property name="age" value="12"/>
<property name="sex" value="男"/>
</bean>
5.1.2、构造方法注入
<bean id="student_02" class="com.lyy.mode.Student">
<!--
index : 指定第几个值的索引,从0开始
type : 指定当前参数是什么属性
-->
<constructor-arg value="12" index="0" type="java.lang.Integer"></constructor-arg>
<constructor-arg value="老王"></constructor-arg>
<constructor-arg value="男"></constructor-arg>
</bean>
6、P名称空间
可以简化 XML 文件配置,可以通过<bean>元素属性的方式配置 bean 的属性
命名空间:标置该 XML 文件只能使用什么标签
6.1、引用p命名空间
<beans
xmlns:p="http://www.springframework.org/schema/p">
</beans>
6.2、使用P命名空间
p:属性名="value值"
<bean id="s4" class="com.lyy.mode.Student" p:age="12" p:sex="男" p:name="老王">
</bean>
7、为属性赋值的时候使用的值
7.1、字面量(可以把值写成字符串的值)、
可以直接通过 value 对其属性赋值
-
可以使用字符串表示的值,可以通过value属性或者 value 子节点的方式指定
<property name="name" value="老王"/>
<!-- 使用 value 子节点 -->
<property name="age">
<value>12</value>
</property> -
基本数据类型及封装类,Spring 等类型都可以采用字面值注入的方式
-
若字面量中包含特殊字符,可以使用 <![CDATA{}]> 把该字面量包裹起来
7.2、非字面量
使用 ref 进行赋值 : 引用 Spring 管理的某一个 bean
需要使用 ref="对应的 bean 的 ID",代表把 Spring 管理的对象赋值给另一个 bean
<bean id="student_01" class="com.lyy.mode.Student">
<!-- name 中的值,是对应的set方法名 -->
<property name="name" value="老王"/>
<property name="age" value="12"/>
<property name="teacher" ref="teacher"/>
<property name="sex" value="男"/>
</bean>
<bean id="teacher" class="com.lyy.mode.Teacher">
<property name="tID" value="1"/>
<property name="tName" value="123"/>
</bean>
<!-- 使用 p 标签,进行引用赋值 -->
<bean id="s4" class="com.lyy.mode.Student"
p:age="12" p:sex="男" p:name="老王" p:teacher-ref="teacher">
</bean>
7.3、联动属性赋值
给实体类中的属性的属性赋值
<bean id="s5" class="com.lyy.mode.Student">
<property name="teacher" ref="teacher"/>
<property name="teacher.tName" value="老王2"/>
<property name="teacher.tID" value="13"/>
</bean>
7.4、内部 bean
当 bean 的实例仅仅给一个特点的属性使用的时候,可以将其声明为内部 bean。内部 bean 声明直接包含在 <property> 或 <constructor-arg> 元素中,不需要设置任何的 id 或者 name 属性
只能是使用的 bean 才可以使用这个内部bean 其他的bean 无法使用这个内部 bean
<bean id="s6" class="com.lyy.mode.Student">
<property name="teacher">
<bean class="com.lyy.mode.Teacher">
<property name="tID" value="121"/>
<property name="tName" value="老王2"/>
</bean>
</property>
</bean>
7.5、集合属性赋值
7.5.1、数组和List
<bean id="t1" class="com.lyy.mode.Teacher">
<property name="tID" value="0000"/>
<property name="tName" value="老王"/>
<property name="lists">
<!--
给数组类型的属性进行赋值
-->
<array>
</array>
<list>
<value>1</value>
<value>2</value>
<value>3</value>
</list>
</property>
</bean>
//如果 List 是其他的引用,并不是字面量
<bean id="t1" class="com.lyy.mode.Teacher">
<property name="tID" value="0000"/>
<property name="tName" value="老王"/>
<property name="students">
<list>
<ref bean="需要引用的 bean 的 ID"/>
<ref bean="需要引用的 bean 的 ID"/>
<ref bean="需要引用的 bean 的 ID"/>
</list>
</property>
</bean>
7.5.2、map
<bean id="t2" class="com.lyy.mode.Teacher">
<property name="tName" value="老王2"/>
<property name="tID" value="1001"/>
<property name="maps">
<map>
<entry>
<key>
<value>1001</value>
</key>
<value>1001</value>
</entry>
<entry>
<key><value>1002</value></key>
<value>1002</value>
</entry>
</map>
</property>
</bean>
7.5.3、使用集合对象注入
需要引用 java.util 的标签
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd">
使用
<bean id="t3" class="com.lyy.mode.Teacher">
<property name="tID" value="1003"/>
<property name="tName" value="1003"/>
<property name="lists" ref="list"/>
</bean>
<util:list id="list">
<value>1</value>
<value>2</value>
<value>3</value>
</util:list>
<!-- 也可以引用其他的 bean -->
<util:list id="students">
<ref bean="s1"/>
<ref bean="s4"/>
<ref bean="s6"/>
</util:list>
7.6、FactoryBean (工厂 bean)
一个对象工厂,Spring 中的 IOC 就是一个 Bean 的工厂,所以 IOC 使用了工厂模式,不需要创建对象而是直接获取对象
工厂 bean 必须实现 org.springFramework.beans.factory.FactoryBean 接口
工厂 bean 会返回工厂的所返回的对象
public class MyFactory implements FactoryBean<Car> {
public Car getObject() throws Exception {
Car car = new Car();
car.setBrand("五菱宏光");
car.setPrice(200000.0);
return car;
}
public Class<?> getObjectType() {
return Car.class;
}
public boolean isSingleton() {
return false;
}
}
<?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 http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--
工厂 Bean
获取的对象是工厂创建的对象 car
-->
<bean id="factory" class="com.lyy.spring.FactoryBean.MyFactory"></bean>
</beans>
此返回的工厂,会返回工厂所创建的对象
8、Bean 的作用域
-
singleton:单例
使用这个bean创建的实例只会是一个实例对象,在初始化 Spring 容器的时候就会创建使用 singleton 声明的 bean
<bean id="s1" class="com.lyy.spring.ioc.scop.student" scope="singleton"> <constructor-arg index="0" type="java.lang.Integer" value="1"/> <constructor-arg index="1" type="java.lang.String" value="老王"/> </bean> -
prototype : 多例
使用这个bean创建的实例会有多个实例对象
<bean id="s1" class="com.lyy.spring.ioc.scop.student" scope="prototype"> <constructor-arg index="0" type="java.lang.Integer" value="1"/> <constructor-arg index="1" type="java.lang.String" value="老王"/> </bean>
9、Bean 的生命周期
9.1、默认的 Bean 的生命周期
1、通过构造器或者构造工厂创建 bean 实例(实例化)
2、未 bean 的属性设置值和对其他 bean 的引用(依赖注入)
3、调用 bean 的初始方法(初始化)
4、bean 可以使用
5、关闭 IOC 容器的时候,调用 bean 的销毁方法
Persion 类
package com.lyy.spring.ioc.life;
public class Persion {
private Integer pid;
private String pname;
private String sex;
public Integer getPid() {
return pid;
}
public void setPid(Integer pid) {
// 第二个周期 : 赋值
System.out.println("Two : 依赖注入");
this.pid = pid;
}
public String getPname() {
return pname;
}
public void setPname(String pname) {
this.pname = pname;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public Persion() {
// 第一个周期
System.out.println("One : 创建对象");
}
// 三 : 初始化
public void init(){
System.out.println("Three : 初始化");
}
// 五 : 销毁
public void destroy(){
System.out.println("Five : 销毁");
}
@Override
public String toString() {
// 第四步 : 使用
return "Four : 使用";
}
}
persion 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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--
init-method:指定调用的初始化方法
destroy-method:指定销毁的方法
-->
<bean id="persion" class="com.lyy.spring.ioc.life.Persion" init-method="init" destroy-method="destroy">
<property name="pid" value="1"/>
<property name="pname" value="老王"/>
</bean>
</beans>
测试初始化过程
package com.lyy.spring.ioc.test;
import com.lyy.spring.ioc.life.Persion;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class testLife {
public static void main(String[] args){
ApplicationContext ac = new ClassPathXmlApplicationContext("persionLife.xml");
Persion p = ac.getBean("persion",Persion.class);
System.out.println(p.toString());
((ClassPathXmlApplicationContext) ac).close();
}
}
9.2、bean 的后置处理器
配置后置处理器后的 Bean 的生命周期
-
通过构造器或者构造工厂创建 bean 实例(实例化)
-
未 bean 的属性设置值和对其他 bean 的引用(依赖注入)
-
初始化前需要进行的操作
-
调用 bean 的初始方法(初始化)
-
初始化后的操作
-
bean 可以使用
-
关闭 IOC 容器的时候,调用 bean 的销毁方法
bean 的后置处理器允许在调用初始化方法(初始化前和初始化后)对 bean 进行额外处理,bean 后置处理器对 IOC 容器里面的所有的 bean 实例逐一处理,而非单一处理
后置处理器需要实现: beans.factory.config.BeanPostProcessor 接口,实现其中的方法
public class AfterHandle implements BeanPostProcessor {
/**
* bean :
* beanName :
* 返回值 : 处理之后的新的 bean
* */
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("初始前");
Persion persion = (Persion)bean;
if(persion.getSex().equals("男")){
persion.setPname("老王 男");
}else {
persion.setPname("老王 女");
}
return persion;
}
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("初始化后");
return bean;
}
}
配置后置处理器
<!--
配置后置处理器,把后置处理器交个 Spring 容器进行管理
不需要设置后置处理器的 id 值,后置处理器会管理在这个配置文件所有的 bean
-->
<bean class="com.lyy.spring.ioc.life.AfterHandle"></bean>
10、引用外部属性文件
-
需要导入 druid.jar 包和 mysql-connector.jar 包
-
创建配置文件(如配置的数据库连接池)
<?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 http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd"> <!-- 加载资源文件 --> <bean class="org.springframework.beans.factory.config.PropertyOverrideConfigurer"> <property name="location" value="db.properties"/> </bean> <!-- 第二种添加资源文件 --> <context:property-placeholder location="db.properties" /> <!-- 使用配置文件 --> <bean id="datascource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="${jdbc.driverClassName}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </bean> </beans>
11、自动注入(自动装配,自动赋值)
手动装配:以 value 或 ref 的方式明确指定属性值都是手动的
自动装配:根据指定的装配规则,不需要明确指定,spring 自动将匹配的属性注入 bean 中
只能作用于非字面量的属性,需要使用ref的
11.1、装配模式
-
根据类型自动装配:将类型匹配的 bean 作为属性注入到另一个 bean 中。如 IOC 容器中有多个于目标 bean 类型一致的 bean,Spring 将无法判定哪个 bean 最合适该属性,所以不能执行自动装配
-
根据名称自动装配:必须将目标 bean 的名称和属性名设置的完全相同
-
通过构造器自动装配:当 bean 中存在多个构造器是这吃种自动装配方式将很复杂,不建议使用
建议使用 byName 自需要属性名和 bean 的 id 相同就可以,但是可能被不同的 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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--
autowire="byName" : 更具名字自动装配
autowire="byType" : 更具类型自动装配
-->
<bean id="emp" class="com.lyy.spring.ioc.AutoAssembling.Emp">
<property name="empID" value="12"/>
<property name="empName" value="老王"/>
<property name="car" ref="car"/>
<property name="dept" ref="dept"/>
</bean>
<!-- id 跟属性名相同就可以根据名字自动装配 car 与某一个 bean 的 ID 相同就赋值 -->
<bean id="empAoutByName" class="com.lyy.spring.ioc.AutoAssembling.Emp" autowire="byName">
<property name="empID" value="12"/>
<property name="empName" value="AoutByName"/>
</bean>
<!--
通过类型进行自动装配,在 spring 种有一个对象可以给其赋值,就会吧值赋给这个 bean
如果有多个类型相同的 bean 就无法赋值
-->
<bean id="empAoutByType" class="com.lyy.spring.ioc.AutoAssembling.Emp" autowire="byType">
<property name="empID" value="12"/>
<property name="empName" value="AoutByType"/>
</bean>
<bean id="car" class="com.lyy.spring.ioc.AutoAssembling.Car">
<property name="carID" value="1001"/>
<property name="carName" value="五菱宏光"/>
</bean>
<bean id="dept" class="com.lyy.spring.ioc.AutoAssembling.Dept">
<property name="deptID" value="1001"/>
<property name="deptName" value="1001/"/>
</bean>
</beans>
12、通过注解配置 bean
相对 xml 方式而言,通过注解的方式配置 bean 更加简介和优雅,而且和 MVC 组件化开发的理论十分契合,是开发中常用的使用方式
12.1、注解标识组件
servlet:控制层(控制请求响应)
service:业务逻辑层(处理业务逻辑)
DAO:数据访问层(用于处理数据)
-
普通组件:@Component (标识一个受 Spring IOC 容器管理的组件)
-
持久化组件:@Repository (标识一个受 Spring IOC 容器管理的持久化层组件)
-
业务逻辑层:@Service(标识一个受 Spring IOC 容器管理的业务逻辑层组件)
-
表述层控制器组件:@Controller(标识一个受 Spring IOC 容器管理的表诉层控制组件)
需要在使用的类上加入注解
@Controller
public class UserControler {
public UserControler() {
System.out.println("UserControler");
}
}
@Repository
public class UserDaoImpl implements UserService {
public UserDaoImpl() {
System.out.println("UserDaoImpl");
}
}
@Service
public class UserServiceImpl implements UserService {
public UserServiceImpl() {
System.out.println("UserService");
}
}
配置 XML 文件扫描 java 类上面的注解
<?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 http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<!--
<context:component-scan> : 会扫描包中的所有的类,但是只有加可注解的类才会被作为 Spring 的组件进行加载(Spring 所管理的类,会自动在 Spring 的配置文件中生成相对应的 Bean 这些 Bean 的 id 会以类的首字母的小写作为值 )
-->
<context:component-scan base-package="com.lyy.spring.ioc.UserMode"/>
<bean id="userControler" class="com.lyy.spring.ioc.UserMode.Controller.UserControler"/>
</beans>
使用
public class Test {
// 创建出来的 bean 默认使用的是单例模式
public static void main(String[] args){
ApplicationContext ac = new ClassPathXmlApplicationContext("UserMode.xml");
}
}
12.2、组件包含
主要是配置文件中的 <context:component-scan> 组件,包含和排除不能同时使用
包含关系:
context:include-filter : 扫描注解为什么类型的,在包结构下,再一次通过注解和类型具体包含到某个或某几个类
type :定义是通过注解类型扫描进 Spring 管理
-
annotation : 更具注解进行包含
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
-
assignable : 更具具体的类的类型
<context:include-filter type="assignable" expression="com.lyy.spring.ioc.UserMode.Controller.UserControler"
expression : 定义注解的类型
注意:在写包含关系的时候,一定要设置默认的过滤关闭(扫描包下所有的类)
<!-- 使用默认的扫描 --> <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" use-default-filters="false"/>
排除关系
1、通过注解类型排除
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
2、通过类的类型进行排除
<context:exclude-filter type="assignable" expression="com.lyy.spring.ioc.UserMode.Service.UserServiceImpl"/>
注意:必须使用 use-default-filters="true" 需要先扫描所有的包,才可以进行排除操作
13、基于注解的自动装配
会在 Spring 的容器中找一个能够给其赋值的 bean 赋值,需要在 Spring 中至少有一个对象给其赋值,否则会返回 null
@Autowired 根据类型执行自动装配
// 去 Spring 的容器中找一个能够给其赋值的 bean 赋值,需要在 Spring 中至少有一个对象给其赋值,否则会返回 null
@Autowired
// 需要和 Autowired 一起使用,在存在有多个可以给其赋值的 bean ,需要指定用那个 bean 进行赋值
@Qualifier(value = "myUserComtroller")
private UserService userService;
//如果以上注解作用与方法上,相当于注解作用与参数上
@Autowired
@Qualifier(value = "myUserComtroller")
public void mySet(UserService userService){
this.UserService = userService
}
14、AOP
14.1、动态代理模式
可以帮助有接口的对象,实现代理
代理模式是实现 AOP 的方式,代理的对象和目标对象必须实现同一个接口
package com.lyy.spring.Aop.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
public class ProxyUtil {
// 目标对象
private MathlImpl mathl;
public ProxyUtil(MathlImpl mathl) {
this.mathl = mathl;
}
public Object getProxy(){
// 指定类加载器
ClassLoader loader = this.getClass().getClassLoader();
// 获取目标对象实现的所以的接口的对象
Class[] interfaces = mathl.getClass().getInterfaces();
//InvocationHandler:代理对象实现功能的控制器
return Proxy.newProxyInstance(loader, interfaces, new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
MyLogger.before(method.getName(),Arrays.toString(args));
//动态代理对象实现功能,指定需要代理的对象和传入对象的参数
Object result = method.invoke(mathl,args);
MyLogger.after(method.getName(),result);
return result;
}
});
}
}
class MathlImpl impl Math{
public int add(int i ,int j){
return i+j;
}
}
interface Math{
int add();
}
14.2、AOP 概述
AOP(Aspect-Orientde Peogramming) 面向切面编程,是一种新的方法论,AOP是对OOP(面向对象)的补充
面向切面:横向抽取机制,把公共功能从业务逻辑中抽取出来,适用于模块化横切关注点(公共方法),切面(存储公共功能的类)
AOP好处
1、每个事物逻辑位于同一个位置,代码不分散,便于维护和升级
2、业务模块更简洁,只包含核心业务代码
3、AOP 图解
14.3、AOP 术语
-
切面关注点
从每个方法抽取出来的同一类的非核心业务(每个核心代码的公共部分)
-
切面(Aspect)
封装横切关注点信息的类,每个关注点体行为一个通知方法
存储每一个提取出来的公共功能的类
-
通知(Advice)
切面必须完成的各个具体工作
把公共部分存储到切面的时候就把公共部分称为通知
-
目标(Target)
被通知的对象
需要运行公共部分的对象
-
代理(Proxy)
向目标对象应用通知后创建的对象
运行
-
连接点(lojinpoint)
横切关注点在程序代码的体行,对应程序执行的某一个位置。例如:类某个方法调用前后。方法捕获的异常后
在应用程序中可以使用横纵两个坐标定位一个具体的连接点
连接点:公共功能执行的各个位置
-
切入点(pointcut)
使用切面的条件,使用 org.springfranework.aop.Pointcut 接口进行描述。它使用类和方法作为连接点的查询条件
包含在目标上的方法上
14.4、AspectJ
14.4.1、简介
AspectJ java 社区里面最完整的流行的 AOP 框架
在 Spring 2.0 以上版本中,可以使用基于 Asoectj 注解基于 xml 配置的 Aop
14.4.2、使用
-
导入jar包
com.springsource.net.sf.cglib
com.springsource.org.aopalliance
com.springsource.org.aspectij.weaver
spring-aop
spring-aspects
-
引入命名空间
-
声明切面和切面需要使用的切入点
// 需要加载到 Spring 容器中,才可以使用切面 @Component // 切面,存放横切关注点 @Aspect // 标注此类为切面 public class MyLoggerAspect { /** * @Before 将方法指定为前置通知 * 必须设置 value 其值为如点表达式 * 设置切入点表达式"execution(public int com.lyy.spring.Aop.MathImpl.add(int,int))" * 设置方法作用与哪一个切入点 * */ @Before(value = "execution(public int com.lyy.spring.Aop.MathImpl.add(int,int))") public void beforeMethod(){ System.out.println("方法执行前"); } }目标对象类
package com.lyy.spring.Aop; import org.springframework.stereotype.Component; // 需要把目标对象也加入到 Spring 管理 @Component // 相当于目标 public class MathImpl implements Math{ public int add(int i, int j) { //日志功能相当于横切关注点 // System.out.println(i+""+j); return i+j; } public int jian(int i, int j) { return i-j; } } -
配置 AOP 的部分功能和 Spring 的注解扫描
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd"> <!-- 开启 aspectj 的自动代理功能 --> <aop:aspectj-autoproxy/> <context:component-scan base-package="com.lyy.spring.Aop"/> </beans> -
使用
package com.lyy.spring.Test; import com.lyy.spring.Aop.MathImpl; import com.lyy.spring.Aop.Math; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class test { public static void main(String[] args){ ApplicationContext ac = new ClassPathXmlApplicationContext("aop.xml"); //为使用 Spring 容器获取的对象,不能使用 aop // Math math = new MathImpl(); // System.out.println( math.add(1,2)); // 必须使用 Spring 容器获取对象才能使用 AOP 功能 Math mathByAC = ac.getBean("mathImpl",Math.class); System.out.println(mathByAC.add(1,2)); } } -
注意事项
-
切面必须交由 Spring 容器管理
-
目标对象类也必须通过 Spring 进行管理
-
在使用的时候,必须通过 Spring 容器获取
-
-
14.5、AOP 通知
14.5.1、前置通知
/**
* @Before 将方法指定为前置通知
* 必须设置 value 其值为如点表达式
* 设置切入点表达式"execution(public int com.lyy.spring.Aop.MathImpl.add(int,int))"
* 设置方法作用与哪一个切入点
* */
@Before(value = "execution(public int com.lyy.spring.Aop.MathImpl.add(int,int))")
//JoinPoint 连接点,用于获取切入点的参数
public void beforeMethod(JoinPoint joinPoint){
// 获取方法参数
Object[] agrs = joinPoint.getArgs();
// 获取方法名
String methodName = joinPoint.getSignature().getName();
joinPoint.getSignature().getClass();
System.out.println("methodName : "+methodName+",agrs : "+ Arrays.toString(agrs));
}
14.5.2、切入点表达式
// 以返回值为 int 而且传入的参数是两个 int 类型的值作为切入点
// @Before(value = "execution(public int com.lyy.spring.Aop.MathImpl.*(int,int))")
// 传入的参数是两个 int 类型的值作为切入点
// @Before(value = "execution(* com.lyy.spring.Aop.MathImpl.*(int,int))")
// 第一个 * 表示任意的返回值,第二个 * 表示仍有的类,第三个 * 表示任意发方法,(..) 表示任意的参数列表
@Before(value = "execution(* com.lyy.spring.Aop.*.*(..))")
public void newBeforeMethod(JoinPoint joinPoint){
Object[] args = joinPoint.getArgs();
System.out.println(Arrays.toString(args));
}
14.5.3、后置通知
/**
* @After : 将方法标注为后置通知,作用于方法的 finaly 语句块,即不论有没有异常都会执行
* */
@After(value = "execution(* com.lyy.spring.Aop.*.*(..))")
public void afterMethod(JoinPoint joinPoint){
System.out.println("后置通知");
}
14.5.4、返回通知
/**
* @AfterReturning : 没有异常才会有作用,如果有异常就会停止
* returning : 用来接收方法的返回值的变量名
如果像在方法使用,这只需要在方法的参数列表中设置 于变量名相同的参数名的参数
* */
@AfterReturning(value = "execution(* com.lyy.spring.Aop.*.*(..))",returning = "result")
public void AfterReturning(JoinPoint joinPoint,Object result){
System.out.println("返回通知 : " + result);
}
14.5.5、异常通知
/**
* @AfterThrowing : 将方法标注为异常通知,当方法抛出异常是进行处理
* 可以使用 throwing 捕获方法的异常
* 在参数列表中可通过具体的异常类型来对指定的异常信息进行操作
* */
@AfterThrowing(value = "execution(* com.lyy.spring.Aop.*.*(..))",throwing = "ex")
public void Exported(JoinPoint joinPoint,Exception ex){
System.out.println("异常通知,message: "+ex);
}
14.5.6、环绕通知
/**
* @Around :定义环绕通知,环绕方法的过程中,包含前置通知,返回通知,异常通知,后置通知
*
* */
@Around(value = "execution(* com.lyy.spring.Aop.*.*(..))")
public Object aroundMethod(ProceedingJoinPoint proceedingJoinPoint){
Object result = null;
try {
//前置通知
System.out.println("----前置通知");
result = proceedingJoinPoint.proceed();
// 后置返回通知
System.out.println("----返回通知");
return result;
} catch (Throwable throwable) {
throwable.printStackTrace();
//异常通知
System.out.println("----异常通知");
} finally {
// 后置通知
System.out.println("----后置通知");
}
return -1;
}
15.5.7、切面的优先级
两个切面管理同一个切入点,跟 Spring 加载切面类的前后存在一定关系
可以实使用 @Order(value = 1) 注解定义切面的优先级,数值越小说明切面的优先级越高
@Component @Aspect //@Order : 定义切面的优先级,数值越小说明优先级越高,默认值为 int 的最大值 @Order(value = 1) public class TestAspect
14.6、以xml方式配置切面
如果不属于注解的方式配置切面则不需要使用 @Aspect 注解
<aop:config proxy-target-class="true">
<aop:pointcut expression="execution(* com.lyy.spring.Aop.*.*(..))" id="cat"/>
<!-- 需要引用切面的 bean -->
<aop:aspect ref="myLogger">
<!-- 定义切面的切入点和连接点 -->
<aop:before method="before" pointcut="execution(* com.lyy.spring.Aop.*.*(..))" />
<!-- 引入定义的切入点-->
<aop:before method="before" pointcut-ref="cat"/>
</aop:aspect>
</aop:config>
15、 JDBCTemplate
是 Spring 在 JDBC API 上定义的一个抽象层,为了更加方便的使用 JDBC 建立了一个 JDBC 存取框架
JDBCTemplate 是一个轻量级的持久层框架
15.1、导入 jar 包
-
IOC 容器所需要的 JAR 包
-
JdbcTemplate 所需要的 jar 包
spring-jdbc-4.0.0.jar
spring-orm-4.0.0.jar
spring-tx-4.0.0.jar
-
数据库驱动和数据源
druid-1.1.9.jar
mysql-connector-java-5.1.7-bin.jar
15.2、编写配置文件
<?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 http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<!-- 引入资源文件 -->
<!--<bean class="org.springframework.beans.factory.config.PropertyOverrideConfigurer">-->
<!--<property name="location" value="db.properties"/>-->
<!--</bean>-->
<!-- 引入资源文件 -->
<context:property-placeholder location="db.properties"/>
<!-- 创建数据源 -->
<bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<property name="url" value="${jdbc.url}"/>
</bean>
<!-- 配置数据源 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<!-- 通过 DruidDataSource 创建连接池 -->
<property name="dataSource" ref="druidDataSource">
</property>
</bean>
</beans>
15.3、使用
public static void main(String[] args){
// 获取 Spring 容器
ApplicationContext ac = new ClassPathXmlApplicationContext("jdbc.xml");
// 获取 SPring 管理的 jdbcTemplate 对象
JdbcTemplate jdbcTemplate = ac.getBean("jdbcTemplate",JdbcTemplate.class);
//使用
jdbcTemplate.update("");
}
16、Spring的事务管理
要么同时成功,要么同时失败,每一个事务都是独立的不受其他事务影响,而且处理完·数据后,数据会永久的留下
16.1、配置事务管理器
<!-- 配置事务管理的类,把事务管理器交个 Spring 容器管理 -->
<bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 配置管理的是哪一个数据源产生的连接对象,事务处理器依赖于数据源产生的连接对象 -->
<property name="dataSource" ref="druidDataSource"></property>
</bean>
<!--通知-->
<!--<tx:advice></tx:advice>-->
<!--
开启注解驱动,扫描关于事务的注解,然后解析含义并执行功能
transaction-manager= 指定事务管理器
-->
<tx:annotation-driven transaction-manager="dataSourceTransactionManager"/>
16.2、使用事务管理器
// @Transactional 声明这个方方是一个事务
@Transactional
public void bayBook(String uid, String bid) {
Integer price = bookDao.selectBookPrice(bid);
bookDao.updateStock(bid);
bookDao.updateUserBalance(uid,price);
}
16.3、@Transactional 属性
@Transactional :对方中所有的操作作为一个事务进行管理
可以在类上(说明这个类的每一个方法都是是事务)
可以在方法上(声明这个方法是一个事务)
如果类和方法中 Transactional 属性相同,则不会选择通过那个事务的属性为依据,但是如果类和方法上面的 Transactional 属性不同,这以方法的属性为准,但是类定义的属性和方法的属性都对这个事务由效果
Transactional 可以设置的属性
-
Propagation:事务的传播行为,默认为 Propagation.REQUIRED 的事务
A方法和B方法都有事务,当A在调用时,会将A中的数位传播给B方法,B方法对于事务的处理方式就是事务的传播行为
@Transactional(propagation = Propagation.REQUIRED) :声明必须使用调用者的事务管理
@Transactional(propagation = Propagation.REQUIRES_NEW) : 不使用调用者的事务,使用新的事务进行处理
-
Isolation:事务的隔离级别,多出现于并发中,操作数据的一种规定
默认:Isolation.DEFAULT(以数据库的隔离级别,来定义事务的隔离级别)
读未提交(Isolation.READ_UNCOMMITTED):能够读到别人未提交的事务,会造成脏读(读到未提交的数据,数据可能会回滚),读到未 提交的对整体没有意义,因为未提交的数据可能会造成事务的回滚
读已提交(Isolation.READ_COMMITTED):只能读到已经提交的数据,可能出现不可重复读
可重复读(Isolation.REPEATABLE_READ):读数据的时候,不允许任何请求对其进行任何操作操作,可能出现幻读(MySQL的默认隔离级别)
串行化(Isolation.SERIALIZABLE):会编程单线程,降低了运行效率
-
timeout:超时,在事务强制回滚前最多可以执行(等待)的时间
-
readOnly:只读,代表的一种操作,指定当前的一系列操作是否为只读,不能更改其值
MySQL 在读取数据的时候会给数据加锁,在进行除读操作的以外操作不能正常进行
若设置为只读,MySQL 就会在访问数据的时候不加锁,来提高性能
不全是读操作:不能加上 readOnly ,如果设置为只读操作,MySQL 就不会把操作加锁,会导致 MySQL 数据出现问题
全是读操作:
-
rollbackFor|rollbackForClassName|noRollbackFor|noRollbackForClassName:
代表事务抛出了什么异常才进行回滚操作
noRollbackFor:代表获取到异常,但是不会进行回滚
-
使用
// timeout : 单位秒
@Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.SERIALIZABLE,timeout = 3,
rollbackFor = {NullPointerException.class})
public void bayBook(String uid, String bid) {
Integer price = bookDao.selectBookPrice(bid);
bookDao.updateStock(bid);
bookDao.updateUserBalance(uid,price);
}
17、xml 配置事务
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-4.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd">
<!-- 引入数据库资源文件 -->
<context:property-placeholder location="db.properties"/>
<!-- 创建数据源 -->
<bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<property name="url" value="${jdbc.url}"/>
</bean>
<!-- 配置数据源 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<!-- 通过 DruidDataSource 创建连接池 -->
<property name="dataSource" ref="druidDataSource">
</property>
</bean>
<!--
配置事务管理的类,把事务管理器交个 Spring 容器管理,不够使用 xml 方式还是使用注解的方式配置事务,都需要配置事务管理器
相当于 AOP 的公共功能类(切面)
-->
<bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 配置管理的是哪一个数据源产生的连接对象,事务处理器依赖于数据源产生的连接对象 -->
<property name="dataSource" ref="druidDataSource"></property>
</bean>
<!-- 配置事务通知 -->
<tx:advice id="tx" transaction-manager="dataSourceTransactionManager">
<tx:attributes>
<!-- 设置好的切入点表达式下,再次设置哪一些方法被事务所管理 -->
<tx:method name="bayBook" timeout="3" isolation="DEFAULT"/>
<tx:method name="checkOut"/>
<!-- 以 select 开头的方法才会被事务管理器管理 -->
<tx:method name="select*"/>
</tx:attributes>
</tx:advice>
<aop:config>
<!-- 配置切入点 -->
<aop:pointcut id="pointcut" expression="execution(* com.lyy.book.Service.Impl.*.*(..))"/>
<!-- 连接切入点表达式和事务的通知 -->
<aop:advisor advice-ref="tx" pointcut-ref="pointcut"/>
</aop:config>
</beans>
18、注解
@RestController
声明这个类中的所有的方法返回值都是JSON数据
Spring MVC
MyBatis
搭建MyBatis
1、导入包
logj4 是作为日志使用
2、创建myBatis的全局(核心)配置文件,并配置
mybatis 核心配置文件,配置文件需要和 src 文件夹同一等级
-
<environments> : 用来设置连接数据库的环境 default关键字(设置默认的数据库环境)
<environments default="mysql">
-
<environment> : 设置莫一个具体的数据环境 id关键字:设置数据环境的唯一标识
<environment id="mysql">
-
<transactionManager> : 设置事务事务管理器,type 关键字,设置事务管理方式,JDBC代表使用 jdbc 最原始的方式来管理对象,MANAGED 意思是谁可以管理谁来管理(会交给 Spring 的声明式事务管理进行管理)
<transactionManager type="JDBC或者MANAGED"/>
-
<dataSource> : 设置数据源
type有三个值
POOLED : 使用数据库连接池,使用默认的数据库连接池,第一次使用后会放入缓存,在使用则直接在缓存里面取
UNPOOLEN:不使用数据库连接池,每一次连接就会生成一个连接对象
JNDI:掉用上下文的数据源
<dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/test"/> <property name="username" value="root"/> <property name="password" value=""/> </dataSource>
-
<properties> : 设置或引入 properties 资源文件
-
resource:在类路径下访问资源文件
-
url : 在网络路径下访问资源文件
-
-
<settings>设置部分属性
-
<typeAliases> : 为类设置类型别名
type : 类型,若只设置type,默认的的别名就是类名,不区分大小写
alies : 设置自己的别名
<typeAliase type="java的类型" alias="设置的别名" />
-
例子
<?xml version="1.0" encoding="UTF-8"?>
<!-- 引入命名空间,要求配置文件可以使用的标签 -->
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--<properties>
<property name="......" value="......"></property>
</properties>-->
<properties resource="文件路径" />
<settings>
<setting name="" value=""></setting>
</settings>
<!--
<environments> : 用来设置连接数据库的环境 default关键字(设置默认的数据库环境)
-->
<environments default="mysql">
<!--
<environment> : 设置莫一个具体的数据环境 id:设置数据环境的唯一标识
-->
<environment id="mysql">
<!-- 配置数据源 -->
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/test"/>
<property name="username" value="root"/>
<property name="password" value=""/>
</dataSource>
</environment>
</environments>
<!-- 配置 mapper 映射-->
<mappers>
<mapper resource=""/>
<pavkage ></pavkage>
</mappers>
</configuration>
log4j配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration>
<!-- 将日志信息输出到控制台 -->
<appender name="ConsoleAppender" class="org.apache.log4j.ConsoleAppender">
<!-- 设置日志输出的样式 -->
<layout class="org.apache.log4j.PatternLayout">
<!-- 设置日志输出的格式 -->
<param name="ConversionPattern" value="[%d{yyyy-MM-dd HH:mm:ss:SSS}] [%-5p] [method:%l]%n%m%n%n" />
</layout>
<!--过滤器设置输出的级别-->
<filter class="org.apache.log4j.varia.LevelRangeFilter">
<!-- 设置日志输出的最小级别 -->
<param name="levelMin" value="WARN" />
<!-- 设置日志输出的最大级别 -->
<param name="levelMax" value="ERROR" />
<!-- 设置日志输出的xxx,默认是false -->
<param name="AcceptOnMatch" value="true" />
</filter>
</appender>
<!-- 将日志信息输出到文件,但是当文件的大小达到某个阈值的时候,日志文件会自动回滚 -->
<appender name="RollingFileAppender" class="org.apache.log4j.RollingFileAppender">
<!-- 设置日志信息输出文件全路径名 -->
<param name="File" value="D:/log4j/RollingFileAppender.log" />
<!-- 设置是否在重新启动服务时,在原有日志的基础添加新日志 -->
<param name="Append" value="true" />
<!-- 设置保存备份回滚日志的最大个数 -->
<param name="MaxBackupIndex" value="10" />
<!-- 设置当日志文件达到此阈值的时候自动回滚,单位可以是KB,MB,GB,默认单位是KB -->
<param name="MaxFileSize" value="10KB" />
<!-- 设置日志输出的样式 -->
<layout class="org.apache.log4j.PatternLayout">
<!-- 设置日志输出的格式 -->
<param name="ConversionPattern" value="[%d{yyyy-MM-dd HH:mm:ss:SSS}] [%-5p] [method:%l]%n%m%n%n" />
</layout>
</appender>
<!-- 将日志信息输出到文件,可以配置多久产生一个新的日志信息文件 -->
<appender name="DailyRollingFileAppender" class="org.apache.log4j.DailyRollingFileAppender">
<!-- 设置日志信息输出文件全路径名 -->
<param name="File" value="D:/log4j/DailyRollingFileAppender.log" />
<!-- 设置日志每分钟回滚一次,即产生一个新的日志文件 -->
<param name="DatePattern" value="'.'yyyy-MM-dd-HH-mm'.log'" />
<!-- 设置日志输出的样式 -->
<layout class="org.apache.log4j.PatternLayout">
<!-- 设置日志输出的格式 -->
<param name="ConversionPattern" value="[%d{yyyy-MM-dd HH:mm:ss:SSS}] [%-5p] [method:%l]%n%m%n%n" />
</layout>
</appender>
<!--
注意:
1:当additivity="false"时,root中的配置就失灵了,不遵循缺省的继承机制
2:logger中的name非常重要,它代表记录器的包的形式,有一定的包含关系,试验表明
2-1:当定义的logger的name同名时,只有最后的那一个才能正确的打印日志
2-2:当对应的logger含有包含关系时,比如:name=test.log4j.test8 和 name=test.log4j.test8.UseLog4j,则2-1的情况是一样的
2-3:logger的name表示所有的包含在此名的所有记录器都遵循同样的配置,name的值中的包含关系是指记录器的名称哟!注意啦!
3:logger中定义的level和appender中的filter定义的level的区间取交集
4:如果appender中的filter定义的 levelMin > levelMax ,则打印不出日志信息
-->
<!-- 指定logger的设置,additivity指示是否遵循缺省的继承机制-->
<logger name="test.log4j.test8.UseLog4j" additivity="false">
<level value ="WARN"/>
<appender-ref ref="DailyRollingFileAppender"/>
</logger>
<!--指定logger的设置,additivity指示是否遵循缺省的继承机制 -->
<logger name="test.log4j.test8.UseLog4j_" additivity="false">
<level value ="ERROR"/>
<appender-ref ref="RollingFileAppender"/>
</logger>
<!-- 根logger的设置-->
<root>
<level value ="INFO"/>
<appender-ref ref="ConsoleAppender"/>
<appender-ref ref="DailyRollingFileAppender"/>
</root>
</log4j:configuration>
3、创建映射文件,并配置
<?xml version="1.0" encoding="UTF-8"?> <!-- 引入命名空间,要求配置文件可以使用的标签 --> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper> <Select></Select> </mapper>
4、创建Mapper接口,需要实现两个绑定
mapper 用来处理实体类和表之间的关系的
两个绑定
-
namespace 需要和接口绑定 需要使用全类命
-
id 要和接口的方法名进行绑定和resultType 需要和实体类进行绑定
例:
<!-- namespace 需要和接口绑定 需要使用全类命 --> <mapper namespace="com.lyy.mapper.userMapper"> <!-- <select> : 定义查询语句 id:要和接口的方法名进行绑定,设置sql语句的唯一标识 resultType:定义结构类型,需要和实体类进行绑定 --> <select id="getUser" resultType="com.lyy.deom.user"> SELECT * FROM `user` </select> <mapper/>
5、获取mybatis操作数据库的会话对象SqlSession
sqlSession:可以交给Spring进行管理
//获取 配置文件的字节输入流
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
//获取 SqlSession 对象
SqlSessionFactory sqlSessionFactory =
new SqlSessionFactoryBuilder().build(is);
// 打开 selSession
SqlSession sqlsession = sqlSessionFactory.openSession();
//getMapper 会通过动态代理生成 userMapper 的代理实现类
userMapper mapper = sqlsession.getMapper(userMapper.class);
mapper.getUser();
System.out.println(mapper);
使用 package 注册mapper.xml
<!-- mapper 的映射文件必须和mapper 接口在同一个包中,同时映入包种所有的 mapper.xml 文件 --> <package name="com.lyy.mapper"/>
mybatis 三种查询
<!--
查询所有的实体类对象,查询出来的数据会封装成 resultType 指定的类型,在放入 List 集合种
List<User> getAllUser();
-->
<select id="getAllUser" resultType="com.lyy.deom.User">
SELECT user_id,user_name,age,user.password,user.sex FROM user
</select>
<!--
//设置map的键,在查询式传出所有的员工信息,可以把员工信息作为值,但是必须设置键
//查询出来的对象会把当前查询的数据作为 value 会把 @MapKey("userId") 中的属性作为键值
@MapKey("userId")
Map<String, Object> getAllUserMap();
-->
<select id="getAllUserMap" resultType="java.util.Map">
select * from user
</select>
<!--
// 会查询出单个值,并把查询出来的数据,封装成 resultType 指定的对象
Object name();
-->
<select id="getAllUserMap" resultType="com.lyy.deom.User">
select * from user
</select>
mybatis 的插入
<!--
不需要指定返回值,但是需要使用 parameterType 指定传入的参数
会返回 一个 int 的值
int insertUser(User user);
-->
<insert id="insertUser" parameterType="com.lyy.deom.User">
INSERT INTO user(user.user_id,user.user_name,user.password) VALUE (#{userId},#{userName},#{password})
</insert>
mybatis 的删除
<!--
不需要指定返回值,但是需要使用 parameterType 指定传入的参数
boolean deleteUserByid(String id);
返回boolean
-->
<delete id="deleteUserByid" parameterType="java.lang.String">
DELETE FROM user WHERE user_id=#{userId}
</delete>
mybatis 获取值的种方式
JDBC 的获取参数值
public static void main(String[] args) throws Exception{
Connection connection = DriverManager.getConnection("","","");
String sql = "inster into user values(null,?,?,?)";
// 可以传入 SQL 语句,可以通过通配符进行赋值
PreparedStatement ps = connection.prepareStatement(sql);
ps.setString(1,"root");
ps.setString(1,"root");
ps.setString(1,"root");
ps.executeUpdate();
Statement statement = connection.createStatement();
// 需要先进行语句拼接在运行,使用字符串拼接
String sql2 = "inster into user values(null,"+123+")";
statement.executeLargeUpdate(sql2);
}
Mybatis 获取参数值
${}:使用的是 Statement 对象,如果使用 ${} 需要在 Mybatis 加上单引号
INSERT INTO user(user.user_id,user.user_name,user.password) VALUE (${'userId'},${'userName'},${'password'})
<!--
可以使用 ${value} 或者 ${_parameter} 或者参数值
-->
#{} :使用的是 PreparedStatement 对象,使用的是通配的,不需要使用字符串拼接,可以防止 SQL 注入攻击,可以以任意的值类型获取
INSERT INTO user(user.user_id,user.user_name,user.password) VALUE (#{userId},#{userName},#{password})
建议使用 #{} 在特殊的情况下,需要使用 ${} (模糊查询和批量删除)
不同参数类型 ${} 和 #{} 不同的取值方式
1、传输参数为单个String或者基本数据类型和其他包装类
#{} : 可以以任意的名字获取参数值
${} : 只能以 ${value} 或者 ${_parameter} 获取
2、当传入的参数为javaBean时
#{} 和 ${} 都是通过属性名进行获取
3、当传入的n个参数
当传入多个参数的时候,mybatis会把参数放在 map 集合中
键的两种方式:
1)、键为:0.1.2.3...n-1,以参数为值
2)、键为param1.......paramN
${} : 访问方式,也可传入表达式,不能使用 ${int类型},而且需要注意参数的类型
<select id="" parameterType="">
select * from user where ${param1} and ${param1}
</select>
#{} :两种方式都可以使用
<select id="" parameterType="">
select * from user where #{0} and #{1}
</select>
<select id="" parameterType="">
select * from user where #{param1} and #{param1}
</select>
4、当传输为 map 参数时
#{} 和${} 可以直接使用传入 map 的键值获取value值,但是需要注意单引号问题
<!--
Object name(Map<String,Object> map)
直接使用键的值
-->
<select id="" parameterType="">
select * from user where eid=#{eid}
</select>
5、命名参数
<!--
使用 @Param 指定键的名字,也会把参数的值,放入map集合中
User getUserByUidAndUname(@Param("uid") String uid,@Param("uName") String uname);
-->
<select id="" parameterType="">
select * from user where uid=#{uid} and uname = #{uName}
</select>
6、传输的参数为List 或 Array 时
在动态 SQL 使用
mybatis 会将 List 或 Array 放在map中
List以list为键,Array是以array为键
Mybatis 自动生成主键
在添加的时候,马上获取新添加的主键
需要数据库支持自动生成主键
<!--
useGeneratedKeys="true" 获得自动生成的主键
keyProperty = userId 自动生成的主键,赋值给那个属性
-->
<insert id="insertUser" parameterType="com.lyy.deom.User"
useGeneratedKeys="true"
keyProperty="keyProperty">
INSERT INTO user(user.user_id,user.user_name,user.password) VALUE (#{userId},#{userName},#{password})
</insert>
多表查询结果处理(高级映射)
<!-- 定义resultMap --> <!-- <resultMap> 自定义映射,处理复杂的表关系 <id column="uid" property="uid"/> id : 设置主键的关系 column:设置字段名 property:属性名 <result column="" property=""/> result : 设置非主键的映射关系,column:设置字段名 property:属性名 --> <!-- 方式1 --> <resultMap id="userMap" type="user"> <!--设置主键的关系 column:列名 property:属性名 --> <id column="字段名(数据库中)" property="字段属性(javaBean中)"/> <!-- column 设置列名, property 属性名 --> <result column="" property=""/> </resultMap> <!-- 方式2 --> <resultMap id="userMap" type="user"> <id column="uid" property="uid"/> <result column="" property=""/> <!-- javaType : 设置java 的类型 --> <association property="" javaType="Dept" > <id column="" property=""/> <result column="" property=""/> </association> <!-- 支持分步查询 --> </resultMap> <Select id="" resultMap=""> SQL 语句 </Select>
分步查询可以代替表的多对一的关系
<resultMap id="userMap" type="user">
<id column="uid(数据库的主键)" property="uid(javabean的对应的属性)"/>
<result column="" property=""/>
<!-- javaType : 设置java 的类型 -->
<!--
select : 分步查询的SQL的id,即接口的全类名.方法名或者namespace.SQL的id
com.lyy.userMapper.getUserByID()
counmn:分步查询的条件:
此条件必须是从数据库查询过的
-->
<association property="" javaType="Dept" select="SQL语句的ID">
<id column="字段名(数据库中)" property="字段属性(javaBean中)"/>
<result column="字段名" property="字段属性"/>
</association>
<!-- 支持分步查询 -->
<!--
collection : 处理一对多护额多对多的关系
ofType : 集合中的类型,不需要指定javaType
-->
<collection property="emps" ofType="">
<id column="uid" property="uid"/>
<result column="" property=""/>
</collection>
</resultMap>
延迟加载
MyBatis 动态SQL语句
用于拼接SQL的操作,使用 OGNL 表达式来简化操作
多条件查询
IF WHERE
IF 是进行条件的拼接
空字符串和 null
如果是直接通过方法来进行赋值,而且那个值未写,那么传入的值为 NULL
如果是通过文本框输入值,而且输入框中没有写入值,那么数据传入到后台进行处理 是空字符串,需要判断两次
多条件查询,必须注意如果页面中没有设置子条件(文本框中没有值,传入SQL语句中式NULL)
SQL语句的条件中一定不能有该条件
<if test="boyName != null">
boyName=#{boyName}
</if>
Test 中进行的判断 需要根据查询的数值的数据类型进行判断,通过 if 判断并获拼接 SQL 语句
单选框和复选框:需要判断是否是 NULL
输入框 : 需要判断是否是空字符串和是否为 null
where : 给 SQL 添加 where 关键字,并且去掉多余的 and 关键字
<where>
<if test="id != null">
and id=#{id}
</if>
<if test="boyName != null and boyName != '' ">
and boyName=#{boyName}
</if>
<if test="userCP != null">
and boys.userCP = #{userCP}
</if>
</where>
trim
<!--
prefix : 前缀,在操作的SQL语句前加入某一些内容
suffix : 后缀,在操作的SQL语句后加入某一些内容
prefixOverrides : 对前缀进行重写,把操作SQL语句前语句删除
suffixOverrides : 对后缀进行重写,把操作SQL语句后语句删除
<trim></trim> : 截取并拼接
prefixOverrides="and|or" : 表示去掉 and 或者 or
-->
<select id="getBoy" resultType="com.lyy.deom.Boy">
SELECT * FROM `boys` where
<trim prefix="" suffix="" prefixOverrides="and|or" suffixOverrides="">
<if test="id != null">
and id=#{id}
</if>
<if test="boyName != null and boyName != '' ">
and boyName=#{boyName}
</if>
<if test="userCP != null">
and boys.userCP = #{userCP}
</if>
<trim/>
</select>
SELECT * FROM `boys` where id=? and boyname=? and userCP=?
set
用于解决修改操作时,语句中可能多出的逗号问题,一般使用 TRIM 标签解决
<update id="updateUser" parameterType="com.lyy.deom.Boy">
update boy
<set>
<if test="boyName!=null and boyName != '' ">
boyName = #{boyName},
</if>
<if test="id!=null">
userCP = #{userCP}
</if>
</set>
where id=#{id}
</update>
choose(when,otherwise)
主要用于分支判断,只会满足所有分支的一个,根据某一个条件查询数据
<!--如果所有的情况都包含了,就不用使用 otherwise ,如果when的条件都不符合使用otherwise-->
<select id="getBoy" resultType="com.lyy.deom.Boy">
SELECT * FROM `boys`
<where>
<choose>
<when test="id != null">
id = #{id}
</when>
<when test="bouName != null and bouName='' ">
bouName = #{bouName}
</when>
<otherwise>
userCP = #{userCP}
</otherwise>
</choose>
</where>
</select>
<!--可以运用在插入语句中,进行判断来插入值-->
<choose>
<when test="ser == 1">'男'</when>
<when test="ser == 1">'女'</when>
<otherwise>'不像'</otherwise>
</choose>
批量操作
批量删除
<!--deleteBoy(String ids),使用${} 里面的参数必须是 value或param-->
<delete id="deleteBoy">
delete from boy where id in (${value})
</delete>
使用 foreach
foreach 是对数值或者集合进行遍历
foreach 参数
collection : 指定要遍历的集合或者数值,需要指定集合的属性
item:设置别名
close:设置循环体的结束内容
open:设置循环体的开始内容
separator:设置每一次循环之间的分割符,最后一次没有分隔符
index:如果是遍历的是 List 集合 Index 代表下标(索引),map 集合index代表键值
<!--
通过List集合实现批量删除
void deleatBoyByList(List<Integer> ids)
-->
<delete id="">
delete from boy where id in
<foreach collection="list" item="id" close=")" open="(" separator="," index="">
#{id}
</foreach>
</delete>
<delete id="">
delete from boy where id =
<foreach collection="list" item="id" close="" open="" separator="or" index="">
#{id}
</foreach>
</delete>
批量查询
于delete操作相同
<select id="">
select from boy where id in
<foreach collection="list" item="id" close=")" open="(" separator="," index="">
#{id}
</foreach>
</select>
<select id="">
delete from boy where id =
<foreach collection="list" item="id" close="" open="" separator="or" index="">
#{id}
</foreach>
</select>
批量修改
把每条数据修改成相同内容
<update id="">
update emp set .... where id in
<foreach collection="list" item="id" close=")" open="(" separator="," index="">
#{id}
</foreach>
</update>
<update id="">
update emp set .... where id =
<foreach collection="list" item="id" close="" open="" separator="or" index="">
#{id}
</foreach>
</update>
把没条数据修改为对应内容
需要配置 allowMultiQueries=true 才可以执行多条语句,在数据源的url添加
<property name="url" value="jdbc:mysql://localhost:3306/girls/allowMultiQueries=true"/>
update emp set .... where id =1; update emp set .... where id =2; update emp set .... where id =3;
<!--
void insertBoy(@Param("boys")boy[] boys)
-->
<update id="">
<foreach collection="boys" item="boy">
update boy set userName=#{boy.userName},userCP=#{userCP} where id=#{boy.id};
</foreach>
</update>
批量插入
<!--
void insertBoy(@Param("boys")boy[] boys)
<foreach collection="boys" item="boy" separator=",">
(null,#{boy.的属性},#{boy.的属性})
</foreach>
-->
<insert id="">
insert into boy values
<foreach collection="boys" item="boy" separator=",">
(null,#{boy.userName},#{boy.userCP})
</foreach>
</insert>
批量操作 SQL 关键字
<!--
<sql id=""></sql>:设置一段SQL片段,公共SQL可以被映射文件中所有SQL语句访问
-->
<Sql id="XXXX">
sellect * from boy
</Sql>
<!--
其他SQL语句进行访问
-->
<select>
<!--访问某个SQL片段-->
<inclide refid="XXXX"></inclide>
</select>
MyBatis 缓存
一级缓存(frist level cache) SqlSession级缓存
一个 SqlSession 当调用相同的 SQL 语句会把第一次执行 SQL 语句的结果放入缓存中,如果这一个 SqlSession 再一次调用相同的 SQL 语句就不会在执行 Mapper.xml 中 SQL 语句,而是直接从缓存中读取上一次的数据
一级缓存失效的情况
-
不同的 SqlSession 对应不同的一级缓存
-
同一个 SqlSesion 但是查询的条件不同
-
同一个 SqlSession 两次查询的期间执行了任何增加,更改,删除操作
不论增删该是否成功,都会把对应的 SqlSession 中的一级缓存清除
-
同一个 SqlSession 两次查询期间,执行了清除缓存的操作
二级缓存 (second level cache),全局作用域,映射文件级别的缓存
只要是访问同一个映射文件的同一个 SQL 语句,就可以被缓存
默认不开启,需要手动在 mybatis 的配置文件中开启
缓存实现需要 pojo(实体类) 实现 Serializable 接口(序列化的接口)
二级缓存在 SqlSession 关闭或提交后才会生效
二级缓存的使用
-
全局配置二级缓存开启
<configuration> <settings> <!--开启二级缓存--> <setting name="cacheEnabled" value="true"/> </settings> </configuration> -
需要使用二级缓存的映射文件(mapper.xml文件)处使用 cache 配置缓存
<mapper namespace="com.lyy.mapper.BeautyMapper"> <cache blocking="" eviction="" flushInterval="" readOnly="" size="" type="" /> </mapper> -
需要在实体类实现 Serializable 接口
public class Beauty implements Serializable {}
二级缓存 cache 关键字
-
eviction : 缓存回收策略,当内存放不下数据,定义如何回收
-
LRU :最近最少使用原则,回收长时间不被使用的对象(默认)
-
FIFO : 先进先出原则,先进入的数据先清除
-
SOFT :软引用,移除基于垃圾回收器状态和软引用规则的对象
-
WEAK :弱引用,更积极的移除基于垃圾收集器装态和弱引用规则的对象
-
-
flushInterval :刷新间隔,单位是毫秒,默认情况是不设置的,会在调用SQL语句的时候自动刷新
-
size : 引用数目,正整数,代表缓存最多可以储存多少个对象,太大会导致内存溢出
-
readOnly : 只读 true/false
true : 只读缓存:会给所有调用者返回缓存对象的相同实例,因此这些对象不能被修改,提供很重要的性能优势,会影响缓存中的对象
false :读写缓存 : 返回缓存对象的拷贝(通过序列化),会变慢,但是安全(默认情况是false),对缓存中的对象没有影响,只会影拷贝的对象
-
type:设置第三方缓存
mybatis 的缓存常用属性
-
全局的 setting 的 cacheEnable :
配置二级缓存是否开启,一级缓存默认开启
-
select 标签的 useCache 属性 :
配置这个 select 是否使用二级缓存,一级缓存默认一直使用
-
sql 标签的 flushCache 属性 :
增删改默认 flushCache=true,Sql 执行以后,会同时清空一级和二级缓存
查询 flushCache=false
-
sqlSession:clearCache : 只是用来清除一级缓存
第三方缓存
需要在 mapper.xml 的 <catch type=""></cache>
扩展MyBatis的缓存框架 (EhCache 定义缓存的接口,具有快速,精干等特点,是Hibernate中默认的CacheProvider)
整合 EhCache 缓存步骤:
-
导入 jar 包,ehcache 包,整合包和日志包
ehcache-core-2.6.18.jar、mybatis-ehcache-1.0.3.jar、
日志jar包:shf4j-api-1.6.1.jar、slf4j-log4j12-1.6.2.jar
-
编写 ehcache.xml 配置文件
-
使用第三方缓存
需要在 mapper.xml 中
<mapper> <!-- type 导入缓存的实现类,会把二级缓存交给这个第三方缓存 --> <cache type=""></cache> </mapper>
MyBatis 逆向工程(自动生成部分代码)
-
导入 mybatis-generator-core-1.3.2.jar 包
-
配置 generator 的配置文件
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN" "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd"> <generatorConfiguration> <context id="DB2Tables" targetRuntime="MyBatis3"> <!-- 设置数据库信息 --> <jdbcConnection driverClass="com.mysql.jdbc.Driver" connectionURL="jdbc:mysql://localhost:3306/girls" userId="root" password=""> </jdbcConnection> <javaTypeResolver> <property name="forceBigDecimals" value="false"/> </javaTypeResolver> <!--生成实体类存放位置--> <javaModelGenerator targetPackage="com.lyy.deom" targetProject=".\src"> <!-- 是否使用子包 --> <property name="enableSubPackages" value="true"/> <property name="trimStrings" value="true"/> </javaModelGenerator> <!--生成映射文件存放位置--> <sqlMapGenerator targetPackage="com.lyy.mapper" targetProject=".\config"> <property name="enableSubPackages" value="true"/> </sqlMapGenerator> <!--生成Dao类存放位置--> <javaClientGenerator type="XMLMAPPER" targetPackage="com.lyy.mapper" targetProject=".\src"> <property name="enableSubPackages" value="true"/> </javaClientGenerator> <table tableName="admin" domainObjectName="Admin"></table> <table tableName="beauty" domainObjectName="Beauty"></table> <table tableName="boys" domainObjectName="Boys"></table> </context> </generatorConfiguration> -
执行逆向工程的核心代码
public void testMBG() throws Exception{ List<String> warings = new ArrayList<String>(); boolean overwrite = true; File configile = new File("mbg.xml"); ConfigurationParser cp = new ConfigurationParser(warings); Configuration config = cp.parseConfiguration(configile); DefaultShellCallback callback = new DefaultShellCallback(overwrite); MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config,callback,warings); myBatisGenerator.generate(null); }
MyBatis 分页插件
-
添加 pagehelper-5.0.0.jar 和 jsqlparser-0.8.0.jar 包
-
配置 MyBatis.xml 文件
<plugins> <!-- 配置插件,全类名 --> <plugin interceptor="com.github.pagehelper.PageInterceptor"> </plugin> </plugins> -
调用
@Test public void testPage()throws Exception{ SqlSessionFactory sqlSessionFactory = getSqlSessionFactory(); SqlSession sqlSession = sqlSessionFactory.openSession(true); BoysMapper mapper = sqlSession.getMapper(BoysMapper.class); PageHelper.startPage(8,1); List<Boys> boys = mapper.getAllBoy(); // PageInfo<>(查询后的组,显示的总页码数) PageInfo<Boys> boysPageInfo = new PageInfo<Boys>(boys,3); System.out.println(Arrays.toString(boysPageInfo.getNavigatepageNums())); }
模拟分页
package com.lyy.Test;
import com.github.pagehelper.PageInfo;
import com.lyy.deom.Boys;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
public class PageUtil {
public static String getPageInfo(PageInfo<Boys> pageInfo,HttpServletRequest request
){
// 获取相应的路径
// String path = request.getContextPath() +"/";
String path = "/ssm/";
StringBuilder builder = new StringBuilder();
//拼接首页
builder.append("<a href='"+path+"emps/1'>首页</a>");
//拼接上一页
//先判断是否有上一页
if(pageInfo.isHasPreviousPage()){
builder.append("<a href='"+path+"emps/'"+pageInfo.getPrePage()+">上一页</a>");
}else {
builder.append("上一页");
}
// 拼接页码
// 1、获取当前页码
int[] nums = pageInfo.getNavigatepageNums();
for (int i:nums){
if(i == pageInfo.getPageNum()){
builder.append("<a style='color:red' href='"+path+"emps/"+i+"'>"+i+"</a>");
}else {
builder.append("<a href='"+path+"emps/"+i+"'>"+i+"</a>");
}
}
//拼接下一页
if(pageInfo.isHasNextPage()){
builder.append("<a href='"+path+"emps/'"+pageInfo.getNextPage()+">下一页</a>");
}else {
builder.append("下一页");
}
// 拼接未页
builder.append("<a href='"+path+"emps/"+pageInfo.getPages()+"'>尾页</a>");
return builder.toString();
}
}
SSM框架整合
Spring 和 Mybatis 整合原理
整合需要的操作
加入的依赖最好跟需要写的工程有关系
-
在子工程中加载环境具体的依赖( Jar 包)
-
准备 jdbc.properties:数据源文件
-
创建 Spring 的全局配置文件和 Mybatis 整合相关
-
在 Spring 配置文件加载 jdbc.properties
-
配置数据源
-
测式 前面的配置是否正确
-
配置 SqlSessionFactoryBean
-
装配数据源
-
指定 Mapper 配置文件的位置
-
指定 mybatis 全局配置文件位置(可选)
-
-
配置 MapperScannerConfigurer : 扫描 Mytatis 的 mapper 配置文件位置,需要扫描的包
-
测试是否可以装配 XXXMapper 接口并测试是否可以操作数据库
替换 Spring 的日志框架(commons-logging)
1、关闭项目所有的(commons-logging)
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-orm</artifactId> <!-- 排除 commons-logging(Spring 自带的日志jar包)--> <exclusions> <exclusion> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> </exclusion> </exclusions> </dependency>
2、导入日志包(例:logback)
<!-- 日志 --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> </dependency>
3、使用其他jar日志替换(commons-logging)
<!-- 其他日志框架的中间转换包 --> <dependency> <groupId>org.slf4j</groupId> <artifactId>jcl-over-slf4j</artifactId> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>jul-to-slf4j</artifactId> </dependency>
4、控制打印的日志文件
文件名必须为 logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="true">
<!-- 指定日志输出的位置 -->
<appender name="STDOUT"
<!-- 指定在控制台打印 -->
class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<!-- 日志输出的格式 -->
<!-- 按照顺序分别是:时间、日志级别、线程名称、打印日志的类、日志主体内容、换行 -->
<pattern>[%d{HH:mm:ss.SSS}] [%-5level] [%thread] [%logger] [%msg]%n</pattern>
</encoder>
</appender>
<!-- 设置全局日志级别。日志级别按顺序分别是:DEBUG、INFO、WARN、ERROR -->
<!-- 指定任何一个日志级别都只打印当前级别和后面级别的日志。 -->
<!-- 只会打印 debug 级别的日志 -->
<root level="INFO">
<!-- 指定打印日志的appender,这里通过“STDOUT”引用了前面配置的appender -->
<appender-ref ref="STDOUT" />
</root>
<!-- 根据特殊需求指定局部日志级别 -->
<!-- 在 com.atguigu.crowd.mapper 打印 debug 级别-->
<logger name="com.atguigu.crowd.mapper" level="DEBUG"/>
</configuration>
注:运行的时候需要先加载日志文件在加载日志文件前日志全部都会打印,在加载日志文件后日志就不会打印其他级别的日志
声明式事务
try{
// 开启事务,关闭自动提交
connection.setAutCommit(false)
// 核心操作
....
// 提交事务
connection.commit();
}catch(Excspetion e){
// 回滚事务
connection.rollBack();
}finally{
// 释放数据库连接
connection.close();
}
在框架的情况下,使用 Spring 来管理事务,通用的事务操作
可以把事务操作当成 AOP 切面操作
1、选择合适的事务管理器
2、创建事务的配置文件
3、配置配置文件 AOP 和事务管理
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:mybatis-spring="http://mybatis.org/schema/mybatis-spring" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://mybatis.org/schema/mybatis-spring http://mybatis.org/schema/mybatis-spring-1.2.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd"> <!-- 配置自动扫描包,一般扫描服务层 --> <context:component-scan base-package="com.lyy.crowd.service"></context:component-scan> <!-- 配置事务管理器 --> <bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!-- 配置数据源,在 Spring IOC 容器中获取 --> <property name="dataSource" ref="dataSource"></property> </bean> <!-- 配置事务的切面 --> <aop:config> <!-- 表示切面式任意包中以 Service 结尾的类的所有的方法 execution(* *..*Service.*(..)) 第一个 * : 表示任意的返回值 *.. : 代表任意的包 *Service : 表示任意以 Service 结尾的类 *(..) : 表示任意的参数和方法 原来的表达式 : public int com.lyy.crowd.testService.test(int ,int ) 如果整合 SpringSercurily,避免包UserDatailsService 加入到事务管理器,需要让切入点表达式定位到 ServiceImpl类 --> <aop:pointcut expression="execution(* *..*ServiceImpl.*(..))" id="txPointcut"/> <!-- 将事务通知和切入点表达式关联 --> <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/> </aop:config> <!-- 配置事务通知 --> <tx:advice id="txAdvice" transaction-manager="dataSourceTransactionManager"> <!-- 配置事务的属性 --> <tx:attributes> <!-- 查询方法 : 配置只读属性,可以对数据库进行一定的优化 ..* : 代表以 .. 开头的方法 --> <tx:method name="get*" read-only="true"/> <tx:method name="find*" read-only="true"/> <tx:method name="query*" read-only="true"/> <tx:method name="count*" read-only="true"/> <!-- 配置写方法,需要配置事务的传播行为,和回滚 propagation : REQUIRED : 默认值,表示当前方法必须在事务中,如果当前线程上没有开启的事务,则开启新事务 如果已经有,就使用已有的事务,就算已经有谱了,也在自己开启的事务 用别人的事务来代替自己的事务,有可能被回滚 REQUIRES_NEW:建议使用,表示不管线程上有没有事务,都有自己开事务,在自己的事务中运行 不会受到别的事务影响 rollback-for: 配置事务遇到什么异常进行回滚 默认:运行时异常回滚 建议:运行时和编译时事务都回滚 --> <tx:method name="save*" propagation="REQUIRES_NEW" rollback-for="java.long.Exception"/> <tx:method name="update*" propagation="REQUIRES_NEW" rollback-for="java.long.Exception"/> <tx:method name="remove*" propagation="REQUIRES_NEW" rollback-for="java.long.Exception"/> <tx:method name="batch*" propagation="REQUIRES_NEW" rollback-for="java.long.Exception"/> </tx:attributes> </tx:advice> </beans>
注意:
<tx:method> 是必须配置的,否者就方法就不会生效
配置时包名必须小写
web.xml 配置
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!-- 配置首页 -->
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
<display-name>Archetype Created Web Application</display-name>
<!-- 配置 spring 的配置文件 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:spring-*.xml
</param-value>
</context-param>
<!-- 配置监听器 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- 配置字符集处理 -->
<filter>
<filter-name>encodingFilter</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>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 配置 servlet 主要是springMVC 复杂拦截请求和返回 -->
<servlet>
<servlet-name>SpringMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-webmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>SpringMVC</servlet-name>
<!-- 此处可以可以配置成*.do,对应struts的后缀习惯 -->
<url-pattern>*.html</url-pattern>
<url-pattern>*.json</url-pattern>
</servlet-mapping>
</web-app>
配置 SpringMVC
主要注意:命名空间和扫描的包的包名要小写
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 扫描service、dao组件 -->
<!--<context:component-scan base-package="com.**.service,com.**.dao" />-->
<!-- 注解方式 -->
<mvc:annotation-driven />
<!--静态资源的访问-->
<mvc:default-servlet-handler />
<!--扫描 handler 层,主要是拦截器,拦截浏览器的请求并进行返回内容 -->
<context:component-scan base-package="com.lyy.crowd.mvc.handler" />
<mvc:annotation-driven/>
<!-- JSP视图解析器 -->
<bean id="viewResolverJsp" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/"/>
<property name="suffix" value=".jsp"/>
</bean>
</beans>
配置 SpringMyBatis 文件
注意:命名空间和包名需要小写
<?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
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.lyy.crowd.service"/>
<!--
配置数据源,使用 Druid 配置,需要引入依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
</dependency>
-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="username" value="root"/>
<property name="url" value="jdbc:mysql://localhost:3306/project_crowd"/>
<property name="password" value=""/>
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
</bean>
<!--
配置 sqlSession 的工厂需要加入依赖
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
</dependency>
//mybatis Spring 的依赖
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
</dependency>
-->
<bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--加载 mybatis 的配置文件-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<!-- 配置 mapper.xml 配置文件的位置 -->
<property name="mapperLocations" value="classpath:mybatis/mapper/*Mapper.xml"/>
<!-- 配置数据源 -->
<property name="dataSource" ref="dataSource"/>
</bean>
<!--
配置mapper接口所在的包,并创建其bean
-->
<bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.lyy.crowd.mapper"/>
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactoryBean"/>
</bean>
</beans>
事务的配置文件
需要映入以下依赖
<!-- JSP页面使用的依赖 -->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.1.3-b06</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>1.7.25</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jul-to-slf4j</artifactId>
<version>1.7.25</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.7</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.2</version>
</dependency>
事务的 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"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://mybatis.org/schema/mybatis-spring http://mybatis.org/schema/mybatis-spring-1.2.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">
<!-- 配置自动扫描包,一般扫描服务层 -->
<context:component-scan base-package="com.lyy.crowd.service"></context:component-scan>
<bean id="dataSourceTx" class="com.alibaba.druid.pool.DruidDataSource">
<property name="username" value="root"/>
<property name="url" value="jdbc:mysql://localhost:3306/project_crowd"/>
<property name="password" value=""/>
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
</bean>
<!-- 配置事务管理器 -->
<bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 配置数据源,在 Spring IOC 容器中获取 -->
<property name="dataSource" ref="dataSourceTx"></property>
</bean>
<!-- 配置事务的切面 -->
<aop:config>
<!--
表示切面式任意包中以 Service 结尾的类的所有的方法
execution(* *..*Service.*(..))
第一个 * : 表示任意的返回值
*.. : 代表任意的包
*Service : 表示任意以 Service 结尾的类
*(..) : 表示任意的参数和方法
原来的表达式 : public int com.lyy.crowd.testService.test(int ,int )
如果整合 SpringSercurily,避免包UserDatailsService 加入到事务管理器,需要让切入点表达式定位到
ServiceImpl类
-->
<aop:pointcut expression="execution(public void com.lyy.crowd.service.imple.AdminServiceImpl.test())" id="txPointcut"/>
<!-- 将事务通知和切入点表达式关联 -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
</aop:config>
<!-- 配置事务通知 -->
<tx:advice id="txAdvice" transaction-manager="dataSourceTransactionManager">
<!-- 配置事务的属性 -->
<tx:attributes>
<!--
查询方法 : 配置只读属性,可以对数据库进行一定的优化
..* : 代表以 .. 开头的方法
-->
<tx:method name="get*" read-only="true"/>
<tx:method name="find*" read-only="true"/>
<tx:method name="query*" read-only="true"/>
<tx:method name="count*" read-only="true"/>
<!--
配置写方法,需要配置事务的传播行为,和回滚
propagation :
REQUIRED : 默认值,表示当前方法必须在事务中,如果当前线程上没有开启的事务,则开启新事务
如果已经有,就使用已有的事务,就算已经有谱了,也在自己开启的事务
用别人的事务来代替自己的事务,有可能被回滚
REQUIRES_NEW:建议使用,表示不管线程上有没有事务,都有自己开事务,在自己的事务中运行
不会受到别的事务影响
rollback-for:
配置事务遇到什么异常进行回滚
默认:运行时异常回滚
建议:运行时和编译时事务都回滚
-->
<tx:method name="save*" propagation="REQUIRES_NEW" rollback-for="java.long.Exception"/>
<tx:method name="update*" propagation="REQUIRES_NEW" rollback-for="java.long.Exception"/>
<tx:method name="remove*" propagation="REQUIRES_NEW" rollback-for="java.long.Exception"/>
<tx:method name="batch*" propagation="REQUIRES_NEW" rollback-for="java.long.Exception"/>
</tx:attributes>
</tx:advice>
</beans>
事务的配置文件
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="true">
<!-- 指定日志输出的位置 -->
<!-- class="ch.qos.logback.core.ConsoleAppender 指定在控制台打印 -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<!-- 日志输出的格式 -->
<!-- 按照顺序分别是:时间、日志级别、线程名称、打印日志的类、日志主体内容、换行 -->
<pattern>[%d{HH:mm:ss.SSS}] [%-5level] [%thread] [%logger] [%msg]%n</pattern>
</encoder>
</appender>
<!-- 设置全局日志级别。日志级别按顺序分别是:DEBUG、INFO、WARN、ERROR -->
<!-- 指定任何一个日志级别都只打印当前级别和后面级别的日志。 -->
<!-- 只会打印 debug 级别的日志 -->
<root level="INFO">
<!-- 指定打印日志的appender,这里通过“STDOUT”引用了前面配置的appender -->
<appender-ref ref="STDOUT" />
</root>
<!-- 根据特殊需求指定局部日志级别 -->
<!-- 在 com.atguigu.crowd.mapper 打印 debug 级别-->
<logger name="com.lyy.crowd.mapper" level="DEBUG"/>
</configuration>
JSON 和 AJAX 的使用
使用jquery-3.1.1.min.js封装的 AJAX
需要引用依赖
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.10.3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.10.3</version>
</dependency>
JSP界面
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<html lang="en">
<base href="http://${pageContext.request.serverName}:${pageContext.request.serverPort}${pageContext.request.contextPath}/">
<script type="text/javascript" src="jquery/jquery-3.1.1.min.js"></script>
<script type="text/javascript">
$(function () {
$("#btn1").click(function () {
$.ajax({
"url":"send/one/array.html",//请求地址
"type":"post",//请求方式
"data":{
"array":[5,2,12]
},//请求参数
"dataType":"text",//如何对待服务器返回发数据
"success":function (response) {//服务器成功处理请求后调用的函数
alert(response)
},
"error":function (response) {//务器处理请求失败后调用的函数
alert(response)
}
})
})
$("#btn2").click(function () {
$.ajax({
"url":"send/two/array.html",//请求地址
"type":"post",//请求方式
"data":{
"array[0]":5,
"array[1]":8,
"array[2]":12
},//请求参数
"dataType":"text",//如何对待服务器返回发数据
"success":function (response) {//服务器成功处理请求后调用的函数
alert(response)
},
"error":function (response) {//务器处理请求失败后调用的函数
alert(response)
}
})
})
$("#btn3").click(function () {
// 准备发送的数据
var array = [5,8,12];
console.log(array.length);
// 将json数值转换为json字符串
var requestBody = JSON.stringify(array);
console.log("requestBody : "+requestBody);
$.ajax({
"url":"send/three/array.html",//请求地址
"type":"post",//请求方式
"data":requestBody,//请求参数
"contentType":"application/json;charset=UTF-8",//指定请求体的内容类型
"dataType":"text",//如何对待服务器返回发数据
"success":function (response) {//服务器成功处理请求后调用的函数
alert(response)
},
"error":function (response) {//务器处理请求失败后调用的函数
alert(response)
}
})
})
$("#btn4").click(function () {
var student = {
"stuId":5,
"stuName":"老王",
"address":{
"provice":"四川",
"city":"成都",
"stresst":"锦江区"
},
"subjects":[
{
"subjectName":"javaSE",
"SubjectScore":100
},
{
"subjectName":"C++",
"SubjectScore":100
}
],
"map":{
"k1":"v1",
"k2":"v2"
}
};
// console.log(student)
// 把json对象转换成 json 字符串
var requestBody = JSON.stringify(student);
console.log("requestBody" + requestBody);
$.ajax({
"url":"/send/compose/Object.html",
"type":"post",
"data":requestBody,
"contentType":"application/json;charset=UTF-8",
"dataType":"text",
"success":function (response) {
alert(response);
},
"error":function (response) {
alert(response);
}
});
})
$("#btn5").click(function () {
var test = {
"id":1,
"name":"LW.老王"
};
var resultTest = JSON.stringify(test);
$.ajax({
"url":"/send/compose/Test.html",
"type":"post",
"data":resultTest,
"contentType":"application/json;charset=UTF-8",
"dataType":"text",
"success":function (response) {
alert(response);
},
"error":function (response) {
alert(response);
}
})
})
})
</script>
<head>
<meta charset="UTF-8">
</head>
<body>
<h2>Hello World!</h2>
<a href="test.html">测试</a>
<br/>
<button id="btn1">Send [5.8.12] one</button>
<br/>
<br/>
<button id="btn2">Send [5.8.12] Two</button>
<br/>
<br/>
<button id="btn3">Send [5.8.12] three</button>
<br/>
<br/>
<button id="btn4">Send object</button>
<br/>
<br/>
<button id="btn5">Send Test</button>
</body>
</html>
后台
import java.util.List;
@Controller
public class TestHandler {
private Logger logger = LoggerFactory.getLogger(TestHandler.class);
@Autowired
private AdminService adminService;
@ResponseBody
@RequestMapping(value = "/send/compose/Test.html",produces = {"text/html;charset=utf-8"})
public String testReceiveTest(@RequestBody Test test){
System.out.println(test.getName());
logger.info(test.getName());
logger.info(test.toString());
return "success";
}
@ResponseBody
@RequestMapping(value = "/send/compose/Object.html",produces = {"text/html;charset=utf-8"})
public String testReceiveComposeObjectTest(@RequestBody Student student){
logger.info(student.toString());
return "success";
}
@ResponseBody
@RequestMapping("/send/three/array.html")
public String testReceicerArrayThree(@RequestBody List<Integer> array){
for (Integer num: array) {
logger.info("num = " + num);
}
return "success";
}
@RequestMapping("/test.html")
public String test(ModelMap modelMap){
List<Admin> adminList = adminService.getAll();
modelMap.addAttribute("adminList",adminList);
return "success";
}
@ResponseBody
@RequestMapping("/send/one/array.html")
public String testReceicerArrayOne(@RequestParam("array")List<Integer> array){
for (Integer num: array) {
System.out.println("num = " + num);
}
return "success";
}
@ResponseBody
@RequestMapping("/send/two/array.html")
public String testReceicerArrayTOne(ParamData paramData){
List<Integer> array = paramData.getArray();
System.out.println(array);
// for (Integer num: array) {
// System.out.println("num = " + num);
// }
return "success";
}
}
Ajax 请求返回的结果进行规范
package com.lyy.crowd.ytil;
import java.util.Date;
/**
* 统一整个项目中 AJAX 请求返回的结果
*
* @program: crowd-Prent
* @description:
* @author: LYY
* @create: 2020-04-06 11:47
*/
public class ResultEntity<T> {
public static final String SUCCESS = "SUCCESS";
public static final String FAILED = "FAILED";
// 用来封装当前请求的结果是成功还是失败
private String result;
// 请求处理失败时返回的错误消息
private String message;
// 带返回的数据,指定一个泛型
private T data;
/**
* 请求处理成功而且不需要返回数据的使用方法
* @param <E>
* @return
*/
// 声明一个泛型 <E>
public static <E> ResultEntity<E> sucessWithoutData(){
return new ResultEntity<E>(SUCCESS,null,null);
}
/**
* 请求成功而且需要返回值的方法
* @param data
* @param <Type>
* @return
*/
public static <Type> ResultEntity<Type> successWithData(Type data){
return new ResultEntity<Type>(SUCCESS,null,data);
}
/**
* 请求处理失败返回的参数,失败的错误消息
* @param message
* @param <Type>
* @return
*/
public static <Type> ResultEntity<Type> failed(String message){
return new ResultEntity<Type>(FAILED,message,null);
}
public ResultEntity(String result, String message, T data) {
this.result = result;
this.message = message;
this.data = data;
}
public ResultEntity() {
}
public String getResult() {
return result;
}
public void setResult(String result) {
this.result = result;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
@Override
public String toString() {
return "ResultEntity{" +
"result='" + result + '\'' +
", message='" + message + '\'' +
", data=" + data +
'}';
}
}
异常映射
使用异常映射机制将整个项目的异常和错误提示进行统一管理
基于XML配置的异常映射
<!-- 配置基于 XML 的异常映射 -->
<bean id="simpleMappingExceptionResolver" class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<!-- 配置异常类型和其视图页面对应关系 -->
<property name="exceptionMappings">
<props>
<!-- ket 指定异常的全类名 -->
<!-- 标签体对应的视图(jsp) -->
<prop key="java.lang.Exception">
system-error
</prop>
</props>
</property>
</bean>
基于注解的异常映射
判断请求的类型的方法
package com.lyy.crowd.ytil;
import javax.servlet.http.HttpServletRequest;
/**
*
* 用于判断请求过来的类型,是不是 json 的请求还是普通的请求
* @program: crowd-Prent
* @description:
* @author: LYY
* @create: 2020-04-06 13:15
*/
public class CrowdUtil {
/**
* 判断当前请求是否是 AJAX 请求
* @param request
* @return
* trun : 是 ajax 请求
* false : 不是 ajax 请求
*/
public static boolean judgeRequestType(HttpServletRequest request){
// 1、获取请求的消息头
String Accept = request.getHeader("Accept");
System.out.println(Accept);
String xRequestHeader = request.getHeader("X-Requested-With");
System.out.println(xRequestHeader);
// 2、判断其类型,并返回
return (Accept != null && Accept.contains("application/json")) || (xRequestHeader != null && xRequestHeader.equals("XMLHttpRequest"));
}
}
Layer弹框
导入 layer 文件目录
导入layer所需要的js文件
<script src="layer/layer.js"></script>
使用 layer 弹出框
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<html lang="en">
<base href="http://${pageContext.request.serverName}:${pageContext.request.serverPort}${pageContext.request.contextPath}/">
<script type="text/javascript" src="jquery/jquery-3.1.1.min.js"></script>
<script src="layer/layer.js"></script>
<script type="text/javascript">
$(function () {
$("#btn6").click(function () {
layer.msg("弹框")
})
})
</script>
<head>
<meta charset="UTF-8">
</head>
<body>
<h2>Hello World!</h2>
<button id="btn6">点击 弹框</button>
</body>
</html>
再页面中设置回退按钮
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="">
<meta name="keys" content="">
<meta name="author" content="">
<base
href="http://${pageContext.request.serverName }:${pageContext.request.serverPort }${pageContext.request.contextPath }/" />
<link rel="stylesheet" href="bootstrap/css/bootstrap.min.css">
<link rel="stylesheet" href="css/font-awesome.min.css">
<link rel="stylesheet" href="css/login.css">
<script type="text/javascript" src="jquery/jquery-3.1.1.min.js"></script>
<script type="text/javascript" src="bootstrap/js/bootstrap.min.js"></script>
<script type="text/javascript">
$(function(){
$("button").click(function(){
// 相当于浏览器的后退按钮
window.history.back();
});
});
</script>
<style>
</style>
</head>
<body>
<div class="container">
<h2 class="form-signin-heading" >
<i class="glyphicon glyphicon-log-in"></i> 系统消息
</h2>
<!--
获取服务器错误后抛出的异常
requestScope对应的是存放request域数据的Map
requestScope.exception相当于request.getAttribute("exception")
requestScope.exception.message相当于exception.getMessage()
-->
<h3 >${requestScope.exception.message }</h3>
<button class="btn btn-lg btn-success btn-block">点我返回上一步</button>
</div>
</body>
</html>
使用MD5加密
/**
* 工具类:使用MD5 进行加密用户密码
* @param source 传入的字符串
* @return
*/
public static String MD5(String source) {
// 1、判断 source 是否有效
if (source != null && source.length() == 0){
// 2、如果不是有效的字符串,抛出异常
throw new RuntimeException(CrowdConstant.MESSAGE_STRING_INVALIDATE);
}
try {
// 3、创建 MessageDigest 对象
String algorithm = "md5";
MessageDigest messageDigest = MessageDigest.getInstance(algorithm);
// 4、获取明文字符串的字符数组
byte[] input = algorithm.getBytes();
// 5、执行加密
byte[] output = messageDigest.digest(input);
// 6、创建 BigInteger 对象
int signum = 1;
BigInteger bigInteger = new BigInteger(signum,output);
// 7、按照16进制将bigInteger转换为字符串
int radix = 16;
String encoded = bigInteger.toString(radix);
return encoded;
} catch (NoSuchAlgorithmException e){
} finally {
}
return null;
}
AJAX的同步和异步请求
AJAX异步请求
在浏览器中等待服务器的响应和浏览器执行的方法是两个线程,在执行AJAX请求的时候,浏览器并不会等待服务器响应后返回结果后在执行其他操作,而是直接按照执行的顺序执行
执行AJAX以下的操作不用等待服务器响应后在执行
会在不同的线程内,不一定按照顺序执行
AJAX同步请求
$("#asyncBtn").click(function () {
console.log("ajax的函数前");
$.ajax({
"url":"test/ajax/async.json",
"type":"post",
"dataType":"text",
<!-- 使用同步操作,所有的操作在同一个线程内,顺序完成 -->
"async":false,
"success":function (response) {
console.log("ajax的success函数 :" +response)
}
});
console.log("ajax的函数后");
});
AJAX 传递的时候需要,使用 HttpServletResponse 传递值
HttpServletResponse .getWriter().print();
Spring Security:Spring的安全框架
当用户使用访问的时候,会对比用户的权限是否和该功能需要的权限是否相同,如果相同才可以使用该功能,也可以检查是否登录
开发者:只需要决定用户的权限,和功能需要什么权限
主体:谁使用系统谁就是主体,登录系统的用户
认证:确认主体的身份,需要主体说明自己是谁
授权:给用户分配权限
分布式的理论
项目的主体结构
-
实现业务功能的模块
-
应对业务功能之外的需求,压力给项目增加中间键等系统
-
使整个项目能够顺畅高效的运行,需要把业务功能和中间件以及其他系统有效的整合起来
单一架构
单一架构的概念
一个工程 --> 一个war包 --> 运行在一个Tomcat服务器中
单一架构的演变
水平拆分
把一个工程拆分成多个模块分别进行开发
使用maven依赖的方式把工程整合在一起,只会把Web工程打包成WAR包,模块工程会打包成jar包然后使用war包依赖jar包,只有这个war包会在tomcat服务器中运行
垂直拆分
按照不同的业务功能进行拆分,然后运行在不同的Tomcat服务器中
互联网对软件的要求
高可控
需要让架构设计的时候需要考虑功能的持续更新
高性能
系统处理一个请求的时间需要尽可能的短,缩短用户等待的时间
高并发
用户访问量时会增加系统的并发能力,同时处理请求的能力
分布式架构
概念
把一个项目拆分成多个模块进行开发,每个模块都是war包,在不同的tomcat进行运行,每个模块间可以通过网络相互进行的调用
方法的远程调用
本地调用
在同一个项目内部,调用方法,不经过网络调用方法
远程调用
在网络上发送请求的方式对方法的调用
远程调用的意义
对内:可以实现分布式的架构
对外:可以调用第三方的接口
分布式理论SOA
Service Oriented Archiltenture(面向服务的架构)
Service 在整个系统中把相同的功能抽取出来作为一个服务,每一个模块都可以调用这一个服务,可以提高代码的复用性,被调用的服务称为服务的提供者(provider),调用服务的模块功能叫服务的消费者(consumer)
分布式理论微服务
指的是SAO理论的新的发展,强调独立,可部署
SpringBoot 是微服务的理念
分布式的技术演进
WebService
解决应用程序之间可以跨平台访问,基于SOAP/WSDL协议,让应用程序间可以进行远程通信
Dubbo+Zookeeper
Dubbo:基于 RPC 远程过程调用的框架
Zookeeper:基于树形目录结构,异步通知机制的注册中心,和Dubbp一起使用负责注册中心
SpringBoot+SpringCloud
SpringBoot:是开发具体的微服务,使用”场景启动器(start)“快速整合第三方的中间键
SpringCloud:是微服务整体管理的一战式解决方案
Eureke:负责注册中心
Ribbon:客户端的负载均衡
Feign:远程的接口调用
Hystrix:服务的熔断,降级,监控
zuul:相当于程序的唯一入口
相关概率
分布式架构的接口
服务的消费者调用服务提供者的功能,服务的提供者只会提供方法的接口,隐藏具体的实现的细节
分布式的接口相当于一个以”接口“为代表的功能模块
远程接口的声明式调用
像调用本地方法的方式调用远程方法,远程方法调用的细节,被框架所屏蔽
注册中心
有注册中心,才可以实现远程接口的声明式调用
把被调用的模块的具体信息存入到注册中心,调用模块会订阅注册中心的接口,就可以使用被调用的模块中的注册的功能
分布式架构的优缺点
优点
-
模块化程度高,有利于分工
-
有利于提升项目的性能
-
整体提升
整个项目中每个模块都可以独占一台服务器,整个项目分配到的服务器资源更多
-
局部提升
由于对项目进行了拆分,可以对局部的模块进行局部的优化,可以提升单个模块的硬件资源,也可以给当前模块配置集群(用三个服务器运行同一个模块)
-
缺点
-
结构复杂
-
调用关系复杂
-
部署复杂
-
-
数据不一致
-
session 不一致
-
分布式事务的问题(数据库会在读取数据出现问题 脏读|不可重复读|幻读)
-
分布式和集群
相同的
都使用多台服务器
不同点
分布式不同服务器运行的是不同的模块而集群运行的是相同的模块
SpringCloud
核心
基于 HTTP 和 Dubbo 的本质的区别(Dubbo基于RPC(远程方法调用))
SpringCloud是工作在SpringBoot环境中,使用SpringCloud必须要有SpringBoot
SpringCloud组件
注册中心:Eureka(注册Provider(服务的提供者))
客户端负载均衡:Ribbon(在集群中具体决定调用哪一个Provider(服务的提供者)实例)
声明书远程方法调用:Feighn(调用 Provider(服务的提供者)暴露的接口)
服务降级、熔断:Hystrix
熔断时:Provider 提供的备用方案,在没有这个服务的时候
服务降级:时使用方(Consumer)提供的备用方案
Hystrix Dash Board : 监控服务的状态
网关:Zuut (作为服务的统一的入口)
Zuut
网关:提供一个统一的入口,如果没有网关如果访问服务,就会直接访问提供服务的服务
Session共享用于springCloud的session不同的问题
使用SpringSession框架解决微服务之间的session共享问题,底层完全接管 Tomcat 对session的处理
使用 Redis 存储 session 的数据
Redis数据库的使用(以Windows10为例)
1、安装redis数据库
2、使用管理员允许CMD窗口
3、切换到redis的安装目录
4、输入 redis-server.exe redis.windows.conf 命令
如果出现以下错误需要重启redis
[3300] 11 May 14:50:47.463 # Creating Server TCP listening socket 127.0.0.1:6379: bind: No error
4.1、运行 Redis-cli.exe
4.2、运行 shutdown
4.3、运行 exit
4.4、运行 redis-server.exe redis.windows.conf
5、在打开另一个cmd窗口
6、进入redis目录使用 redis-cli.exe 命令开启redis数据库
获取多个表单的值然后保存
可以先保存到session域中,在完成后进行提交
Spring的异常拦截器
package com.lyy.oa.handler;
import com.lyy.oa.exception.RegisterException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* @program: oa-parent
* @description:用于拦截器异常的类
* @author: LYY
* @create: 2020-05-14 21:43
*/
@ControllerAdvice
public class ExceptionHandler {
@ResponseBody
//ExceptionHandler 注解是抛出这个异常后进行如和处理
@org.springframework.web.bind.annotation.ExceptionHandler(RegisterException.class)
public String RegisterException(RegisterException e){
System.out.println("ExceptionHandler RegisterException = " + e.getMessage());
return e.getMessage();
}
}
servlet容器
开发 servlet 有 3 种方式,但是每重写一次就需要重新部署 servlet , HttpServlet 是使用最多的一种
servlet 的核心方法
-
init():该init函数用于初始化该servlet,该函数只会被调用一次(当用户第一次访问该servlet时被调用)
-
destroy():销毁servlet实例(释放内存),关闭tomcat是调用
-
service:service函数用于处理业务逻辑,业务逻辑代码都写在这,当用户访问servlet时,都会调用该函数
servlet 非核心方法
-
getServletInfo():获取servlet信息,可选项
-
getServletConfig:获取servlet配置信息,可选项
实现 servlet 的 3 种方式(使用时需要导入 servlet-api.jar 包)
1、实现servlet接口
public class myServlet implements Servlet {
@Override
public void init(ServletConfig servletConfig) throws ServletException {
System.out.println("myServlit init(初始化)");
}
@Override
public ServletConfig getServletConfig() {
return null;
}
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("service");
//打印在浏览器上
PrintWriter pw=servletResponse.getWriter();
pw.println("hello,this is servlet");
}
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
System.out.println("myServlit destroy(销毁)");
}
}
2、继承GenericServlet
需要重写 service 方法,不自己写初始化和销毁
public class MyServletGenericServlet extends GenericServlet {
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("service");
PrintWriter pw = servletResponse.getWriter();
pw.write("MyServletGenericServlet");
}
}
3、继承HttpServlet(重写 doGet 和 doPost 方法)
Servlet 生命周期
-
加载(load) -----> 容器进行加载
-
实例化(instantiation) -----> 容器实例化 servlet 对象,调用无参构造器
-
初始化(inti) ------> Servlet被装载后,Servlet容器创建一个Servlet实例并且调用Servlet的init()方法进行初始化。在Servlet的整个生命周期内,init()方法只被调用一次。
-
使用 (service)-----> 使用,处理客户端请求如果 HttpServlet 的子类,就是时 doPost() 或者 doGet() 方法
-
销毁 (destory) -----> 销毁 servlet 在关闭服务器时销毁,如关闭(Tomcat)
Servlet 容器何时初始化
1、自动装载某些 servlet 容器,需要在 web.xml 中的 <servlet></servlet> 中加入 <loadon-startup>1</loadon-startup>
2、在 servlet 容器启动后,用户首次向 servlet 发送请求
3,Servlet 类文件被更新后,重新装载 Servlet
在启动时会先初始化服务器实例,关闭时先销毁 servlet 容器,在销毁服务器容器
Servlet 和 Jsp 的区别
-
jsp 本质时 servlet,jsp 在经过编译后会变成类似于 Servlet 的 java 文件
-
Servlet 基本是由 java 程序代码构成,擅长于流程控制和事务处理,也可以用来生成 html 代码,但是直接通过 servlet 生成动态网页很不乐观
-
JSP 是由 HTML 代码和 JSP 标签构成的,可以方便的编写动态网页,也i可以编写 java 代码,但是整体看上去不够优雅,而且比较麻烦
JSP 侧重于视图(界面),servlet 主要用于控制流程逻辑
JSP 的九个隐藏对象
request:是一个 HttpServletRequest 的对象,封装请求信息
pageContext:页面的上下文,是PageContext的一个对象,可以从该对象中获取其它8个隐含对象。
session:代表浏览器和服务器的一次会话,是HttpSession 的一个对象
application:代表当前WEB应用,是ServletContext对象
config:当前JSP对应Servlet的ServletConfig对象
out:JspWriter对象,调用out.prinln()可以直接把字符串打印到浏览器上
page:指向当前JSP对应的Servlet对象的应用,但为Object类型,只能调用 Object 类的方法
exception:在声明了page指令的isErrorPage="true"时,才可以使用
转发和重定向
重定向:
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
response.sendRedirect("index.jsp");//重定向
}
转发:
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
//response.sendRedirect("index.jsp");
request.getRequestDispatcher("/index.jsp").forward(request, response);//转发
区别:
本质区别:转发只发出了一次请求,而重定向发送了两次请求
关于发起的请求地址:转发是初次发出请求地址,而重定向不是初次发出的请求地址,地址栏为最后的响应地址
request 对象和中转的 request 对象:转发的 request 和其中转对象是同一个对象,而重定向两个 request 对象不是同一个对象
是否可以转发给其他的 WEB 应用资源:转发不能发送给其他 WEB 资源,而重定向可以定向到任何的资源
注:转发和重定向不能在同一个 <a></a> 标签中,否则会报错

浙公网安备 33010602011771号