springboot

1,springboot入门

(1),配置文件

application.properties文件
文件格式:

#服务器的端口号
server.port=8081
#当前web应用的名称
server.servlet.context-path=/demo

application.yml文件
文件格式:

#普通数据的配置
name: tom
#配置对象
person:
  name: jack
  age: 18
  addr: china
#行内对象配置
#person: {name: tom,age: 18,addr: china}

#server:
#  port: 8082

#配置数组和集合(普通字符串)
city:
  - beijing
  - tianjing
  - shanghai

#city: [beijng,tianjing,shanghai]

student:
  - name: tom
    age: 18
    addr: beijng
  - name: jack
    age: 18
    addr: tianjing

#map配置
#map: {k1: v1, k2: v2}

map:
  key1: value1
  key2: value2
(2),注解的使用

@RestController 是组合注解 @ResponseBody + @Controller
@GetMapping 相当于 @RequestMapping(value = "quick",RequestMethod.Get)
@ConfigurationProperties(prefix = "person") 用于配置文件注入数据

@Controller
@ConfigurationProperties(prefix = "person")
public class QuickController3 {

    private String name;
    private String addr;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAddr() {
        return addr;
    }

    public void setAddr(String addr) {
        this.addr = addr;
    }

    @RequestMapping("/quick3")
    @ResponseBody
    public String quick2(){
        //获取配置文件
        return name+","+addr;
    }
}

用自定以文件给配置类注入数据
@Configuration 指定当前类为配置类
@PropertySource("classpath:test.properties") 指定自定义文件的名称和位置
@EnableConfigurationProperties(MyProperties.class) 开启配置类的属性注入功能

@Configuration
@PropertySource("classpath:test.properties")
@EnableConfigurationProperties(MyProperties.class)
@ConfigurationProperties(prefix = "test")
public class MyProperties {
    private int id;
    private String name;

