SpringBoot 学习 一 thymeleaf使用
加入thymeleaf依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
编辑一个不起眼的controller
@Controller
public class HelloSpringBoot {
@RequestMapping("/index")
public String index(ModelMap map ){
map.addAttribute("message","hello thymeleaf");
return "index";
}
@RequestMapping("/")
@ResponseBody
public String hello(){
return "hello springboot";
}
}
编辑一个不起眼的index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" th:href="@{/css/bootstrap.css}"></link>
</head>
<body>
<td th:text="${message}"></td>
<h2 th:text="${message}"></h2>
</body>
</html>
springboot静态资源的文件格式为 : src\main\resources\templates\index.html 一般将html文件放在 templates文件下,也可以在thymleaf配置文件中自定义templates文件名 springboot学习系列借鉴文章大多来自 该作者 http://blog.didispace.com/springbootaoplog/ 收工。啊,普通程序员的快乐往往就是这么简单。
SpringBoot 学习 二 springboot 集成jpa
加入jpa必须依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
application.properties配置
spring.datasource.url=jdbc:mysql://localhost:3306/posp?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&rewriteBatchedStatements=true
spring.datasource.username=java
spring.datasource.password=java
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.jpa.properties.hibernate.hbm2ddl.auto=update
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.hbm2ddl.auto是hibernate的配置属性,其主要作用是:自动创建、更新、验证数据库表结构。该参数的几种配置如下:
create:每次加载hibernate时都会删除上一次的生成的表,然后根据你的model类再重新来生成新表,哪怕两次没有任何改变也要这样执行,这就是导致数据库表数据丢失的一个重要原因。
create-drop:每次加载hibernate时根据model类生成表,但是sessionFactory一关闭,表就自动删除。
update:最常用的属性,第一次加载hibernate时根据model类会自动建立起表的结构(前提是先建立好数据库),以后加载hibernate时根据model类自动更新表结构,即使表结构改变了但表中的行仍然存在不会删除以前的行。要注意的是当部署到服务器后,表结构是不会被马上建立起来的,是要等应用第一次运行起来后才会。
validate:每次加载hibernate时,验证创建数据库表结构,只会和数据库中的表进行比较,不会创建新表,但是会插入新值。 spring.jpa.show-sql=true 显示执行sql
一个不起眼的测试demo
不起眼的sql
INSERT INTO teacher (tno, tname, tsex, tbirthday, prof, depart)
VALUES (804, '李诚', '男', '1958-12-02', '副教授', '计算机系');
INSERT INTO teacher (tno, tname, tsex, tbirthday, prof, depart)
VALUES (856, '张旭', '男', '1969-03-12', '讲师', '电子工程系');
INSERT INTO teacher (tno, tname, tsex, tbirthday, prof, depart)
VALUES (825, '王萍', '女', '1972-05-05', '助教', '计算机系');
INSERT INTO teacher (tno, tname, tsex, tbirthday, prof, depart)
VALUES (831, '刘冰', '女', '1977-08-14', '助教', '电子工程系');
一个不起眼的实体类
@Data
@Entity
@Table(name="teacher")
public class Teacher {
@Id
private String tno;
private String tname;
private String tsex;
private Date tbirthday;
private String prof;
private String depart;
}
注意:这里的@Id 注解一定要有否则启动抛错 'No identifier specified for entity'
一个不起眼的repository
@Repository
public interface TeacherRepository extends JpaRepository<Teacher,String> {
List<Teacher> findByDEPART(String depart);
@Query(value = "select * from teacher WHERE depart = ?",nativeQuery = true)
List<Teacher> findTeacher(String depart);
}
一个不起眼的controller
@RestController
public class TeacherController {
@Autowired
TeacherRepository teacherRepository;
@RequestMapping(value = "/findByDepart",method = RequestMethod.GET)
public List<Teacher> findByDepart(String depart){
return teacherRepository.findByDepart(depart);
}
@RequestMapping(value = "/findByDepart1",method = RequestMethod.GET)
public List<Teacher> findByDepart1(String depart){
return teacherRepository.findTeacher(depart);
}
}
收工。啊,普通程序员的快乐往往就是这么简单。
SpringBoot 学习 三 springboot +jpa多数据源配置
建立一个Spring配置类
定义两个DataSource用来读取配置文件(application.property)中的多数据源,这里定义一个primary,一个slave
@Configuration
public class DataSourceConfig {
@Bean
@Qualifier("primaryDataSource")
@ConfigurationProperties(prefix = "spring.datasource.primary")
@Primary
public DataSource primaryDataSource(){
return DataSourceBuilder.create().build();
}
@Bean
@Qualifier("slaveDataSource")
@ConfigurationProperties(prefix = "spring.datasource.slave")
public DataSource salveDataSource(){
return DataSourceBuilder.create().build();
}
}
建立JPA配置
注意主数据源和从数据源repository路径的配置,和entity路径的配置。这里用@Primary区分主数据源
/**
主数据源配置
*/
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
entityManagerFactoryRef="entityManagerFactoryPrimary",
transactionManagerRef="transactionManagerPrimary",
basePackages= { "com.myc.springboot.study.study.service.primary" }) //设置Repository所在位置
public class PrimaryConfig {
@Autowired @Qualifier("primaryDataSource")
private DataSource primaryDataSource;
@Autowired
private HibernateProperties hibernateProperties;
@Autowired
private JpaProperties jpaProperties;
@Primary
@Bean(name = "entityManagerPrimary")
public EntityManager entityManager(EntityManagerFactoryBuilder builder) {
return entityManagerFactoryPrimary(builder).getObject().createEntityManager();
}
@Primary
@Bean(name = "entityManagerFactoryPrimary")
public LocalContainerEntityManagerFactoryBean entityManagerFactoryPrimary (EntityManagerFactoryBuilder builder) {
Map<String, Object> properties = hibernateProperties.determineHibernateProperties(
jpaProperties.getProperties(), new HibernateSettings());
return builder
.dataSource(primaryDataSource)
.properties(properties)
.packages("com.myc.springboot.study.study.domain.primary") //设置实体类所在位置
.persistenceUnit("primaryPersistenceUnit")
.build();
}
@Primary
@Bean(name = "transactionManagerPrimary")
public PlatformTransactionManager transactionManagerPrimary(EntityManagerFactoryBuilder builder) {
return new JpaTransactionManager(entityManagerFactoryPrimary(builder).getObject());
}
}
/**
从数据源配置
*/
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
entityManagerFactoryRef="entityManagerFactorySlave",
transactionManagerRef="transactionManagerSlave",
basePackages= { "com.***.***.***.***.service.slave" }) //设置Repository所在位置
public class SlaveConfig {
@Autowired @Qualifier("slaveDataSource")
private DataSource slaveDataSource;
@Autowired
private HibernateProperties hibernateProperties;
@Autowired
private JpaProperties jpaProperties;
@Bean(name = "entityManagerSlave")
public EntityManager entityManager(EntityManagerFactoryBuilder builder) {
return entityManagerFactorySlave(builder).getObject().createEntityManager();
}
@Bean(name = "entityManagerFactorySlave")
public LocalContainerEntityManagerFactoryBean entityManagerFactorySlave (EntityManagerFactoryBuilder builder) {
Map<String, Object> properties = hibernateProperties.determineHibernateProperties(
jpaProperties.getProperties(), new HibernateSettings());
return builder
.dataSource(slaveDataSource)
.properties(properties)
.packages("com.myc.springboot.study.study.domain.slave") //设置实体类所在位置
.persistenceUnit("slavePersistenceUnit")
.build();
}
@Bean(name = "transactionManagerSlave")
public PlatformTransactionManager transactionManagerSlave(EntityManagerFactoryBuilder builder) {
return new JpaTransactionManager(entityManagerFactorySlave(builder).getObject());
}
}
注意: 在配置主数据源和从数据源时,一是两处路径的配置,二是@Bean名称别冲突,三是 @Primary即主数据源只能有一个
配置文件
#注意这里的jdbc-url 通常是 url,但是这里需要用jdbc-url,不然启动会抛错
spring.datasource.primary.jdbc-url=jdbc:mysql://localhost:3306/posp?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&rewriteBatchedStatements=true
spring.datasource.primary.username=java
spring.datasource.primary.password=java
spring.datasource.primary.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.slave.jdbc-url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&rewriteBatchedStatements=true
spring.datasource.slave.username=java
spring.datasource.slave.password=java
spring.datasource.slave.driver-class-name=com.mysql.jdbc.Driver
spring.jpa.properties.hibernate.hbm2ddl.auto=update
spring.jpa.show-sql=true
测试数据sql
insert into student (sno, sname, ssex, sbirthday, sclass) values (108, '曾华'
, '男', '1977-09-01', 95033);
insert into student (sno, sname, ssex, sbirthday, sclass) values (105, '匡明'
, '男', '1975-10-02', 95031);
insert into student (sno, sname, ssex, sbirthday, sclass) values (107, '王丽'
, '女', '1976-01-23', 95033);
insert into student (sno, sname, ssex, sbirthday, sclass) values (101, '李军'
, '男', '1976-02-20', 95033);
insert into student (sno, sname, ssex, sbirthday, sclass) values (109, '王芳'
, '女', '1975-02-10', 95031);
insert into student (sno, sname, ssex, sbirthday, sclass) values (103, '陆君'
, '男', '1974-06-03', 95031);
一个不起眼的测试 controller
@RestController
public class StudentController {
@Autowired
private StudentRepository studentRepository;
@RequestMapping(value = "/getStudentByClass",method = RequestMethod.GET)
public List<Student> getStudentByClass(String class_){
return studentRepository.findBySclass(class_);
}
}
码云地址 https://gitee.com/BoMumyc/spboot-study study 收工。啊,普通程序员的快乐往往就是这么简单。
SpringBoot 学习 四 springboot Aop处理请求日志
引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
log aop配置
@Aspect
@Component
public class LogAspect {
private Logger logger = LoggerFactory.getLogger(getClass());
@Pointcut("execution(* com.myc.demo.web..*.*(..))")
public void log(){}
@Before("log()")
public void doBefore(JoinPoint joinPoint) throws Throwable {
// 接收到请求,记录请求内容
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
// 记录下请求内容
logger.info("URL : " + request.getRequestURL().toString());
logger.info("HTTP_METHOD : " + request.getMethod());
logger.info("IP : " + request.getRemoteAddr());
logger.info("CLASS_METHOD : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
logger.info("ARGS : " + Arrays.toString(joinPoint.getArgs()));
}
@AfterReturning(returning = "ret", pointcut = "log()")
public void doAfterReturning(Object ret) throws Throwable {
// 处理完请求,返回内容
logger.info("RESPONSE : " + ret);
}
}
测试类
@RestController
public class HelloController {
@RequestMapping(value = "/",method = RequestMethod.GET)
public String hello(){
return "hello springboot";
}
@RequestMapping(value = "/index",method = RequestMethod.GET)
public String idex(){
return "hello index";
}
}
码云地址 https://gitee.com/BoMumyc/spboot-study> demo-0809 收工。啊,普通程序员的快乐往往就是这么简单。
SpringCloud 学习一 服务注册与发现
注册中心 Eureka
-
pom文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.7.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.myc</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Greenwich.SR2</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
在启动类上添加注解 @EnableEurekaServer
@SpringBootApplication
@EnableEurekaServer
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
> @EnableEurekaServer 注册中心标志
配置文件配置 application.properties
server.port=9000
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false
eureka.client.serviceUrl.defaultZone=http://localhost:${server.port}/eureka/
false 表示自己是注册中心
服务方配置 与上类似
区别在于 ,一,启动类的@EnableEurekaServer注解变为@EnableEurekaClient。二,配置文件(application.properties)的不同具体如下:
spring.application.name=service-04
server.port=8001
eureka.client.serviceUrl.defaultZone=http://localhost:9000/eureka/
注意端口不要重复
码云地址 https://gitee.com/BoMumyc/spboot-study> demo-0809-03 demo-0809-04
SpringCloud 学习二Ribbon 和feign
pom文件加入ribbon
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
</dependency>
allication.property配置
spring.application.name=service-06
server.port=8003
eureka.client.serviceUrl.defaultZone=http://localhost:9000/eureka/
启动类加入 restTemplate
@SpringBootApplication
@EnableDiscoveryClient
public class Demo080906Application {
public static void main(String[] args) {
SpringApplication.run(Demo080906Application.class, args);
}
@Bean
@LoadBalanced
RestTemplate restTemplate(){
return new RestTemplate();
}
}
测试类
@RestController
public class ConsumerController {
@Autowired
RestTemplate restTemplate;
@RequestMapping(value = "/",method = RequestMethod.GET)
public String hello(){
return restTemplate.getForObject("http://service-04/hello?name=唱跳rap篮球" , String.class);
}
}
码云地址 https://gitee.com/BoMumyc/spboot-study> demo-0809-06
feign pom配置 加入
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
不同版本的springcloud feign略有不同 ,使用时注意版本 ,本文springcloud版本为:Greenwich.SR2
application.properties 配置
spring.application.name=service-05
server.port=8002
eureka.client.serviceUrl.defaultZone=http://localhost:9000/eureka/
eureka.client.registerWithEureka=true
eureka.client.fetchRegistry=true
注意要加入最后两行配置,否则调用其他服务会抛错 'Load balancer does not have available server for client'
feign 服务
@FeignClient("SERVICE-04")
public interface ComputeClient {
@RequestMapping(method = RequestMethod.GET, value = "/hello")
String hello(@RequestParam(value = "name") String name);
}
简单的测试类
@RestController
public class ConsumerController {
@Autowired
ComputeClient computeClient;
@RequestMapping(value = "/",method = RequestMethod.GET)
public String hello(){
return computeClient.hello("张三");
}
}
码云地址 https://gitee.com/BoMumyc/spboot-study> demo-0809-05
SpringCloud 学习三 hystrix 断路器
feign中使用断路器
加入依赖
<dependency>
<groupId>com.netflix.hystrix</groupId>
<artifactId>hystrix-javanica</artifactId>
</dependency>
feign 中已经有hystrix依赖了所以不用加,但是用到缺少javanica这个依赖,不同的版本是有区别的,比如我用的这个spirngcloud版本,feign中是没有javanica这个依赖的,而别的版本或许有比如Finchley.M8这个版本就有,因此运用是注意
开启断路器---在application.properties中加入
feign.hystrix.enabled=true
修改代码
@FeignClient(value = "SERVICE-04",fallback = ConsumerClientImpl.class)
public interface ConsumerClient {
@RequestMapping(method = RequestMethod.GET, value = "/hello")
String hello(@RequestParam(value = "name") String name);
}
@Component
public class ConsumerClientImpl implements ConsumerClient {
@Override
public String hello(String name) {
return "server time out error!!!";
}
}
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients(basePackageClasses = {com.myc.demo080905.ConsumerClient.class})
@EnableCircuitBreaker
public class Demo080905Application {
public static void main(String[] args) {
SpringApplication.run(Demo080905Application.class, args);
}
}
码云地址 https://gitee.com/BoMumyc/spboot-study> demo-0809-05
Ribbon中用hystrix
加入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
<dependency>
<groupId>com.netflix.hystrix</groupId>
<artifactId>hystrix-javanica</artifactId>
</dependency>
修改之前的代码
@SpringBootApplication
@EnableDiscoveryClient
@EnableCircuitBreaker
public class Demo080906Application {
public static void main(String[] args) {
SpringApplication.run(Demo080906Application.class, args);
}
@Bean
@LoadBalanced
RestTemplate restTemplate(){
return new RestTemplate();
}
}
@RestController
public class ConsumerController {
@Autowired
RestTemplate restTemplate;
@HystrixCommand(fallbackMethod = "helloCallBack")
@RequestMapping(value = "/",method = RequestMethod.GET)
public String hello(){
return restTemplate.getForObject("http://service-04/hello?name=唱跳rap篮球" , String.class);
}
public String helloCallBack(){
return "servce time out error";
}
}
码云地址 https://gitee.com/BoMumyc/spboot-study> demo-0809-06
SpringCloud 学习四 zuul
加入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zuul</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
启动类加入注解
@EnableZuulProxy
@SpringCloudApplication
public class ApiGatewayApplication {
public static void main(String[] args) {
SpringApplication.run(ApiGatewayApplication.class, args);
}
@Bean
public AccessFilter accessFilter() {
return new AccessFilter();
}
}
这里用了@SpringCloudApplication注解,它整合了@SpringBootApplication、@EnableDiscoveryClient、@EnableCircuitBreaker,主要目的还是简化配置。
application.properties文件配置 ----- serviceId的映射
spring.application.name=api-gateway
server.port=5000
zuul.routes.api-a.path=/api-05/**
zuul.routes.api-a.serviceId=service-05
zuul.routes.api-b.path=/api-06/**
zuul.routes.api-b.serviceId=service-06
eureka.client.serviceUrl.defaultZone=http://localhost:9000/eureka/
zuul 服务过滤 ------小作用demo
public class AccessFilter extends ZuulFilter {
private static Logger log = LoggerFactory.getLogger(AccessFilter.class);
@Override
public String filterType() {
return "pre";
}
@Override
public int filterOrder() {
return 0;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() throws ZuulException {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
log.info(String.format("%s request to %s", request.getMethod(), request.getRequestURL().toString()));
Object accessToken = request.getParameter("accessToken");
if(accessToken == null) {
log.info("access token is empty");
ctx.setSendZuulResponse(false);
ctx.setResponseStatusCode(401);
try {
ctx.getResponse().getWriter().write("token is empty");
} catch (IOException e) {
}
return null;
}
log.info("access token ok");
return null;
}
}
自定义过滤器的实现,需要继承ZuulFilter,需要重写实现下面四个方法: filterType:返回一个字符串代表过滤器的类型,在zuul中定义了四种不同生命周期的过滤器类型,具体如下: pre:可以在请求被路由之前调用 routing:在路由请求时候被调用 post:在routing和error过滤器之后被调用 error:处理请求时发生错误时被调用 filterOrder:通过int值来定义过滤器的执行顺序 shouldFilter:返回一个boolean类型来判断该过滤器是否要执行,所以通过此函数可实现过滤器的开关。在上例中,我们直接返回true,所以该过滤器总是生效。 run:过滤器的具体逻辑。需要注意,这里我们通过ctx.setSendZuulResponse(false)令zuul过滤该请求,不对其进行路由,然后通过ctx.setResponseStatusCode(401)设置了其返回的错误码,当然我们也可以进一步优化我们的返回,比如,通过ctx.setResponseBody(body)对返回body内容进行编辑等。