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;
}

img

自定义启动图标(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>   
    
    - 需要什么功能就导入对应启动器
    
    - https://docs.spring.io/spring-boot/docs/current/reference/html/using-spring-boot.html#using-boot-starter 
  • 主程序

    @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) 自动配置导入选择
      • SpringBootConfiguration:springboot配置类

        • Configuration:spring配置类

          • Component:这一是spring
      • ComponentScan 扫描启动类同级别的包

    多文件配置

    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>
    
    ![image-20200816135456182](https://i.loli.net/2020/08/16/tYJ8boeL1rpRDO9.png)
  • 不自己配置时:

    • 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方式)

 导入依赖
    <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">

<select id="queryEmployeeList" resultType="Employee">
    select * from Employee
</select>


# 整合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思想

<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>

@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:连接数据

![img](https://img-blog.csdn.net/20151205212625574)

![img](https://img-blog.csdn.net/20151205210817437)

![img](https://img-blog.csdn.net/20151205213147424)

- ```xml
  <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 # 创建节点
    
    # 分布式
    
    
  • 网络之上的软件系统
  • 原来的分布式,全部的业务放在一个机器上,机器之间安全相同,nginx做负载均衡

    • 有些流程很简单,有些复杂
    • 机器利用率不高

    image-20200817155613692

    小型网站

    img

    现在的小型网站,功能呢模块无法利用

    img

    分布式架构,提取公共模块

    img

    现在流行的分布式

    img

    基础就是微服务架构

    Dubbo + Zookeeper +Springboot

    img

    RPC

  • 远程过程调用
  • 电脑一A方法调用电脑二B方法
  • 核心模块

    • 通信
    • 序列化

    dubbo

  • 高性能,轻量级 javaRPC框架
  • 面向接口的远程方法调用
  • 智能容错与负载均衡
  • 服务自动注册与发现,使用zookeeper
  • img
  • 使用监控管理后台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>
    <!-- 输出到控制台 -->

<!-- 默认日志过滤器 -->
<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>

${log.path}

    <!-- 日志问价切割策略 :每天一个日志-->
    <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-ref ref="console"/>
<appender-ref ref="file"/>

<logger name="com.dzf.controller.SpringController"  level="warn" additivity="false">             <appender-ref ref="console"/>                 
</logger>  


## 使用log4j日志管理,去掉logback

<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>           

<groupId>org.springframework.boot</groupId>                
<artifactId>spring-boot-starter-log4j</artifactId>
<version>1.3.8.RELEASE</version>           


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开发标准,开箱即用
    - 自动配置了很多

-  微服务架构

  - 模块化,功能化

    - 原来是所有功能在一起,签到,支付,娱乐
    - 人多,
    - 横向增加机器
    - 签到多,支付少-----》模块化

  - 微服务架构问题,因为网络不可靠

    - <font color=red>服务很多,客户端如何访问?API网关,服务路由</font>
    - <font color=red>服务很多,服务之间如何通信 ,RPC框架,异步调用</font>
    - <font color=red>服务很多,如何治理 ,服务注册与发现,高可用</font>
    - <font color=red>服务挂了,咋办 ?熔断机制,服务降级</font>

  - 解决方案

    - SpringCloud,生态圈,解决上边4个问题,SpringCloud基于SpringBoot

    - 方案一:SpringCloud netfix ,一站式解决方案,但2018年底无限期不再维护

      - api网关 zuul组件
      - Feign --》httpclient---》http的通信方式,同步阻塞
      - 服务注册与发现:Eureka
      - 熔断机制:hystrix

    - 方案二:<font color=red>Apache Dubbo zookeeper</font>,本来是不维护了,后来又维护了,

      - API:没有,第三方组件

      - dubbo,高性能RPC框架

      - 服务注册与发现,zookeeper;

      - 没有,借助了hystrix

        dubbo3.0还为出现,预览中,有很多新东西

    - <font color=red>方案三:SpringCloud Alibaba,一站式解决方案</font>

    - 服务网格,下一代微服务架构 Server mesh

      - 对应方案:istio


​    Springboot视频地址:https://www.bilibili.com/video/BV1PE411i7CV





 

 

标签: none

评论已关闭