    @Override
    public String toString() {
        return "MyProperties{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

test.properties文件

#对实体类对象MyProperties进行属性配置
test.id=110
test.name=test
(3),多环境配置

方法1:
application-dev.properties
application-prod.properties
application-test.properties
三种文件:
通过application.properties文件进行激活

spring.profiles.active=test

方法2:
通过使用注解配置多环境配置类
@Profile(value = "dev") 指定多环境配类,放在config包下

@Configuration
@Profile(value = "dev") //指定多环境配类
public class DevDBConnector implements DBConnector{
    @Override
    public void configure() {
        System.out.println("数据库配置环境——dev");
    }
}

通过application.properties文件进行激活

spring.profiles.active=test

2,springboot整合Mybatis

(1),整合mybatis

连接数据库的信息,添加到qpplication.properties文件中

#DB Configuration:
spring.datasource.driverClassName=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/spring?
useUnicode=true&characterEncoding=utf8
spring.datasource.username=root
spring.datasource.password=123456

#数据源类型
#spring.datasource.type
#初始化连接数
spring.datasource.initialSize=20
#最小空闲数
spring.daasource.minIdle=10
#最大连接数
spring.datasource.maxActive=100

如果要用映射文件编写mysql还下也要加上

#spring集成Mybatis环境
#pojo别名扫描包
mybatis.type-aliases-package=com.itheima.domain
#加载Mybatis映射文件
mybatis.mapper-locations=classpath:mapper/*Mapper.xml

如果用注解的什么都不用,按注解的正常方法写

(2),springboot Date JPA

Discuss实体类
@Entity(name = "account") 该注解表示当前实体类是与表有映射关系的实体类
@Id 该注解表示配置该属性的字段为主键
@GeneratedValue(strategy = GenerationType.IDENTITY) 主键自增
@Column(name = "name") 对应数据库的名字

@Entity(name = "account")  //该注解表示当前实体类是与表有映射关系的实体类
public class Discuss {
    @Id  //该注解表示配置该属性的字段为主键
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    Integer id;
    @Column(name = "name")
    String name;
    @Column(name = "money")
    Double money;
    
    Getter and Setter.....
}

然后定义接口DiscussRepository继承 JpaRepository<Discuss,Integer> ,可以重写方法,也可以自定义sql语句

public interface DiscussRepository extends JpaRepository<Discuss,Integer> {
    //查询姓名非空的
    public List<Discuss> findByNameNotNull();

    @Query("select a from account a where a.id = ?1")
    public List<Discuss> getDiscussPage(Integer id, Pageable pageable);


    @Query(value = "select a from account a where a.id = ?1",nativeQuery = true)
    public List<Discuss> getDiscussPage2(Integer id, Pageable pageable);

    @Transactional
    @Modifying
    @Query("update account a set a.name = ?1 where a.id = ?2")
    public void update(String name, Integer id);
}
(3),springboot 整合Redis

首先在application.properties文件中配置连接

#配置redis连接
#redis服务器地址
spring.redis.host=127.0.0.1
#redis连接端口
spring.redis.port=6379
#连接密码
spring.redis.password=

然后在实体类上添加注解
@RedisHash("persons") //在redis中开启一块空间
@Indexed //该属性会在redis数据库中生成二级索引

@RedisHash("persons")  //在redis中开启一块空间
public class Person {
    @Id
    private  String id;

    @Indexed        //该属性会在redis数据库中生成二级索引
    private String firstName;
    @Indexed
    private String lastName;
    private Address address;
    private List<Family> familyList;

    Getter and Setter.....
}

然后定义一个PersonRepository接口,并继承 CrudRepository<Person,String>实现里面的方法

public interface PersonRepository extends CrudRepository<Person,String> {
    List<Person> findByLastname(String lastname);
    List<Person> findPersonByLastname(String Lastname, Pageable page);
    List<Person> findByFirstnameAndLastname(String firstname, String lastname);
    List<Person> findByAddress_City(String city);
    List<Person> findByFamilyList_Username(String username);
}
(4),springboot视图技术

使用Thymeleaf模板
在application.properties文件中配置相关的设置

#模板缓存的:
spring.thymeleaf.cache=false
#模板编码
spring.thymeleaf.encoding=UTF-8
#模板样式
spring.thymeleaf.mode=HTML5
#指定模板页面存放的路径
spring.thymeleaf.prefix=classpath:/templates/
#指定模板的后缀
spring.thymeleaf.suffix=.html


#配置国际化文化的基础名
spring.messages.basename=i18n.login

在资源文件resources下的templates下放入写好的html页面,在static页面下放入需要的静态资源
变量表达式:${}
选择变量表达式:*{}
消息表达式:#{}
连接URL表达式:@{}
片段表达式:~{}

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1,shrink-to-fit=no">
    <title>用户登录界面</title>
    <link th:href="@{/login/css/bootstrap.min.css}" rel="stylesheet">
    <link th:href="@{/login/css/signin.css}" rel="stylesheet">
</head>
<body class="text-center">
<!--  用户登录form表单 -->
<form class="form-signin">
    <img class="mb-4" th:src="@{/login/img/login.jpg}" width="72" height="72">
    <h1 class="h3 mb-3 font-weight-normal" th:text="#{login.tip}">请登录</h1>
    <input type="text" class="form-control"
           th:placeholder="#{login.username}" required="" autofocus="">
    <input type="password" class="form-control"
           th:placeholder="#{login.password}" required="">
    <div class="checkbox mb-3">
        <label>
            <input type="checkbox" value="remember-me"> [[#{login.rememberme}]]
        </label>
    </div>
    <button class="btn btn-lg btn-primary btn-block" type="submit" th:text="#{login.button}">登录</button>
    <p class="mt-5 mb-3 text-muted">© <span th:text="${currentYear}">2018</span>-<span th:text="${currentYear}+1">2019</span></p>
    <a class="btn btn-sm" th:href="@{/toLoginPage(l='zh_CN')}">中文</a>
    <a class="btn btn-sm" th:href="@{/toLoginPage(l='en_US')}">English</a>
</form>
</body>
</html>

在resources下再放入i18n国际化的文件
login.properties
login_en_US.properties
login_zh.properties

login.tip=请登录
login.username=用户名
login.password=密码
login.remember=记住我
login.button=登录

login.tip=please sign in
login.username=Username
login.password=Password
login.remember=Remember me
login.button=Login

login.tip=请登录
login.username=用户名
login.password=密码
login.remember=记住我
login.button=登录

编写语言转换的配置类

@Configuration
public class MyLocalResolver implements LocaleResolver {
    @Override
    public Locale resolveLocale(HttpServletRequest httpServletRequest) {
        // 获取页面手动切换传递的语言参数l
        String l = httpServletRequest.getParameter("l");
        // 获取请求头自动传递的语言参数Accept-Language
        String header = httpServletRequest.getHeader("Accept-Language");
        Locale locale=null;
        // 如果手动切换参数不为空,就根据手动参数进行语言切换,否则默认根据请求头信息切换
        if(!StringUtils.isEmpty(l)){
            String[] split = l.split("_");
            locale=new Locale(split[0],split[1]);
        }else {
            // Accept-Language: en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7
            String[] splits = header.split(",");
            String[] split = splits[0].split("-");
            locale=new Locale(split[0],split[1]);
        }
        return locale;
    }

    @Override
    public void setLocale(HttpServletRequest httpServletRequest, @Nullable HttpServletResponse httpServletResponse, @Nullable Locale locale) {

    }
    // 将自定义的MyLocalResolver类重新注册为一个类型LocaleResolver的Bean组件
    @Bean
    public LocaleResolver localeResolver(){
        return new MyLocalResolver();
    }
}

在controller层用Java代码打开页面 项目day13_springboot01

3,springboot整合 spring MVC

(1),WebMvcConfigurer and HandlerInterceptor

使用框架中的接口WebMvcConfigurer来实现页面的跳转,并且自定义拦截器

@Configuration
public class MyInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //用户请求/admin开头路径时,判断用户是否登录
        String requestURI = request.getRequestURI();
        System.out.println(requestURI);
        Object loginUser = request.getSession().getAttribute("loginUser");
        if(requestURI.startsWith("/admin")&& null == loginUser)
            response.sendRedirect("/toLoginPage");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        int i = Calendar.getInstance().get(Calendar.YEAR);
        request.setAttribute("currentYear",i);
    }
}

@Component
public class MyInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //用户请求/admin开头路径时,判断用户是否登录
        String requestURI = request.getRequestURI();
        System.out.println(requestURI);
        Object loginUser = request.getSession().getAttribute("loginUser");
        if(requestURI.startsWith("/admin")&& null == loginUser)
            response.sendRedirect("/toLoginPage");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        int i = Calendar.getInstance().get(Calendar.YEAR);
        request.setAttribute("currentYear",i);
    }
}
(2),springboot整合servlet三大组件

创建自定义的servlet等
MyServilet

@Component
@WebServlet("/myServlet")
public class MyServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.getWriter().print("hello myServlet");
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }
}

MyFilter

@Component
@WebFilter("/toLoginPage")
public class MyFilter implements Filter {
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("自定义的MyFilter执行了.....");
        filterChain.doFilter(servletRequest,servletResponse);
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void destroy() {

    }
}

MyListener

@Component
@WebListener
public class MyListener implements ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        System.out.println("ContextInitialized执行了");
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        System.out.println("contextDestroyed执行了");
    }
}

servlet三大组件的注册

@Configuration
public class ServletConfig {
    /**
     * servlet组件的注册
     */
    @Bean
    public ServletRegistrationBean getServlet(MyServlet myServlet){
        ServletRegistrationBean<MyServlet> myServletServletRegistrationBean = new ServletRegistrationBean<>(myServlet, "/myServlet");
        return myServletServletRegistrationBean;
    }

    @Bean
    public FilterRegistrationBean getFilter(MyFilter myFilter){
        FilterRegistrationBean<MyFilter> myFilterFilterRegistrationBean = new FilterRegistrationBean<>(myFilter);
        myFilterFilterRegistrationBean.setUrlPatterns(Arrays.asList("/toLoginPage"));
        return myFilterFilterRegistrationBean;
    }

    @Bean
    public ServletListenerRegistrationBean getListener(MyListener myListener){
        ServletListenerRegistrationBean<MyListener> myListenerServletListenerRegistrationBean = new ServletListenerRegistrationBean<>(myListener);
        return myListenerServletListenerRegistrationBean;
    }
}
(3),文件的上传和下载
@Controller
public class FileController {
    /**
     * 跳转到 upload.html
     */

