Springboot
Springboot是什么?
javaweb框架,简化开发,扩展性好
核心特点
-
Jar形式独立运行
-
内嵌servlet容器,tomcat集成springmvc,spring
-
简化Maven配置
-
自动装配dean
-
提供基于http,ssh,telnet对运行时项目的监控
-
不需要任何xml文件,借助注解,properties实现spring配置
微服务
-
MVC
-
MVVM:model view viewmodel
- model:服务器上的业务逻辑操作
- view:页面
- viewmodel:model,view核心枢纽流入vue.js
- view-》viewmodel--》model
-
微服务:将原来的userservice===》模块
- 原来是所有的功能放在一个项目
- 微服务是将功能分开,向外提供接口
创建工程
方式一:https://start.spring.io/ 根据需要选择,最后添spring web依赖,
方式二:idea spring initilizer,需要自己添加web包
<!-- 父级依赖 -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.3.RELEASE</version>
</parent>
<!--启动器:web环境下的依赖, 使用springmvc spring的jar,tomcat等 -->
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<!--打包插件-->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
# properties格式
# 配置文件
# 更改端口号
server.port=8081
debug: true
# Yml(:后有空格)
server:
port: 8081
student: # 在实体类中注入,属性值必须相同,也可用于配置文件的注入
name: Dean${random.int} # spel表达式
age: 120
birthday: 2019/11/02
maps: {k1: v1,k2: v2}
student: {name: Dean,age: 120}
pets:
- cat
- dog
- pig
pets:[cat,dog,pig]
@Component
@ConfigurationProperties(prefix = "student")
@Validated //数据校验
public class Student {
@Email("邮箱格式错误")
private String email;
}
自定义启动图标(banner)
http://patorjk.com/software/taag
resources下复制保存为banner.txt
常用maven命令
- clean:清理
- package:打包jar或war
示例
@RequestMapping("/login")
@ResponseBody
public User login(User user){
return user;
}
//User字段:userName pwd
//那么在前台接收到的数据为:'{"userName":"xxx","pwd":"xxx"}'效果等同于如下代码:
@RequestMapping("/login")
public void login(User user, HttpServletResponse response){
response.getWriter.write(JSONObject.fromObject(user).toString());
}
自动配置
-
pom.xml
- 核心依赖在父工程中
-
启动器
-
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies>
-
需要什么功能就导入对应启动器
-
-
主程序
@SpringBootApplication //标注是springboot应用 public class HellowordApplication { public static void main(String[] args) { //启动 SpringApplication.run(HellowordApplication.class, args); } }
- SpringBootApplication包含比较重要的几个
- EnableAutoConfiguration(exclude={xxxConfiguration.class,yyy.Configuartion})启动自动配置
- AutoConfigurationPackage:自动配置包
- Import(AutoConfigurationPackages.Registrar.class) 导入选择器 包注册
- Import(AutoConfigurationImportSelector.class) 自动配置导入选择
- AutoConfigurationPackage:自动配置包
- SpringBootConfiguration:springboot配置类
- Configuration:spring配置类
- Component:这一是spring
- Configuration:spring配置类
- ComponentScan 扫描启动类同级别的包
- EnableAutoConfiguration(exclude={xxxConfiguration.class,yyy.Configuartion})启动自动配置
- SpringBootApplication包含比较重要的几个
多文件配置
classpath:
- java文件夹
- resources文件夹
配置文件位置:
- resources下 优先级最低
- resources/config/下 优先级较低
- 根目录下 优先级次高
- 根目录/config/下 优先级最高
多配置文件:
-
默认的还是原名
-
建立其他的application-dev.properties
-
测试开发时,每次分别使用不同properties,在主properties中指定: spring.profiles.active=dev ,指定配置文件
-
# --- 可用作分割,不用多个文件 server: port: 8081 spring: profile: active: dev --- server: port: 8081 spring: profile: dev --- server: port: 8081 spring: profile: test
application.yaml 与spring.factories 有很大联系
spring.factories:可能需要配置文件类
- 里边是各种配置文件xxxxContiguration,需要xxxproperties,而properties需要自动注入,配置信息通过上边的方式,yaml装入
- 每个类的注解
- Configuration:表名是一个配置类
- EnableConfigurationProperties:允许yaml注入属性
- ConditionOnWebapplication:根据条件确定是否要配置
WEB开发
静态问价访问
-
不自己配置时:引入的js文件等静态依赖,默认会在Resources/webjars/下边
-
<dependency> <groupId>org.webjars</groupId> <artifactId>jquery</artifactId> <version>3.5.1</version> </dependency>
-
-
不自己配置时:
- classpath:/resources 优先级最高
- classpath:/static 其次
- classpath:/public 最后
- classpath:/mate-inf/resources
-
自己配置
spring.mvc.static-path-pattern=/Dean/**,classpath:/xxxx
首页与图标
- 默认在静态文件夹下找index.html
- 当网站是Restful类型时,用restfulcontroller注解
- 若要返回html,用controller注解
- html要位于template文件夹下
- 方法返回对应文件名,不带后缀
- 需要导入模板引擎 thymeleaf,等等
图标:静态文件夹下/favicon.ico
自定义配置类
读取配置时会看是否有用户自定义配置,有的话使用用户的,
对于有的配置可有多个,例如视图解析器,就结合使用
// 扩展springmvc ,dispatcherservlet
@Configuration
public class MyWebConfig implements WebMvcConfigurer {
// 请求跳转 dzf--》hello 但url不变
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/dzf").setViewName("/hello");
}
// 实现视图解析器接口的类,我们就可以把他看做视图解析器
@Bean // 交给Springboot自动装配
public ViewResolver myViewResolver(){
return new MyViewResolver();
}
//自定义视图解析器
public static class MyViewResolver implements ViewResolver{
@Override
public View resolveViewName(String viewName, Locale locale) throws Exception {
return null;
}
}
}
拦截器
public class LoginHandlerInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
Object loginUser = request.getSession().getAttribute("key");
if(loginUser==null){
request.getRequestDispatcher("/index.html").forward(request,response);
return false;
}
return true;
}
}
// 自定义webconfig中添加
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginHandlerInterceptor()).addPathPatterns("/**")
.excludePathPatterns("/index.html","/user/login/**","/css/**");
}
404页面
在template下边建立error文件夹建立404.html,对应错误会找到对应的
数据源配置
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- 引入依赖-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.21</version>
</dependency>
spring:
datasource:
username: root
password: 123456
url: jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
driver-class-name: com.mysql.cj.jdbc.Driver
# driver-class-name: com.mysql.jdbc.Driver this is lower Driver
type: com.alibaba.druid.pool.DruidDataSource
# druid's owner config, springboot doesn't these
# if you wang to use this ,you should write one config
initialSize: 5
minIdle: 5
# and so on
# driud's plugins config
filters: stat,wall,log4j
maxPoolPreparedStatementPerConnectionSize: 20
useGlobalDataSourceStat: true
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
@Autowired
DataSource dataSource;
@Test
void contextLoads() throws SQLException {
// class com.zaxxer.hikari.HikariDataSource 也是一个连接池,类似c3p0,几乎是最快,默认,通过yaml中type选择
System.out.println(dataSource.getClass());
Connection connection = dataSource.getConnection();
System.out.println(connection);
connection.close();
}
@Configuration
public class DruidConfig {
@ConfigurationProperties(prefix = "spring.datasource")
@Bean
public DataSource druidDataSource(){
return new DruidDataSource();
}
// 后台监控
// springboot 内置servlet容器,没有web.xml 替代方法: ServletRegistrationBean 注册进去即可
@Bean
public ServletRegistrationBean a(){
ServletRegistrationBean<StatViewServlet> bean = new ServletRegistrationBean<>(new StatViewServlet(),"/druid/*");
// 后台登录,账号密码
HashMap<String,String> initParameters = new HashMap<>();
initParameters.put("loginUsername","admin"); // 名字不能改
initParameters.put("loginPassword","admin");
initParameters.put("allow","");
bean.setInitParameters(initParameters);
return bean;
}
//filter
public FilterRegistrationBean webSataFilter(){
FilterRegistrationBean bean = new FilterRegistrationBean();
bean.setFilter(new WebStatFilter());
// 可以过滤那些请求
Map<String,String> initParameters = new HashMap<>();
// 不过滤
initParameters.put("exclusions","*.js,*.css,/durid/*");
return bean;
}
}
数据库JDBC
//jdbc ,有dao
@Autowired
JdbcTemplate jdbcTemplate;
@RequestMapping("/getAll")
public List<Map<String,Object>> userList(){
List<Map<String,Object>> list = jdbcTemplate.queryForList("select * from user");
return list;
}
整合mybatis(xml方式)
导入依赖
<!-- 不是springboot官方的,是mybatis自己的-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.1</version>
</dependency>
配置yaml
mybatis:
type-aliases-package: top.dean0731.model
mapper-locations: classpath:mybatis/mapper/*xml
mapper 文件
@Mapper
@Repository
public interface EmployeeMapper {
List<Employee> queryEmployeeList();
Employee queryEmployerById();
int addEmployee(Employee employee);
int updateEmployee(Employee employee);
int deleteEmployee(int id);
}
//mapper 代替dao,Resource下mybatis/mapper/下创建配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0/EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="top.dean0731.mapper.EmployeeMapper">
<select id="queryEmployeeList" resultType="Employee">
select * from Employee
</select>
</mapper>
整合mybatis(注解方式)
@Mapper
@Repository
public interface UsersMapper {
@Select("select * from t_user where name = #{name}")
User findUserByName(@Param("name")String name);
@Insert("insert into t_user(name,password)values(#{name},#{password})")
void addUser(@Param("name")String name,@Param("password")String password);
}
SpringSecurity(安全)
-
与shiro类似,只不过类,名字不同而已
-
功能:认证,授权
-
以前使用拦截器,代码量很大,现在使用框架
-
功能权限,访问权限,菜单权限
-
AOP思想
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
//首页都可以访问,其他不行,
// 授权的规则
http.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/level1/*").hasRole("vip1")
.antMatchers("/level2/*").hasRole("vip2")
.antMatchers("/level3/*").hasRole("vip3");
// 开启记住我功能
//http.rememberMe();
http.rememberMe().rememberMeParameter("remeber");
// 没有权限跳转到登录页面,
// 没有认证到/login,认证失败到/login?error ,是自带的login页面与逻辑是一个请求
//http.formLogin();
// 自定义登录页面,但提交的url之能是 /login,即mylogin html中action=/login
//http.formLogin().loginPage("/mylogin.html").loginProcessingUrl("/login") ;
http.formLogin().loginPage("/mylogin.html").usernameParameter("pwd").loginProcessingUrl("/login") ;
// 防止跨站攻击功能,自定义登录页面时使用
http.csrf().disable();
// 开启注销功能 回去请求/logout
http.logout().logoutSuccessUrl("/a");
}
//
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// 正常应该从数据库选择
// 若在数据库中按照此方式写入即可
auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
.withUser("username").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1","vip2")
.and()
.withUser("root").password(new BCryptPasswordEncoder().encode("123456")).roles("vip3","vip1","vip2");
}
}
- 自带登录部分,有登录页面,登录controller,
- 若要自己定义登录页面,html输入用户名username,pasword,remeberme到/login,注意需要关闭csrf,
整合Shiro
Subject:用户
SecurityManager:管理所有用户
Realm:连接数据
-
<dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.4.1</version>
-
配置类
@Configuration public class ShiroConfig { @Bean public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("defaultWebSecurityManager")DefaultWebSecurityManager securityManager){ ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean(); // 设置管理器 bean.setSecurityManager(securityManager); // 添加内置过滤器,判断当前用户的权限 /* * anon:无需认证 * authc:必须认证 * user:必须有记住我才能使用 * perms:有对某个资源的权限才能 * role:有某个角色才能 * */ Map<String,String> filter = new LinkedHashMap<>(); filter.put("/","anon"); filter.put("/user/*","authc"); filter.put("/user/add","perms[user:add]"); bean.setFilterChainDefinitionMap(filter); // 设置登录url,默认是/login.jsp,里面的登录逻辑还需要自己在controller中写,没有自带 // bean.setLoginUrl("/登录.html"); // bean.setUnauthorizedUrl("/未授权.html"); return bean; } // SecurityManager @Bean public DefaultWebSecurityManager defaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){ DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); // 关联UserRealm securityManager.setRealm(userRealm); return securityManager; } // 创建realm ,需要自定义,此时bean名字就是userRealm @Bean public UserRealm userRealm(){ return new UserRealm(); } } class UserRealm extends AuthorizingRealm{ // @Autowired UserService userservice // 进入页面时调用 //授权 经过后,用户数据库中的权限读取出来,付给subject @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); info.addStringPermission("user:add"); //拿到对象 Subject subject= SecurityUtils.getSubject(); //User currentUser = (User)subject.getPrincipal() // info.addStringPermissions(user.get权限); 数据库中直接字符串存储 user:add,一般都是按键权限,角色对应表 return info; } // 用户认证,经过后 用户有认证权限 // 登录是调用 @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { //连接数据库 UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken; // User user = userService.queryUserByName(token.getUsername()); // if(user==null)return null // 密码认证 // return new SimpleAuthenticationInfo("",user.getPassword(),""); return new SimpleAuthenticationInfo("","123456",""); } }
-
导入依赖并且写log4j.properties,
开源Springboot项目
Swagger
前后端分离:Vue+SpringBoot
- 后端:后端控制,服务层,数据库
- 前端:前端控制,视图层
- 前后端交互 ====》API接口
写代码时前后端需要及时交互,因此后端API需要及时更新
Swagger
- Restful API,API文档自动生成
- 可以在线测试
- 支持多种语言
Springboot使用:
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
@Configuration
@EnableSwagger2 // 开启swagger2
public class SwaggerConfig {
@Bean
public Docket docket2() {
return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo()).groupName("B");
}
@Bean
public Docket docket(){
return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo()) // 到这里默认扫描全部接口
// 配置api分组明,配置多个docket
.groupName("分组A")
// 开启或关闭 swagger2
.enable(true)
// 配置扫描的api接口
.select()
// RequestHandlerSelectors.any()全部扫描
// RequestHandlerSelectors.none()不扫描
// RequestHandlerSelectors.withMethodAnnotation(RestController.class)注解扫描
.apis(RequestHandlerSelectors.basePackage("top.dean0731.helloworld.controller"))
// 过滤路径
.paths(PathSelectors.ant("/user/add/**"))
.build();
}
private ApiInfo apiInfo(){
return new ApiInfoBuilder().title("WEAF项目整合Swagger")
.description("SpringBoot整合Swagger,详细信息......")
.version("1.0")
.contact(new Contact("啊啊啊啊","blog.csdn.net","aaa@gmail.com"))
.license("The Apache License")
.licenseUrl("http://www.baidu.com")
.build();
}
}
访问:http://localhost:/swagger-ui.html
异步调用
用户执行耗时工作:
- 正常情况,直接等待,不能操作
- 多线程,后台主线程返回提示,子线程继续操作,自己实现
- Spingboot的异步操作
@@EnableAsync 启动类加上
Service
@Async
public Future<String> doTask1() throws Exception {
System.out.println("---任务一---");
long start = System.currentTimeMillis();
Thread.sleep(random.nextInt(10000));
long end = System.currentTimeMillis();
System.out.println("---任务一结束---耗时:"+(end-start));
return new AsyncResult<String>("任务一结束");
}
Controller
public String async()throws Exception{
System.out.println("---async---");
long start = System.currentTimeMillis();
Future<String> task1 = service.doTask1();
Future<String> task2 = service.doTask2();
Future<String> task3 = service.doTask3();
while (true) {
if(task1.isDone()&&task2.isDone()&&task3.isDone()){
break;
}
Thread.sleep(1000);
}
long end = System.currentTimeMillis();
return "all executed time:"+(start-end);
}
整合email
163邮箱授权码直接写密码
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
spring:
mail:
username: dean0731qq.com
password: xxxxx
host: smtp.qq.com
properties.mail.smtp.ssl.enable: true
@Service
public class EmailServiceImpl implements EmailService{
@Autowired
private JavaMailSender mailSender;
@Override
public void sendSimpleMail(String sendTo, String content) {
SimpleMailMessage message = new SimpleMailMessage();
message.setSubject("yyy");
message.setFrom("xxxx");
message.setTo(sendTo);
message.setText(content);
mailSender.send(message);
}
@Override
public void mimeMail(String sendTo, String content) {
MimeMessage m = mailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(m,true,"utf-8");
helper.setSubject("xxx")
helper.setFrom("xxxx");
helper.setTo(sendTo);
helper.setText("<html</html>",true);
helper.addAttachment("x,jpg",new File("xxxxx"))
mailSender.send(m);
}
}
@Controller
public class EmailController {
@Autowired
private EmailService service;
@RequestMapping("/simple")
@ResponseBody
public String sendEmail(){
service.sendSimpleMail("1028968939@qq.com", "测试邮件", "Hello world!");
return "success";
}
}
// 模板邮件
@Override
public void sendTemplateMail(String sendTo, String title, String infoTemplate) {
MimeMessage msg = mailSender.createMimeMessage();
try {
//true设置为mutlipart模式
MimeMessageHelper helper = new MimeMessageHelper(msg,true);
helper.setFrom("xxx");
helper.setTo(sendTo);
helper.setSubject(title);
//封装模板数据
Map<String, Object> model = new HashMap<>();
model.put("username","dean")
//model.put("path", "img/d.png");
//得到模板
Template template = freemarkerConfigurer.getConfiguration().getTemplate(infoTemplate);
String html = FreeMarkerTemplateUtils.*processTemplateIntoString*(template, model);
helper.setText(html,true);
} catch (MessagingException | IOException e) {
e.printStackTrace();
} catch (TemplateException e) {
e.printStackTrace();
}
mailSender.send(msg);
}
定时任务
- @EnableScheduling 开启定时任务 启动类加上
- @Scheduled 什么时候执行
- TaskScheduler 调度者
- TaskExecutor 执行者
@Service
// 要求在某一时间执行
@Scheduled("cron表达式,与linux相同秒:分 时 日 月 周")
@Scheduled("* * * * * ?")// 每秒一次
public void service(){
}
整合redis
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
spring
redis:
host: 127.0.0.1
port: 6379
# 集群
cluster:
# Springboot 2.0 之后连接池用 lettuce
lettuce:
@service
@AutoWired
@RedisTemplate template
public void test(){
// 原生操作
//template.opsForList()
//template.opsForZSet()
// 常用方法,事物,增删改查等
// 获取链接
//RedisConnect con = template.getConnectionFactory().getConnection();
//con.flushDb();
//con.flushAll()
template.opsValue().set('key','value')
template.opsValue().get('key')
// 真实场景,对象写入redis需要序列化,所以 model一般都回实现序列化接口
}
Springboot中的RedisTemplate完成了自己的序列化,
不能识别,因为RedisTemplate的序列化方式不完善,是JDK自己的序列化,此时redis查看的字符会转义
要自己定义,解决问题,
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory redisConnectionFactory){
// 为了开放方便 使用 string,object
RedisTemplate<String,Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
Jackson2JsonRedisSerializer jack =new Jackson2JsonRedisSerializer(Object.class);
// 对象转义
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
// om.activateDefaultTyping(); 2.10之后使用这个
jack.setObjectMapper(om);
// 设置key的序列化方式
template.setKeySerializer(stringRedisSerializer);
template.setHashKeySerializer(stringRedisSerializer);
// 设置value的序列化方式
template.setValueSerializer(jack);
template.setHashValueSerializer(jack);
template.afterPropertiesSet();
return template;
}
}
zookeeeper
- 分布式应用程序协调服务
- 是hadoop与hbase的重要组件
- 为分布式应用提供一致性服务的软件
- 配置维护
- 域名服务
- 分布式同步
- 组服务 ,例如服务的注册与发现
windows下使用:
-
下载
-
运行 ~/bin/zKserver.cmd
- java环境要配置好classpath等等
- conf/zoo_simple.cfg 复制到conf/zoo.cfg 默认端口2181
-
~/bin/zkCli.cmd 客户端连接
-
ls / create /firstname 123 # 创建节点 get /fristname # 查看节点内容
-
分布式
-
网络之上的软件系统
-
原来的分布式,全部的业务放在一个机器上,机器之间安全相同,nginx做负载均衡
- 有些流程很简单,有些复杂
- 机器利用率不高
小型网站
现在的小型网站,功能呢模块无法利用
分布式架构,提取公共模块
现在流行的分布式
基础就是微服务架构
Dubbo + Zookeeper +Springboot
RPC
- 远程过程调用
- 电脑一A方法调用电脑二B方法
- 核心模块
- 通信
- 序列化
dubbo
- 高性能,轻量级 javaRPC框架
- 面向接口的远程方法调用
- 智能容错与负载均衡
- 服务自动注册与发现,使用zookeeper
- 使用监控管理后台dubbo-admin,可以不要
- github下载,是个Springboot项目
- 修改配置文件application.properties
- 按需要修改默认端口
- 后台端口7001 root,root
- dubbo.registry.address=zookeeper://ip:port
- 打包该项目 mvn clean package -Dmaven.test.skip=true
- 生成jar
服务提供者:可以理解为一些后端项目APi接口
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>2.7.7</version>
</dependency>
<dependency>
<groupId>com.github.sgroschupf</groupId>
<artifactId>zkclient</artifactId>
<version>0.1</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>5.1.0</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>5.1.0</version>
</dependency>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.6.1</version>
<exclusions>
<!-- 与springboot日志冲突,排除-->
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
</exclusions>
</dependency>
dubbo:
application:
name: provider-server1
registry:
address: zookeeper://127.0.0.1:2181
scan:
base-packages: top.dean0731.helloworld.service # 服务
//service
@DubboService // dubbo的service ,启动时可以被扫描注册到注册中心
@Component // spring 的万能注解
public class UserserviceImpl implement UserService{
public String service1(){}
}
public interface UserService{
public String service1();
}
- 启动zookeeper 端口2181
- 启动dubbo监控 端口7001
- 启动项目服务端dubbo,此时该服务的访问地址是 ip:20880
- 注意此时暴露在外面的是service,不是controller,要访问controller还是8080
- 启动customer
客户端使用:
同上
dubbo:
application:
name: consumer-1
registry:
address: zookeeper://127.0.0.1:2181
scan:
@DubboService //dubbo的service 放到容器中
public class 调用Service{ //
@Reference //dubbo的reference
UserService service; //路径相同建立UserService类或者使用pom坐标
public void 使用(){
String s= service.service1()
}
}
日志管理
-
(架包默认添加是logback) 若想更改logback:加入全局配置文件
-
其他日志系统使用时只用把配置问价放在src下即可,不用全局配置文件
-
默认info级别信息
- Trace,debug,info,warn,error,fatal,off从低到高:设置为warn,低与warn的不会输出
- root日志以warn级别即以上输出
- springframework.web日主以debug级别输出
Logging.level.root=warn
Logging.level.org.springframework.web=debug
自定义日志配置
<?xml version="1.0" encoding="utf-8"?>
<!-- 发生改变时:重新加载, 每间隔60s扫描一下,是否改变 ,debug:打印内部日志信息 -->
<configuration scan="true" scanPeriod="60 seconds" debug="false">
<!-- 上下文名字 -->
<contextName>logback</contextName>
<!-- 定义变量 -->
<property name="log.path" value="G:\\soft_doc\\Myeclispe_Project\\info.log"></property>
<!-- 输出到控制台 -->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<!-- 默认日志过滤器 -->
<filter class="ch.qos.logback.classic.filter.ThresholdFilter"> <level>ERROR</level>
</filter> -->
<encoder>
<pattern>%d{HH:mm:ss.SSS}%contextName[%thread]%-5level%logger{36} -%msg%n</pattern>
</encoder>
</appender>
<!-- 输出到文本 -->
<appender name="file" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>${log.path}</file>
<!-- 日志问价切割策略 :每天一个日志-->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>${log.path}/logback.%d{yyyy-MM-dd}.log</fileNamePattern> </rollingPolicy>
<encoder>
<pattern>%d{HH:mm:ss.SSS}%contextName[%thread]%-5level%logger{36} -%msg%n
</pattern>
</encoder>
</appender>
<root level="info">
<appender-ref ref="console"/>
<appender-ref ref="file"/>
</root>
<!-- logback为java中的包单独设置日志级别 -->
<logger name="com.dzf.controller"/>
<!-- 具体到类:设置新的级别,是否向上级传递打印信息 -->
<logger name="com.dzf.controller.SpringController" level="warn" additivity="false"> <appender-ref ref="console"/>
</logger>
</configuration>
使用log4j日志管理,去掉logback
<!-- 取出日志文件logback -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 添加loj4j -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j</artifactId>
<version>1.3.8.RELEASE</version>
</dependency>
2,log4j.properties
#############
# 输出到控制台
#############
# log4j.rootLogger日志输出类别和级别:只输出不低于该级别的日志信息DEBUG < INFO < WARN < ERROR < FATAL
# WARN:日志级别 CONSOLE:输出位置自己定义的一个名字 logfile:输出位置自己定义的一个名字
log4j.rootLogger=WARN,CONSOLE,logfile
# 配置CONSOLE输出到控制台
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
# 配置CONSOLE设置为自定义布局模式
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
# 配置CONSOLE日志的输出格式 [frame] 2019-08-22 22:52:12,000 %r耗费毫秒数 %p日志的优先级 %t线程名 %C所属类名通常为全类名 %L代码中的行号 %x线程相关联的NDC %m日志 %n换行
log4j.appender.CONSOLE.layout.ConversionPattern=[frame] %d{yyyy-MM-dd HH:mm:ss,SSS} - %-4r %-5p [%t] %C:%L %x - %m%n
################
# 输出到日志文件中
################
# 配置logfile输出到文件中 文件大小到达指定尺寸的时候产生新的日志文件
log4j.appender.logfile=org.apache.log4j.RollingFileAppender
# 保存编码格式
log4j.appender.logfile.Encoding=UTF-8
# 输出文件位置此为项目根目录下的logs文件夹中
log4j.appender.logfile.File=logs/root.log
# 后缀可以是KB,MB,GB达到该大小后创建新的日志文件
log4j.appender.logfile.MaxFileSize=10MB
# 设置滚定文件的最大值3 指可以产生root.log.1、root.log.2、root.log.3和root.log四个日志文件
log4j.appender.logfile.MaxBackupIndex=3
# 配置logfile为自定义布局模式
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %F %p %m%n
##########################
# 对不同的类输出不同的日志文件
##########################
# club.bagedate包下的日志单独输出
log4j.logger.club.bagedate=DEBUG,bagedate
# 设置为false该日志信息就不会加入到rootLogger中了
log4j.additivity.club.bagedate=false
# 下面就和上面配置一样了
log4j.appender.bagedate=org.apache.log4j.RollingFileAppender
log4j.appender.bagedate.Encoding=UTF-8
log4j.appender.bagedate.File=logs/bagedate.log
log4j.appender.bagedate.MaxFileSize=10MB
log4j.appender.bagedate.MaxBackupIndex=3
log4j.appender.bagedate.layout=org.apache.log4j.PatternLayout
log4j.appender.bagedate.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %F %p %m%n
自定义消息转换器
@Bean
private StringHttpMessageConverter converter(){
StringHttpMessageConverter converter = new
StringHttpMessageConverter(Charset.*forName*("utf-8"));
return converter;
}
使用FastJson解析json
配置FastJson
1,在MyWebConfig中重写
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) { //创建fastjson消息解析器
FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();
//创建fastjson的配置对象
FastJsonConfig config = new FastJsonConfig();
//对json数据进行格式化
config.setSerializerFeatures(SerializerFeature.PrettyFormat);
converter.setFastJsonConfig(config);
converters.add(converter);
}
@Bean
public HttpMessageConverters fastJsonMessageConverter(){
//创建fastjson消息解析器
FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();
//创建fastjson的配置对象
FastJsonConfig config = new FastJsonConfig();
//对json数据进行格式化
config.setSerializerFeatures(SerializerFeature.PrettyFormat);
converter.setFastJsonConfig(config);
return new HttpMessageConverters(converter);
}
现在和未来
回顾以前:架构
-
三层架构MVC
- 解耦合
-
开发框架
- spring
- IOC
- AOP
- 切面,动态代理
- 不影响业务情况下增加功能
- 日志,事物等
- 轻量级java开源容器
- 解决开发复杂性
- Springboot
- 不是新东西,就是Spring的升级版
- 新一代JAVAEE开发标准,开箱即用
- 自动配置了很多
- spring
-
微服务架构
-
模块化,功能化
- 原来是所有功能在一起,签到,支付,娱乐
- 人多,
- 横向增加机器
- 签到多,支付少-----》模块化
-
微服务架构问题,因为网络不可靠
- 服务很多,客户端如何访问?API网关,服务路由
- 服务很多,服务之间如何通信 ,RPC框架,异步调用
- 服务很多,如何治理 ,服务注册与发现,高可用
- 服务挂了,咋办 ?熔断机制,服务降级
-
解决方案
-
SpringCloud,生态圈,解决上边4个问题,SpringCloud基于SpringBoot
-
方案一:SpringCloud netfix ,一站式解决方案,但2018年底无限期不再维护
- api网关 zuul组件
- Feign --》httpclient---》http的通信方式,同步阻塞
- 服务注册与发现:Eureka
- 熔断机制:hystrix
-
方案二:Apache Dubbo zookeeper,本来是不维护了,后来又维护了,
-
API:没有,第三方组件
-
dubbo,高性能RPC框架
-
服务注册与发现,zookeeper;
-
没有,借助了hystrix
dubbo3.0还为出现,预览中,有很多新东西
-
-
方案三:SpringCloud Alibaba,一站式解决方案
-
服务网格,下一代微服务架构 Server mesh
- 对应方案:istio
-
Springboot视频地址:https://www.bilibili.com/video/BV1PE411i7CV