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

原文地址:https://www.cnblogs.com/Dean0731/p/13518595.html