    @RequestMapping("/toUpload")
    public String toUpload(){
        return "upload";
    }

    /**
     * 实现文件上传
     */
    @PostMapping("/uploadFile")
    public String uploadFile(MultipartFile[] fileUpload, Model model){
        //返回上传成功的状态信息
        model.addAttribute("uploadStatus","上传成功");
        //上传文件
        for (MultipartFile multipartFile : fileUpload) {
            //获取上传文件的后缀名
            String originalFilename = multipartFile.getOriginalFilename();
            //重新生成文件名
            String fileName = UUID.randomUUID() + "-" + originalFilename;
            //设置存储目录
            String dirPath = "D:/file/";
            //如果文件夹不存在,要创建
            File file = new File(dirPath);
            if(!file.exists()){
                file.mkdir();
            }
            try {
                multipartFile.transferTo(new File(dirPath));
            } catch (IOException e) {
                model.addAttribute("uploadStatus","上传失败");
            }
        }
        return "upload";
    }


    /**
     * 跳转到download.html
     */
    @GetMapping("/toDownload")
    public String toDownload(){
        return "download";
    }
    /**
     * 文件下载
     */
    @GetMapping("/download")
    public ResponseEntity<byte[]> fileDownload(String filename){
        //指定要下载的文件路径
        String dirPath = "D:/file/";
        //创建文件对象
        File file = new File(dirPath + File.separator + filename);
        //设置响应头
        HttpHeaders httpHeaders = new HttpHeaders();
        //通知浏览器以下载的方式打开
        httpHeaders.setContentDispositionFormData("attachment",filename);
        //定义以流的形式下载返回文件数据
        httpHeaders.setContentType(MediaType.APPLICATION_OCTET_STREAM);

        try {
            return new ResponseEntity<>(FileUtils.readFileToByteArray(file),httpHeaders, HttpStatus.OK);
        } catch (IOException e) {
            e.printStackTrace();
            return new ResponseEntity<>(e.getMessage().getBytes(),HttpStatus.EXPECTATION_FAILED);
        }
    }

}

4,Springboot缓存管理

(1),springboot缓存注解

@EnableCaching 加在启动类
自定义实体类

@Entity(name = "account")
public class Account implements Serializable {
    @Id  //该注解表示配置该属性的字段为主键
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    Integer id;
    @Column(name = "name")  //一样的时候可以不写,不一样的时候必须写
    String name;
    @Column(name = "money")
    Double money;

    getter  and setter....
}

定义Repository

public interface AccountRepository extends JpaRepository<Account,Integer> {

    //根据id修改姓名
    @Modifying
    @Query("update account a set a.name = ?1 where a.id = ?2")
    public int updateAccount(String name,Integer id);
}

定义service

@Service
@Transactional
public class AccountService {
    @Autowired
    private AccountRepository accountRepository;

