Spring框架
Spring框架
学什么: IOC(控制反转) DI(依赖注入) AOP(面向切面编程){事物处理}
Spring是? 作用? 入门案例 注解开发
是:多个框架技术的统称,每个具体的技术都可以完成不同的功能
作用:1.可以简化代码开发;
2.可以将代码中创建对象的行为进行控制反转,从而解决代码的耦合度;
3.可以自动管理对象与对象之间的依赖关系;(对于对象来说,就是依赖注入)
SpringFramework框架是:其他功能的基础;使用的回收需要导入Spring-context的坐标,并创建IOC容器;
核心容器IOC: 中文名叫控制反转,就是程序中所有创建对象的行为,不能再使用关键字new创建对象了,
统一交给Spring的ioc容器负责创建对象并保存对象,当我们的程序中需要使用到具体某个对象的时候,
直接ioc容器中获取对象即可;(ioc容器相当于是一个map集合)
入门案例: 1.导入Spring-context的坐标;
2.编写Spring的核心配置文件,并指定哪些类需要交给Spring管理;
3.编写测试的接口和具体的实现类;
4.编写测试类,从ioc容器中获取对象并使用;
![]()
四、 DI中文意思是:依赖注入,就是当某个对象的成员变量位置,需要其他的对象或数据的时候,可以让Spring自动的给
这个成员变量注入值;
如果是:简单类型,使用value;
如果是:自定义引用数据类型,使用ref,ref的值是和Spring管理的某个bean的id一致;
![]()
有关配置文件的配置介绍:
bean的name属性:可以给同一个对象,起多个名字,以便于通过多种方式获取该对象;
bean的scope:可以控制该对象是单列还是非单列的
scope="prototype"原型,非单利的,每创建一个对象开辟一个空间
scope="singleton"单列,spring默认的是单利的
自动装配意思:告诉spring,某个类中的成员变量,让spring自动把值注入进去;
代码实现:直接在bean的标签中添加一个:autowire="byType"属性即可;
要求被装配的bean必须唯一 !
依赖自动装配特征:1.自动装配用于:引用类型依赖注入,不能对简单类型进行操作
2.使用按类型装配时(byType)必须保障容器中相同类型的bean唯一,推荐使用
3.使用按名称装配时(byName)必须保障容器中具有指定名称的bean,
因变量名与配置耦合,不推荐使用
4.自动装配优先级低于setter注入与构造器注入,同时出现时自动装配配置失效
spring给成员变量注入集合和数组类型的数据:
![]()
框架整合:MyBatis Mybatis-plus SpringMVC .....
五、 第三方资源配置管理:
管理第三方bean-Druid:1.导入Druid的连接池坐标;
2.在pring的核心配置文件中,配置bean;
3.给bean中的4个成员变量注入值;
4.在测试类中通过ioc容器获取bean对象即可;
![]()
1.管理DataSource连接池对象:
2.加载properties属性文件【重点】 jdbc.properties:符号前面的吗名字相当于是map的key,可以自定义,符号后面的数据,是属于连接数据库时需要的信息,不能乱码
【第二步】:
使用spring加载properties配置文件:1.在spring的核心配置文件中开启(context)命名空间
2.在核心配置文件中,指定properties文件的路径
3.在bean标签中,所有使用value注入值的地方,都可以通过${配置文件中的key}
获取对应的值
4.获取ioc,测试 ;
【第一步】编写jdbc.properties属性文件
jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://127.0.0.1:3306/spring_db jdbc.username=root jdbc.password=root
小技巧:如果觉得上述复制粘贴方式不好改或者容易改错,其实idea是有提示功
能的,注意不要选错就行了。
有些版本的idea没有这个提示,那么就按照上面复制粘贴的方式改,
改完之后可以做成live template模板,后期直接用。
【第二步】在applicationContext.xml中开启开启context命名空间,
加载dbc.properties属性文件
【第三步】在配置连接池Bean的地方使用EL表达式获取jdbc.properties属性文件
中的值
<bean class="com.alibaba.druid.pool.DruidDataSource"><property name="driverClassName" value="${jdbc.driver}"/><property name="url" value="${jdbc.url}"/><property name="username" value="${jdbc.username}"/><property name="password" value="${jdbc.password}"/> </bean>
注意事项:配置不加载系统属性
问题:如果属性问价你中配置的不是jdbc.username,
而是username=root666,那么使用${username}获取到的
不是root66,而是计算机的名称。
原因:系统属性的优先级比我们属性文件中的高,
替换了我们的username=root666。
解决1:换一个名称 ,例如不叫username,叫jdbc.username。
解决2:使用system-properties-mode="NEVER"属性表示不使用系统属性
加载 properties文件写法标准格式:<context:property-placeholder location="classpath:*.properties"/>
spring半注解开发:1.在核心配置文件中,开启注解扫描的功能
2.在指定的包的类中添加注解:@Controller @service @Repository
3.如果涉及到简单类型的数据,可以直接使用@Value("注入的值")
4.如果涉及到引用数据类型的数据,可以直接使用@Autowired按类型自动注入;
spring提供的component三个衍生注解:
@Component:用于表现层bean定义
@service:用于业务层bean定义
@Repository:用于数据层bean定义
spring的纯注解开发:
不需要编写spring的核心配置文件, 直接通过一个配置类,替代核心配置文件文件即可;
1.自定义一个核心配置类,替代核心配置文件;
2.在指定的包中定义的类的上面添加指定的注解
3.在测试类中获取ioc容器的时候,使用的是AnnotationConfigApplicationContext即可;
注解管理单列@Scope("prototype")
![]()
生命周期 :
@PostConstruct 创建对象的时候,会执行带有这个注解的方法
@PreDestroy 销毁对象的时候,会执行带有这个注解的方法![]()
指定类型或指定名称
注解加载properties配置文件和依赖注入
简单类型使用 @Value(数据值) @Value(${配置文件中的key})
自定义类型 @Autowired 如果类型不唯一,可以指定按名称装配 @Qualifier("abc")// 明确的指定按名称注入,这里面的名称必须和dao中配置的名称一致
在配置类的上面通过@PropertySource({"classpath:jdbc.properties"})可以加载配置文件
Spring整合junit:
好处:使用spring整合junit之后,我们无需自己创建ioc容器对象了,可以直接在测试中注入业务层对象,
从而在测试方法中使用注入的对象直接调用方法,简化spring操作
步骤:1.导入两个坐标;
2.在测试类的上面添加两个注解;
@RunWith(SpringJUnit4ClassRunner.class) :告诉spring这个测试类希望spring管理
@ContextConfiguration:告诉spring核心配置类是谁
3.在测试中注入业务层对象:直接设计成员变量,使用@Autowired注解即可;
4. 在测试方法中直接使用业务层对象调用方法即可;@Test:测试业务层方法的代码
dao数据层和业务层搭建及配置文件:
![]()
注意事项:junit的依赖至少要是4.12版本,可以是4.13等版本,否则出现如下异常:
![]()
Spring整合mybatis:
好处:所有有关联mybatis的代码,包括事务管理,都可以让spring处理,简化mybatis代码
步骤 :1.导入坐标;
2.写配置类,在类中定义两个方法,
分别返回:SqlSessionFactoryBean,MapperScannerConfigurer
3.在测试类中测试即可;
![]()
简单的javabean-->dao层数据层接口-->service业务层接口和业务层实现类
测试类注入:核心配置类,然后核心配置类注入:配置文件、扫描包、找代码
![]()
流程分析示意图:
![]()
代码:spring整合mybatis创建带驼峰和分页的bean
//spring整合mybatis的类 public class MyBatisConfig {private String javaBeanPackage="com.sprpro.demo02_Anno_MyBatis.pojo";private String mapperPackage="com.sprpro.demo02_Anno_MyBatis.dao";//自定义一个方法,返回mybatis的核心对象交给ioc容器即可整合成功@Beanpublic SqlSessionFactoryBean mybatis(DataSource ds){// 1: 创建用于创建SqlSessionFactory的对象SqlSessionFactoryBeanSqlSessionFactoryBean bean = new SqlSessionFactoryBean();// 2: 给SqlSessionFactoryBean 设置数据源;称连接池bean.setDataSource(ds);//DataSource:数据源// 3: 给SqlSessionFactoryBean 设置扫描javabean所在的包:称别名bean.setTypeAliasesPackage(javaBeanPackage);// 4: 给SqlSessionFactoryBean 设置其他插件,例如: 分页插件,驼峰命名等// 4.1: 驼峰命名Configuration con = new Configuration();con.setMapUnderscoreToCamelCase(true);bean.setConfiguration(con);// 4.2: 分页插件// 4.2.1: 准备插件需要的参数Properties p = new Properties();// 4.2.2: 开启分页插件的固定写法p.setProperty("value","true");// 4.2.3: 创建分页插件对象PageInterceptor page = new PageInterceptor();// 4.2.4: 给分页插件设置参数page.setProperties(p);// 4.2.5 : 将分页插件交给SqlSessionFactoryBean管理bean.setPlugins(new Interceptor[]{page});// 5: 返回准备好的SqlSessionFactoryBean,spring的ioc管理 SqlSessionFactoryBean的时候,会自动从SqlSessionFactoryBean中获取SqlSessionFactoryreturn bean;}// 专门处理扫描mybatis的接口映射的对象@Beanpublic MapperScannerConfigurer mapper(){//1:创建阿里的druid连接池信息MapperScannerConfigurer mapper = new MapperScannerConfigurer();// 2: 设置扫描的接口包mapper.setBasePackage(mapperPackage);return mapper;} }
AOP:
OOP强调是所有的事情都让对象去做,而AOP强调的是我可以拿刀把原来的代码切开,在切开刀刃里面加一些东西
是:一种面向切面编程的思想;用于做升级可以用
作用:可以在已经写好的功能的基础上,不改变原有代码的前提下,利用动态代理的技术对原有功能进行增强;
术语:1.连接点:接口中的任意一个方法,都可以叫连接点;例如:update()、delete()、select()等都是连接点
2.切入点:进行功能增强了的方法,例如:update()、delete()、select()方法没有被增强所以不是切入点,
但是是连接点。切入点也可以叫做切点;
在SpringAOP中:一个切入点可以只描述一个具体方法,也可以匹配多个方法
一个具体方法:com...dao包下的BookDao接口中的无形参无返回值的save方法
匹配多个方法:所有的save方法,所有的get开头的方法,所有以Dao结尾的接
口中的任意方法,所有带有一个参数的方法
3.通知:在切入点前后执行的操作,也就是增强的共享功能
在SpringAOP中,功能最终以方法的形式呈现
4.通知类:通知方法所在的类叫做通知类
5.切面:描述通知与切入点的对应关系,也就是哪些通知方法对应哪些切入点方法。
6.织入:把通知添加到切入点(切点)的行为,叫做织入;
切入点(Pointcut) 通知(Advice) 切面(Aspect)
代码步骤: 1.导入坐标
2.编写通知类
3.在通知类中编写切点和通知及切面
4.测试类测试即可
例如代码如下:
准备工作
/*这是一个通知类,需要让spring管理*/ @Component @Aspect// 告诉spring,这是一个通知类 public class MyAd {// 通知类// 描述切点 格式: 方法是什么样的返回值 哪个包哪个类哪个方法带什么参数@Pointcut("execution(* com...demo03_Anno_AOP.service.AccountService.selectAll())")public void qieDian(){}// 写一个具体的增强的内容,(通知)@Before("qieDian()") // 切面,让我们自定义的myZQ方法和上面定义的切点产生绑定,这个关系就是切面public Object myZQ(){System.out.println("我要增强了:"+System.currentTimeMillis());return null;//currentTimeMillis获取的时间} }
AOP注解:@EnableAspectJAutoProxy:开启AOP注解功能 @Aspect:通知类注解 @Pointcut:切入点注解
@Before:自定义切面注解;执行之前先要干的事情
@Around表示,同时方法带有一个形参: ProceedingJoinPoint
AOP环绕通知:
使用:@Around表示,同时方法带有一个形参:ProceedingJoinPoint
可以通过ProceedingJoinPoint对象的getArgs方法,获取原始方法的实际参数,从而对原始方 法的进行修改
可以通过ProceedingJoinPoint对象的proceed方法,让原始方法真正的执行起来;从而控制原 始方法的执行时机
可以通过...对象的proceed方法的返回值,对返回值做任意的修改,从而控制原始方法的返回结果
最终达到的效果就是环绕通知可以控制原始方法的参数,返回值,异常,执行时机等;
参考代码: 返回值类型:*、包名:com..、类名:*service+、方法名:*、参数..
![]()
//这是一个通知类,需要让spring管理 @Component @Aspect public class MyAd {// 通知类// 描述切点 格式: 方法是什么样的返回值 哪个包哪个类哪个方法带什么参数//@Pointcut("execution(* com.sprpro.demo03_Anno_AOP.service.AccountService.selectAll())")// * 表示返回值类型是任意的// com.. com包下的任意层// *Service+ 表示类名以任意单词开头,中间必须包含service,后面必须还有1至多个字符// *(..) 表示任意方法名和任意参数个数和类型@Pointcut("execution(* com..*Service+.*(..))")public void qieDian(){}// 写一个具体的增强的内容,(通知)@Around("qieDian()") // 切面,让我们自定义的myZQ方法和上面定义的切点产生绑定,这个关系就是切面public Object myZQ(ProceedingJoinPoint pjp) throws Throwable {System.out.println("我要增强了:"+System.currentTimeMillis());// 1: 通过pjp获取要增强的方法的原始参数Object[] args = pjp.getArgs();// 2: 对参数的类型进行判断,如果是Account类型,就将里面的名称和金额进行修改if (args!=null){for (int i = 0; i < args.length; i++) {// 判断数组中的每一个元素是不是Account类型if(args[i] instanceof Account){System.out.println("参数增强了....");Account a = (Account) args[i];a.setName(a.getName()+"嘿嘿");a.setMoney(a.getMoney()*10);args[i] = a;}}}Object proceed = null;try {// 3: 让原始方法执行proceed = pjp.proceed(args);if(proceed!=null){if(proceed instanceof Account){System.out.println("对返回值增强了....");Account a = (Account) proceed;a.setName(a.getName()+"嘿嘿结果");a.setMoney(a.getMoney()/100);}}} catch (Throwable e) {e.printStackTrace();System.out.println("原始方法出异常了,在这里补救....");}return proceed;} }
案例:
需求:在不改变原始service和dao代码的情况下,完成对业务层方法执行一万次的用时统计;
分析:可以使用aop思想,利用环绕通知,循环调用方法1万次,且方法执行前后,分别记录系统时间,用时间差就可以算出程序的万次执行效率了;
//这是一个通知类,需要让spring管理 @Component @Aspect public class MyAd {// 通知类//切点表达式,用于描述什么样的方法,可以成为切点@Pointcut("execution(* com..*Service+.*(..))")public void qieDian(){}// 写一个具体的增强的内容,(通知)@Around("qieDian()") // 切面,让我们自定义的myZQ方法和上面定义的切点产生绑定,这个关系就是切面public Object myZQ(ProceedingJoinPoint pjp) throws Throwable {System.out.println("我要增强了:"+System.currentTimeMillis());// 1: 通过pjp获取要增强的方法的原始参数Object[] args = pjp.getArgs();// 2:不修改参数//获取原始方法的签名,通过签名获取正在执行的类和方法//获取执行的签名对象Signature signature = pjp.getSignature();//获取接口/类全限定名String className = signature.getDeclaringTypeName();//获取方法名String methodName = signature.getName();long t1 = System.currentTimeMillis();Object proceed = null;for (int i = 0; i < 10000; i++) {proceed = pjp.proceed(args);}long t2 = System.currentTimeMillis();// 3:在方法执行之前,记录当前系统时间,在方法执行之后,再次记录时间,两个时间差就是方法运行的时间//4:不修改返回值,直接返回方法执行的结束//System.out.println("执行一万次用时:"+(t2-t1)+"毫秒");System.out.println("执行"+className+"中的"+methodName+"一万次用时:"+(t2-t1)+"毫秒");return proceed;} }
Spring事务:
是:认为规定的一组操作,这组操作中可以包含多个动作,这些动作应该保证要么全部成功,要么全部失败,
事务就是专门做这个事情的;
相关类和接口:
Spring管理事务使用的是:PlatformTransactionManager接口;
而mybatis管理事务使用的是:DataSourceTransactionManager类;
事务管理的思路:
由于DataSourceTransactionManager类已经实现了PlatFormTransactionManager接口,
所以我们只需要将DataSourceTransactionManager以第三方bean的形式交给ioc容器管理,
再配合spring事务相关的注解即可完成事物的控制了;
代码实现步骤:
1.自定义一个专门加载DataSourceTransactionManager对象的配置类,并在核心配置类上
导入该配置类;
2.在核心配置列的上面使用@EnableTransactionManagement开启事务支持;
(代码参考上面的MySpringConfig)
3.在业务层接口中,想要开启事务的方法上面添加@Transactional注解即可;
(方法出运行时异常会自动回滚,不出异常会自动提交)
转账案例:价钱和减钱
1.config包里配置创建个(加载对象的配置类)
//2.注入平台事务管理器 固定 //专门处理mybatis的事务交给ioc容器的配置类 public class ShiWuConfig {@Bean//Spring管理事务使用的是:PlatformTransactionManager接口;public PlatformTransactionManager shiWu(DataSource ds){//1:创建mybatis的事务管理器对象DataSourceTransactionManager manager = new DataSourceTransactionManager();//2:给事务管理器设置一个连接池manager.setDataSource(ds);//3:返回事务管理器return manager;} }
2.在核心配置列的上面使用@EnableTransactionManagement开启事务支持;
@Configuration @ComponentScan("com.sprpro.demo06_Anno_ShiWu") @PropertySource({"classpath:jdbc.properties"}) @Import({DruidConfig.class, MyBatisConfig.class, ShiWuConfig.class}) //@EnableAspectJAutoProxy//开启AOP注解功能 @EnableTransactionManagement//开启事务 public class MySpringConfig { }
dao层接口层:
public interface AccountMapper {@Select("Select * FROM tbl_account")List<Account> selectAll();@Insert("insert into tbl_account values (null,#{name},#{money})")void insertAccount(Account a);@Select("select * from tbl_account where id=#{id}")Account selectById(Integer id);@Update("UPDATE tbl_account SET money=money+#{money} WHERE name = #{name}")void inMoney(@Param("name") String name, @Param("money") Double money);@Update("UPDATE tbl_account SET money=money-#{money} WHERE name = #{name}")void outMoney(@Param("name") String name, @Param("money") Double money); }
service层:业接口务层
3.在业务层接口中,想要开启事务的方法上面添加@Transactional注解即可;
(方法出运行时异常会自动回滚,不出异常会自动提交)
public interface AccountService {// 该接口中包含了两个连接点List<Account> selectAll();// 由于该方法符合切点表达式的描述,所以该方法就是切点void insertAccount(Account a);// 该方法只是一个普通的连接点而已Account selectById(Integer id);//转账的方法@Transactionalvoid trans(String inname,String outname,Double money); }
service层:业务实现类层
// 要加注解,让spring直接管理 @Service public class AccountServiceImpl implements AccountService {//设计一个dao的对象@Autowiredprivate AccountMapper dao;@Overridepublic List<Account> selectAll() {return dao.selectAll();}@Overridepublic void insertAccount(Account a) {dao.insertAccount(a);}@Overridepublic Account selectById(Integer id) {return dao.selectById(id);}@Overridepublic void trans(String inname, String outname, Double money) {//调用dao的加钱和减钱的方法即可完成转账的行为dao.inMoney(inname,money);//加钱dao.outMoney(outname,money);//减钱} }
测试类:
@RunWith(SpringJUnit4ClassRunner.class) // 告诉spring我们的核心配置类是谁 @ContextConfiguration(classes = MySpringConfig.class) public class MyAOPTest {@Autowiredprivate AccountService as;@Testpublic void testAopSeleactAll(){//转账as.trans("tom","jerry",100.0);List<Account> list = as.selectAll();System.out.println(list);} }
配置:
实现:
事务案例:
需求:无论转账的行为是否成功,都要记录转账日志;
分析:由于转账的业务层已经开启了一个事务,所以必须让日志的业务层在额外开启一个
新的日志即可实现效果;
//创建数据库 USE spring_db; CREATE TABLE tbl_log(id INT PRIMARY KEY AUTO_INCREMENT,info VARCHAR(255),createDate DATE );
/*专门操作日志的dao接口*/ public interface LogMapper {@Insert("insert into tbl_log values (null,CONCAT(#{outname},'给',#{inname},'转了',#{money}),now())")void insertLog(@Param("inname") String iname, @Param("outname")String outname, @Param("money")Double money); }
//业务层接口 public interface LogService {//给他单独加一个事务,告诉spring,不管调用我这个方法的其他类有没有事务,我自己都要开启一个新的事务@Transactional(propagation = Propagation.REQUIRES_NEW)void insertLog(String iname, String outname, Double money); }
//业务层实现类 @Service public class LogServiceImpl implements LogService {@Autowiredprivate LogMapper dao;@Overridepublic void insertLog(String iname, String outname, Double money) {dao.insertLog(iname,outname,money);} }
//业务层实现类 // 要加注解,让spring直接管理 @Service public class AccountServiceImpl implements AccountService {//设计一个dao的对象@Autowiredprivate AccountMapper dao;//日志 @Autowiredprivate LogService log;@Overridepublic List<Account> selectAll() {return dao.selectAll();}@Overridepublic void insertAccount(Account a) {dao.insertAccount(a);}@Overridepublic Account selectById(Integer id) {return dao.selectById(id);}@Overridepublic void trans(String inname, String outname, Double money) {try {//调用dao的加钱和减钱的方法即可完成转账的行为dao.inMoney(inname, money);//加钱int i=1/0;//转账失败 注释了则转账成功日志库记录dao.outMoney(outname, money);//减钱} finally {System.out.println("fnally执行了.....");log.insertLog(inname,outname,money);只要我不出异常,都会执行,记录日志库}} }
//测试类 @RunWith(SpringJUnit4ClassRunner.class) // 告诉spring我们的核心配置类是谁 @ContextConfiguration(classes = MySpringConfig.class) public class MyAOPTest {@Autowiredprivate AccountService as;@Testpublic void testAopSeleactAll(){//转账as.trans("tom","jerry",100.0);List<Account> list = as.selectAll();System.out.println(list);} }
SpringMVC:
是:spring家族提供的一个封装了servlet的框架,是基于java实现MVC模型的轻量级Web框架
SpringMVC就是对servlet的一个封装!
作用:1.可以简化servlet的开发,便捷的对浏览器做请求和响应处理;
2.比servlet灵活性更强,代码开发更方便;
优点:1.使用简单,开发便捷(相比于servlet)2.灵活性强
步骤:1.创建web工程(Mave架构)
2.设置tomcat服务武器,加载web工程(tomcat插件 )
3.导入坐标(SpringMVC+Servlet)
4.定义处理请求的功能类(UserController)
5.编写SpringMVC配置类,加载处理请求的Bean
6.加载SpringMVC配置,并设置SpringMVC请求拦截的路径
![]()
![]()
注意:对于SpringMVC而言,Controller方法返回值默认表示要跳转的页面,没有对应的页面就会报错
如果不想跳转页面而是响应数据,那么久需要在方法上使用@ResponseBody注解
@Controller:SpringMVC控制器类定义上方
@RequestMapping:设置当前控制器方法请求访问路径
@ResponseBody:设置当前控制器方法响应内容为当前返回值,无需解析
让返回值作为响应体,而不是跳转的资源
![]()
合并成一个容器,遇到service,controller,dao等都往spring容器里加
AbstractDispatcherServletInitializer:这个类当底层的DispatherServlet初始化后会执行这个类中的逻辑
1.告诉框架web层的类在哪一个包中存在
![]()
2.设定SpringMVC控制的范围
3.加载非SpringMVC的bean
AbstractAnnotationConfigDispatcherServletInitializer(简化书写):
POSTMan:
是:一款软件,可以帮助我们给服务器发送各种格式的请求,并获取响应;
使用:无需安装,双击即可使用,第一次使用需要注册账号;
1.创建workspace;(工作空间)
2.在workspace下面创建connections(文件夹)
3.在connection下面创建多个连接并存储,以便于下一次直接发送请求
简单参数处理:
概述:Springmvc可以帮助我们处理浏览器请求是携带的参数,我们只需要在controller的方法中
设计与浏览器对应的参数名的形式参数即可接收到浏览器的参数了
参数为普通参数:当参数名和变量名不同,使用@ReaquestParam绑定参数关系
get请求中文参数:
post请求中文参数:
![]()
参数为POJO:请求参数名与形参对象属性名相同,定义POJO类型形参既可以接收参数
注意强调:请求参数key的名称要和POJO中属性的名称一致,否则无法封装
嵌套POJO类型参数:请求参数名与形参对象属性名相同,按照对象层次结构关系即可接收嵌套POJO属性次数
参数为数组类型:请求参数名与形参对象属性名相同且请求参数为多个,定义数组类型即可接收参数
集合类型参数:请求参数名与形参集合对象名相同且请求参数为多个,@RequestParam绑定参数关系 @RequestParam和@RequestBody的区别:
@RequestParam用于接收url地址参数,表单传参【application/x-www-from-urlencoded】
@RequestBody用于接收json数据【application/json】
json参数的获取:
1.导入坐标
2.设置发送json数据(请求boby中天机json数据)
3.开启json自动解析支持:@ableWebMvc
4.在方法的形参前面添加注解
//1.导入坐标<dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.9.0</version></dependency>
@RequestBody:将请求中请求体所包含的数据传递给请求参数, 此注解:一个处理器方法只能使用一 次
注意:json一般是在请求体中携带,所有这里需要使用@RequestBody转成参数需要的
//4.在方法的形参前面添加注解 // 获取请求体中的数组参数@RequestMapping("/listJson")//这个就是一个普通的数组不是json数组public String listParamForJson(@RequestBody List<String> likes){System.out.println("list common(json)参数传递 list ==> "+likes);return "{'module':'list common for json param'}";}//注意json一般是在请求体中携带,所有这里需要使用@RequestBody转成参数需要的
//5种简单参数获取方式 //- 普通参数 //- POJO类型参数 //- 嵌套POJO类型参数 //- 数组类型参数 //- 集合类型参数 // 获取get请求的普通参数@RequestMapping("/get")public String getParam(String name,int age){System.out.println("getParam方法获取的name="+name);System.out.println("getParam方法获取的age="+age);return name+"====>"+age;}// 获取javabean请求的参数@RequestMapping("/javabean")public String javabeanParam(Student stu){System.out.println(stu);return "ok";}// 获取数组参数@RequestMapping("/array")public String arrayParam(String[] likes){System.out.println("数组参数传递 likes ==> "+ Arrays.toString(likes));return "ok";}// 获取集合参数 @RequestParam 专门针对集合类型的参数做了一个标记@RequestMapping("/list")public String listParam(@RequestParam List<String> likes){System.out.println("集合参数传递 likes ==> "+ likes);return "{'module':'list param'}";}
json参数的形式:
1.普通数组[元素1,元素2....]
2.json对象{key1:value,key2:value2...}
3.json数组[{key1:value1,key2:value2...},{key1:value1,key2:value2...}]
日期参数处理:默认支持用 / 连接日期信息,如果需要自己指定格式,可以使用@DataTimeFormat注解指定格式即可
// 获取请求体中的json形式的javabean数组@RequestMapping("/date")public String date(@DateTimeFormat(pattern = "yyyy-MM-dd") Date date,// M d 表示月份的数值跟着实际情况走,MM dd表示实际月份不够两位的时候,必须使用0补够两位@DateTimeFormat(pattern = "yy年M月d日") Date date1,Date date2){System.out.println(date);System.out.println(date1);System.out.println(date2);return "中文测试";}
响应json数据:
只要开启:@EnableWebMvc在方法中直接返回对象或集合,springMvc就可以把对象或集合转成json格式的数据
开启springMvc多项辅助功能,其中一个是开启json和java对象互转的开关
// 响应学生对象,springmvc会自动将学生对象转成json形式;@RequestMapping("/toStu")public Student toStudentJson(){Student student = new Student(12,"张三",22);student.setAddr(new Address("s678","119期"));return student;}// 响应学生对象,springmvc会自动将学生对象转成json形式;@RequestMapping("/toListStu")public List<Student> toStudentListJson(){List<Student> list = new ArrayList<>();Student student = new Student(12,"张三",22);student.setAddr(new Address("s678","119期"));list.add(student);Student student2 = new Student(22,"张三22",22);student2.setAddr(new Address("s122678","119期22"));list.add(student2);return list;}
Rest风格:
Rest风格综合案例:
创建数据库表:
-- 创建ssm_db数据库 CREATE DATABASE IF NOT EXISTS ssm_db CHARACTER SET utf8;-- 使用ssm_db数据库 USE ssm_db;-- 创建tbl_book表 CREATE TABLE tbl_book(id INT PRIMARY KEY AUTO_INCREMENT, -- 图书编号TYPE VARCHAR(100), -- 图书类型NAME VARCHAR(100), -- 图书名称description VARCHAR(100) -- 图书描述 ); -- 添加初始化数据 INSERT INTO tbl_book VALUES(NULL,'计算机理论','Spring实战 第5版','Spring入门经典教材,深入理解Spring原理技术内幕'); INSERT INTO tbl_book VALUES(NULL,'计算机理论','Spring 5核心原理与30个类手写实战','十年沉淀之作,手写Spring精华思想'); INSERT INTO tbl_book VALUES(NULL,'计算机理论','Spring 5设计模式','深入Spring源码剖析,Spring源码蕴含的10大设计模式'); INSERT INTO tbl_book VALUES(NULL,'市场营销','直播就该这么做:主播高效沟通实战指南','李子柒、李佳琦、薇娅成长为网红的秘密都在书中'); INSERT INTO tbl_book VALUES(NULL,'市场营销','直播销讲实战一本通','和秋叶一起学系列网络营销书籍'); INSERT INTO tbl_book VALUES(NULL,'市场营销','直播带货:淘宝、天猫直播从新手到高手','一本教你如何玩转直播的书,10堂课轻松实现带货月入3W+');
pojo层:javaBean-简单的
@Data @NoArgsConstructor @AllArgsConstructor //POJO实体类 public class Book {private Integer id;private String type;private String name;private String description;//同学们自己重写getter、setter、toString()方法... }
dao层 :-数据层
// dao层 -数据层 public interface BookMapper {//专门操作数据库的接口层 dao@Select("SELECT * FROM tbl_book")List<Book> selectAll();@Select("SELECT * FROM tbl_book WHERE NAME Like CONCAT('%',#{name},'%')")List<Book> selectByLikeName(String name);@Insert("INSERT INTO tbl_book VALUES (null,#{type},#{name},#{description})")int insertBook(Book book);@Delete("DELETE FROM tbl_book WHERE id=#{id}")int delById(Integer id);@Update("update tbl_book set type = #{type},name=#{name},description=#{description} where id=#{id}")int updateById(Book book); }
service层:接口业务层
public interface BookService {List<Book> selectAll();List<Book> selectByLikeName(String name);int insertBook(Book book);int delById(Integer id);int updateById(Book book); }
service层:业务实现类层
@Service public class BookServiceImpl implements BookService {@Autowiredprivate BookMapper dao;@Overridepublic List<Book> selectAll() {return dao.selectAll();}@Overridepublic List<Book> selectByLikeName(String name) {return dao.selectByLikeName(name);}@Overridepublic int insertBook(Book book) {return dao.insertBook(book);}@Overridepublic int delById(Integer id) {return dao.delById(id);}@Overridepublic int updateById(Book book) {return dao.updateById(book);} }
springMVC轻量级Web层框架,对servlet的一个封装;参 调 响 不用再写servlet。+rest风格请求方式及简化参数的获取和代码操作
//controller层 @RestController @RequestMapping(value = "/book", produces = "application/json;charset=utf-8") public class BookController {@Autowiredprivate BookService bs;@GetMappingpublic List<Book> selectAll() {return bs.selectAll();}@GetMapping("/{name}")public List<Book> selectByLikeName(String name) {return bs.selectByLikeName(name);}@PostMappingpublic int insertBook(@RequestBody Book book) {return bs.insertBook(book);}@DeleteMapping("/{id}")public int delById(Integer id) {return bs.delById(id);}@PutMappingpublic int updateById(Book book) {return bs.updateById(book);} }
接下来是进行配置
resources:jdbc.properties配置 使用纯注解对mybatis封装
0.创建工程添加依赖和插件
1.sring整合Mybatis:数据连接 JDBC数据属性的注入
配置JDBC abc=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql:///ssm_db?useSSL=false&useUnicode=true&characterEn=utf-8 jdbc.username=root jdbc.password=....//对应配置文件下的如下: //数据连接的配置类public class DruidConfig {// 定义4个成员变量,利用@value注入properties中的信息,以共 pool方法使用@Value("${abc}")private String dc;@Value("${jdbc.url}")private String url;@Value("${jdbc.username}")private String un;@Value("${jdbc.password}")private String pw;// 自定义一个方法,生成一个连接池信息,交给ioc容器@Beanpublic DataSource pool(){// 1: 创建阿里的druid连接池信息DruidDataSource ds = new DruidDataSource();// 2: 将成员变量的4个信息保存到ds中ds.setDriverClassName(dc);ds.setUrl(url);ds.setUsername(un);ds.setPassword(pw);return ds;} }
2.固定不变的MyBatisConfig的类:
public class MyBatisConfig {private String javaBeanPackage="com.spmvc.demo01_BookSystem.pojo";//填写自已对应的javabean包名private String mapperPackage="com.spmvc.demo01_BookSystem.dao";//填写自已对应的数据包名@Beanpublic SqlSessionFactoryBean mybatis(DataSource ds){// 1: 创建用于创建SqlSessionFactory的对象SqlSessionFactoryBeanSqlSessionFactoryBean bean = new SqlSessionFactoryBean();// 2: 给SqlSessionFactoryBean 设置数据源bean.setDataSource(ds);// 3: 给SqlSessionFactoryBean 设置扫描javabean所在的包bean.setTypeAliasesPackage(javaBeanPackage);// 4: 给SqlSessionFactoryBean 设置其他插件,例如: 分页插件,驼峰命名等// 4.1: 驼峰命名Configuration con = new Configuration();con.setMapUnderscoreToCamelCase(true);bean.setConfiguration(con);// 4.2: 分页插件// 4.2.1: 准备插件需要的参数Properties p = new Properties();// 4.2.2: 开启分页插件的固定写法p.setProperty("value","true");// 4.2.3: 创建分页插件对象PageInterceptor page = new PageInterceptor();// 4.2.4: 给分页插件设置参数page.setProperties(p);// 4.2.5 : 将分页插件交给SqlSessionFactoryBean管理bean.setPlugins(new Interceptor[]{page});// 5: 返回准备好的SqlSessionFactoryBean,spring的ioc管理 SqlSessionFactoryBean的时候,会自动从SqlSessionFactoryBean中获取SqlSessionFactoryreturn bean;}@Beanpublic MapperScannerConfigurer mapper(){MapperScannerConfigurer mapper = new MapperScannerConfigurer();// 设置要扫描的接口映射包mapper.setBasePackage(mapperPackage);return mapper;} }
3.MySpringConfig类:ioc容器,spring统一管理的配置类
//纯注解的 @Configuration @ComponentScan("com.spmvc.demo01_BookSystem") @Import({DruidConfig.class,MyBatisConfig.class,SpringMvcSupport.class}) @PropertySource({"classpath:jdbc.properties"}) @EnableWebMvc @EnableTransactionManagement public class MySpringConfig { }
4.开启事务
/*专门处理mybatis的事务交给ioc容器的配置类*/ public class ShiWuConfig {@Beanpublic PlatformTransactionManager shiWu(DataSource ds){// 1: 创建mybatis的事务管理器对象DataSourceTransactionManager manager = new DataSourceTransactionManager();// 2: 给事务管理器设置一个连接池manager.setDataSource(ds);// 3: 返回事务管理器return manager;} }
以上1.2.3.4是spring对mybatis配置的简化开发,且固定不变的
4.spring整合SpringMVC:SpringMvc将spring的核心配置类交给这个方法即可
// springmvc要求我们继承AbstractAnnotationConfigDispatcherServletInitializer类,重写三个方法,提供spring的核心容器和拦截路径 public class MySpringMVCConfig extends AbstractAnnotationConfigDispatcherServletInitializer {// web核心容器是什么,只需要将spring的核心配置类交给这个方法即可@Overrideprotected Class<?>[] getRootConfigClasses() {return new Class[]{MySpringConfig.class};}// 专门存放controller的容器,依然可以继续使用spring的ioc容器@Overrideprotected Class<?>[] getServletConfigClasses() {return new Class[]{MySpringConfig.class};}// 告诉web服务器,当浏览器发出什么样的请求的时候,我们就从专门存放controller的容器中获取controller对象,对浏览器进行处理@Overrideprotected String[] getServletMappings() {return new String[]{"/"};}//乱码处理@Overrideprotected Filter[] getServletFilters() {CharacterEncodingFilter filter = new CharacterEncodingFilter();filter.setEncoding("UTF-8");return new Filter[]{filter};} }
5.整合静态资源:设置静态资源访问过滤,当前类需要设置为配置类,并被扫描加载
@Configuration public class SpringMvcSupport extends WebMvcConfigurationSupport {//设置静态资源访问过滤,当前类需要设置为配置类,并被扫描加载@Overrideprotected void addResourceHandlers(ResourceHandlerRegistry registry) {//当访问/pages/????时候,从/pages目录下查找内容registry.addResourceHandler("/pages/").addResourceLocations("/pages/");registry.addResourceHandler("/js/").addResourceLocations("/js/"); registry.addResourceHandler("/css/").addResourceLocations("/css/"); registry.addResourceHandler("/plugins/").addResourceLocations("/plugins/");registry.addResourceHandler("/img/").addResourceLocations("/img/");} }
以上4.5是对利用SpringMvc对spring的整合也称为ssm整合
运行:用Postman工具测试:
二、对响应结果再做统一格式
原因:由于一个系统中,会进行各种不同的操作,如果每种操作都响应不同格式的结果,不利于前后端分离开发,
因此需要对后端响应数据做统一格式处理;
好处:便于前后端分离开发时候的数据交互;
格式:会由3部分组成 1.状态码 2.提示信息 3.正文数据
参考代码:pojo层下Javabean中创建类+状态码(写一些的常量)
/*专门用于封装响应信息的统一格式的结果类*/ @Data @NoArgsConstructor @AllArgsConstructor public class MyResult {// 包含三部分信息; 自定义的状态码 提示信息 正式数据private Integer code;private String msg;private Object data; }public class MyStatusCode {public static final Integer SAVE_OK = 20001;public static final Integer DEL_OK = 20002;public static final Integer UPDATE_OK = 20003;public static final Integer GET_ONE_OK = 20004;public static final Integer GET_ALL_OK = 20005;public static final Integer SAVE_ERR = 20010;public static final Integer DEL_ERR = 20020;public static final Integer UPDATE_ERR = 20030;public static final Integer GET_ONE_ERR = 20040;public static final Integer GET_ALL_ERR = 20050; }
对controller层改造
代码:controller层
@RestController @RequestMapping(value = "/book") public class BookController {@Autowiredprivate BookService bs;@GetMappingpublic MyResult selectAll() {List<Book> list = bs.selectAll();if (list != null && list.size() > 0) {//自定义的 状态码 提示信息 正式数据return new MyResult(MyStatusCode.GET_ALL_OK, "查询所有成功了", list);}return new MyResult(MyStatusCode.GET_ALL_ERR, "查询所有失败", null);}@GetMapping("/{name}")public MyResult selectByLikeName(@PathVariable String name) {List<Book> list = bs.selectByLikeName(name);if (list != null && list.size() > 0) {return new MyResult(MyStatusCode.GET_ONE_OK, "模糊查询成功拉", list);}return new MyResult(MyStatusCode.GET_ONE_ERR, "模糊查询失败", null);}@PostMappingpublic MyResult insertBook(@RequestBody Book book) {int i = bs.insertBook(book);if (i > 0) {return new MyResult(MyStatusCode.SAVE_OK, "添加成功拉", null);}return new MyResult(MyStatusCode.SAVE_ERR, "添加失败", null);}@DeleteMapping("/{id}")public MyResult delById(@PathVariable Integer id) {int i = bs.delById(id);if (i > 0) {return new MyResult(MyStatusCode.DEL_OK, "删除成功拉", null);}return new MyResult(MyStatusCode.DEL_ERR, "删除失败", null);}@PutMappingpublic MyResult updateById(@RequestBody Book book) {int i = bs.updateById(book);if (i > 0) {return new MyResult(MyStatusCode.UPDATE_OK, "修改成功拉", null);}return new MyResult(MyStatusCode.UPDATE_ERR, "修改失败", null);} }
运行结果:
三、SSM案例完成与HTML页面交互
1.分页查询功能:
实现思路:
1.浏览器给服务器发送请求的时候,必须携带分页相关参数和模糊查询的名称;因此需要在html页面
发送请求,并从响应的结果中获取分页相关的集合和总条数两个数据
2.controller中以json方式获取浏览器传递的参数,并将参数交给业务层查询,得到分页对象后封装到
统一结果中响应给后端;
![]()
![]()
![]()
然后改后台:
业务层接口:
业务层实现类
pojo层:javaBean-简单的
controller层进行修改:
这就完成分页查询的功能了
代码:
前端代码:
//主页列表查询getAll() {//alert(this.pagination.pageSize+"===>"+this.pagination.currentPage)axios({method: 'post',url: "http://localhost/book/getAllByPage",data: this.pagination}).then(resp => {// 获取结果对象中的状态码if(resp.data.code == 20005){// 把list集合的数据,交给 表格this.dataList = resp.data.data.list;this.pagination.total = resp.data.data.total;}else {// 失败,alert(resp.data.msg);}})},
service代码
@PostMapping("/getAllByPage")public MyResult selectAll(@RequestBody Pagination p){PageInfo<Book> pi = bs.selectAll(p.getCurrentPage(),p.getPageSize());if(pi.getList()!=null&&pi.getList().size()>0){return new MyResult(MyStatusCode.GET_ALL_OK,"查询所有成功拉",pi);}return new MyResult(MyStatusCode.GET_ALL_ERR,"查询所有失败",null);}impl@Overridepublic PageInfo<Book> selectAll(int currentPage, int pagesize) {//1:开启分页PageHelper.startPage(currentPage,pagesize);//2:正常查询List<Book> books = dao.selectAll();//3:封装结果PageInfo<Book> pi = new PageInfo<>(books);return pi;}
controller代码
@PostMapping ("/getAllByPage")public MyResult selectAll(@RequestBody Pagination p) {PageInfo<Book> pi = bs.selectAll(p.getCurrentPage(),p.getPagesize());if (pi.getList() != null && pi.getList().size() > 0) {//自定义的 状态码 提示信息 正式数据return new MyResult(MyStatusCode.GET_ALL_OK, "查询所有成功了", pi);}return new MyResult(MyStatusCode.GET_ALL_ERR, "查询所有失败", null);}
模糊查询: