1. 程式人生 > 實用技巧 >pytest入坑到放棄2--pytest.main()

pytest入坑到放棄2--pytest.main()

Springboot是什麼?

javaweb框架,簡化開發,擴充套件性好

核心特點

  • Jar形式獨立執行

  • 內嵌servlet容器,tomcat整合springmvc,spring

  • 簡化Maven配置

  • 自動裝配dean

  • 提供基於http,ssh,telnet對執行時專案的監控

  • 不需要任何xml檔案,藉助註解,properties實現spring配置

微服務

  • MVC

  • MVVM:model view viewmodel

    • model:伺服器上的業務邏輯操作
    • view:頁面
    • viewmodel:model,view核心樞紐流入vue.js
    • view-》viewmodel--》model
  • 微服務:將原來的userservice===》模組

    • 原來是所有的功能放在一個專案
    • 微服務是將功能分開,向外提供介面

建立工程

方式一:https://start.spring.io/ 根據需要選擇,最後添spring web依賴,

方式二:idea spring initilizer,需要自己新增web包

 <!-- 父級依賴 -->   
<parent>      
    <groupId>org.springframework.boot</groupId>      
    <artifactId>spring-boot-starter-parent</artifactId>  
    <version>2.2.3.RELEASE</version>   
</parent>  
<!--啟動器:web環境下的依賴, 使用springmvc spring的jar,tomcat等 -->   
<dependencies>      
    <dependency>           
        <groupId>org.springframework.boot</groupId>           
        <artifactId>spring-boot-starter-web</artifactId>      
    </dependency>   
</dependencies> 
<!--打包外掛-->
<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>
# properties格式
# 配置檔案
# 更改埠號
server.port=8081
debug: true
# Yml(:後有空格)
server:
	port: 8081
student: # 在實體類中注入,屬性值必須相同,也可用於配置檔案的注入
	name: Dean${random.int} # spel表示式
	age: 120
	birthday: 2019/11/02
	maps: {k1: v1,k2: v2}
student: {name: Dean,age: 120}
pets:
	- cat
	- dog
	- pig
pets:[cat,dog,pig]
@Component
@ConfigurationProperties(prefix = "student")
@Validated //資料校驗
public class Student {
    @Email("郵箱格式錯誤")
    private String email;
}

自定義啟動圖示(banner)

http://patorjk.com/software/taag

resources下複製儲存為banner.txt

常用maven命令

  • clean:清理
  • package:打包jar或war

示例

@RequestMapping("/login")     
@ResponseBody     
public  User login(User user){       
    return  user;     
}     
//User欄位:userName  pwd     
//那麼在前臺接收到的資料為:'{"userName":"xxx","pwd":"xxx"}'效果等同於如下程式碼:
@RequestMapping("/login")     
public  void login(User user, HttpServletResponse response){
    response.getWriter.write(JSONObject.fromObject(user).toString());
}  

自動配置

  • pom.xml

    • 核心依賴在父工程中
  • 啟動器

  • 主程式

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

  • 不自己配置時:

    • classpath:/resources 優先順序最高
    • classpath:/static 其次
    • classpath:/public 最後
    • classpath:/mate-inf/resources
  • 自己配置

