本周内容
1.mybatis分页查询快速使用
1)导入分页查询的jar包 pageheleper-->5.1.10
2)在mabatis核心配置文件的配置中(位于环境配置的上面),需要引入pagaHelper插件(实现了interceptor的自实现类的全限定名称)--
<plugins interceptor = "com.githelp.pageheleper.PageInterceptor"></plugins>
3)配置分页条件pageHelper.startPage()
参数一:当前页码
参数二:每页显示条数
4)调用方法查询数据(获取mapper接口的对象)
5)封装pageInfode的参数
PageInfo pageinfo = new PageInfo(list);
2.引入Spring–定义用户业务接口以及业务实现–获取
传统MVC情况下,控制器需要不断的去new服务层的对象,耦合性不断增加,会造成oom(内存溢出)的情况,解决此现象的三种方式
2.1 方式一:创建工厂类–通过读取资源文件和反射获取service的实体对象
1)通过配置properties文件,包含服务层的实现类的全限定名称
2)创建一个工厂类
2.1)读取resource类路径下的资源文件
2.2)创建属性集合列表
2.3)将资源文件所在的输入流加载属性集合列表中
2.4)通过key获取value
2.5)反射,获取服务层的自实现类的对象
3)测试类中,通过工厂类获取服务层的对象,执行方法
public class BeanFactory {
//方式一:使用工厂类读取配置文件,加载资源文件
//构造方法私有化
private BeanFactory(){};
public static Object getBean(String Beanname) throws Exception{
//加载资源文件,获取资源输入流
InputStream inputStream = BeanFactory.class.getClassLoader().getResourceAsStream("Bean.properties");
//创建属性集合列表
Properties properties = new Properties();
//封装属性集合列表
properties.load(inputStream);
//通过key值获取value
String userServiceImpl = properties.getProperty(Beanname);
Class clazz = Class.forName(userServiceImpl);
Object obj = clazz.newInstance();
return obj;
}
2.2 创建一个工具(工厂)类–>通过解析xml文件和反射的方式获取service的实体对象
1) 导入dom4j这个jar包解析xml文件,版本1.6.1
2)-使用dom4j里面包含的xpath表达式快速定位到某个标签,导包jaxen,版本1.1-beta-6
3)自定义xml文件,beans标签中的子标签bean,
id属性:是子标签的唯一标识,解析器通过id属性获取到标签对象
class属性:业务接口的实现类的全限定名称
4)读取xml文件获取根标签对象,创建解析器对象
SAXReader saxReader = new SAXReader();
5)读取路径下的配置文件bean.xml(反射的方法)
InputStream inputStream = BeanFactory.class.getClassLoader().getResourceAsStream("bean.xml");
Document document = saxReader.read(inputStream) ;
6)获取到元素对象:bean标签对象
利用dom4j里面有xpath表达式:可以快速定位到某个标签-->xpath-->"//不分层级关系选择标签",格式://标签对象[@id='"+id名称+"']
Element beanElement = (Element) document.selectSingleNode("//bean[@id='"+id+"']"); //id就是等会传递userService
7)通过标签对象获取class属性值的内容
String userServiceImplClass = beanElement.attributeValue("class");
8)反射,通过全限定名称获取字节码文件进而获取实体类对象
9)通过工厂类获取实体类对象,然后调用方法
public static Object getObj(String id) throws Exception {
//第一步;导包-->log4J->版本1.2.17
//第二步:导入xpath表达式的jar包-->快速定位到xml文件中某个标签的位置
//第三步 自定义xml文件.确定访问路径
//第四步: 创建解析器对象
SAXReader saxReader = new SAXReader();
//第五步:读取xml文件,获取资源输入流
InputStream inputStream = BeanFactory.class.getClassLoader().getResourceAsStream("bean.xml");
Document document = saxReader.read(inputStream);
//第六步:获取元素对象,"//不分层级关系选择标签"
Element element = (Element) document.selectSingleNode("//bean[@id='" + id + "']");
//再通过元素对象获取classs属性值
String userServiceImplClass = element.attributeValue("class");
//获取服务层的字节码文件
Class clazz = Class.forName(userServiceImplClass);
Object obj = clazz.newInstance();
return obj;
}
2.3 通过Spring使用spring来管理bean对象,里面底层还是工厂模式
1)导包 spring-context-版本5.3.12
2)准备Spring的xml文件
2.1) 添加Spring的表头限制文件,使用本地连接,去掉https中的s
2.2)添加Spring的容器标签bean
3)创建Spring容器对象applicationContext
ClassPathXmlApplicationContext applicationContext =
new ClassPathXmlApplicationContext("spring-config.xml") ;
4)获取业务接口类对象
UserService userService = (UserService) applicationContext.getBean("userService");//id值
public void testSpring(){
//创建spring容器对象
//并解析spring配置文件
ClassPathXmlApplicationContext applicationContext =
new ClassPathXmlApplicationContext("spring-config.xml") ;
//获取业务接口代理对象
UserService userService = (UserService) applicationContext.getBean("userService");
String message = userService.getMessage();
System.out.println(message);
}
3.SpringIOC管理Bean对象:spring 的控制反转(依赖注入(DI))
定义:SpringIOC称为控制反转或者反转控制,管理各个层的对象的创建工作,降低程序之间的耦合性;底层是一种工厂模式,解析配置文件,通过spring 容器解析配置文件,还可以DI依赖注入,一个类依赖另一个引用类型,可以通过注入的方式注解注入,(set注入,构造器注入)
1)导包 ->spring-context 5.3.9
2)配置spring的xml文件
<bean>标签:Spring中的标签
id属性:是子标签的唯一标识,解析器通过id属性获取到标签对象
class属性:业务接口的实现类的全限定名称
3.1 set注入
<set/ property="属性名称" value="赋值">
value是直接给属性名称赋值
ref是关联的意思,关联其他id,也就是实体类中含有其他自定义的类
<bean id="user1" class="com.qf.pojo.User">
<property name="userId" value="1"></property>
<property name="userName" value="王桑"></property>
<property name="userBirthday" value="1996-10-01"></property>
<property name="userAddress" value="西安市"></property>
</bean>
3.2 构造器注入
前提条件:实体类必须提供有参构造方法
<constructor name="属性名称" value="赋值"/>
value是直接给属性名称赋值
ref是关联的意思,关联其他id,也就是实体类中含有其他自定义的类
<bean id="user2" class="com.qf.pojo.User">
<constructor-arg name="userId" value="1"></constructor-arg>
<constructor-arg name="userName" value="付桑"></constructor-arg>
<constructor-arg name="userBirthday" value="1996-01-01"></constructor-arg>
<constructor-arg name="userAddress" value="西安市"></constructor-arg>
</bean>
3.3 p标签注入(实际开发中使用较少)
前提条件:
1)在当前xml文件的文档声明前面,需要声明一个p的名称空间约束
xmlns:p="http://www.springframework.org/schema/p"
2)必须有set方法/get方法,否则不行!
<bean>
p:属性名称="属性值"
</bean>
<bean id="user3" class="com.qf.pojo.User"
p:userId="1" p:userName="田桑" p:userBirthday="1996" p:userAddress="西安市">
</bean>
</beans>
3.4 注入复杂数据类型
<property>中分别添加类型标签,属性name表示实体类中的属性名称
1)数组类型 添加<array>标签-->再添加value标签.进行赋值
<property name="hobbys">
<array>
<value>足球</value>
<value>健身</value>
<value>玩游戏</value>
</array>
</property>
2)set集合--->添加<set>标签
<property name="phones">
<set>
<value>13335390494</value>
<value>13689257999</value>
<value>13888886666</value>
</set>
</property>
3)list集合-->添加<list>集合
<property name="names">
<list>
<value>刘桑</value>
<value>符桑</value>
<value>魏桑</value>
</list>
</property>
4)map集合-->添加<map>标签--><entry>标签-->key属性和value属性
<property name="peple">
<!--键值对元素-->
<map>
<entry key="id" value="001"></entry>
<entry key="name" value="文章"></entry>
<entry key="age" value="35"></entry>
<entry key="address" value="西安市"></entry>
</map>
</property>
5)properties属性集合列表--><props>标签--><prop>标签-->属性key
<property name="files">
<props>
<prop key="prduct_id">1</prop>
<prop key="product_name">华为仓颉</prop>
<prop key="product_desc">为中国人使用中文编程而生</prop>
</props>
</property>
4.SpringIOC管理Bean对象的生命周期
1)默认是一个单例,scope不写的话,默认就是(singleton:单例),前提是在一个容器中
2)如果scope的值是property,则表示Spring是多例的,表示在一个spring容器中,可以创建多个对象
3)init-method:初始化方法
4)destroy-method:销毁方法
(1)Spring的生命周期:被Spring容器管理,.
如果模式是单例模式,获取的实例对象是一致的(前提是在同一个Spring容器中)
实例化和初始化就一次,加载当前类的时候就会创建对象;
当将容器关闭掉,当前类对象被Spring容器销毁
(2)多例 prototype:在一个spring容器,获取的实例对象不一致的(通过id标识符)
生命周期:对象的创建以及初始化是多次,每一次都需要通过无参构造方法创建对象,获取的实例对象不一致
实例化和初始化非一次,类加载一次就创建一个实例和初始化
对象的销毁,并非Spring容器,通过Jvm的垃圾回收器 回收没有更多引用的对象!
5.Servlet-mybatis_Spring整合(xml配置方式)
mybatis和Spring整合的时候,就不需要mybatis的核心xml了
操作步骤:
1)导包
spring和mybatis的整合包-->mybatis提供的-->mybatis-spring,版本2.0.6
spring提供的连接池jdbc的jar包,spring-jdbc.jar 版本5.2.5.RELEASE
2)在spring的xml中,进行整个,管理业务层,注入业务层信息--->服务层中不需要new对象,加入自定义持久层类型属性,提供set/get方法
3)在spring.xml文件中对服务层进行bean管理,set注入服务层属性
4)失去了持久层的核心配置文件,现在需要交给Spring代理,需要让Spring识别sqlSessionFactory,sqlsessionFactory需要加载映射文件,并且扫描持久层的接口
5)注入sqlsessionFactory需要加载映射文件
//mybatis和spring整合.管理sqlsessionFactory,提供了一个类:
//org.mybatis.spring.SqlSessionFactoryBea
这个类的属性有关联数据源,实名包扫描,加载映射文件
6)连接数据源
连接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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
//-进行spring和mybatis的整合,管理业务层以及持久层-->
// set注入进行bean管理-->
<bean id="shopService" class="com.qf.service.impl.ShopServiceImpl">
<property name="shopMapper" ref="shopMapper"></property>
</bean>
// 需要让spring识别sqlsessionFactory -->
// private String sqlSessionFactoryBeanName:内置属性 指定的SqlSessionFactory,这个id必须叫mapperScannerConfigurer-->
<bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
// private String sqlSessionFactoryBeanName:内置属性 指定的SqlSessionFactory-->
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
// 告知spring扫描持久层的mapper接口的内置属性
// private String basePackage;相当于扫描持久层-->
<property name="basePackage" value="com.qf.mapper"></property>
</bean>
// mybatis和spring整合.管理sqlsessionFactory,提供了一个类:
//org.mybatis.spring.SqlSessionFactoryBean
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
// 关联数据源-->
<property name="dataSource" ref="dataSource"></property>
// 实体类的别名包扫描-->
// 内置属性:private String typeAliasesPackage set方法注入进去-->
<property name="typeAliasesPackage" value="com.qf.pojo"></property>
// 配置映射文件的路径加载 扫描映射文件-->
// 内置属性:private Resource[] mapperLocations;
// set方法注入,路径必须是类路径下 value="classpath:/*Mapper.xml-->
<property name="mapperLocations" value="classpath:com/qf/mapper/*Mapper.xml"></property>
</bean>
<!--连接spring自带的数据池-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<!-- 加载数据源信息-->
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306//myee_2113"></property>
<property name="username" value="root"></property>
<property name="password" value="root"></property>
</bean>
</beans>
6.Spring常用的几个注解
6.1 创建对象的注解
spring提供了很多的注解
(1)一般创建对象的三个注解
1)关于对象创建的注解 @component:这个注解一般作用在类上,标记在谁上,就创建谁的对象,表示将类的创建交给spring容器管理,代替<bean id="xx" class="xxx">,如果不给类名,默认给当前类的类型(class文件)创建对象
使用:在 xml中添加扫描注解的头部文件和本地地址----通用
2)@Service--在业务层类上使用
3)@controller--在控制器层
4)@Repository---在数据访问层
6.2 注入的注解
(1)@Autowired:自动按照类型注入,前提是当前类型是唯一的,如果持久层有多个实现类,则不能使用
(2)@Qualifier:要结合autowired使用,通过唯一的标识注入,也就是当autowired不能使用时,在创建对象注解给上标识,在自动注入时,增加Qualifier("持久层的标识")
(3)@Resource(name="唯一的标识id"):单独使用,就是类似于bean标签中的唯一标签id
6.3 生命周期的注解
(1)@PostConstruct:代替了之前<bean init-method = "初始化方法名">
(2)@preDestroy:代替了之前的<bean detroy-method="销毁的方法名">
(3)@Scope(单例(默认)还是多例):
7.将Spring-mybatis-servlet整合xml优化,加入相关注解
注意:因为使用了注解方式,在servlet中,获取服务层的对象时,传递参数需要变为类名.class
shopService = (ShopService) classPathXmlApplicationContext.getBean(ShopServiceImpl.class);
在使用注解时,需要在spring的xml文件的表头文件中,加入 xmlns:context="http://www.springframework.org/schema/context"限定和本地地址
http://www.springframework.org/schema/context
http=://www.springframework.org/schema/context/spring-context.xsd
在xml文件中开启注解即可
<!--开启spring的包扫描,扫描com.qf下面的包下单有关的注解-->
<context:component-scan base-package="com.qf"/>
<!-- set注入进行bean管理-->
<context:component-scan base-package="com.qf"></context:component-scan>
//代替了bean标签的定位
<!--<bean id="shopService" class="com.qf.service.impl.ShopServiceImpl">-->
<!-- <property name="shopMapper" ref="shopMapper"></property>-->
<!--</bean>-->
7,1 ,使用德鲁伊连接池代替spring连接池
(1)导入外部资源文件-->jdbc.properties
<context:property-placeholder location="classpath:jdbc.properties"/>
(2)连接数据源
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
8.SpringAOP–横向切面技术
8.1 SpringAOP的定义
Aspect Oriented Programmar是一种面向切面编程,基于OOP完成(封装,继承,多态),他融合了jdk动态代理有一集cglib动态代理:不仅基于接口,也可以基于类来实现代理,但是默认使用jdk动态代理;
8.2 Aop的主要作用
(1)对逻辑叶落方法和非业务方法,进行分离(RUD--对各个方法进行增强)
(2)开闭原则:对修改关闭,对扩展开发!不修改先用代码,只需要改动Spring相关的配置文件即可
(3)降低了程序之间的耦合,后期维护更为方便
8.2 AOP开发术语
(1)连接点(joinpoint):当前业务的方法(增删查改
(2)切点(pointcurt):通过SpringAOP的配置切入到连接点(横向切面)
切点表达式--找到我们对应的业务层中的所有方法,都可以进行增强
(3)通知(advice):需要告知Spring执行什么通知进行切入
前置通知 BeforeHandleAdvice--->实现MethodBeforeAdvice接口
后置通知 AfterReturningAdvice-->实现AfterReturningAdvice接口
异常通知(thorwAdvice)出现问题了的通知类型-->实现了ThrowsAdvice接口
最终通知(afterAdvice)方法
环绕通知(methodInterceptor):将上面的通知融合到一块去--->实现MethodInterceptor接口
(4)织入:通过通知的类型,切入到连接点,创建代理对象的过程
(5)代理(proxy):织入之后,产生的结果(代理对象)
(6)切面(Aspect):切入到指定的连接点(自定义一个方法),将所有的通知放在一起,可以用多个切点形成一个切面
8.3 cg_lib动态代理
定义:属于动态代理的一种,基于子实现类实现(基于类实现,不需要接口)
(1)导包-->导入ch_lib的jar包,版本3.3.0
(2)创建增强类-->Enhance,底层要实现一个接口net.sf.cglib.proxy.InvocationHandler
(3)给增强类对象进行赋值,参数为真是角色的字节码文件
(4)设置回调对象:代表当前类对象的地址值引用
(5)创建当前角色对象
public class CglibHandler implements InvocationHandler {
//成员位置声明真实角色的变量 Object target
private Object target ;
//定义一个方法,直接在这个方法中产地代理角色
public Object bindObject(Object target){
//固定的写法
this.target = target ;
//cglib使用步骤
//1)创建增强类(核心类)
Enhancer enhancer = new Enhancer() ;
//2)给Enhancer增强类对象,进行赋值
//public void setSuperclass(Class superclass):参数:真实角色的字节码文件对象
enhancer.setSuperclass(target.getClass());
//3)设置回调对象:代表当前类对象的地址值引用 this
enhancer.setCallback(this) ;
//4)创建当前角色对象---
Object obj = enhancer.create();
return obj ;
}
//底层方法调用:
@Override
public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
//method:自己的业务方法---需要通过反射的方式调用 add/getName()
System.out.println("执行业务方法之前,校验权限");
Object obj = method.invoke(target, objects);//真实角色对象以及实际参数
System.out.println("执行业务方法之后,产生日志文件");
return obj; //和jdk动态代理一致的
}
}
测试类
public void test3(){
CglibHandler cglibHandler = new CglibHandler();
//创建子实现类对象
UserServiceImpl userServiceimpl = new UserServiceImpl();
//创建基于代理类的增强类对象
//调用增加的方法,返回子实现类对象
UserServiceImpl userService = (UserServiceImpl) cglibHandler.bindObject(userServiceimpl);
//调用方法
userService.add();
}
8.5 SpringAOP的操作步骤(xml配置)
(1)导入Spring的aspect的jar包,版本5.2.5.RELEASE
(2)添加aop的头部约束文件和本地库
xmlns:aop="http://www.springframework.org/schema/aop"
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
(3)自定义一个类,实现一个处理器-->springAOP提供的,可以作为前置通知,在执行业务之前,先进行前置通知的执行,继承自AOP中的接口MethodBeforeAdvice
(4)在xml中配置前置通知
<bean id="beforeHandler" class="com.qf.spring_aop_proxy.BeforeHandler"></bean>
(5)前置通知通过切点表达式切入到连接点中,进行业务方法增强
<aop:advisor advice-ref="beforeHandler" pointcut="execution(* *.*.service.impl.*.*(..))"></aop:advisor>
<aop:advisor>---通知切入标签
属性 advice-ref:关联通知类型
pointcut:切点
execution:切点表达式
pointcut="execution(* *.*.service.impl.*.*(..))" 切入到业务层方法中了
第一个* 是默认的
第二个* 就是com
第三个* 就是qf
第四个*:一般不写*,直接进入到业务层包名 service
第五个*:一把不写*,impl 包名
第六个*: 代表当前impl包下的类名 (类名不用给出来) 直接写*
第七个*:代表这个类下面的方法名,如果业务方法很多,直接写*
(..):不明确方法的参数
8.5.1 环绕通知的配置
配置方式是一样的
<bean id="myAround" class="com.qf.spring_aop_proxy.MyAround"></bean>//配置通知管理
<aop:advisor advice-ref="myAround"
pointcut="execution(* *.*.service.impl.*.*(..))"></aop:advisor>//配置切点
//通知类
public class MyAround implements MethodInterceptor {
//声明object变量
Object obj =null;
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
try{
//前置通知的方法
System.out.println("在添加用户之前,需要进行权限校验");
//调用业务数据,返回的是真实角色对象
obj = invocation.proceed();
//后置通知的方法
System.out.println("在添加用户之后,需要加载日志");
return obj;
}catch (Exception e){
//异常通知
System.out.println("出异常了");
}finally {
//最终通知
System.out.println("最后的通知...");
}
return null;
}
}
8.6 配置切面(由切点和通知组成)(xml配置方式)
(1)自定义一个切面类,将所有的增强类通过方法的形式定义出来
(2)在xml中配置切面
<aop:config>
<aop:aspect ref="切面类">
子标签:配置切点<aop:pointcut>--->expression("execution(切点表达式)")
配置通知<aop:before>,<aop:after-returning><aop:after-throwing><aop:around>
</aop:aspect>
属性:method="通知方法名"
pointcut-ref="切点的id标识"
</aop:config>
//切面类
package com.qf.spring_aspect;
import org.aspectj.lang.ProceedingJoinPoint;
public class MyAspect {
//将各类通知定义为方法即可
/* public void before(){
System.out.println("执行方法前");
}
public void after(){
System.out.println("执行方法后");
}
public void throwing(){
System.out.println("这是异常通知");
}*/
//参数:ProceedingJoinPoint:执行连接点的接口:执行的是业务层方法...
public Object myRound(ProceedingJoinPoint joinPoint){
Object target = null ;//产生的代理角色
try{
//要将其他通知关联进来
//before(); //前置通知
System.out.println("权限校验...");
//执行业务方法
target = joinPoint.proceed() ;//执行业务方法
// afterReturning(); //后置通知
System.out.println("产生日志");
return target ;
} catch (Throwable throwable) {
throwable.printStackTrace();//打印了
// afterThrwoing(); //异常通知
System.out.println("异常打印...");
} finally {
System.out.println("after最终.. 通知,");
}
return null ;
}
}
//xml配置
<bean id="myAspect" class="com.qf.spring_aspect.MyAspect"></bean>
<!--配置切面-->
<aop:config>
<!-- 组装切面,关联到切面的通知类-->
<aop:aspect ref="myAspect">
<!-- 将切点抽取出来 -->
<aop:pointcut id="mycut" expression="execution(* *.*.service.impl.*.*(..))"/>
<!-- 配置通知-->
<!-- <aop:before method="before" pointcut-ref="mycut"></aop:before>-->
<!-- <aop:after-returning method="after" pointcut-ref="mycut"></aop:after-returning>-->
<!-- <aop:after-throwing method="throwing" pointcut-ref="mycut"></aop:after-throwing>-->
<aop:around method="myRound" pointcut-ref="mycut"></aop:around>
</aop:aspect>
</aop:config>
8.7 springAOP的注解配置方式(还是aop的jar包)
(1)导包
spring-aspects,版本5.2.5.RELEASE
(2)添加头文件约束和本地库
xmlns:aop="http://www.springframework.org/schema/aop"
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
(3)@Aspect:标记切面的自定义类,标记为切面类
就相当于刚才在xml文件配置的 <aop:config><aop:aspect> </aop:aspect></aop-config>
(4)定义创建对象的注解,通知spring管理切面@component
因为自动注入是,相当于定位到切面类创建对象
(5)再切面类中再创建一个方法,定义注解@pointcut(切面表达式),这个方法就是切点表达式
相当于<aop:pointcut id="mycut" expression="execution(* *.*.service.impl.*.*(..))"/>
(6)在各个通知上配置注解-
-@Before("pt1()")
@AfterReturning("pt1()")
@AfterThrowing("pt1()")
@Around("pt1()")
(7)在xml中,开启包扫描
<context:component-scan base-package="com.qf.spring_aspect"/>
(8)开启SpringAOP的注解.开启切面模式自动代理
<aop:aspectj-autoproxy/>
/注解的方式:标记当前这个MyAspect这个类是一个切面类
@Aspect //就相当于刚才在xml文件配置的 <aop:config><aop:aspect> </aop:aspect></aop-config>
//要spring将切面进行管理
@Component
public class MyAspect {
// 自定义一个方法:
@Pointcut("execution("* *.*.service.impl.*.*(..)"))
public void pt1(){} //pt1就是切点表达式
//参数:ProceedingJoinPoint:执行的连接点的接口:执行的是业务层方法...
@Around("pt1()")
public Object myRound(ProceedingJoinPoint joinPoint){
Object target = null ;//产生的代理角色
try{
//要将其他通知关联进来
//before(); //前置通知
System.out.println("权限校验...");
//执行业务方法
target = joinPoint.proceed() ;//执行业务方法
// afterReturning(); //后置通知
System.out.println("产生日志");
return target ;
} catch (Throwable throwable) {
throwable.printStackTrace();//打印了
// afterThrwoing(); //异常通知
System.out.println("异常打印...");
} finally {
System.out.println("after最终.. 通知,");
}
return null ;
}
}
xml文件注解配置
//开启注解包扫描
<context:component-scan base-package="com.qf.spring_aspect"/>
//spring管理服务层
<bean id="userService" class="com.qf.service.impl.UserServiceImpl"></bean>
//开启aop自动代理,开启aop注解
<aop:aspectj-autoproxy/>
8.8 Spring和Junit整合
(1)导包-->spring-junit ,版本5.3.9
(2)junit注解:现在启动器使用的是Spring-test里面的启动器,
在测试类加入注解@RunWith(SpringRunner.class)
//加载类路径下的spring的核心配置文件
@contextconfiguration(classpath:spring_config.xml)//读取文件,加载spring所有注解
9.Spring管理事务–>提供Spring-tx.jar(事务管理器)
spring控制事务
(1)导包 spring-tx,jar ,版本5.2.5 release存在事务管理器
(2)xml配置--添加头部约束和地址
(3)引入事务管理器,在spring-jdbc的事务管理器中连接数据源
(4)管理数据源
<bean id="tx" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 连接数据源-->
<property name="dataSource" ref="dataSource"></property>
</bean>
(5)事务控制-->配置事务通知
//id:给事务起的名字 transaction-manager:连接事务的数据源
<tx:advice id="tx-update" transaction-manager="tx">
配置事务的属性
<tx:attribute>
<tx:method name="控制事务的方法名" propagetion="事务的传播行为,REQUIED:默认的,针对增删改必须使用事务控制""SUPPORTS:针对查询语句" read-only="true":代表只读 rollback-for="Exception":如果出现异常,直接回滚>
</tx:attribute>
</tx:advice>
(6)让springAOP控制事务管理
<aop:config>
<aop:advisor advice-ref="事务id" pointcut="切点表达式
</aop:config>
9.1 事务管理器的注解形式
(1)导包 spring-tx,jar ,版本5.2.5 release存在事务管理器
(2)xml配置--添加头部约束和地址
(3)引入事务管理器
(4)管理数据源
(5)在需要加入事务管理的类上或者方法上加上@trasactional(propagation=Propagation.REQUIED)
(6)在xml中开启事务注解,并且关联事务管理器
<tx:annotation-driven transaction-manager="tx"></tx:annotation-driven>
10.SpringMVC介绍以及它的执行流程
定义:是spring框架提供的一个产品,可以快速构建web应用程序,针对mvc架构可以融合
11.SpringMVC的使用
(1)导包-->引入spring-webmvc.jar,版本5.3.9
并且大war包,<packaging>war</packaging>
(2) 编写控制器(DispatcherServlet:前端控制器(中央处理器)):springmvc本质就是一个servlet
(3)需要在web.xml文件去配置springmvc--前端控制器-->spring的监听器,spring-webmvc提供的, 项目一启动,通过ContextLoaderListener加载类路径下的springmvc.xml
前提:添加头部约束文件
3.1)配置全局参数
<context-param>
<param-name>contextConfigLocation</param-name>
<!--加载springmvc.xml-->
<param-value>classpath:springmvc.xml</param-value>-->监听的类路径下springmvc.xml
</context-param>
3.2)serlvet基本配置
<servlet>
<servlet-name>dispatcherServlet</servlet-name>--名字自己起
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>-->不需要变动
<param-value/>
</init-param>
//配置加载时机,当项目已启动,就直接创建我们的前端控制器 DispatcherServlet
<load-on-startup>1</load-on-startup>
</servlet>
3.3)servlet的映射配置
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>-->映射到springmvc.xml中
// 前端控制器 处理所有的请求
<url-pattern>/</url-pattern> /代表全部
</servlet-mapping>
(4)配置springmvc.xml.加入aop和mvc的头文件约束和地址
(4.1) 扫描spring相关的注解
<context:component-scan base-package="com.qf.controller"/>
(4.2)启动springmvc的注解,自动加载控制器里面处理器映射器 /处理器适配器
<mvc:annotation-driven/>
(4.3)配置springmvc视图解析器,set方法注入两个属性,private String prefix = ""; 前缀
private String suffix = ""; 后缀
<mvc:view-resolvers>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
</mvc:view-resolvers>
(5)编写控制器,@controller标记为表现层--称为表现层-->需要调用service层-->需要调用mapper/dao持久层
(6)在方法上加上注解@RequestMapping("/别名")--相当于自定义servlet中的方法
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">
<!-- 开始配置前端控制器-->
<!-- 配置spring的监听器,只要项目一启动,通过ContextLoaderListener加载类路径下的springmvc.xml-->
<!-- 引入监听器contextConfigLocation-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!--开始配置全局参数,spring的监听器-->
<context-param>
<param-name>contextConfigLocation</param-name>
<!-- 初始化springioc容器,扫描spring的xml文件,地址是类路径下的spring.xml-->
<param-value>classpath:springmvc.xml</param-value>
</context-param>
<!-- 配置servlet,就是前端控制器DispatcherServlet,名称必须和映射名称一致-->
<servlet>
<servlet-name>DispacteryServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 初始化参数-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value/>
</init-param>
<!-- 还要web容器已启动,就创建前端控制器的对象-->
<load-on-startup>1</load-on-startup>
</servlet>
<!-- 配置servlet的一个映射路径-->
<servlet-mapping>
<servlet-name>DispacteryServlet</servlet-name>
<!-- 跟servlet配置一样,映射路径需要指定到spring管理的具体路径中,/代表所有-->
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
springmvc.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:aop="http://www.springframework.org/schema/aop"
xmlns:mvc="http://www.springframework.org/schema/mvc"
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
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">
<!-- springMVC是spring的后续产品.会额外提供其他注解-->
<!-- 开启spring的注解包扫描,创建对象-->
<context:component-scan base-package="com.qf.controller"/>
<!-- 开启springmvc的注解,自动加载前端控制器里边的映射器,适配器-->
<mvc:annotation-driven/>
<!-- 配置试图解析器-->
<mvc:view-resolvers>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 注入配置前缀和后缀-->
<property name="prefix" value="/WEB-INF/jsp/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
</mvc:view-resolvers>
</beans>
11.1 SpringMVC 的三大组件
前端控制器(中央处理器):dispactcherServlet
处理器映射器:RequestMappingHandlerMapping
处理器适配器:RequestMappingHandler
11.2 SpringMVC怎么传参
方式一:路径携带参数请求(当前方法的形参的参数名称需要和地址栏上携带的参数名称一致)
解析日期格式的注解@DateTimeFormat(pattern="yyyy-mm-dd")
通过定义一个方法,带上形式参数,默认获取参数
方式二:路径携带参数请求
方法形参是HttpServletRequest对象,利用ServletAPI解决
方式三:前端路径携带参数,但是后端方法中的形参和携带的方法名不一致的情况
解决:使用springmvc提供的注解@RequestParam(参数绑定)
在每一个新参前面进行参数绑定
@RequestParam("路径参数名称") 形式参数
方式四:定义实体类pojo,封装实体类
在方法中直接将实体类做为形式参数,但是实体类的属性名称必须和路径携带参数名称一致
方式五:路径直接"传递参数"--?url
在方法的注解上RequestMappings追加数据("/{值1}/{值2}/{值3}/.....")
使用springmvc注解@pathVariable Integet id-->类似参数绑定
地址栏带参:http://localhost:8080/springmvc_param_war/requestparam/third/1/%E5%88%98%E5%AE%9D%E5%AF%BF/123456
方法六:如果实体是一个集合(属于路径携带参数)(tomcat的版本不能太高,否则400错误)
http://localhost:8080/SpringMVC_02_war/requestData/sevenReq?userList[0].id=1&userList[0].username=xx &userList[0].address=xxx&userList[0].time=xx
400错误解决:修改tomcat的server.xml配置文件配置文件
给port="8080" 后面配置三个属性,
urlEcnoding="utf-8"
relaxedPathChars="|{}[],%"
relaxedQueryChars="|{}[],%"
方式7:前端提交方式是一个表单
方法的形式参数为数组的形式参数,并且数组的变量名必须和表单的选项的name属性一致
方式8:当前台提交的数据是json形式,后台需要将他封装到实体类中
(1)导包,springmvc可以解析json数据
jackjson的三个包
(2)必须在当前形式参数上加入注解@RequestBody
(3)当前提交方式必须是POST
(4)发送的json数据的属性名称必须和实体类保持一致
11.3 SpringMVC响应数据的方式
第一种方式:传统方式,通过servlet-api(HttpRequest)完成
request.setAttriibute("名称","内容")
第二种方式:返回还是string,方法形参是Model model-->类似于域对象
model.addAtrribute("名称",Object);在model中存贮数据
第三种方式:返回值是字符串,方法形参是Session
session.setAtrribute();
第四种方式:返回值是modelandview:试图模型
(1)创建试图模型对象
ModelAndView modelAndView = new ModelAndView() ;
(2)设置模型数据// addObject(String attributeName, Object attributeValue)
modelAndView.addObject("money",13999) ;
(3)设置返回视图---->交给前端控制器DispatcherServlet ---借助于它来请求转发
//public void setViewName( String viewName) :参数为视图的路径
modelAndView.setViewName("success")
return ModelAndView;
第五种方式:返回数据是json数据,需要导入三个jar包,实现json的前后端交互
jackson-annotations-2.4.0.jar、jackson-core-2.4.2.jar、jackson-databind-2.4.2.jar
(1)jackson提供的springmvc注解@ResponseBody:将现在的实体类对象转换成json,在方法名上添加@ResponseBody,即可返回值是一个json格式
(2)如果前台发送的数据也是json格式,则在形式参数前面加入注解@ResponseBody
11.3.1 过滤器的使用
(1)在web.xml文件中配置过滤器,解决全局中文乱码
<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>
(2)在springmvc中开启过滤器,释放html,css等静态资源,解决全局中文乱码
<mvc:default-servlet-handler/>
11.4 SpringMVC提供拦截器(Interceptor)
过滤器的配置:注解,在springmvc中开启过滤器注解<mvc:default-servlet-handler/>:释放html,css等静态资源
拦截器的使用步骤:
(1)自定义一个类实现HandlerInterceptor,重写Prehandler,posthandler,aftercompletion方法
(2)定义拦截业务-->preHandler
定义拦截的业务方法,return-->true则表示放行,false则表示拦截
(3)定义posthandler,表示控制器执行相关的方法之后执行的
(4)定义aftercompletion,表示在执行完posthandler之后执行的,进行渲染试视图
注解形式:在springmvc中加入注解
<!--告知springmvc拦截器信息-->
<mvc:interceptors>
<!--可以 配置多个拦截器-->
<mvc:interceptor >
<!--拦截路径-->
<!--拦截所有-->
<!-- <mvc:mapping path="/**"/>-->
<!--拦截指定的路径-->
<mvc:mapping path="/student/findAll"/>
<!--放行执行的路径-->
<mvc:exclude-mapping path="/admin/login"></mvc:exclude-mapping>
<!--管理自定义的拦截器-->
<bean class="com.qf.interceptor.MyInterceptor"></bean>
</mvc:interceptor>
</mvc:interceptors>
11.5 SpringMVC图片上传(本地上传,云端上传(七牛云存储))
1)准备前端的上传组件 input type="file"--->上传文件的表单标签,表单必须是post提交,并且必须带上表单中的重点属性enctype="multipart/form-data",这样路径就可以携带文件的名称
<form action="/SpringMVC_03_war/QiNiuYunpload/img" method="post" enctype="multipart/form-data">
上传图片:<input type="file" name="file"/><br/> <%--会携带file参数到后台地址上--%>
<input type="submit" value="上传" />
</form>
2)导入jar包:commons-io.jar和upload-file.jar,创建一个controlle,实现本地上传的业务
3)在springmvc.xml中配置图片的上传组件
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="10485760"></property>--->最大上传内存大小,按毫秒值计算
</bean>
public String uploadImg(@RequestParam("file") MultipartFile multipartFile, HttpSession session) throws IOException {//这个里面multipartFile 绑定参数需要和前台提交的图片的name属性一致
//springmvc--提供接口 里面的方法:
//InputStream getInputStream() throws IOException;
//获取图片源文件 (文件包括后缀)
String originalFilename = multipartFile.getOriginalFilename();
System.out.println(originalFilename); //高圆圆.jpg
//图片是不同的,但是图片名称一致的,下一次再次去上传,会上第一次图片覆盖
//使用UUID工具类产生唯一的图片的名称
String fileName = UUID.randomUUID().toString();
//通过原生文件获取它的后置.jpg 的jpg
//FilenameUtils工具类 --commons-io,获取原始文件名称的后缀名称
//参数:原始文件
String ext = FilenameUtils.getExtension(originalFilename);
//组装一个新的文件
String newFileName = fileName+"."+ext ;
//将上传的文件存储到当前项目下的upload_file中
//四个域对象:前三个都可以获取全局对象ServletContext,也就是当前项目的绝对路径
String realPath = session.getServletContext().getRealPath("/upload_file");
System.out.println(realPath);
//上传操作
//MultipartFile有个上传的方法transferTo:将文件上传到指定路径中
multipartFile.transferTo(new File(realPath+"\\"+newFileName));//上传的路径
return "upload_ok" ;
}
11.5.1 七牛云上传
(1)自定义一个工具类,将本地磁盘上的图片存储七牛云(服务器),返回值是使用生成的域名+唯一的图片的名称
(2)将七牛云文件上传-->通过数据流上传-->工具类复制下来,并且修改自己的秘钥
(3)自定义一个controller,调用工具类,实现从本地仓库的图片上传到七牛云
public class UploadUtils {
private UploadUtils(){}
/*
* 上传 方法 将本地磁盘上的图片存储七牛云(服务器)
*
* 返回:使用生成的域名+唯一的图片的名称
* */
public static String uploadFile(MultipartFile multipartFile){ //使用数据流 ---将图片上传
//使用七牛云---sdk完成 上传 ---查看七牛云开发文档
//数据流的上传的代码--(七牛云的)
//构造一个带指定 Region 对象的配置类
Configuration cfg = new Configuration(Region.region0()); //huadong() //华东区域 (云存储的区域地址(服务器))
//...其他参数参考类注释
//上传管理器,将Configuration进行解析
UploadManager uploadManager = new UploadManager(cfg);
//...生成上传凭证,然后准备上传
//访问到七牛云的密钥(个人中心自己的)
String accessKey = "yGotxUejuad1rVeY4Cc7kkgZ5HTQwSU1Yqdc90II"; //访问密钥
String secretKey = "kofIM0oF8UtBkN398ZUEmOymJh9up3-9bi-EtO6F";//安全密钥
//等会需要将本地图片上传到七牛云指定个存储空间,七牛云的存储空间名称
String bucket = "javaee-2113-upload";
//默认不指定key的情况下,以文件内容的hash值作为文件名
String key = null;
try {
//身份认证:将上面的配置好的访问密钥,和安全密钥封装到Auth
Auth auth = Auth.create(accessKey, secretKey);
//生成upToke="令牌字符串" 里面有空间名称以及accessKey, secretKey都在里面
String upToken = auth.uploadToken(bucket); //使用认证将指定的图片存储空间名称上
//使用uploadManager上传管理器上传
// public Response put(InputStream stream, String key, String token, StringMap params, String mime)
//参数1:上传文件的字节流--->使用自己文件的字节流
//参数2:哈希字符串
//参数3:upToken 密钥+空间名称 字符串
//参数4:其他参数
//参数5:文件类型
Response response = null;
response = uploadManager.put(multipartFile.getInputStream(),key,upToken,null, null);
//解析上传成功的结果
DefaultPutRet putRet = new Gson().fromJson(response.bodyString(), DefaultPutRet.class);
System.out.println(putRet.key);
System.out.println(putRet.hash);//文件名
//返回字符串
//http://r9c3yzgt4.hd-bkt.clouddn.com/ 外链 +文件名
return "http://r9c3yzgt4.hd-bkt.clouddn.com/"+putRet.hash ;
} catch (Exception e) {
e.printStackTrace();
}
return null ;
}
}
@Controller
@RequestMapping("/QiNiuYunpload")
public class QiNiuYunUploadController {
@RequestMapping("/img")
public String uloadFile(@RequestParam("file") MultipartFile multipartFile, Model model){
//使用自定义工具类,用到了七牛云的sdk,获取到七牛云的存储地址-->全限定名称
String uploadFile = UploadUtils.uploadFile(multipartFile);
//存储Model中
model.addAttribute("imgUrl",uploadFile) ;
return "upload_ok" ;
}
}
11.6 SpringMVC的resultFul风格(加入新的注解(@RestController))
前后端分离时,前端传过来和相应给前端的都是json格式,springmvc提供了很多的注解
@RestController //包含两个注解:@Controller@ResponseBody :标记这个控制器中每一个方法都返回json格式 @PostMapping("/login")等价于@RequestMapping(value="/login",method=RequestMethod.post)
12 验证码–githup提供–>本质还是一个servlet
(1)导包
<dependency>
<groupId>com.github.penggle</groupId>
<artifactId>kaptcha</artifactId>
<version>2.3.2</version>
<!--里面包含了servlet.jar包
加载这个包的时候,过滤里面servlet.jar包
-->
<exclusions>
<exclusion>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
</exclusion>
</exclusions>
</dependency>
(2)配置web.xml
<servlet>
<servlet-name>cap</servlet-name>
<servlet-class>com.google.code.kaptcha.servlet.KaptchaServlet</servlet-class>
<init-param>
<param-name>kaptcha.border</param-name>
<param-value>no</param-value>
</init-param>
<init-param>
<param-name>kaptcha.textproducer.char.length</param-name>
<param-value>4</param-value>
</init-param>
<init-param>
<param-name>kaptcha.textproducer.char.string</param-name>
<param-value>abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789</param-value>
</init-param>
<init-param>
<param-name>kaptcha.background.clear.to</param-name>
<param-value>211,229,237</param-value>
</init-param>
<init-param>
<!-- session.setAttribute("captcha","验证码") -->
<param-name>kaptcha.session.key</param-name>
<param-value>captcha</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>cap</servlet-name>
<url-pattern>/captcha</url-pattern>
</servlet-mapping>
(3)在jsp中设置验证码即可
验证码:<input type="text" name="captcha" />
<img src="${pageContext.request.contextPath}/captcha" id="img" width="80px" height="20px"/><br/>
</div>
设置点击事件,刷新图片
$(this).attr("src",path) ;*/
this.src="${pageContext.request.contextPath}/captcha?time="+new Date().getTime() ;
13.spring和springmvc的整合
在springmvc.xml文件中导入spring.xml即可
<import resource="classpath:spring-config.xml"/>