    /**
     * 查询
     */
    @Cacheable(cacheNames = "account",unless ="#result==null")
    public Account findById(Integer id){
        Optional<Account> byId = accountRepository.findById(id);
        if(byId.isPresent()){
            return byId.get();
        }
        return null;
    }


    /**
     * 更新
     */
    @CachePut(cacheNames = "account",key = "#result.id")
    public Account update(Account account){
        int i = accountRepository.updateAccount(account.getName(), account.getId());
        Optional<Account> account1 = accountRepository.findById(account.getId());
        if(account1.isPresent()){
            return account1.get();
        }
        return null;
    }

    /**
     * 删除
     */
    @CacheEvict(cacheNames = "account")
    public void delete(Integer id){
        accountRepository.deleteById(id);
    }
}

定义controller

@Controller
public class AccountController {
    @Autowired
    private AccountService accountService;

    /**
     * 查询
     */
    @GetMapping("/get/{id}")
    @ResponseBody
    public Account findById(@PathVariable("id") Integer id){
        Account account = accountService.findById(id);
        System.out.println(account);
        return account;
    }

    /**
     * 更新
     */
    @GetMapping("/update/{id}/{name}")
    @ResponseBody
    public Account update(@PathVariable("id") Integer id,@PathVariable("name") String name){
        Account account = accountService.findById(id);
        account.setName(name);
        Account account1 = accountService.update(account);
        return account1;
    }

    /**
     * 删除
     */
    @GetMapping("/delete/{id}")
    public void delete(@PathVariable("id") Integer id){
        accountService.delete(id);
    }
}
(2),springboot缓存API

APIService

@Service
@Transactional
public class ApiAccountService {
    @Autowired
    private AccountRepository accountRepository;
    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 查询方法
     * @param id
     * @return
     */
    public Account findById(Integer id) {
        Object o = redisTemplate.opsForValue().get("account_" + "id");
        if(o != null){
            return (Account) o;
        }else {
            Optional<Account> account = accountRepository.findById(id);
            if(account.isPresent()){
                Account account1 = account.get();
                redisTemplate.opsForValue().set("account_"+id,account1);
                return account1;
            }
            return null;
        }
    }

    /**
     * 更新数据
     * @param account
     * @return
     */
    public Account updateAccount(Account account) {
        int i = accountRepository.updateAccount(account.getName(), account.getId());
        redisTemplate.opsForValue().set("account_"+account.getId(),account);
        return account;
    }

    public void deleteAccount(Integer id) {
        accountRepository.deleteById(id);
        redisTemplate.delete("account"+id);
    }
}

APIController

@Controller
@RequestMapping("/Api")
public class ApiAccountController {
    @Autowired
    private AccountService accountService;

    @Autowired
    private ApiAccountService apiAccountService;

    /**
     * 查询
     */
    @GetMapping("/get/{id}")
    @ResponseBody
    public Account findById(@PathVariable("id") Integer id){
        Account account = apiAccountService.findById(id);
        System.out.println(account);
        return account;
    }

    /**
     * 更新
     */
    @GetMapping("/update/{id}/{name}")
    @ResponseBody
    public Account update(@PathVariable("id") Integer id,@PathVariable("name") String name){
        Account account = apiAccountService.findById(id);
        account.setName(name);
        Account account1 = apiAccountService.updateAccount(account);
        return account1;
    }

    /**
     * 删除
     */
    @GetMapping("/delete/{id}")
    public void delete(@PathVariable("id") Integer id){
        apiAccountService.deleteAccount(id);
    }
}
(3),自定义序列化机制
@Configuration
public class RedisConfig {

    @Bean
    public RedisTemplate<Object,Object> redisTemplate(RedisConnectionFactory redisConnectionFactory){
        RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);

        //使用Json格式序列化对象,对缓存数据key和value进行转换
        Jackson2JsonRedisSerializer<Object> jsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);

        //解决查询转换异常问题
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);

        jsonRedisSerializer.setObjectMapper(objectMapper);

        //设置redisTemplate模板API的序列化方式为json
        redisTemplate.setDefaultSerializer(jsonRedisSerializer);
        return redisTemplate;
    }

    @Bean
    public RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
        // 分别创建String和JSON格式序列化对象,对缓存数据key和value进行转换
        RedisSerializer<String> strSerializer = new StringRedisSerializer();
        Jackson2JsonRedisSerializer jacksonSeial =
                new Jackson2JsonRedisSerializer(Object.class);
        // 解决查询缓存转换异常的问题
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jacksonSeial.setObjectMapper(om);

        // 定制缓存数据序列化方式及时效
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofDays(1))
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(strSerializer))
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jacksonSeial))
                .disableCachingNullValues();

        RedisCacheManager cacheManager = RedisCacheManager.builder(redisConnectionFactory).cacheDefaults(config).build();
        return cacheManager;
    }
}

5,springboot整合Security

定义domian

@Entity(name = "t_customer")
public class Customer implements Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    private String username;
    private String password;
   
     getter and setter.....
}


@Entity(name = "t_authority ")
public class Authority implements Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    private String authority ;
   
    getter and setter.....
}