spring.mvc.static-path-pattern=/Dean/**,classpath:/xxxx

首頁與圖示

  • 預設在靜態資料夾下找index.html
  • 當網站是Restful型別時,用restfulcontroller註解
  • 若要返回html,用controller註解
    • html要位於template資料夾下
    • 方法返回對應檔名,不帶字尾
    • 需要匯入模板引擎 thymeleaf,等等

圖示:靜態資料夾下/favicon.ico

自定義配置類

讀取配置時會看是否有使用者自定義配置,有的話使用使用者的,

對於有的配置可有多個,例如檢視解析器,就結合使用

// 擴充套件springmvc ,dispatcherservlet
@Configuration
public class MyWebConfig implements WebMvcConfigurer {
    // 請求跳轉 dzf--》hello 但url不變
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/dzf").setViewName("/hello");
    }
    // 實現檢視解析器介面的類,我們就可以把他看做檢視解析器
    @Bean // 交給Springboot自動裝配
    public ViewResolver myViewResolver(){
        return new MyViewResolver();
    }
    //自定義檢視解析器
    public static class MyViewResolver implements ViewResolver{
        @Override
        public View resolveViewName(String viewName, Locale locale) throws Exception {
            return null;
        }
    }
}

攔截器

public class LoginHandlerInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        Object loginUser = request.getSession().getAttribute("key");
        if(loginUser==null){
            request.getRequestDispatcher("/index.html").forward(request,response);
            return false;
        }
        return true;
    }
}
// 自定義webconfig中新增
@Override
public void addInterceptors(InterceptorRegistry registry) {
	registry.addInterceptor(new LoginHandlerInterceptor()).addPathPatterns("/**")
                .excludePathPatterns("/index.html","/user/login/**","/css/**");
}

404頁面

在template下邊建立error資料夾建立404.html,對應錯誤會找到對應的

資料來源配置

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>
<!-- 引入依賴-->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.21</version>
</dependency>
spring:
  datasource:
    username: root
    password: 123456
    url: jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
    driver-class-name: com.mysql.cj.jdbc.Driver
    # driver-class-name: com.mysql.jdbc.Driver  this is lower Driver
    type: com.alibaba.druid.pool.DruidDataSource

    # druid's owner config, springboot doesn't these
    # if you wang to use this ,you should write one config
    initialSize: 5
    minIdle: 5
    # and so on

    # driud's plugins config
    filters: stat,wall,log4j
    maxPoolPreparedStatementPerConnectionSize: 20
    useGlobalDataSourceStat: true
    connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500

	@Autowired
	DataSource dataSource;
	@Test
	void contextLoads() throws SQLException {
        // class com.zaxxer.hikari.HikariDataSource 也是一個連線池,類似c3p0,幾乎是最快,預設,通過yaml中type選擇
		System.out.println(dataSource.getClass());  
		Connection connection = dataSource.getConnection();
		System.out.println(connection);
		connection.close();
	}
@Configuration
public class DruidConfig {
    @ConfigurationProperties(prefix = "spring.datasource")
    @Bean
    public DataSource druidDataSource(){
        return new DruidDataSource();
    }
    // 後臺監控
    // springboot 內建servlet容器,沒有web.xml 替代方法: ServletRegistrationBean 註冊進去即可
    @Bean
    public ServletRegistrationBean a(){
        ServletRegistrationBean<StatViewServlet> bean =  new ServletRegistrationBean<>(new StatViewServlet(),"/druid/*");
        // 後臺登入,賬號密碼
        HashMap<String,String> initParameters = new HashMap<>();
        initParameters.put("loginUsername","admin"); // 名字不能改
        initParameters.put("loginPassword","admin");
        initParameters.put("allow","");

        bean.setInitParameters(initParameters);
        return bean;

    }
    //filter
    public FilterRegistrationBean webSataFilter(){
        FilterRegistrationBean bean = new FilterRegistrationBean();
        bean.setFilter(new WebStatFilter());
        // 可以過濾那些請求
        Map<String,String> initParameters = new HashMap<>();
        // 不過濾
        initParameters.put("exclusions","*.js,*.css,/durid/*");
        return bean;
    }
}

資料庫JDBC

//jdbc ,有dao
@Autowired
JdbcTemplate jdbcTemplate;
@RequestMapping("/getAll")
public List<Map<String,Object>> userList(){
    List<Map<String,Object>> list = jdbcTemplate.queryForList("select * from user");
    return list;
}

整合mybatis(xml方式)

匯入依賴

<!-- 不是springboot官方的,是mybatis自己的-->
		<dependency>
			<groupId>org.mybatis.spring.boot</groupId>
			<artifactId>mybatis-spring-boot-starter</artifactId>
			<version>2.1.1</version>
		</dependency>

配置yaml

mybatis:
  type-aliases-package: top.dean0731.model
  mapper-locations: classpath:mybatis/mapper/*xml

mapper 檔案

@Mapper
@Repository
public interface EmployeeMapper {
    List<Employee> queryEmployeeList();
    Employee queryEmployerById();
    int addEmployee(Employee employee);
    int updateEmployee(Employee employee);
    int deleteEmployee(int id);
}
//mapper 代替dao,Resource下mybatis/mapper/下建立配置檔案
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
         PUBLIC "-//mybatis.org//DTD Mapper 3.0/EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="top.dean0731.mapper.EmployeeMapper">
    <select id="queryEmployeeList" resultType="Employee">
        select * from Employee
    </select>
</mapper>

整合mybatis(註解方式)

@Mapper
@Repository
public interface UsersMapper {    
@Select("select *  from t_user where name = #{name}")    
User findUserByName(@Param("name")String name);    
@Insert("insert  into t_user(name,password)values(#{name},#{password})")    
void addUser(@Param("name")String name,@Param("password")String password);  
}

SpringSecurity(安全)

  • 與shiro類似,只不過類,名字不同而已

  • 功能:認證,授權

  • 以前使用攔截器,程式碼量很大,現在使用框架

  • 功能許可權,訪問許可權,選單許可權

  • AOP思想

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //首頁都可以訪問,其他不行,
        // 授權的規則
        http.authorizeRequests()
                .antMatchers("/").permitAll()
                .antMatchers("/level1/*").hasRole("vip1")
                .antMatchers("/level2/*").hasRole("vip2")
                .antMatchers("/level3/*").hasRole("vip3");
        // 開啟記住我功能
        //http.rememberMe();
        http.rememberMe().rememberMeParameter("remeber");
        // 沒有許可權跳轉到登入頁面,
        // 沒有認證到/login,認證失敗到/login?error ,是自帶的login頁面與邏輯是一個請求
        //http.formLogin();
        // 自定義登入頁面,但提交的url之能是 /login,即mylogin html中action=/login
        //http.formLogin().loginPage("/mylogin.html").loginProcessingUrl("/login") ;
        http.formLogin().loginPage("/mylogin.html").usernameParameter("pwd").loginProcessingUrl("/login") ;
        // 防止跨站攻擊功能,自定義登入頁面時使用
        http.csrf().disable();
        // 開啟登出功能 回去請求/logout
        http.logout().logoutSuccessUrl("/a");
    }
    //
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        // 正常應該從資料庫選擇
        // 若在資料庫中按照此方式寫入即可
        auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
                .withUser("username").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1","vip2")
                .and()
                .withUser("root").password(new BCryptPasswordEncoder().encode("123456")).roles("vip3","vip1","vip2");
    }
}
  • 自帶登入部分,有登入頁面,登入controller,
  • 若要自己定義登入頁面,html輸入使用者名稱username,pasword,remeberme到/login,注意需要關閉csrf,

整合Shiro

Subject:使用者

SecurityManager:管理所有使用者

Realm:連線資料

  • <dependency>
      			<groupId>org.apache.shiro</groupId>
      			<artifactId>shiro-spring</artifactId>
      			<version>1.4.1</version>
    
  • 配置類

    @Configuration
    public class ShiroConfig {
        @Bean
        public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("defaultWebSecurityManager")DefaultWebSecurityManager securityManager){
            ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
            // 設定管理器
            bean.setSecurityManager(securityManager);
    
    
            // 新增內建過濾器,判斷當前使用者的許可權
            /*
            * anon:無需認證
            * authc:必須認證
            * user:必須有記住我才能使用
            * perms:有對某個資源的許可權才能
            * role:有某個角色才能
            * */
            Map<String,String> filter = new LinkedHashMap<>();
            filter.put("/","anon");
            filter.put("/user/*","authc");
            filter.put("/user/add","perms[user:add]");
    
    
            bean.setFilterChainDefinitionMap(filter);
            // 設定登入url,預設是/login.jsp,裡面的登入邏輯還需要自己在controller中寫,沒有自帶
    //        bean.setLoginUrl("/登入.html");
    //        bean.setUnauthorizedUrl("/未授權.html");
            return bean;
    
        }
        // SecurityManager
        @Bean
        public DefaultWebSecurityManager defaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
            DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
            // 關聯UserRealm
            securityManager.setRealm(userRealm);
            return securityManager;
        }
        // 建立realm ,需要自定義,此時bean名字就是userRealm
        @Bean
        public UserRealm userRealm(){
            return new UserRealm();
        }
    }
    
    class UserRealm extends AuthorizingRealm{
    //    @Autowired UserService userservice
        // 進入頁面時呼叫
        //授權  經過後,使用者資料庫中的許可權讀取出來,付給subject
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
            SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
            info.addStringPermission("user:add");
            //拿到物件
            Subject subject= SecurityUtils.getSubject();
            //User currentUser = (User)subject.getPrincipal()
            // info.addStringPermissions(user.get許可權); 資料庫中直接字串儲存 user:add,一般都是按鍵許可權,角色對應表
            return info;
        }
        // 使用者認證,經過後 使用者有認證許可權
        // 登入是呼叫
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
            //連線資料庫
            UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
    //        User user = userService.queryUserByName(token.getUsername());
    //        if(user==null)return null
    
            // 密碼認證
    //        return new SimpleAuthenticationInfo("",user.getPassword(),"");
            return new SimpleAuthenticationInfo("","123456","");
        }
    }
    
  • 匯入依賴並且寫log4j.properties,

