1,springboot入門
(1),配置文件
application.properties文件
文件格式:
#服務(wù)器的端口號(hào)
server.port=8081
#當(dāng)前web應(yīng)用的名稱
server.servlet.context-path=/demo
application.yml文件
文件格式:
#普通數(shù)據(jù)的配置
name: tom
#配置對象
person:
name: jack
age: 18
addr: china
#行內(nèi)對象配置
#person: {name: tom,age: 18,addr: china}
#server:
# port: 8082
#配置數(shù)組和集合(普通字符串)
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 相當(dāng)于 @RequestMapping(value = "quick",RequestMethod.Get)
@ConfigurationProperties(prefix = "person") 用于配置文件注入數(shù)據(jù)
@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;
}
}
用自定以文件給配置類注入數(shù)據(jù)
@Configuration 指定當(dāng)前類為配置類
@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文件
#對實(shí)體類對象MyProperties進(jìn)行屬性配置
test.id=110
test.name=test
(3),多環(huán)境配置
方法1:
application-dev.properties
application-prod.properties
application-test.properties
三種文件:
通過application.properties文件進(jìn)行激活
spring.profiles.active=test
方法2:
通過使用注解配置多環(huán)境配置類
@Profile(value = "dev") 指定多環(huán)境配類,放在config包下
@Configuration
@Profile(value = "dev") //指定多環(huán)境配類
public class DevDBConnector implements DBConnector{
@Override
public void configure() {
System.out.println("數(shù)據(jù)庫配置環(huán)境——dev");
}
}
通過application.properties文件進(jìn)行激活
spring.profiles.active=test
2,springboot整合Mybatis
(1),整合mybatis
連接數(shù)據(jù)庫的信息,添加到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
#數(shù)據(jù)源類型
#spring.datasource.type
#初始化連接數(shù)
spring.datasource.initialSize=20
#最小空閑數(shù)
spring.daasource.minIdle=10
#最大連接數(shù)
spring.datasource.maxActive=100
如果要用映射文件編寫mysql還下也要加上
#spring集成Mybatis環(huán)境
#pojo別名掃描包
mybatis.type-aliases-package=com.itheima.domain
#加載Mybatis映射文件
mybatis.mapper-locations=classpath:mapper/*Mapper.xml
如果用注解的什么都不用,按注解的正常方法寫
(2),springboot Date JPA
Discuss實(shí)體類
@Entity(name = "account") 該注解表示當(dāng)前實(shí)體類是與表有映射關(guān)系的實(shí)體類
@Id 該注解表示配置該屬性的字段為主鍵
@GeneratedValue(strategy = GenerationType.IDENTITY) 主鍵自增
@Column(name = "name") 對應(yīng)數(shù)據(jù)庫的名字
@Entity(name = "account") //該注解表示當(dāng)前實(shí)體類是與表有映射關(guān)系的實(shí)體類
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服務(wù)器地址
spring.redis.host=127.0.0.1
#redis連接端口
spring.redis.port=6379
#連接密碼
spring.redis.password=
然后在實(shí)體類上添加注解
@RedisHash("persons") //在redis中開啟一塊空間
@Indexed //該屬性會(huì)在redis數(shù)據(jù)庫中生成二級(jí)索引
@RedisHash("persons") //在redis中開啟一塊空間
public class Person {
@Id
private String id;
@Indexed //該屬性會(huì)在redis數(shù)據(jù)庫中生成二級(jí)索引
private String firstName;
@Indexed
private String lastName;
private Address address;
private List<Family> familyList;
Getter and Setter.....
}
然后定義一個(gè)PersonRepository接口,并繼承 CrudRepository<Person,String>實(shí)現(xiàn)里面的方法
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視圖技術(shù)
使用Thymeleaf模板
在application.properties文件中配置相關(guān)的設(shè)置
#模板緩存的:
spring.thymeleaf.cache=false
#模板編碼
spring.thymeleaf.encoding=UTF-8
#模板樣式
spring.thymeleaf.mode=HTML5
#指定模板頁面存放的路徑
spring.thymeleaf.prefix=classpath:/templates/
#指定模板的后綴
spring.thymeleaf.suffix=.html
#配置國際化文化的基礎(chǔ)名
spring.messages.basename=i18n.login
在資源文件resources下的templates下放入寫好的html頁面,在static頁面下放入需要的靜態(tài)資源
變量表達(dá)式:${}
選擇變量表達(dá)式:*{}
消息表達(dá)式:#{}
連接URL表達(dá)式:@{}
片段表達(dá)式:~{}
<!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=登錄
編寫語言轉(zhuǎn)換的配置類
@Configuration
public class MyLocalResolver implements LocaleResolver {
@Override
public Locale resolveLocale(HttpServletRequest httpServletRequest) {
// 獲取頁面手動(dòng)切換傳遞的語言參數(shù)l
String l = httpServletRequest.getParameter("l");
// 獲取請求頭自動(dòng)傳遞的語言參數(shù)Accept-Language
String header = httpServletRequest.getHeader("Accept-Language");
Locale locale=null;
// 如果手動(dòng)切換參數(shù)不為空,就根據(jù)手動(dòng)參數(shù)進(jìn)行語言切換,否則默認(rèn)根據(jù)請求頭信息切換
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類重新注冊為一個(gè)類型LocaleResolver的Bean組件
@Bean
public LocaleResolver localeResolver(){
return new MyLocalResolver();
}
}
在controller層用Java代碼打開頁面 項(xiàng)目day13_springboot01
3,springboot整合 spring MVC
(1),WebMvcConfigurer and HandlerInterceptor
使用框架中的接口WebMvcConfigurer來實(shí)現(xiàn)頁面的跳轉(zhuǎn),并且自定義攔截器
@Configuration
public class MyInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//用戶請求/admin開頭路徑時(shí),判斷用戶是否登錄
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開頭路徑時(shí),判斷用戶是否登錄
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三大組件
創(chuàng)建自定義的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執(zhí)行了.....");
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執(zhí)行了");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("contextDestroyed執(zhí)行了");
}
}
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 {
/**
* 跳轉(zhuǎn)到 upload.html
*/
@RequestMapping("/toUpload")
public String toUpload(){
return "upload";
}
/**
* 實(shí)現(xiàn)文件上傳
*/
@PostMapping("/uploadFile")
public String uploadFile(MultipartFile[] fileUpload, Model model){
//返回上傳成功的狀態(tài)信息
model.addAttribute("uploadStatus","上傳成功");
//上傳文件
for (MultipartFile multipartFile : fileUpload) {
//獲取上傳文件的后綴名
String originalFilename = multipartFile.getOriginalFilename();
//重新生成文件名
String fileName = UUID.randomUUID() + "-" + originalFilename;
//設(shè)置存儲(chǔ)目錄
String dirPath = "D:/file/";
//如果文件夾不存在,要?jiǎng)?chuàng)建
File file = new File(dirPath);
if(!file.exists()){
file.mkdir();
}
try {
multipartFile.transferTo(new File(dirPath));
} catch (IOException e) {
model.addAttribute("uploadStatus","上傳失敗");
}
}
return "upload";
}
/**
* 跳轉(zhuǎn)到download.html
*/
@GetMapping("/toDownload")
public String toDownload(){
return "download";
}
/**
* 文件下載
*/
@GetMapping("/download")
public ResponseEntity<byte[]> fileDownload(String filename){
//指定要下載的文件路徑
String dirPath = "D:/file/";
//創(chuàng)建文件對象
File file = new File(dirPath + File.separator + filename);
//設(shè)置響應(yīng)頭
HttpHeaders httpHeaders = new HttpHeaders();
//通知瀏覽器以下載的方式打開
httpHeaders.setContentDispositionFormData("attachment",filename);
//定義以流的形式下載返回文件數(shù)據(jù)
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 加在啟動(dòng)類
自定義實(shí)體類
@Entity(name = "account")
public class Account implements Serializable {
@Id //該注解表示配置該屬性的字段為主鍵
@GeneratedValue(strategy = GenerationType.IDENTITY)
Integer id;
@Column(name = "name") //一樣的時(shí)候可以不寫,不一樣的時(shí)候必須寫
String name;
@Column(name = "money")
Double money;
getter and setter....
}
定義Repository
public interface AccountRepository extends JpaRepository<Account,Integer> {
//根據(jù)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;
}
}
/**
* 更新數(shù)據(jù)
* @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),自定義序列化機(jī)制
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<Object,Object> redisTemplate(RedisConnectionFactory redisConnectionFactory){
RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
//使用Json格式序列化對象,對緩存數(shù)據(jù)key和value進(jìn)行轉(zhuǎn)換
Jackson2JsonRedisSerializer<Object> jsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
//解決查詢轉(zhuǎn)換異常問題
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jsonRedisSerializer.setObjectMapper(objectMapper);
//設(shè)置redisTemplate模板API的序列化方式為json
redisTemplate.setDefaultSerializer(jsonRedisSerializer);
return redisTemplate;
}
@Bean
public RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
// 分別創(chuàng)建String和JSON格式序列化對象,對緩存數(shù)據(jù)key和value進(jìn)行轉(zhuǎn)換
RedisSerializer<String> strSerializer = new StringRedisSerializer();
Jackson2JsonRedisSerializer jacksonSeial =
new Jackson2JsonRedisSerializer(Object.class);
// 解決查詢緩存轉(zhuǎn)換異常的問題
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jacksonSeial.setObjectMapper(om);
// 定制緩存數(shù)據(jù)序列化方式及時(shí)效
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 方式進(jìn)行身份驗(yàn)證
定義service
custtomerService
@Service
public class CustomerService {
@Autowired
private CustomerRepository customerRepository;
@Autowired
private AuthorityRepository authorityRepository;
@Autowired
private RedisTemplate redisTemplate;
// 業(yè)務(wù)控制:使用唯一用戶名查詢用戶信息
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;
}
// 業(yè)務(wù)控制:使用唯一用戶名查詢用戶權(quán)限
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 {
// 通過業(yè)務(wù)方法獲取用戶及權(quán)限信息
Customer customer = customerService.getCustomer(s);
List<Authority> authorities = customerService.getCustomerAuthority(s);
// 對用戶權(quán)限進(jìn)行封裝
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開啟自定義全局認(rèn)證
@EnableWebSecurity //@EnableGlobalAuthentication開啟自定義全局認(rèn)證
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Qualifier("dataSource")
@Autowired
private DataSource dataSource;
@Autowired
private UserDetailsServiceImpl userDetailsService;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//設(shè)置密碼編碼器
BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
/*
//使用內(nèi)存進(jìn)行身份認(rèn)證
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進(jìn)行身份認(rèn)證
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進(jìn)行身份驗(yàn)證
auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/").permitAll()
// 需要對static文件夾下靜態(tài)資源進(jìn)行統(tǒng)一放行
.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進(jìn)行操作
@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();
//獲取用戶相關(guān)信息
Authentication authentication = context.getAuthentication();
UserDetails principal = (UserDetails) authentication.getPrincipal();
System.out.println(principal.getUsername());
}
}
在resource文件下存放資源文件
day15_springboot02
6,springboot消息服務(wù)
配置qpplication.properties
#配置RabbitMQ消息中間的連接配置
spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
#配置RabbitMQ虛擬主機(jī)路徑/,可以省略
spring.rabbitmq.virtual-host=/
config
@Configuration
public class RabbitMQConfig {
@Bean
public MessageConverter messageConverter() {
return new Jackson2JsonMessageConverter();
}
}
service
@Service
public class RabbitMQService {
/**
* Publish/Subscribe工作模式接收,處理郵件業(yè)務(wù)
* @param message
*/
/**
@RabbitListener(queues = "fanout_queue_email")
public void psubConsumerEmail(Message message){
byte[] body = message.getBody();
String s = new String(body);
System.out.println("郵件業(yè)務(wù)接收到消息:"+s);
}
*/
/**
*Publish/Subscribe工作模式接收,處理短信業(yè)務(wù)
* @param message
*/
/**
@RabbitListener(queues = "fanout_queue_sms")
public void psubConsumerSms(Message message){
byte[] body = message.getBody();
String s = new String(body);
System.out.println("短信業(yè)務(wù)接收到消息:"+s);
}
*/
/**
* **使用基于注解的方式實(shí)現(xiàn)消息服務(wù)
* 1.1、Publish/Subscribe工作模式接收,處理郵件業(yè)務(wù)
* @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("郵件業(yè)務(wù)接收到消息: "+user);
}
/**
* 1.2、Publish/Subscribe工作模式接收,處理短信業(yè)務(wù)
* @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("短信業(yè)務(wù)接收到消息: "+user);
}
/**
* 2.1、路由模式消息接收,處理error級(jí)別日志信息
* @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級(jí)別日志消息"+message);
}
/**
* 2.2、路由模式消息接收,處理info、error、warning級(jí)別日志信息
* @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等級(jí)別日志消息"+message);
}
/**
* 3.1、通配符模式消息接收,進(jìn)行郵件業(yè)務(wù)訂閱處理
* @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、通配符模式消息接收,進(jìn)行短信業(yè)務(wù)訂閱處理
* @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、定義兩個(gè)默認(rèn)持久化隊(duì)列,分別處理email和sms
amqpAdmin.declareQueue(new Queue("fanout_queue_email"));
amqpAdmin.declareQueue(new Queue("fanout_queue_sms"));
// 3、將隊(duì)列分別與交換器進(jìn)行綁定
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工作模式消息發(fā)送端
*/
@Test
public void psubPublisher() {
User user=new User();
user.setId(1);
user.setUsername("石頭");
rabbitTemplate.convertAndSend("fanout_exchange","",user);
}
/**
* 2、Routing工作模式消息發(fā)送端
*/
@Test
public void routingPublisher() {
rabbitTemplate.convertAndSend("routing_exchange","error_routing_key","routing send error message");
}
/**
* 3、Topcis工作模式消息發(fā)送端
*/
@Test
public void topicPublisher() {
// 1、只發(fā)送郵件訂閱用戶消息
// rabbitTemplate.convertAndSend("topic_exchange","info.email","topics send email message");
// 2、只發(fā)送短信訂閱用戶消息
// rabbitTemplate.convertAndSend("topic_exchange","info.sms","topics send sms message");
// 3、發(fā)送同時(shí)訂閱郵件和短信的用戶消息
rabbitTemplate.convertAndSend("topic_exchange","info.email.sms","topics send email and sms message");
}
}
7,消息處理
(1),無返回值的消息處理和有返回值的消息處理 在啟動(dòng)上加@EnableAsyn
@Service
public class MyASyncService {
/**
* 模擬無返回值的異步任務(wù)處理
* @throws Exception
*/
@Async
public void sendSMS() throws Exception{
long startTime = System.currentTimeMillis();
Thread.sleep(5000);
long endTime = System.currentTimeMillis();
System.out.println("短信業(yè)務(wù)執(zhí)行完成耗時(shí),"+(endTime-startTime));
}
/**
* 模擬有返回值的異步任務(wù)處理
* @return
* @throws Exception
*/
@Async
public Future<Integer> processA() throws Exception {
System.out.println("開始分析并統(tǒng)計(jì)業(yè)務(wù)A數(shù)據(jù)...");
Long startTime = System.currentTimeMillis();
Thread.sleep(4000);
// 模擬定義一個(gè)假的統(tǒng)計(jì)結(jié)果
int count=123456;
Long endTime = System.currentTimeMillis();
System.out.println("業(yè)務(wù)A數(shù)據(jù)統(tǒng)計(jì)耗時(shí):" + (endTime - startTime));
return new AsyncResult<Integer>(count);
}
@Async
public Future<Integer> processB() throws Exception {
System.out.println("開始分析并統(tǒng)計(jì)業(yè)務(wù)B數(shù)據(jù)...");
Long startTime = System.currentTimeMillis();
Thread.sleep(5000);
// 模擬定義一個(gè)假的統(tǒng)計(jì)結(jié)果
int count=654321;
Long endTime = System.currentTimeMillis();
System.out.println("業(yè)務(wù)B數(shù)據(jù)統(tǒng)計(jì)耗時(shí):" + (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("主流耗時(shí),"+(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("異步任務(wù)數(shù)據(jù)統(tǒng)計(jì)匯總結(jié)果: "+total);
Long endTime = System.currentTimeMillis();
System.out.println("主流程耗時(shí): "+(endTime-startTime));
return "success";
}
}
(2),定時(shí)任務(wù) 在啟動(dòng)類上加@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次執(zhí)行,當(dāng)前時(shí)間為:%s", count1++, dateFormat.format(new Date())));
}
@Scheduled(fixedDelay = 60000)
public void scheduledTaskAfterSleep() throws InterruptedException {
System.out.println(String.format("fixedDelay第%s次執(zhí)行,當(dāng)前時(shí)間為:%s", count2++, dateFormat.format(new Date())));
Thread.sleep(10000);
}
@Scheduled(cron = "0 * * * * *")
public void scheduledTaskCron(){
System.out.println(String.format("cron第%s次執(zhí)行,當(dāng)前時(shí)間為:%s",count3++, dateFormat.format(new Date())));
}
}
(3),郵件發(fā)送
application.properties 中配置郵箱
#發(fā)件人郵箱服務(wù)器配置
spring.mail.host=smtp.qq.com
spring.mail.port=587
#配置個(gè)人qq賬戶和密碼
spring.mail.username=785846052@qq.com
spring.mail.password=duinweojybdubgaf
spring.mail.default-encoding=UTF-8
#郵箱服務(wù)超時(shí)間配置
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;
/**
* 發(fā)送純文本郵件
*
* @param to 收件人地址
* @param subject 郵件標(biāo)題
* @param text 郵件內(nèi)容
*/
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 {
// 發(fā)送郵件
mailSender.send(message);
System.out.println("純文本郵件發(fā)送成功");
} catch (MailException e) {
System.out.println("純文本郵件發(fā)送失敗 " + e.getMessage());
e.printStackTrace();
}
}
/**
* 發(fā)送復(fù)雜郵件(包括靜態(tài)資源和附件)
* @param to 收件人地址
* @param subject 郵件標(biāo)題
* @param text 郵件內(nèi)容
* @param filePath 附件地址
* @param rscId 靜態(tài)資源唯一標(biāo)識(shí)
* @param rscPath 靜態(tài)資源地址
*/
public void sendComplexEmail(String to,String subject,String text,String filePath,String rscId,String rscPath){
// 定制復(fù)雜郵件信息MimeMessage
MimeMessage message = mailSender.createMimeMessage();
try {
// 使用MimeMessageHelper幫助類,并設(shè)置multipart多部件使用為true
MimeMessageHelper helper = new MimeMessageHelper(message, true);
helper.setFrom(from);
helper.setTo(to);
helper.setSubject(subject);
helper.setText(text, true);
// 設(shè)置郵件靜態(tài)資源
FileSystemResource res = new FileSystemResource(new File(rscPath));
helper.addInline(rscId, res);
// 設(shè)置郵件附件
FileSystemResource file = new FileSystemResource(new File(filePath));
String fileName = filePath.substring(filePath.lastIndexOf(File.separator));
helper.addAttachment(fileName, file);
// 發(fā)送郵件
mailSender.send(message);
System.out.println("復(fù)雜郵件發(fā)送成功");
} catch (MessagingException e) {
System.out.println("復(fù)雜郵件發(fā)送失敗 "+e.getMessage());
e.printStackTrace();
}
}
/**
* 發(fā)送模板郵件
* @param to 收件人地址
* @param subject 郵件標(biāo)題
* @param content 郵件內(nèi)容
*/
public void sendTemplateEmail(String to, String subject, String content) {
MimeMessage message = mailSender.createMimeMessage();
try {
// 使用MimeMessageHelper幫助類,并設(shè)置multipart多部件使用為true
MimeMessageHelper helper = new MimeMessageHelper(message, true);
helper.setFrom(from);
helper.setTo(to);
helper.setSubject(subject);
helper.setText(content, true);
// 發(fā)送郵件
mailSender.send(message);
System.out.println("模板郵件發(fā)送成功");
} catch (MessagingException e) {
System.out.println("模板郵件發(fā)送失敗 "+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="【純文本郵件】標(biāo)題";
String text="Spring Boot純文本郵件發(fā)送內(nèi)容測試.....";
// 發(fā)送簡單郵件
sendEmailService.sendSimpleEmail(to,subject,text);
}
@Test
public void sendComplexEmailTest() {
String to="209962770@qq.com";
String subject="【復(fù)雜郵件】標(biāo)題";
// 定義郵件內(nèi)容
StringBuilder text = new StringBuilder();
text.append("<html><head></head>");
text.append("<body><h1>祝大家元旦快樂!</h1>");
// cid為固定寫法,rscId指定一個(gè)唯一標(biāo)識(shí)
String rscId = "img001";
text.append("<img src='cid:" +rscId+"'/></body>");
text.append("</html>");
// 指定靜態(tài)資源文件和附件路徑
String rscPath="E:\\圖片\\美女\\8.jpg";
String filePath="E:\\Download\\哈哈.docx";
// 發(fā)送復(fù)雜郵件
sendEmailService.sendComplexEmail(to,subject,text.toString(),filePath,rscId,rscPath);
}
@Test
public void sendTemplateEmailTest() {
String to="209962770@qq.com";
String subject="【模板郵件】標(biāo)題";
// 使用模板郵件定制郵件正文內(nèi)容
Context context = new Context();
context.setVariable("username", "石頭");
context.setVariable("code", "456123");
// 使用TemplateEngine設(shè)置要處理的模板頁面
String emailContent = templateEngine.process("emailTemplate_vercode", context);
// 發(fā)送模板郵件
sendEmailService.sendTemplateEmail(to,subject,emailContent);
}
}