1.spring优缺点分析
(1)优点:通过ioc,aop管理bean对象,节省了内存空间,更好的编写业务逻辑,解除耦合
(2)缺点:
<1>1.虽然组件是轻量级,但是配置是重量级的。
<2>依赖管理问题,spring的依赖需要保持一致,同时需要避免依赖冲突问题
2.SpringBoot的基本原理
(1)SpringBoot的优势
<1>springboot是对spring+springmvc的封装,基本功能并没有增加。将繁琐的配置,进行自动化的管理,不再需要配置文件。
<2>springboot使用依赖传递的功能,只需要引入父层依赖,其他的依赖的版本由父层springboot来进行管理,避免了依赖冲突问题。
(2)SpringBoot的特点
<1>提供了快速使用spring的一种方式
<2>提供当前springboot的一些环境,以及内存,以及健康状况
<3>开箱即用,不需要任何配置信息,就可以使用springboot,但是代码层面与spring+springmvc相同
(3)SpringBoot的注意事项
<1>springBoot 不需要配置tomcat,因为在springboot的依赖中 spring-boot-starter-web中已经内置了tomcat,帮助我们启动项目
<2>针对于启动类,是我们springboot的入口,同时启动类上方需要加入注解@SpringbootApplication 标识当前类为启动类。在springboot工程中,通常只有一个启动类.
<3>根据springboot的自动装配原理可知,当我们在Pom中引入某个组件,或者框架后,springboot启动时,会装配该框架,组件的环境,我们就需要有该框架/组件的配置信息,如果没有,则会报错,或者说,引入pom依赖后,不想使用则需要在启动类@SpringBootApplication(exclude=配置.class)排除掉该配置类.
<4>通过springboot的启动类启动项目时,默认会加载启动类,同级,或者下级的包的注解。如果启动类在单独的一个包中,则需要配置scanBasePackages 告诉springboot启动时,需要扫描的包路径.
(4)SpringBoot的启动原理
<1>从启动类入手,跟踪启动的流程,大致为:
run->SpringApplication->ConfigurableApplicationContext->StopWatch 声明监听器->开始监听项目的
启动时间->ConfigurableApplicationContext(配置文件应用容器)->exceptionReporters (异常记录)-
>configureHeadlessProperty(配置属性)->getRunListeners(项目启动监听器)-
>listeners.starting(); 启动监听->(springBoot启动的准备工作)
启动的核心代码为: refreshContext(context); //刷新容器 -->执行了spring的bean创建逻辑
<2>如何来改变启动的banner图,SpringApplicationBannerPrinter 该类是banner图的基类,如果我们想要改变banner图,可以在resources 资源目录下,创建 banner.txe文件,启动时,会读取该文件的内容,进行打印
(5)springboot的自动装配原理
<1>当springBoot引入某个组件依赖时,springBoot会在启动时,自动装配该组件的环境,例如,引入了Mybatis 需要操作数据时,回在启动springboot时,创建SqlSessionFactory,如果没有配置有关数据库的信息,则会报错,无法启动该项目。
<2>springBoot是如何发现你引入了该依赖,并且需要配置环境呢。在@SpringBootApplication注解中有一个注解@EnableAutoConfiguration(自动装配配置类)。在@EnableAutoConfiguration中有一个@Import(AutoConfigurationImportSelector.class) 自动装配选中的组件
<3>在AutoConfigurationImportSelector 中的selectImports方法中,会将springboot默认集成的所有的框架/组件,进行加载(org.springframework.boot:spring-boot-autoconfiguration中,META-INF中的spring.factories)在spring.factories 中维护了110个springboot默认集成的框架/组件,在selectImports中会扫描spring.factories 进行加载,同时根据pom中的引入的依赖信息,判断需要加载哪些基本配置类,如果默认的只有springboot-starter-web得话,只有24个基本配置类,每增加一个pom依赖,则加载的配置类会进行对应的添加。
<4>约定大于配置的思想
(6)springboot的配置文件
<1>.properties 在properties中是以key=value来进行配置的
server.port=端口号
<2>.yaml 在yaml中是以父子结构来进行配置 中间以:进行分割
server:
port: 端口号
<3>.yml 与第二种配置的方式一致
server:
port: 端口号
注意:如果三种配置同时存在,则 properties发生效用, 因为properties最后加载。会将yaml.yml中的以配置好的信息进行覆盖.
3.SpringBoot整合Mybatis
(1)导入pom依赖
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.0.0</version>
</dependency>
(2)配置数据源
# 配置数据源
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/ee2110?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true
spring.datasource.username=root
spring.datasource.password=root
# 配置别名和映射文件路径
mybatis.type-aliases-package=com.qf.pojo
mybatis.mapper-locations=mapper/*Mapper.xml
(3)编写接口
@Mapper //标识为mybatis的接口 同时交由spring管理
public interface UserMapper {
TbUser findByUserName(@Param("userName")String userName);
}
(4)编写测试类
导入pom依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
在test中的java下创建与main的java下的同名以及同级目录,帮助测试类找到springboot的启动类
@RunWith(SpringRunner.class)
@SpringBootTest //启动测试类时,会找到springboot的启动类,先来加载spring的环境,再来进行测试
public class TestUser {
@Autowired
UserMapper userMapper;
@Test
public void testUser(){
TbUser admin = userMapper.findByUserName("admin");
System.out.println(admin);
}
}
4.SpringBoot整合拦截器
(1)定义拦截类
public class MyInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
Admin admin = (Admin) request.getSession().getAttribute("admin");
if(admin!=null){
return true;
}
response.setCharacterEncoding("utf-8");
response.setContentType("application/json");
ResponseData responseData = new ResponseData(1,"用户未登录",null,null);
String string = JSON.toJSONString(responseData);
response.getWriter().write(string);
return false;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {}
}
(2)配置
@Configuration
public class MyWebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyInterceptor()).addPathPatterns("/dept/**").excludePathPatterns("");
}
}
5.SpringBoot整合验证码插件Captcha
(1)导入依赖
(2)添加配置
(3)添加controller
6.SpringBoot整合分页插件
(1)导入依赖
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.3.0</version>
</dependency>
(2)使用
PageHelper.startPage(page,limit);
List<QfCourse> qfCourses = qfCourseMapper.queryAllByLimit();
PageInfo<QfCourse> courses = PageInfo.of(qfCourses);
7.SpringBoot的热加载
(1)导入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
(2)修改setting中的配置
8.Swagger的使用
Swagger 是一个规范和完整的框架,用于生成、描述、调用和可视化 RESTful 风格的 Web 服务。总体目标是使客户端和文件系统作为服务器以同样的速度来更新文件的方法,参数和模型紧密集成到服务器端的代码,允许API来始终保持同步。
(1)作用
<1>接口的文档在线自动生成
<2>功能测试
(2)使用步骤
<1>导入依赖
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.8.0</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.8.0</version>
</dependency>
<2>SpringBoot配置
@Configuration
@EnableSwagger2
public class Swagger2Config {
@Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
// 自行修改为自己的包路径
.apis(RequestHandlerSelectors.basePackage("com.qf.controller"))
.paths(PathSelectors.any())
.build();
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("swagger-api文档")
.description("swagger接入教程")
//服务条款网址
.version("1.0")
.build();
}
}
<3>添加文档内容
@ApiOperation:注解来给API增加方法说明
@RequestMapping("/course")
@RestController
public class CourseController {
@Autowired
private CourseService courseService;
@RequestMapping("/findAllCourse")
@ApiOperation(value = "查询所有课程")
@ApiImplicitParams(value = {@ApiImplicitParam(paramType = "query",name = "page",value = "当前页",required = true,dataType = "int"),
@ApiImplicitParam(paramType = "query",name = "limit",value = "每页显示的条数",required = true,dataType = "int")})
public ResponseData findAll(@RequestParam("page")Integer page,@RequestParam("limit")Integer limit){
ResponseData responseData = courseService.findAll(page,limit);
}
}
9.SpringBoot整合SpringDataJPA数据库持久层框架
springDataJPA 是spring框架提供给我们的操作数据库的一个框架,他是基于hibernate而研发出来的。hibernate是最先将所有的sql语句进行封装,以接口得方式提供出来得一种框架。hibernate基于JdbC开发出来的。
springdataJPA与Mybatis得区别:
(1)springdatajpa可以更方便的完成程序员对表得crud,不用写sql语句,由Jpa框架自动反射生成sql,进行执行.
(2)mybatis 程序员需要完成sql的编写,程序员对sql的掌控力度更高,使用mybatis完成较为复杂得查询时,更为简单.
(3)使用步骤
<1>导入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<2>配置文件
# datasource的配置
.......
#jpa的配置
#1.jpa连接的数据库
spring.jpa.database=mysql
#2.jpa执行sql是否打印sql语句
spring.jpa.show-sql=true
#3.启动项目时,如果发现数据库中没有对应的表/字段是,否自动创建
spring.jpa.generate-ddl=true
<3>编写实体类
@Data
@Entity 表示当前类是jpa的实体类
@Table(name = "teacher") 标识当前实体类与数据库哪张表建立联系
public class Teacher {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY) 设置自增长主键策略
private Integer id;
@Column(name = "t_name")
private String name;
@Column(name = "t_address")
private String address;
}
<4>编写持久层
声明接口继承 JpaRepository<实体类,主键的类型>
public interface TeacherMapper extends JpaRepository<Teacher,Integer> {
可以自定义条件 findBy固定写法,后边跟实体类的属性名,相当与 where条件,属性名=列名
List<Teacher> findByNameAndAddress(String name,String address);
List<Teacher> findByNameOrAddress(String name,String address);
}
<5>测试
@Test
public void testFindAll(){
Integer page=1; 增加分页
Integer size=1;
mybatis中使用PageHelp进行分页 PageHelp.startPage(page,size);
PageRequest of = PageRequest.of(page - 1, size);
Page<testCode> all = testCodeRepository.findAll(of);
获取总的条数
System.out.println("总条数==="+all.getTotalElements());
System.out.println("数据未:"+all.getContent());
}
@Test
public void testFindById(){
Optional<testCode> byId = testCodeRepository.findById(1);
进行非空判断
if (byId.isPresent()){
testCode testCode = byId.get();
System.out.println(testCode);
}
}
saveAndFlush新增或者修改。根据传入的对象中是否有id,如果没有id则为新增,如果有id则为修改
10.springBoot集成前端页面框架thymeleaf
(1)添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
(2)编写controller
@Controller
public class ThymeleafController {
@Autowired
private TeacherMapper teacherMapper;
@RequestMapping("/first")
public String index(Model model){
List<Teacher> all = teacherMapper.findAll();
model.addAttribute("teachers",all);
默认找到templates下的html页面
return "index"; <--return "redirect:/index";-->
}
}
(3)页面编写
//每个页面都必须加入 xmlns:th="http://www.thymeleaf.org"
<html lang="en" xmlns:th="http://www.thymeleaf.org">
//相当于jstl c标签的循环;
//参数1:循环testCodeList的每个对象别名;参数2: stat:对象的下标
<tr th:each="code,state :${testCodeList}">
<td th:text="${code.id}"></td>
//所有的src路径 必须 加入 @{url/{参数}(参数名称=${参数的具体值}})}
<td><a th:href="@{/del/{id}(id=${code.id})}">删除</a>
静态化页面的使用
(1)编写静态页面
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<div ><h1>一码通展示页面</h1></div>
<input th:value="${color}"></input>
(2)生成静态页面
@RequestMapping("/createThymeleaf/{color}")
@ResponseBody
public String creatThymeleaf(@PathVariable("color")String color){
创建需要放在静态化页面中的数据
Context context = new Context();
静态话页面中的变量
context.setVariable("color",color);
静态化页面模板的名称
获取到返回的string 字符串 静态页面+赋值的数据
String staticHtml = templateEngine.process("StaticHtml", context);
进行保存 保存到云服务器
String s = uploadUtils.uploadStatic(staticHtml);
return s;
}
11.SpringBoot整合javamail邮件发送
(1)添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
(2)配置文件
#自定义配置 将配置信息放置到配置文件中,作用:解耦
qiniu:
ak: m7mFmCTX55PMNPkEBaOLFQMLxW4ZFfZwf2EBm1M2
sk: dn220PqIs813-IYrIZtzO_bGLXPo3QSVNep6wNty
bucket: qf-2110
spring:
mail:
#发送邮箱的服务地址
host: smtp.qq.com #smtp.163.com
username: [email protected]
#邮箱的授权码 需要在qqemail进行申请
password: gnzkkrybpomcbceh
default-encoding: utf-8
(3)测试
<1>发送普通邮件
@Autowired
private JavaMailSender javaMailSender;
@Value("${spring.mail.username}")
private String from;
@Test
public void testSendSimpleMail(){
声明简单邮件发送的类
SimpleMailMessage simpleMailMessage = new SimpleMailMessage();
发件人
simpleMailMessage.setFrom(from);
收件人
simpleMailMessage.setTo("[email protected]");
设置发送的内容
simpleMailMessage.setText("这是测试邮件");
设置发送的主题
simpleMailMessage.setSubject("邮件测试");
进行发送
javaMailSender.send(simpleMailMessage);
}
<2>发送复杂邮件
@Test
public void testHtmlMail() throws MessagingException {
1.使用javamailsender创建邮件发送对象
MimeMessage mimeMessage = javaMailSender.createMimeMessage();
发送的是复杂的邮件
MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(mimeMessage, true);
mimeMessageHelper.setFrom(from);
mimeMessageHelper.setTo("[email protected]");
mimeMessageHelper.setSubject("这是html格式邮件");
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append("<font style='color:red'>").append("我是词汇表html格式邮件").append("</font>");
代表是一个html格式。需要解析Html标签
mimeMessageHelper.setText(stringBuffer.toString(),true);
javaMailSender.send(mimeMessage);
}
<3>发送带附件的邮件
@Test
public void sendFileMail() throws MessagingException {
MimeMessage mimeMessage = javaMailSender.createMimeMessage();
MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(mimeMessage, true);
mimeMessageHelper.setFrom(from);
mimeMessageHelper.setTo("[email protected]");
mimeMessageHelper.setSubject("这是携带附件邮件");
mimeMessageHelper.setText("这是附件邮件,附件是苗老师的图片,请注意查收");
获取到附件
FileSystemResource fileSystemResource = new FileSystemResource(new File("C:\\Users\\54110\\Desktop\\1.jpg"));
设置携带的附件 1.图片的名称 2.资源
mimeMessageHelper.addAttachment("miao.jpg",fileSystemResource);
javaMailSender.send(mimeMessage);
}
<4>发送静态化模板页面
@Autowired
TemplateEngine templateEngine;
@Test
public void sendStaticHtml() throws MessagingException {
创建需要放在静态化页面中的数据
Context context = new Context();
静态化页面中的变量
context.setVariable("color","red");
静态化页面模板的名称
获取到返回的string 字符串 静态页面+赋值的数据
String staticHtml = templateEngine.process("StaticHtml", context);
MimeMessage mimeMessage = javaMailSender.createMimeMessage();
MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(mimeMessage, true);
mimeMessageHelper.setFrom(from);
mimeMessageHelper.setTo("[email protected]");
mimeMessageHelper.setSubject("这是携带附件邮件");
mimeMessageHelper.setText(staticHtml,true);
javaMailSender.send(mimeMessage);
}
12.alipay 支付宝支付
(1)导入依赖
<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-sdk-java</artifactId>
<version>4.12.4.ALL</version>
</dependency>
(2)书写测试
@Controller
public class PayController {
private static final String CHARSET ="UTF-8" ;
@RequestMapping("/success")
public String success(){
return "success";
}
@RequestMapping("/pay/{number}/{amount}/{subject}/{body}")
@ResponseBody
public void doPost (
@PathVariable("number")String number,
@PathVariable("amount")Double amount,
@PathVariable("subject")String subject,
@PathVariable("body")String body,
HttpServletRequest httpRequest,
HttpServletResponse httpResponse) throws ServletException, IOException {
创建默认的alpy请求客户端
AlipayClient alipayClient = new DefaultAlipayClient("支付宝网关" ,"APPID", "应用私钥", "json", CHARSET, "支付宝公钥", "RSA2"); 获得初始化的AlipayClient
创建API对应的request
AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest(); //创建API对应的request
同步回调 -->用户支付完成后所看到的页面
alipayRequest.setReturnUrl( "http://47015w1c06.qicp.vip/success" );
异步回调 --> 支付宝通知商户平台是否支付成功的接口地址
alipayRequest.setNotifyUrl( "http://47015w1c06.qicp.vip/notifyUrl" );
在公共参数中设置回跳和通知地址
Map map = new HashMap();
map.put("out_trade_no",number);
map.put("product_code","FAST_INSTANT_TRADE_PAY");
map.put("total_amount",amount);
map.put("subject",subject);
map.put("body",body);
map.put("passback_params","merchantBizType%3d3C%26merchantBizNo%3d2016010101111");
String stringMap = JSON.toJSONString(map);
alipayRequest.setBizContent(stringMap); 填充业务参数
String form= "" ;
我们将该笔订单存储到数据库中。设置支付状态为 未支付
try {
form = alipayClient.pageExecute(alipayRequest).getBody(); 调用SDK生成表单
} catch (AlipayApiException e) {
e.printStackTrace();
}
httpResponse.setContentType( "text/html;charset=" + CHARSET);
httpResponse.getWriter().write(form); 直接将完整的表单html输出到页面
httpResponse.getWriter().flush();
httpResponse.getWriter().close();
}
@RequestMapping("/notifyUrl")
public void notifyUrl(HttpServletRequest request,HttpServletResponse response) throws AlipayApiException {
用从Request请求中解析出支付宝回调的数据
Map<String, String> paramsMap = convertRequestParamsToMap(request);
将异步通知中收到的所有参数都存放到 map 中
boolean signVerified = AlipaySignature.rsaCheckV1(paramsMap, "应用公钥", CHARSET, "RSA2") ; 调用SDK验证签名
if (signVerified){
System.out.println("用户支付成功"+paramsMap);
TODO 验签成功后,按照支付结果异步通知中的描述,对支付结果中的业务内容进行二次校验,校验成功后在response中返回success并继续商户自身业务处理,校验失败返回failure
} else {
TODO 验签失败则记录异常日志,并在response中返回failure.
}
}
private Map<String, String> convertRequestParamsToMap(HttpServletRequest request) {
Map<String, String> retMap = new HashMap<String, String>();
Set<Map.Entry<String, String[]>> entrySet = request.getParameterMap().entrySet();
for (Map.Entry<String, String[]> entry : entrySet) {
String name = entry.getKey();
String[] values = entry.getValue();
int valLen = values.length;
if (valLen == 1) {
retMap.put(name, values[0]);
} else if (valLen > 1) {
StringBuilder sb = new StringBuilder();
for (String val : values) {
sb.append(",").append(val);
}
retMap.put(name, sb.toString().substring(1));
} else {
retMap.put(name, "");
}
}
return retMap;
}
}
版权归原作者 Mr___Lr 所有, 如有侵权,请联系我们删除。