開源Springboot專案

Swagger

前後端分離:Vue+SpringBoot

  • 後端:後端控制,服務層,資料庫
  • 前端:前端控制,檢視層
  • 前後端互動 ====》API介面

寫程式碼時前後端需要及時互動,因此後端API需要及時更新

Swagger

  • Restful API,API文件自動生成
  • 可以線上測試
  • 支援多種語言

Springboot使用:

	<dependency>
			<groupId>io.springfox</groupId>
			<artifactId>springfox-swagger2</artifactId>
			<version>2.9.2</version>
		</dependency>
		<dependency>
			<groupId>io.springfox</groupId>
			<artifactId>springfox-swagger-ui</artifactId>
			<version>2.9.2</version>
		</dependency>
@Configuration
@EnableSwagger2 // 開啟swagger2
public class SwaggerConfig {
    @Bean
    public Docket docket2() {
        return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo()).groupName("B");
    }
    @Bean
    public Docket docket(){
        return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo()) // 到這裡預設掃描全部介面
                // 配置api分組明,配置多個docket
                .groupName("分組A")
                // 開啟或關閉 swagger2
                .enable(true)
                // 配置掃描的api介面
                .select()
                // RequestHandlerSelectors.any()全部掃描
                // RequestHandlerSelectors.none()不掃描
                // RequestHandlerSelectors.withMethodAnnotation(RestController.class)註解掃描
                .apis(RequestHandlerSelectors.basePackage("top.dean0731.helloworld.controller"))
                // 過濾路徑
                .paths(PathSelectors.ant("/user/add/**"))
                .build();
    }
    private ApiInfo apiInfo(){
        return new ApiInfoBuilder().title("WEAF專案整合Swagger")
                .description("SpringBoot整合Swagger,詳細資訊......")
                .version("1.0")
                .contact(new Contact("啊啊啊啊","blog.csdn.net","[email protected]"))
                .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("[email protected]", "測試郵件", "Hello  world!");   
        return "success";    
    }  
} 
// 模板郵件
@Override
public void sendTemplateMail(String sendTo, String title, String infoTemplate) {
    MimeMessage msg = mailSender.createMimeMessage(); 
    try {
        //true設定為mutlipart模式
        MimeMessageHelper helper = new MimeMessageHelper(msg,true);
        helper.setFrom("xxx");
        helper.setTo(sendTo);
        helper.setSubject(title);
        //封裝模板資料
        Map<String, Object> model = new HashMap<>();
        model.put("username","dean")
            //model.put("path", "img/d.png");
            //得到模板
            Template template = freemarkerConfigurer.getConfiguration().getTemplate(infoTemplate);
        String html = FreeMarkerTemplateUtils.*processTemplateIntoString*(template, model);
        helper.setText(html,true);
    } catch (MessagingException | IOException e) {
        e.printStackTrace();
    } catch (TemplateException e) {
        e.printStackTrace();

    }
    mailSender.send(msg);
}  

