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

    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

      • 核心依赖在父工程中
    • 启动器

    • 主程序

      @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>
        </dependency>
        

        image-20200816135456182

    • 不自己配置时:

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

    img

    img

    img

    • <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做负载均衡

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

    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>
    <!-- 输出到控制台 -->
        
    <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开发标准,开箱即用
        • 自动配置了很多
    • 微服务架构

    • 模块化,功能化

      • 原来是所有功能在一起,签到,支付,娱乐
      • 人多,
      • 横向增加机器
      • 签到多,支付少-----》模块化
    • 微服务架构问题,因为网络不可靠

      • 服务很多,客户端如何访问?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

  • 相关阅读:
    elasticsearch 不能通过9200端口访问
    elasticsearch 2.0+ 安装 Marvel
    修改es最大返回结果数
    ElasticSearch的Marvel更新license
    python-execjs(调用js)
    爬取豆瓣电影排名的代码以及思路
    docker的安装
    mysql主从同步
    Docker配置yapi接口
    第24课
  • 原文地址:https://www.cnblogs.com/Dean0731/p/13518595.html
Copyright © 2020-2023  润新知