springboot中对各个层的理解以及流程
-
entity层:
实体层,数据库在项目中的类。也被称为model层,pojo层。
用于定义与数据库对象的属性,提供get/set方法,带参和无参的构造方法
一般数据库一张表对应一个[实体类,类属性同表字段一一对应 -
dao层:
持久层,即mapper层,对数据库进行持久化操作,他的方法是针对数据库操作额,基本上用的就是增删改查,就是一个接口,只有方法名,具体实现在mapper.xml中。
简单的说就是:dao层的作用为访问数据库,向数据库发送sql语句,完成数据的增删改查任务。 -
service层:
业务层,存放业务逻辑处理,不直接对数据库进行操作,有接口和接口实现类,提供controller层调用方法。 -
controller层:
控制层,导入service层,调用你service方法,controller通过接收前端传来的参数进行业务操作,在返回一个指定的路径或数据表。 -
流程:
前端发送请求,controller控制层接收请求信息,然后调用service层的接口以及接口实现类,实现类再调用dao层去操作数据库,dao层把数据返回给service层,然后再在service层进行业务处理,再接着把数据返回给controller控制层。
SpringBoot自动装配
启动类的@SpringBootApplication注解由@SpringBootConfiguration,@EnableAutoConfiguration,@ComponentScan三个注解组成,三个注解共同完成自动装配;
@SpringBootConfiguration 注解标记启动类为配置类
@ComponentScan 注解实现启动时扫描启动类所在的包以及子包下所有标记为bean的类由IOC容器注册为bean
@EnableAutoConfiguration通过 @Import 注解导入 AutoConfigurationImportSelector类,
然后通过AutoConfigurationImportSelector 类的 selectImports 方法去读取需要被自动装配的组件依赖下的
spring.factories文件配置的组件的类全名,并按照一定的规则过滤掉不符合要求的组件的类全名,
将剩余读取到的各个组件的类全名集合返回给IOC容器并将这些组件注册为bean
一、springboot中的常用注解
- springboot中常用的注解主要可以分为三种:放入容器型注解、从容器中取出型注解和功能型注解。其中的放入容器型和从容器中取出型就是我们平时所说的控制反转(IOC)和依赖注入(DI)的概念。
二、控制反转
创建一个实例对象,然后将这个对象交给spring管理
-
1、@Component:放在类上,把普通类实例化到spring容器中。可以说很多注解都是基于这个注解的。
-
2、@Bean: 放在方法上,用@Bean标注方法等价于XML中配置bean,这个方法一般返回一个实体对象,告诉spring这里产生一个对象,然后这个对象会交给Spring管理。产生这个对象的方法Spring只会调用一次,随后这个Spring将会将这个Bean对象放在自己的容器中。
-
3、@Configuration:标注当前类是配置类,并会将当前类内声明的一个或多个以@Bean注解标记的方法的实例纳入到srping容器中,并且实例名就是方法名。(其实就是靠@Component注解)
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public interface Configuration {
String value() default "";
}
- 4、@ConfigurationProperties:将配置文件中的参数映射成一个对象,通过prefix来设定前缀,然后将后面的和对象的属性名一致就能实现注入(当然这个对象需要注入的属性需要提供get和set方法 - - - 因为spring底层其实就是通过反射调用该对象的set方法)
- 5、@Value : value的作用其实和ConfigurationProperties作用差不多,就是读取配置文件中参数的值,但是value是放在变量上面的,且是单值读取,还有一点就是value标注的变量并不需要和配置文件的参数名字一致。
- 6、@RestController、@Controller、@Service、@Repository
三、依赖注入
- 1、@Resource:是按照名称来注入的,当找不到与名称匹配的bean才会按照类型来注入。其实我们平时用的@Resource都是用了他的默认的方式,即都不指定名字和类型。spring通过反射机制使用byName方法自动注入。
@Resource(type = ShiroService.class, name = "shiroService")
private ShiroService shiroService;
@Resource 的装配顺序:
1. 如果同时指定了 name 属性和 type 属性,那么 Spring 将从容器中找唯一匹配的 bean 进行装配,找不到则抛出异常
2. 如果指定了 name 属性值,则从容器中查找名称匹配的 bean 进行装配,找不到则抛出异常
3. 如果指定了 type 属性值,则从容器中查找类型匹配的唯一的 bean 进行装配,找不到或者找到多个都会抛出异常
4. 如果都不指定,则会自动按照 byName 方式进行装配(我们一般都用的是这个。。。)
- 2、@Autowried:默认是按照类型进行装配注入,如果允许 null 值,可以设置它 required 为false。即:当不能确定 Spring 容器中一定拥有某个类的 Bean 时,可以在需要自动注入该类 Bean 的地方可以使用 @Autowired(required = false) ,这等于告诉 Spring:在找不到匹配 Bean 时也不报错。
@Autowired(required = false)
private ShiroService shiroService;
简单来说,Qualifier就是规定一下Bean的名字,相当于@Resource规定了name属性
- 3、@Qualifier:@Autowired是根据类型进行自动装配的。如果当spring上下文中存在不止一个A类型的bean时,就会抛出BeanCreationException异常;如果Spring上下文中不存在A类型的bean,而且我们又使用A类型,也会抛出BeanCreationException异常。针对存在多个A类型的Bean,我们可以联合使用@Qualifier和@Autowired来解决这些问题。
@Autowried
@Qualifier("adminDAO")
private AdminDAO adminDAO;
四、功能型注解
-
1、@SpringBootApplication:这个注解就是集成了:@SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan这三个注解。其中@SpringBootConfiguration:表示这个类为配置类;@EnableAutoConfiguration:表示开启自动配置,我们平时所说springboot无配置就是这个参数起的作用,他读取了springboot默认的配置;@ComponentScan:表示自动扫描,这个扫描默认只能扫同一级的目录。
-
2、@EnableConfigurationProperties:将带有@ConfigurationProperties注解的类注入为Spring容器的Bean。如果使用了@ConfigurationProperties但是没有在启动类上增加这个注解,则@ConfigurationProperties将不起作用
-
3、@Async与@EnableAsync:其中@Async表示这个方法为异步方法;@EnableAsync这个注解需要加在启动类上,表示支持异步操作;如果不加,则@Async将不起作用
@Async就相当于另起一个线程
@RestController
public class AsyncController {
@Autowired
private AsyncService asyncservice;
@RequestMapping("/asyncTest.do")
public String asyncTest() {
System.out.println("############asyncTest############");
System.out.println("############a############");
asyncservice.test();
System.out.println("############b############");
return "success";
}
@RequestMapping("/asyncTest2.do")
public String asyncTest2() {
System.out.println("############asyncTest2############");
System.out.println("############a############");
asyncservice.test2();
System.out.println("############b############");
return "success";
}
@RequestMapping("/asyncTest3.do")
public String asyncTest3() {
System.out.println("############asyncTest3############");
System.out.println("############a############");
asyncservice.test3();
System.out.println("############b############");
return "success";
}
}
@Service
public class AsyncService {
public void test() {
System.out.println("############c############");
for (int i = 0; i < 5; i++) {
System.out.println(i);
}
System.out.println("############d############");
}
@Async
public void test2() {
System.out.println("############c############");
for (int i = 0; i < 5; i++) {
System.out.println(i);
}
System.out.println("############d############");
}
/**
* @Async就相当于另起一个线程
*/
public void test3() {
new Thread() {
@Override
public void run() {
System.out.println("############c############");
for (int i = 0; i < 5; i++) {
System.out.println(i);
}
System.out.println("############d############");
}
}.start();
}
}
- 4、@Scheduled与@EnableScheduling: 定时任务。@EnableScheduling这个注解需要加在启动类上,表示支持定时任务。
@Scheduled(cron = "0/5 * * * * ? ")
public void doTest() {
System.out.println(new SimpleDateFormat("YYYY-MM-dd HH:mm:ss").format(new Date()));
}
五、springboot热部署
1 导入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
2 选择【File】→【Settings】选项,打开Compiler面板设置页。
3 Shift+Alt+/”打开Maintenance选项框,选中并打开Registry页面。
4 启动项目,浏览器访问,修改controller中的内容,直接刷新页面
六、@SpringBootApplication
- Spring Boot应用的启动入口是@SpringBootApplication注解标注类中的main()方法;
- @SpringBootApplication能够扫描Spring组件并自动配置Spring Boot。
- @SpringBootApplication注解是一个组合注解,包含@SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan三个核心注解
七、使用@Profile注解进行多环境配置
八、springboot整合Redis
1. Redis简介
Redis 是一个开源(BSD许可)的、内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件,并提供多种语言的API。
2. Redis优点
- 存取速度快:Redis速度非常快,每秒可执行大约110000次的设值操作,或者执行81000次的读取操作。
- 支持丰富的数据类型:Redis支持开发人员常用的大多数数据类型,例如列表、集合、排序集和散列等。
- 操作具有原子性:所有Redis操作都是原子操作,这确保如果两个客户端并发访问,Redis服务器能接收更新后的值。
- 提供多种功能:Redis提供了多种功能特性,可用作非关系型数据库、缓存中间件、消息中间件等。
3. 导入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
4. 简单Redis
@SpringBootTest
class SpringbootRedisApplicationTests {
@Autowired
private RedisTemplate redisTemplate;
@Test
public void testSet(){
//存数据
redisTemplate.boundValueOps("name").set("zhangsan");
}
@Test
public void testGet(){
//读数据
Object name = redisTemplate.boundValueOps("name").get();
System.out.println(name);
}
}
4.5Redis序列化
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<Object,Object> redisTemplate(RedisConnectionFactory redisConnectionFactory){
RedisTemplate<Object , Object> template = new RedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
//使用JSON格式序列化对象,对缓存数据key和value进行转换
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
//解决查询缓存转换异常的问题
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
//设置RedisTemplate模板PAI的序列化方式为JSON
template.setDefaultSerializer(jackson2JsonRedisSerializer);
return template;
}
}
九、整合mybatis
1. 导入依赖
//mybatis依赖
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.4</version>
</dependency>
//mysql依赖
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
2. 准备数据库
3. 建立实体类
public class User {
private Integer id;
private String username;
private String password;
set and get。。。
}
4. 建立mapper接口
@Mapper
public interface Usermapper {
//查询全部
@Select("select * from user ")
public List<User> findAll();
}
5. 测试
@SpringBootTest
class SpringbootMybatisApplicationTests {
@Autowired
private Usermapper usermapper;
@Test
public void findAll(){
List<User> all = usermapper.findAll();
System.out.println(all);
}
}
十、thymeleaf模板引擎
1. 引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
2. 测试
前端
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>test</h1>
</body>
</html>
controller层
@Controller
public class Test {
@RequestMapping("/test")
public String test(){
return "test";
}
}
3. 存入数据
前端
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"> //要使用thymeleaf,需要在html文件中导入命名空间的约束xmlns:th="http://www.thymeleaf.org"
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>test</h1>
<!--用th:text不会解析html-->
<div th:text="${msg}"></div>
<!--用th:utext会解析html-->
<div th:utext="${msg}"></div>
<!--遍历数据-->
<!--th:each-->
<h4 th:each="m :${msg2}" th:text="${m}"></h4>
<h4>
<!--行内写法-->
<span th:each="user:${msg2}">[[${user}]]</span>
</h4>
</body>
</html>
controller层
@Controller
public class Test {
@RequestMapping("/test")
public String test(Model model){
model.addAttribute("msg","<h1>hello thylemeaf</h1>");
model.addAttribute("msg2", Arrays.asList("zhangsan","lisi"));
return "test";
}
}
4. 页面可以选择语言练习
1. 导入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
2. controller层
@Controller
public class LoginController {
@GetMapping("/login")
public String toLoginPage(Model model) {
model.addAttribute("currentYear", Calendar.getInstance().get(Calendar.YEAR));
return "login";
}
}
3. Configuration
@Configuration
public class MyLocalResovle implements LocaleResolver {
@Override
public Locale resolveLocale(HttpServletRequest httpServletRequest) {
String l = httpServletRequest.getParameter("l");
if (l != null){
String[] split = l.split("_");
Locale locale = new Locale(split[0], split[1]);
return locale;
}else {
Locale locale =Locale.getDefault();
return locale;
}
}
@Override
public void setLocale(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Locale locale) {
}
@Bean
public LocaleResolver localeResolver(){
return new MyLocalResovle();
}
}
4. 前端
<!DCOTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link th:href="@{/css/login.css}" rel="stylesheet">
</head>
<body class="text-center">
<form>
<h1 th:text="#{login.tip}">请登录</h1>
<input type="text" th:placeholder="#{login.username}" required="" autofocus="" class="userName">
<input type="password" th:placeholder="#{login.password}" required="" class="psd">
<div>
<label>
<input type="checkbox" value="remember-me" class="rem">[[#{login.rememberme}]]
</label>
</div>
<button type="submit" th:text="#{login.button}">登录</button>
<p>©<span th:text="${currentYear}">2022</span>
<span th:text="${currentYear+1}">2023</span>
</p>
<a th:href="@{/login(l='zh_CN')}">中文</a>
<a th:href="@{/login(l='en_US')}">English</a>
<a th:href="@{/login(l='ko_KR')}">韩文</a>
</form>
</body>
</html>
form{
text-align: center;
}
input.psd,input.userName{
display: block;
width: 300px;
height: 40px;
margin-bottom: 10px;
margin: 10px auto;
padding-left: 10px;
padding-right: 10px;
}
button{
width: 325px;
border-radius: 10px;
height: 40px;
background: #ccc;
margin-bottom: 40px;
}
div{
margin-bottom: 20px;
}
a{
margin-left: 10px;
}
5. 配置
messages.properties
login.tip=请登录
login.username=用户名
login.password=密码
login.rememberme=记住我
login.button=登录
中文 :messages_zh_CN.properties
login.tip=请登录
login.username=用户名
login.password=密码
login.rememberme=记住我
login.button=登录
英语 :messages_en_US.properties
login.tip=Please sign in
login.username=Username
login.password=Password
login.rememberme=Remember me
login.button=Login
十一、security认证授权
1 基于内存的自定义身份认证
@Autowired
private PasswordEncoder encoder;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// 1.基于内存的自定义身份认证
auth.inMemoryAuthentication()
.passwordEncoder(encoder)
.withUser("admin")
.password(encoder.encode("123456"))
.roles("vip");
}
@Bean
PasswordEncoder encoder() {
return NoOpPasswordEncoder.getInstance();
}
2. 基于JDBC的身份认证
@Autowired
private PasswordEncoder encoder;
@Autowired
private DataSource dataSource;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//2.基于JDBC的身份认证
String userSql = "select username, password, valid from account where username = ?";
String authoritysql = "select username, authority from account where username = ?";
auth.jdbcAuthentication()
.dataSource(dataSource)
.usersByUsernameQuery(userSql)
.authoritiesByUsernameQuery(authoritysql)
.passwordEncoder(encoder);
}
@Bean
PasswordEncoder encoder() {
return NoOpPasswordEncoder.getInstance();
}
3. 基于UserDetailsService的身份认证
@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true,prePostEnabled = true)
@Autowired
private PasswordEncoder encoder;
@Autowired
private AccountService accountService;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// 3.基于UserDetailsService的身份认证
auth.userDetailsService(accountService).passwordEncoder(encoder);
}
@Bean
PasswordEncoder encoder() {
return NoOpPasswordEncoder.getInstance();
}
4. 注销 和 记住我
//开启自动配置的注销的功能
// logout 注销请求
// .logoutSuccessUrl("/跳转首页"); 注销成功来到首页
http.logout().logoutSuccessUrl("/首页地址");
//记住我
http.rememberMe();
十二、异步 定时任务
1. 异步
service层
@Service
public class AsyncService {
@Async //告诉Spring这是一个异步方法
public void hello(){
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("进行中....");
}
}
controller层
@RestController
public class AsyncController {
@Autowired
AsyncService asyncService;
@GetMapping("/hello")
public String hello(){
asyncService.hello();
return "ok";
}
}
主启动类中添加@EnableAsync ,开启异步注解
@EnableAsync //开启异步注解功能
@SpringBootApplication
public class AsApplication {
public static void main(String[] args) {
SpringApplication.run(AsApplication.class, args);
}
}
测试
2. 定时任务
service层
@Service
public class ScheduledService {
//注意cron表达式的用法
//秒 分 时 日 月 周几
//0 * * * * 0-7
@Scheduled(cron = "0 0 12 * * ?") //表示每天中午12点触发
public void hello(){
System.out.println("hello.....");
}
}
主启动类中添加@EnableScheduling,开启基于注解的定时任务
@EnableAsync //开启异步注解功能
@EnableScheduling //开启基于注解的定时任务
@SpringBootApplication
public class SdApplication {
public static void main(String[] args) {
SpringApplication.run(SdApplication.class, args);
}
}
十三、发送邮件
1. 引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
2. 配置文件
配置类中配置
spring:
mail:
host: smtp.qq.com #QQ
username: @qq.com #自己的QQ邮箱
password: #QQ邮箱的授权码
default-encoding: UTF-8
3. 前端编写
<form method="post" style="text-align: center">
username:<input name="username" ><br>
msg:<input name="msg"><br>
<button type="submit">提交</button>
</form>
4. service层
@Service
public class
EmailService {
@Autowired
private JavaMailSender mailSender;
@Value("${spring.mail.username}") //得到QQ邮箱
private String from;
/**
* 发送普通邮件
*/
public void sendSimpleMail(String to, String subject, String content) {
SimpleMailMessage message = new SimpleMailMessage();
message.setTo(to); //收信人
message.setSubject(subject); //主题
message.setText(content); //内容
message.setFrom(from); //发信人
mailSender.send(message);
}
/**
* 发送HTML文件
*/
public void sendHtmlMail(String to, String subject, String content) {
//使用MimeMessage,MIME协议
MimeMessage message = mailSender.createMimeMessage();
MimeMessageHelper helper;
//MimeMessageHelper帮助我们设置更丰富的内容
try {
helper = new MimeMessageHelper(message, true);
helper.setFrom(from);
helper.setTo(to);
helper.setSubject(subject);
helper.setText(content, true); //true代表支持html
mailSender.send(message);
} catch (MessagingException e) {
e.printStackTrace();
}
}
/**
* 发送带附件的邮件
* @param filePath
*/
public void sendAttachmentMail(String to, String subject, String content, String filePath) {
MimeMessage message = mailSender.createMimeMessage();
MimeMessageHelper helper;
try {
helper = new MimeMessageHelper(message, true);
//true代表支持多组件,如附件,图片等
helper.setFrom(from);
helper.setTo(to);
helper.setSubject(subject);
helper.setText(content, true);
FileSystemResource file = new FileSystemResource(new File(filePath));
String fileName = file.getFilename();
helper.addAttachment(fileName, file); //添加附件,可多次调用该方法添加多个附件
mailSender.send(message);
} catch (MessagingException e) {
e.printStackTrace();
}
}
/**
* 发送带图片的邮件
*/
public void sendInlineResourceMail(String to, String subject, String content, String rscPath, String rscId) {
MimeMessage message = mailSender.createMimeMessage();
MimeMessageHelper helper;
try {
helper = new MimeMessageHelper(message, true);
helper.setFrom(from);
helper.setTo(to);
helper.setSubject(subject);
helper.setText(content, true);
FileSystemResource res = new FileSystemResource(new File(rscPath));
helper.addInline(rscId, res); //重复使用添加多个图片
mailSender.send(message);
} catch (MessagingException e) {
e.printStackTrace();
}
}
}
5. 主启动类中测试
@RunWith(SpringRunner.class)
@SpringBootTest
public class EmApplicationTests {
@Autowired
private EmailService emailService;
@Autowired
private TemplateEngine templateEngine;
//普通文件
@Test
public void emailService() {
String to ="2842245203@qq.com";
String subject = "文本";
String text = "需要好好学习springboot";
emailService.sendSimpleMail(to, subject, text);
}
//HTML邮件
@Test
public void sendHtmlMail(){
String to = "2842245203@qq.com";
String subject = "【HTML邮件模板】标题";
//使用HTML模板邮件定制邮件正文内容
Context context = new Context();
context.setVariable("username","2842245203@qq.com");
context.setVariable("msg","shenme");
//使用TemplateEngine设置要处理的模板页面
String emailContent = templateEngine.process("sendMsg",context);
//发送HTML模板邮件
emailService.sendHtmlMail(to,subject,emailContent);
}
@Test
public void sendComplexEmailTest(){
String to = "2842245203@qq.com";
String subject = "【复杂邮件】标题";
//指定静态资源文件和附件路径
String content = "可爱小猫咪";
String filePath = "路径";
//发送复杂邮件
emailService.sendAttachmentMail(to,subject,content,filePath);
}
}
测试