定時任務

  • @EnableScheduling 開啟定時任務 啟動類加上
  • @Scheduled 什麼時候執行
  • TaskScheduler 排程者
  • TaskExecutor 執行者
@Service
// 要求在某一時間執行
@Scheduled("cron表示式,與linux相同秒:分 時 日 月 周")
@Scheduled("* * * * * ?")// 每秒一次
public void service(){
    
}

整合redis

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-redis</artifactId>
		</dependency>
spring
 redis:
    host: 127.0.0.1
    port: 6379
    # 叢集
    cluster:

    # Springboot 2.0 之後連線池用 lettuce
    lettuce:
@service

@AutoWired
@RedisTemplate template
    public void test(){
    // 原生操作
    //template.opsForList()
    //template.opsForZSet() 
    // 常用方法,事物,增刪改查等
        // 獲取連結
        //RedisConnect con = template.getConnectionFactory().getConnection();
        //con.flushDb();
        //con.flushAll()
    template.opsValue().set('key','value')    
    template.opsValue().get('key')  
    // 真實場景,物件寫入redis需要序列化,所以 model一般都回實現序列化介面
}

Springboot中的RedisTemplate完成了自己的序列化,

不能識別,因為RedisTemplate的序列化方式不完善,是JDK自己的序列化,此時redis檢視的字元會轉義