定义repository

public interface AuthorityRepository extends JpaRepository<Authority,Integer> {
    @Query(value = "select a.* from t_customer c,t_authority a,t_customer_authority ca where ca.customer_id=c.id and ca.authority_id=a.id and c.username =?1",nativeQuery = true)
    public List<Authority> findAuthoritiesByUsername(String username);

}

public interface CustomerRepository extends JpaRepository<Customer,Integer> {
    Customer findByUsername(String username);
}

使用User'DetailsService 方式进行身份验证
定义service
custtomerService

@Service
public class CustomerService {
    @Autowired
    private CustomerRepository customerRepository;
    @Autowired
    private AuthorityRepository authorityRepository;
    @Autowired
    private RedisTemplate redisTemplate;

    // 业务控制:使用唯一用户名查询用户信息
    public Customer getCustomer(String username){
        Customer customer=null;
        Object o = redisTemplate.opsForValue().get("customer_"+username);
        if(o!=null){
            customer=(Customer)o;
        }else {
            customer = customerRepository.findByUsername(username);
            if(customer!=null){
                redisTemplate.opsForValue().set("customer_"+username,customer);
            }
        }
        return customer;
    }
    // 业务控制:使用唯一用户名查询用户权限
    public List<Authority> getCustomerAuthority(String username){
        List<Authority> authorities=null;
        Object o = redisTemplate.opsForValue().get("authorities_"+username);
        if(o!=null){
            authorities=(List<Authority>)o;
        }else {
            authorities=authorityRepository.findAuthoritiesByUsername(username);
            if(authorities.size()>0){
                redisTemplate.opsForValue().set("authorities_"+username,authorities);
            }
        }
        return authorities;
    }
}

UserdetailsService

@Service
public class UserDetailsServiceImpl implements UserDetailsService {
    @Autowired
    private CustomerService customerService;
    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
        // 通过业务方法获取用户及权限信息
        Customer customer = customerService.getCustomer(s);
        List<Authority> authorities = customerService.getCustomerAuthority(s);
        // 对用户权限进行封装
        List<SimpleGrantedAuthority> list = authorities.stream().map(authority -> new SimpleGrantedAuthority(authority.getAuthority())).collect(Collectors.toList());
        // 返回封装的UserDetails用户详情类
        UserDetails userDetails= new User(customer.getUsername(),customer.getPassword(),list);
        return userDetails;
    }
}

定义sercurityConfig
@EnableWebSecurity //@EnableGlobalAuthentication开启自定义全局认证

@EnableWebSecurity  //@EnableGlobalAuthentication开启自定义全局认证
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Qualifier("dataSource")
    @Autowired
    private DataSource dataSource;

    @Autowired
    private UserDetailsServiceImpl userDetailsService;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //设置密码编码器
        BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
        /*
        //使用内存进行身份认证
        InMemoryUserDetailsManagerConfigurer<AuthenticationManagerBuilder> authenticationManagerBuilderInMemoryUserDetailsManagerConfigurer = auth.inMemoryAuthentication().passwordEncoder(bCryptPasswordEncoder);
        authenticationManagerBuilderInMemoryUserDetailsManagerConfigurer.withUser("james").password(bCryptPasswordEncoder.encode("123")).roles("comment");
        authenticationManagerBuilderInMemoryUserDetailsManagerConfigurer.withUser("wade").password(bCryptPasswordEncoder.encode("456")).roles("vip");

        //使用jdbc进行身份认证
        JdbcUserDetailsManagerConfigurer<AuthenticationManagerBuilder> authenticationManagerBuilderJdbcUserDetailsManagerConfigurer = auth.jdbcAuthentication().passwordEncoder(bCryptPasswordEncoder);
        authenticationManagerBuilderJdbcUserDetailsManagerConfigurer.dataSource(dataSource);
        authenticationManagerBuilderJdbcUserDetailsManagerConfigurer.usersByUsernameQuery("select username,PASSWORD,STATUS from users where username = ?");
        authenticationManagerBuilderJdbcUserDetailsManagerConfigurer.authoritiesByUsernameQuery("select u.username, r.roleDesc from users u, role r,users_role ur where u.id = ur.userId and r.id=ur.roleId and u.username = ?");

         */

        //使用UserDetailService进行身份验证
        auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/").permitAll()
                // 需要对static文件夹下静态资源进行统一放行
                .antMatchers("/login/**").permitAll()
                .antMatchers("/detail/common/**").hasRole("common")
                .antMatchers("/detail/vip/**").hasRole("vip")
                .anyRequest().authenticated()
                .and().formLogin();
        //自定义用户登录控制
        http.formLogin().loginPage("/userLogin").permitAll()
                .usernameParameter("name")
                .passwordParameter("pwd")
                .defaultSuccessUrl("/")
                .failureUrl("/userLogin?error");


        //自定义用户推出
        http.logout().logoutUrl("/myLogout").logoutSuccessUrl("/");
    }
}

定义controller进行操作

@Controller
public class FileController {


    @GetMapping("/detail/{type}/{path}")
    public String toDetail(@PathVariable("type") String type,@PathVariable("path") String path){
        return "detail/"+type+"/"+path;
    }

