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