要自己定義,解決問題,

@Configuration
public class RedisConfig {
    @Bean
    public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory redisConnectionFactory){
        // 為了開放方便 使用 string,object
        RedisTemplate<String,Object> template = new RedisTemplate<>();
        template.setConnectionFactory(redisConnectionFactory);

        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();

        Jackson2JsonRedisSerializer jack =new Jackson2JsonRedisSerializer(Object.class);
        // 物件轉義
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
//        om.activateDefaultTyping(); 2.10之後使用這個
        jack.setObjectMapper(om);


        // 設定key的序列化方式
        template.setKeySerializer(stringRedisSerializer);
        template.setHashKeySerializer(stringRedisSerializer);
        // 設定value的序列化方式
        template.setValueSerializer(jack);
        template.setHashValueSerializer(jack);
        template.afterPropertiesSet();
        return template;
    }
}

zookeeeper

  • 分散式應用程式協調服務
  • 是hadoop與hbase的重要元件
  • 為分散式應用提供一致性服務的軟體
    • 配置維護
    • 域名服務
    • 分散式同步
    • 組服務 ,例如服務的註冊與發現

windows下使用:

  • 下載

  • 執行 ~/bin/zKserver.cmd

    • java環境要配置好classpath等等
    • conf/zoo_simple.cfg 複製到conf/zoo.cfg 預設埠2181
  • ~/bin/zkCli.cmd 客戶端連線

    • ls / 
      create /firstname 123 # 建立節點
      get /fristname # 檢視節點內容
      

分散式

  • 網路之上的軟體系統

  • 原來的分散式,全部的業務放在一個機器上,機器之間安全相同,nginx做負載均衡

    • 有些流程很簡單,有些複雜
    • 機器利用率不高

小型網站

現在的小型網站,功能呢模組無法利用

分散式架構,提取公共模組

現在流行的分散式

基礎就是微服務架構

Dubbo + Zookeeper +Springboot

RPC

  • 遠端過程呼叫
  • 電腦一A方法呼叫電腦二B方法
  • 核心模組
    • 通訊
    • 序列化

dubbo

  • 高效能,輕量級 javaRPC框架
  • 面向介面的遠端方法呼叫
  • 智慧容錯與負載均衡
  • 服務自動註冊與發現,使用zookeeper
  • 使用監控管理後臺dubbo-admin,可以不要
    • github下載,是個Springboot專案
    • 修改配置檔案application.properties
      • 按需要修改預設埠
      • 後臺埠7001 root,root
      • dubbo.registry.address=zookeeper://ip:port
    • 打包該專案 mvn clean package -Dmaven.test.skip=true
    • 生成jar

服務提供者:可以理解為一些後端專案APi介面

<dependency>
			<groupId>org.apache.dubbo</groupId>
			<artifactId>dubbo-spring-boot-starter</artifactId>
			<version>2.7.7</version>
		</dependency>
		<dependency>
			<groupId>com.github.sgroschupf</groupId>
			<artifactId>zkclient</artifactId>
			<version>0.1</version>
		</dependency>
		<dependency>
			<groupId>org.apache.curator</groupId>
			<artifactId>curator-framework</artifactId>
			<version>5.1.0</version>
		</dependency>
		<dependency>
			<groupId>org.apache.curator</groupId>
			<artifactId>curator-recipes</artifactId>
			<version>5.1.0</version>
		</dependency>
		<dependency>
			<groupId>org.apache.zookeeper</groupId>
			<artifactId>zookeeper</artifactId>
			<version>3.6.1</version>
			<exclusions>
				<!-- 與springboot日誌衝突,排除-->
				<exclusion>
					<groupId>org.slf4j</groupId>
					<artifactId>slf4j-log4j12</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
dubbo:
  application:
    name: provider-server1
  registry:
    address: zookeeper://127.0.0.1:2181
  scan:
    base-packages: top.dean0731.helloworld.service # 服務