    @GetMapping("/userLogin")
    public String toLoginPage(){
        return "login/login";
    }

    @GetMapping("/getUserByContext")
    @ResponseBody
    public void getUser() {
        SecurityContext context = SecurityContextHolder.getContext();
        //获取用户相关信息
        Authentication authentication = context.getAuthentication();
        UserDetails principal = (UserDetails) authentication.getPrincipal();
        System.out.println(principal.getUsername());
    }
}

在resource文件下存放资源文件
day15_springboot02

6,springboot消息服务

配置qpplication.properties

#配置RabbitMQ消息中间的连接配置
spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
#配置RabbitMQ虚拟主机路径/,可以省略
spring.rabbitmq.virtual-host=/

config

@Configuration
public class RabbitMQConfig {
    @Bean
    public MessageConverter messageConverter() {
        return new Jackson2JsonMessageConverter();
    }

}

service

@Service
public class RabbitMQService {
    /**
     * Publish/Subscribe工作模式接收,处理邮件业务
     * @param message
     */
    /**
    @RabbitListener(queues = "fanout_queue_email")
    public void psubConsumerEmail(Message message){
        byte[] body = message.getBody();
        String s = new String(body);
        System.out.println("邮件业务接收到消息:"+s);
    }
     */
    /**
     *Publish/Subscribe工作模式接收,处理短信业务
     * @param message
     */
    /**
    @RabbitListener(queues = "fanout_queue_sms")
    public void psubConsumerSms(Message message){
        byte[] body = message.getBody();
        String s = new String(body);
        System.out.println("短信业务接收到消息:"+s);
    }
     */

    /**
     *  **使用基于注解的方式实现消息服务
     * 1.1、Publish/Subscribe工作模式接收,处理邮件业务
     * @param user
     */
    @RabbitListener(bindings =@QueueBinding(value =@Queue("fanout_queue_email"), exchange =@Exchange(value = "fanout_exchange",type = "fanout")))
    public void psubConsumerEmailAno(User user) {
        System.out.println("邮件业务接收到消息: "+user);
    }
    /**
     * 1.2、Publish/Subscribe工作模式接收,处理短信业务
     * @param user
     */
    @RabbitListener(bindings =@QueueBinding(value =@Queue("fanout_queue_sms"),exchange =@Exchange(value = "fanout_exchange",type = "fanout")))
    public void psubConsumerSmsAno(User user) {
        System.out.println("短信业务接收到消息: "+user);
    }

    /**
     *  2.1、路由模式消息接收,处理error级别日志信息
     * @param message
     */
    @RabbitListener(
            bindings = @QueueBinding(
                    value = @Queue("routing_queue_error"),
                    exchange = @Exchange(value = "routing_exchange", type = "direct"),
                    key = "error_routing_key"))
    public void routingConsumerError(String message){
        System.out.println("接收到error级别日志消息"+message);
    }
    /**
     *  2.2、路由模式消息接收,处理info、error、warning级别日志信息
     * @param message
     */
    @RabbitListener(
            bindings = @QueueBinding(
                    value = @Queue("routing_queue_all"),
                    exchange = @Exchange(value = "routing_exchange", type = "direct"),
                    key = {"error_routing_key","info_routing_key","warning_routing_key"}))
    public void routingConsumerAll(String message){
        System.out.println("接收到info,error,warning等级别日志消息"+message);
    }

    /**
     *  3.1、通配符模式消息接收,进行邮件业务订阅处理
     * @param message
     */
    @RabbitListener(
            bindings =@QueueBinding(
                    value =@Queue("topic_queue_email"),
                    exchange =@Exchange(value = "topic_exchange",type = "topic"),
                    key = "info.#.email.#"))
    public void topicConsumerEmail(String message) {
        System.out.println("接收到邮件订阅需求处理消息: "+message);
    }
    /**
     *  3.2、通配符模式消息接收,进行短信业务订阅处理
     * @param message
     */
    @RabbitListener(
            bindings =@QueueBinding(
                    value =@Queue("topic_queue_sms"),
                    exchange =@Exchange(value = "topic_exchange",type = "topic"),
                    key = "info.#.sms.#"))
    public void topicConsumerSms(String message) {
        System.out.println("接收到短信订阅需求处理消息: "+message);
    }

}

test

@RunWith(SpringRunner.class)
@SpringBootTest
public class Day16Springboot01ApplicationTest {
    @Autowired
    private AmqpAdmin amqpAdmin;
    /**
     * 使用AmqpAdmin管理员API定制消息组件
     */
    @Test
    public void amqpAdmin() {
        // 1、定义fanout类型的交换器
        amqpAdmin.declareExchange(new FanoutExchange("fanout_exchange"));
        // 2、定义两个默认持久化队列,分别处理email和sms
        amqpAdmin.declareQueue(new Queue("fanout_queue_email"));
        amqpAdmin.declareQueue(new Queue("fanout_queue_sms"));
        // 3、将队列分别与交换器进行绑定
        amqpAdmin.declareBinding(new Binding("fanout_queue_email",Binding.DestinationType.QUEUE,"fanout_exchange","",null));
        amqpAdmin.declareBinding(new Binding("fanout_queue_sms",Binding.DestinationType.QUEUE,"fanout_exchange","",null));
    }