//service
@DubboService // dubbo的service ,啟動時可以被掃描註冊到註冊中心
@Component // spring 的萬能註解
public class UserserviceImpl implement UserService{
	public String service1(){}
}
public interface UserService{
	public String service1();
}
  • 啟動zookeeper 埠2181
  • 啟動dubbo監控 埠7001
  • 啟動專案服務端dubbo,此時該服務的訪問地址是 ip:20880
    • 注意此時暴露在外面的是service,不是controller,要訪問controller還是8080
  • 啟動customer

客戶端使用:

同上
dubbo:
  application:
    name: consumer-1
  registry:
    address: zookeeper://127.0.0.1:2181
  scan:
@DubboService //dubbo的service 放到容器中
public class 呼叫Service{ // 
    @Reference //dubbo的reference
    UserService service; //路徑相同建立UserService類或者使用pom座標
    
    public void 使用(){
        String s= service.service1()
    }
}

日誌管理

  • (架包預設新增是logback) 若想更改logback:加入全域性配置檔案

  • 其他日誌系統使用時只用把配置問價放在src下即可,不用全域性配置檔案

  • 預設info級別資訊

    • Trace,debug,info,warn,error,fatal,off從低到高:設定為warn,低與warn的不會輸出
    • root日誌以warn級別即以上輸出
    • springframework.web日主以debug級別輸出
Logging.level.root=warn
Logging.level.org.springframework.web=debug  

自定義日誌配置

<?xml version="1.0"  encoding="utf-8"?>
<!-- 發生改變時:重新載入, 每間隔60s掃描一下,是否改變  ,debug:列印內部日誌資訊 -->
<configuration scan="true"  scanPeriod="60 seconds" debug="false">
<!-- 上下文名字 -->
<contextName>logback</contextName>
<!-- 定義變數 -->
<property name="log.path"  value="G:\\soft_doc\\Myeclispe_Project\\info.log"></property>
<!-- 輸出到控制檯 -->
    
<appender name="console"  class="ch.qos.logback.core.ConsoleAppender">            
    <!-- 預設日誌過濾器 -->
    <filter class="ch.qos.logback.classic.filter.ThresholdFilter">                				<level>ERROR</level>            
    </filter>  -->
    <encoder>
    <pattern>%d{HH:mm:ss.SSS}%contextName[%thread]%-5level%logger{36} -%msg%n</pattern>
    </encoder>
</appender>
    
<!-- 輸出到文字 -->
<appender name="file"  class="ch.qos.logback.core.rolling.RollingFileAppender">                <file>${log.path}</file>
		<!-- 日誌問價切割策略 :每天一個日誌-->
		<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">                     <fileNamePattern>${log.path}/logback.%d{yyyy-MM-dd}.log</fileNamePattern>           </rollingPolicy>
	<encoder>
		<pattern>%d{HH:mm:ss.SSS}%contextName[%thread]%-5level%logger{36} -%msg%n
		</pattern>
		</encoder>
</appender>
    
<root level="info">
    <appender-ref ref="console"/>
    <appender-ref ref="file"/>
</root>
<!-- logback為java中的包單獨設定日誌級別 -->
<logger name="com.dzf.controller"/>
<!-- 具體到類:設定新的級別,是否向上級傳遞列印資訊 -->
	<logger name="com.dzf.controller.SpringController"  level="warn" additivity="false">             <appender-ref ref="console"/>                 
    </logger>  
</configuration>

使用log4j日誌管理,去掉logback

  <!-- 取出日誌檔案logback -->  
<dependency>                
    <groupId>org.springframework.boot</groupId>                
    <artifactId>spring-boot-starter</artifactId>
    <exclusions>                    
    	<exclusion>                         
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-logging</artifactId>                   
        </exclusion>                
    </exclusions>           
</dependency>           
<!-- 新增loj4j  -->           
<dependency>                
    <groupId>org.springframework.boot</groupId>                
    <artifactId>spring-boot-starter-log4j</artifactId>
    <version>1.3.8.RELEASE</version>           
</dependency>  

2,log4j.properties

#############
# 輸出到控制檯
#############

# log4j.rootLogger日誌輸出類別和級別:只輸出不低於該級別的日誌資訊DEBUG < INFO < WARN < ERROR < FATAL
# WARN:日誌級別     CONSOLE:輸出位置自己定義的一個名字       logfile:輸出位置自己定義的一個名字
log4j.rootLogger=WARN,CONSOLE,logfile
# 配置CONSOLE輸出到控制檯
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender 
# 配置CONSOLE設定為自定義佈局模式
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout 
# 配置CONSOLE日誌的輸出格式  [frame] 2019-08-22 22:52:12,000  %r耗費毫秒數 %p日誌的優先順序 %t執行緒名 %C所屬類名通常為全類名 %L程式碼中的行號 %x執行緒相關聯的NDC %m日誌 %n換行
log4j.appender.CONSOLE.layout.ConversionPattern=[frame] %d{yyyy-MM-dd HH:mm:ss,SSS} - %-4r %-5p [%t] %C:%L %x - %m%n

################
# 輸出到日誌檔案中
################

# 配置logfile輸出到檔案中 檔案大小到達指定尺寸的時候產生新的日誌檔案
log4j.appender.logfile=org.apache.log4j.RollingFileAppender
# 儲存編碼格式
log4j.appender.logfile.Encoding=UTF-8
# 輸出檔案位置此為專案根目錄下的logs資料夾中
log4j.appender.logfile.File=logs/root.log
# 字尾可以是KB,MB,GB達到該大小後建立新的日誌檔案
log4j.appender.logfile.MaxFileSize=10MB
# 設定滾定檔案的最大值3 指可以產生root.log.1、root.log.2、root.log.3和root.log四個日誌檔案
log4j.appender.logfile.MaxBackupIndex=3  
# 配置logfile為自定義佈局模式
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %F %p %m%n

##########################
# 對不同的類輸出不同的日誌檔案
##########################

# club.bagedate包下的日誌單獨輸出
log4j.logger.club.bagedate=DEBUG,bagedate
# 設定為false該日誌資訊就不會加入到rootLogger中了
log4j.additivity.club.bagedate=false
# 下面就和上面配置一樣了
log4j.appender.bagedate=org.apache.log4j.RollingFileAppender
log4j.appender.bagedate.Encoding=UTF-8
log4j.appender.bagedate.File=logs/bagedate.log
log4j.appender.bagedate.MaxFileSize=10MB
log4j.appender.bagedate.MaxBackupIndex=3
log4j.appender.bagedate.layout=org.apache.log4j.PatternLayout
log4j.appender.bagedate.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %F %p %m%n

自定義訊息轉換器

@Bean    
private StringHttpMessageConverter  converter(){      
    StringHttpMessageConverter converter = new
    StringHttpMessageConverter(Charset.*forName*("utf-8"));     
    return converter;    
}  

使用FastJson解析json

配置FastJson

1,在MyWebConfig中重寫

@Override    
public void  configureMessageConverters(List<HttpMessageConverter<?>> converters) {      //建立fastjson訊息解析器      
    FastJsonHttpMessageConverter converter = new  FastJsonHttpMessageConverter();      
    //建立fastjson的配置物件      
    FastJsonConfig config = new FastJsonConfig();      
    //對json資料進行格式化 
    config.setSerializerFeatures(SerializerFeature.PrettyFormat);
    converter.setFastJsonConfig(config);      
    converters.add(converter);    
}  

@Bean    
public HttpMessageConverters  fastJsonMessageConverter(){      
    //建立fastjson訊息解析器      
    FastJsonHttpMessageConverter converter = new  FastJsonHttpMessageConverter();      
    //建立fastjson的配置物件      
    FastJsonConfig config = new FastJsonConfig();      
    //對json資料進行格式化 
    config.setSerializerFeatures(SerializerFeature.PrettyFormat);
    converter.setFastJsonConfig(config);      
    return new HttpMessageConverters(converter);   
}  

現在和未來

回顧以前:架構

  • 三層架構MVC

    • 解耦合
  • 開發框架

    • spring
      • IOC
      • AOP
        • 切面,動態代理
        • 不影響業務情況下增加功能
        • 日誌,事物等
      • 輕量級java開源容器
      • 解決開發複雜性
    • Springboot
      • 不是新東西,就是Spring的升級版
      • 新一代JAVAEE開發標準,開箱即用
      • 自動配置了很多
  • 微服務架構

  • 模組化,功能化

    • 原來是所有功能在一起,簽到,支付,娛樂
    • 人多,
    • 橫向增加機器
    • 簽到多,支付少-----》模組化
  • 微服務架構問題,因為網路不可靠

    • 服務很多,客戶端如何訪問?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