    @Autowired
    private RabbitTemplate rabbitTemplate;
    /**
     * 1、Publish/Subscribe工作模式消息发送端
     */
    @Test
    public void psubPublisher() {
        User user=new User();
        user.setId(1);
        user.setUsername("石头");
        rabbitTemplate.convertAndSend("fanout_exchange","",user);
    }
    /**
     *  2、Routing工作模式消息发送端
     */
    @Test
    public void routingPublisher() {
        rabbitTemplate.convertAndSend("routing_exchange","error_routing_key","routing send  error message");
    }

    /**
     *  3、Topcis工作模式消息发送端
     */
    @Test
    public void topicPublisher() {
        // 1、只发送邮件订阅用户消息
//        rabbitTemplate.convertAndSend("topic_exchange","info.email","topics send  email message");
        // 2、只发送短信订阅用户消息
//      rabbitTemplate.convertAndSend("topic_exchange","info.sms","topics send  sms message");
        // 3、发送同时订阅邮件和短信的用户消息
        rabbitTemplate.convertAndSend("topic_exchange","info.email.sms","topics send  email and sms message");
    }

}

7,消息处理

(1),无返回值的消息处理和有返回值的消息处理 在启动上加@EnableAsyn
@Service
public class MyASyncService {
    /**
     * 模拟无返回值的异步任务处理
     * @throws Exception
     */
    @Async
    public void sendSMS() throws Exception{
        long startTime = System.currentTimeMillis();
        Thread.sleep(5000);
        long endTime = System.currentTimeMillis();
        System.out.println("短信业务执行完成耗时,"+(endTime-startTime));
    }

    /**
     * 模拟有返回值的异步任务处理
     * @return
     * @throws Exception
     */
    @Async
    public Future<Integer> processA() throws Exception {
        System.out.println("开始分析并统计业务A数据...");
        Long startTime = System.currentTimeMillis();
        Thread.sleep(4000);
        // 模拟定义一个假的统计结果
        int count=123456;
        Long endTime = System.currentTimeMillis();
        System.out.println("业务A数据统计耗时:" + (endTime - startTime));
        return new AsyncResult<Integer>(count);
    }
    @Async
    public Future<Integer> processB() throws Exception {
        System.out.println("开始分析并统计业务B数据...");
        Long startTime = System.currentTimeMillis();
        Thread.sleep(5000);
        // 模拟定义一个假的统计结果
        int count=654321;
        Long endTime = System.currentTimeMillis();
        System.out.println("业务B数据统计耗时:" + (endTime - startTime));
        return new AsyncResult<Integer>(count);
    }
}



@Controller
public class MyAsyncController {
    @Autowired
    private MyASyncService myASyncService;

    @GetMapping("/sendSMS")
    public String sendSMS() throws Exception{
        long startTime = System.currentTimeMillis();
        myASyncService.sendSMS();
        long endTime = System.currentTimeMillis();
        System.out.println("主流耗时,"+(endTime-startTime));
        return "success";
    }

    @GetMapping("/statistics")
    public String statistics() throws Exception {
        Long startTime = System.currentTimeMillis();
        Future<Integer> futureA = myASyncService.processA();
        Future<Integer> futureB = myASyncService.processB();
        int total = futureA.get() + futureB.get();
        System.out.println("异步任务数据统计汇总结果: "+total);
        Long endTime = System.currentTimeMillis();
        System.out.println("主流程耗时: "+(endTime-startTime));
        return "success";
    }
}
(2),定时任务 在启动类上加@EnableScheduling
@Service
public class ScheduledTaskService {
    private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    private Integer count1 = 1;
    private Integer count2 = 1;
    private Integer count3 = 1;

    @Scheduled(fixedRate = 60000)
    public void scheduledTaskImmediately() {
        System.out.println(String.format("fixedRate第%s次执行,当前时间为:%s", count1++, dateFormat.format(new Date())));
    }

    @Scheduled(fixedDelay = 60000)
    public void scheduledTaskAfterSleep() throws InterruptedException {
        System.out.println(String.format("fixedDelay第%s次执行,当前时间为:%s", count2++, dateFormat.format(new Date())));
        Thread.sleep(10000);
    }

    @Scheduled(cron = "0 * * * * *")
    public void scheduledTaskCron(){
        System.out.println(String.format("cron第%s次执行,当前时间为:%s",count3++, dateFormat.format(new Date())));
    }
}
(3),邮件发送

application.properties 中配置邮箱

#发件人邮箱服务器配置
spring.mail.host=smtp.qq.com
spring.mail.port=587
#配置个人qq账户和密码
spring.mail.username=785846052@qq.com
spring.mail.password=duinweojybdubgaf
spring.mail.default-encoding=UTF-8
#邮箱服务超时间配置
spring.mail.properties.mail.smtp.connectiontimeout=5000
spring.mail.properties.mail.smtp.timeout=3000
spring.mail.properties.mail.smtp.writetimeout=5000

sendemailService

@Service
public class SendEmailService {
    @Autowired
    private JavaMailSenderImpl mailSender;
    @Value("${spring.mail.username}")
    private String from;

    /**
     * 发送纯文本邮件
     *
     * @param to      收件人地址
     * @param subject 邮件标题
     * @param text    邮件内容
     */
    public void sendSimpleEmail(String to, String subject, String text) {
        // 定制纯文本邮件信息SimpleMailMessage
        SimpleMailMessage message = new SimpleMailMessage();
        message.setFrom(from);
        message.setTo(to);
        message.setSubject(subject);
        message.setText(text);
        try {
            // 发送邮件
            mailSender.send(message);
            System.out.println("纯文本邮件发送成功");
        } catch (MailException e) {
            System.out.println("纯文本邮件发送失败 " + e.getMessage());
            e.printStackTrace();
        }
    }

    /**
     * 发送复杂邮件(包括静态资源和附件)
     * @param to           收件人地址
     * @param subject      邮件标题
     * @param text         邮件内容
     * @param filePath     附件地址
     * @param rscId        静态资源唯一标识
     * @param rscPath      静态资源地址
     */
    public void sendComplexEmail(String to,String subject,String text,String filePath,String rscId,String rscPath){
        // 定制复杂邮件信息MimeMessage
        MimeMessage message = mailSender.createMimeMessage();
        try {
            // 使用MimeMessageHelper帮助类,并设置multipart多部件使用为true
            MimeMessageHelper helper = new MimeMessageHelper(message, true);
            helper.setFrom(from);
            helper.setTo(to);
            helper.setSubject(subject);
            helper.setText(text, true);
            // 设置邮件静态资源
            FileSystemResource res = new FileSystemResource(new File(rscPath));
            helper.addInline(rscId, res);
            // 设置邮件附件
            FileSystemResource file = new FileSystemResource(new File(filePath));
            String fileName = filePath.substring(filePath.lastIndexOf(File.separator));
            helper.addAttachment(fileName, file);
            // 发送邮件
            mailSender.send(message);
            System.out.println("复杂邮件发送成功");
        } catch (MessagingException e) {
            System.out.println("复杂邮件发送失败 "+e.getMessage());
            e.printStackTrace();
        }
    }

    /**
     * 发送模板邮件
     * @param to       收件人地址
     * @param subject  邮件标题
     * @param content  邮件内容
     */
    public void sendTemplateEmail(String to, String subject, String content) {
        MimeMessage message = mailSender.createMimeMessage();
        try {
            // 使用MimeMessageHelper帮助类,并设置multipart多部件使用为true
            MimeMessageHelper helper = new MimeMessageHelper(message, true);
            helper.setFrom(from);
            helper.setTo(to);
            helper.setSubject(subject);
            helper.setText(content, true);
            // 发送邮件
            mailSender.send(message);
            System.out.println("模板邮件发送成功");
        } catch (MessagingException e) {
            System.out.println("模板邮件发送失败 "+e.getMessage());
            e.printStackTrace();
        }
    }
}

test

@RunWith(SpringRunner.class)
@SpringBootTest
public class SendEmailServiceTest {

    @Autowired
    private SendEmailService sendEmailService;
    @Autowired
    private TemplateEngine templateEngine;

    @Test
    public void sendSimpleMailTest() {
        String to="209962770@qq.com";
        String subject="【纯文本邮件】标题";
        String text="Spring Boot纯文本邮件发送内容测试.....";
        // 发送简单邮件
        sendEmailService.sendSimpleEmail(to,subject,text);
    }

    @Test
    public void sendComplexEmailTest() {
        String to="209962770@qq.com";
        String subject="【复杂邮件】标题";
        // 定义邮件内容
        StringBuilder text = new StringBuilder();
        text.append("<html><head></head>");
        text.append("<body><h1>祝大家元旦快乐!</h1>");
        // cid为固定写法,rscId指定一个唯一标识
        String rscId = "img001";
        text.append("<img src='cid:" +rscId+"'/></body>");
        text.append("</html>");
        // 指定静态资源文件和附件路径
        String rscPath="E:\\图片\\美女\\8.jpg";
        String filePath="E:\\Download\\哈哈.docx";
        // 发送复杂邮件
        sendEmailService.sendComplexEmail(to,subject,text.toString(),filePath,rscId,rscPath);
    }

    @Test
    public void sendTemplateEmailTest() {
        String to="209962770@qq.com";
        String subject="【模板邮件】标题";
        // 使用模板邮件定制邮件正文内容
        Context context = new Context();
        context.setVariable("username", "石头");
        context.setVariable("code", "456123");
        // 使用TemplateEngine设置要处理的模板页面
        String emailContent = templateEngine.process("emailTemplate_vercode", context);
        // 发送模板邮件
        sendEmailService.sendTemplateEmail(to,subject,emailContent);
    }
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,864评论 6 494
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,175评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,401评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,170评论 1 286
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,276评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,364评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,401评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,179评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,604评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,902评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,070评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,751评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,380评论 3 319
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,077评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,312评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,924评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,957评论 2 351

推荐阅读更多精彩内容