天天看点

springboot controller访问不到_springboot 完整企业项目搭建实记

目标

这里给自己定一个该框架搭建完成的目标,如下 :

框架要求功能: - 处理http/json 请求 - 日志记录 - 持久化 - 数据源,事务控制 - 定时任务 - 视图模版

搭建环境: - 编译器:idea 2016.2.4 - Maven : maven3.0 - JDK: java7 - 系统: mac OS 10.10.4 - 数据库: mysql5.6

搭建记录

============2018-6-28 =============

一.新建maven应用(不需要web应用)

1.在pom中添加以下:

          org.springframework.boot       spring-boot-starter-parent       1.4.0.RELEASE                           org.springframework.boot           spring-boot-starter-web          
           

2.新建controller

@[email protected] class TestBootController {   @RequestMapping("/")   @ResponseBody   String home() {   return "hello world";   }   public static void main(String[] args) throws Exception {   SpringApplication.run(TestBootController.class, args);   }}
           

此时的项目目录结构是这样的

springboot controller访问不到_springboot 完整企业项目搭建实记

3.启动main函数,可以看到控制台输出,非常简单明了

springboot controller访问不到_springboot 完整企业项目搭建实记

端口默认是8080,启动log已经写了,访问后如图所示

4.在搭建了基础应用的基础上,想增加service层抽离控制层和业务层代码

//业务层接口public interface TestInterFace {   public int testInterFace();   public User testUser();}//业务层接口实现@Servicepublic class TestInterFaceImpl implements TestInterFace {   @Override public int testInterFace() {   return 0;   }   @Override public User testUser() {   return new User();   }}
           

修改controller层代码

@[email protected] class TestBootController {   @Autowired   private TestInterFace testInterFace;   @RequestMapping("/num")   @ResponseBody   int home() {   int i = testInterFace.testInterFace();   return i;   }   @RequestMapping("/get")   @ResponseBody User getUser() {   return testInterFace.testUser();   }   public static void main(String[] args) throws Exception {   SpringApplication.run(TestBootController.class, args);   }}
           

启动后抛异常

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name ‘testBootController’: Unsatisfied dependency expressed through field ‘testInterFace’: No qualifying bean of type [com.kx.springboot.service.TestInterFace] found for dependency [com.kx.springboot.service.TestInterFace]: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.kx.springboot.service.TestInterFace] found for dependency [com.kx.springboot.service.TestInterFace]: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}

相信大家对这种异常非常常见,注入失败,这时候问题来了,按照传统的方式,对注入进行检查。service层已经加了@service的注解,怎么还是注入失败了呢,想查看配置文件,发现springboot没有配置文件,那么他是怎么扫描注解的呢?

百度了一下才知道,springboot默认按照包顺序注入,所以在创建controller时service还没有注入,springboot不需要传统的xml配置扫描包,只需要添加注解@ComponentScan(basePackages={“com.kx.springboot.service”}),代码如下

@[email protected]@ComponentScan(basePackages={"com.kx.springboot.service"})//添加的注解public class TestBootController {   @Autowired   private TestInterFace testInterFace;   @RequestMapping("/num")   @ResponseBody   int home() {   int i = testInterFace.testInterFace();   return i;   }   @RequestMapping("/get")   @ResponseBody User getUser() {   return testInterFace.testUser();   }   public static void main(String[] args) throws Exception {   SpringApplication.run(TestBootController.class, args);   }}
           

再次启动,成功,访问http://localhost:8088/get

{“username”:”usernamekx123”,”password”:”password123”}
           

这时候,又有一个问题,web应用都是多controller,这种启动方式一次只能启动一个controller,那么,怎么才能启动应用后访问多个controller,查阅springboot官方教程后,修改代码如下

增加 UserController

@[email protected]("user")public class UserController {   @Autowired   private TestInterFace testInterFace;   @RequestMapping("/get")   @ResponseBody User getUser() {   return testInterFace.testUser();   }}
           

修改原来 TestBootController

@[email protected]("test")public class TestBootController {   @Autowired   private TestInterFace testInterFace;   @RequestMapping("/num")   @ResponseBody   int home() {   int i = testInterFace.testInterFace();   return i;   }   @RequestMapping("/get")   @ResponseBody User getUser() {   return testInterFace.testUser();   }}
           

在包的最外层增加新的应用启动入口 —> Application

@[email protected](basePackages={"com.kx.springboot"})public class Application {   public static void main(String[] args) throws Exception {   SpringApplication.run(Application.class, args);   }}
           

可以看到,应用启动入口配置了扫描所有包。此时整个项目的结构如图所示

springboot controller访问不到_springboot 完整企业项目搭建实记

启动应用后访问

http://localhost:8080/test/get

{“username”:”usernamekx123”,”password”:”password123”}

http://localhost:8080/user/get

{“username”:”username寇鑫123”,”password”:”password寇鑫123”}

到此,符合处理http/json 的web框架已经搭建ok了

============2018-6-30 =============

第一步处理http/json已经完成了,现在给我们的框架里加上日志记录的功能

要求: - 日志按天记录,自动生成当天的记录文件 - 日志分级存储(info,error)

Springboot自带日志,所以我们现在直接在SpringBoot中添加日志

修改 TestController

@[email protected]("test")public class TestBootController {//增加日志   private final Logger log = LoggerFactory.getLogger(TestBootController.class);   @Autowired   private TestInterFace testInterFace;   @RequestMapping("/num")   @ResponseBody   int home() {   int i = testInterFace.testInterFace();   return i;   }   @RequestMapping("/get")   @ResponseBody User getUser() {   //打印日志   log.info("TestBootController getUser info");   return testInterFace.testUser();   }}
           

只增加了两行代码,现在启动尝试打印日志

访问 http://localhost:8080/test/get

springboot controller访问不到_springboot 完整企业项目搭建实记

可以看到我们的日志已经打出来了,那么现在又有疑问了,这个日志只是打在控制台了,并没有生成文件,查询后发现默认日志如果要打印生成文件需要添加application.properties,而添加这个只可以打固定名称格式的日志,并不能灵活的配置,所以添加Springboot默认支持的logback作为标准日志输出

添加新的pom依赖

           org.springframework.boot           spring-boot-starter-logging
           

在resource下添加logback配置文件logback.xml

首先配置按天生成日志,尝试日志文件打印输出

<?xml version="1.0" encoding="UTF-8"?>                                         %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n                                             ${LOG_HOME}/TestSpringBoot.log.%d{yyyy-MM-dd}.log                      30                                    %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n                                10MB                                 
           

启动应用,尝试输出日志

访问 http://localhost:8080/test/get

springboot controller访问不到_springboot 完整企业项目搭建实记

好吧,首先发现我们的日志不是彩色的了,哭~,不过还好日志打出来了,那么看看我们的文件有没有生成

//生成文件-rw-r--r--  1 kx  staff   5117  3 30 22:19 TestSpringBoot.log.2017-03-30.log//info 日志打印效果2017-03-30 22:21:03.341 [http-nio-8080-exec-3] INFO  com.kx.springboot.controller.TestBootController - TestBootController getUser info
           

OK,文件也完美的按照我们指定的路径生成了,并且文件命名方式按照配置的日期命名,日志打印的内容也按照配置的内容正确的打印了,现在配置info日志和error日志,区分info和error打出来的日志文件

修改 logback.log 增加新的error appender 修改原来的appender 为 info

<?xml version="1.0" encoding="UTF-8"?>                                         %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n                                                ${LOG_HOME}/TestSpringBoot_info.log.%d{yyyy-MM-dd}.log                      30                                error           ACCEPT           DENY                                    %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n                                10MB                                             ${LOG_HOME}/TestSpringBoot_error.log.%d{yyyy-MM-dd}.log                      30                                 error           ACCEPT           DENY                                    %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n                                10MB                                        
           

启动应用后发现已经新增了两个日志文件,命名完全按照配置

-rw-r--r--  1 kx  staff      0  3 30 22:19 TestSpringBoot_error.log.2017-03-30.log-rw-r--r--  1 kx  staff   5117  3 30 22:19 TestSpringBoot_info.log.2017-03-30.log
           

访问 http://localhost:8080/test/get

//info 日志打印效果2017-03-30 22:21:03.341 [http-nio-8080-exec-3] INFO  com.kx.springboot.controller.TestBootController - TestBootController getUser info//error 日志打印效果2017-03-30 22:21:03.342 [http-nio-8080-exec-3] ERROR com.kx.springboot.controller.TestBootController - TestBootController getUser error
           

我们发现error日志和info日志已经按照我们的要求已经打印到对应的文件中了,那么现在我们想解决一下控制台彩色输出的模块,刚好百度到了这里的代码(珍藏)

                                    ${CONSOLE_LOG_PATTERN}           utf8          
           

看一下我们现在的logback日志总文件

<?xml version="1.0" encoding="UTF-8"?>                                                   ${CONSOLE_LOG_PATTERN}           utf8                                                                                          ${LOG_HOME}/TestSpringBoot_info.log.%d{yyyy-MM-dd}.log                      30                         info           ACCEPT           DENY                                    %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n                                10MB                                          ${LOG_HOME}/TestSpringBoot_error.log.%d{yyyy-MM-dd}.log                      30                         error           ACCEPT           DENY                                    %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n                                10MB                                        
           

再次运行项目,访问 http://localhost:8080/test/get

springboot controller访问不到_springboot 完整企业项目搭建实记

哈哈哈,我们的彩色日志又回来了,而且结构清晰,调试代码的时候简直神器啊

============2018-7-2 =============

============整合Mybatis============

在众多orm框架中,我对mybatis最熟悉,所以我采用mybatis进行整合,对我们的orm框架,这里我们也提出几点要求:

支持分页

curd接口抽象处理

事务控制

多数据源

在查阅了一些资料后,找到目前为止最简单的一种整合mybatis方式,这里贴出原始博客地址

http://blog.didispace.com/springbootmybatis/

参照大神的整合,我们的整合异常的顺利,贴出我们增加的代码

在pom中添加以下依赖

   org.mybatis.spring.boot   mybatis-spring-boot-starter   1.1.1   mysql   mysql-connector-java   5.1.21
           

在application.properties 中增加以下配置

spring.datasource.url=jdbc:mysql://localhost:3306/testspring.datasource.username=rootspring.datasource.password=123456spring.datasource.driver-class-name=com.mysql.jdbc.Driver
           

表结构

Create Table: CREATE TABLE `userinfo` ( `id` int(20) NOT NULL AUTO_INCREMENT, `username` varchar(20) DEFAULT NULL, `password` varchar(20) DEFAULT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8
           

编写dao层代码,新建UserDao 接口

@Mapperpublic interface UserDao {   @Select("SELECT * FROM USERINFO WHERE username = #{username}")   UserInfo findByName(@Param("username") String username);   @Insert("INSERT INTO USERINFO(username, password) VALUES(#{username}, #{password})")   int insert(@Param("username") String name, @Param("password") String password);}
           

在业务层增加对dao层接口的引用,修改TestInterFace,TestInterFaceImpl

public interface TestInterFace {   public int testInterFace();   public UserInfo testUser();   public int insertUser(String username,String password);//新增的接口}@Servicepublic class TestInterFaceImpl implements TestInterFace {   //引入dao层接口   @Autowired UserDao userDao;   @Override public int testInterFace() {   return 0;   }   @Override public UserInfo testUser() {   return new UserInfo();   }   //新增的接口实现   @Override public int insertUser(String username,String password) {   return userDao.insert(username,password);   }}
           

接下来修改我们的controller,提供对外的接口,修改UserController

@[email protected]("user")public class UserController {   @Autowired   private TestInterFace testInterFace;   @RequestMapping("/get")   @ResponseBody UserInfo getUser() {   return testInterFace.testUser();   }   //增加新的对外访问接口   @RequestMapping("/add")   @ResponseBody String add() {       testInterFace.insertUser("username123寇鑫","password123寇鑫");       return "插入成功";   }}
           

来测试一下我们的新接口,访问 http://localhost:8080/user/add

springboot controller访问不到_springboot 完整企业项目搭建实记

看到浏览器已经正常返回了,接下来去数据库看看数据有没有实际插入

+----+-------------------+-------------------+| id | username          | password          |+----+-------------------+-------------------+|  1 | username123   | password123  |+----+-------------------+-------------------+
           

可以看到,在我们的表中,已经插入了一条数据,并且中文显示正常。但现在每次新加一个接口,都要对应的写一条sql,这样很麻烦,而且不利于开发,业务方不能专注于业务的开发,所以我们要抽象出来通用的curd接口,并且支付分页。

============2018-7-3=============

mybatis有很多成熟的分页插件以及通用接口插件,这里我们也采用目前较为成熟的方案,不必重复造轮子。

添加pom

   com.github.pagehelper   pagehelper   4.2.1   tk.mybatis   mapper   3.3.9
           

因为springboot 不需要设置xml,所以这里都采用注解和代码的形式注入插件

新建配置类

package com.kx.springboot.dao.mybatis;import com.github.pagehelper.PageHelper;import org.apache.ibatis.plugin.Interceptor;import org.apache.ibatis.session.SqlSessionFactory;import org.mybatis.spring.SqlSessionFactoryBean;import org.springframework.context.annotation.Bean;import org.springframework.core.io.support.PathMatchingResourcePatternResolver;import org.springframework.core.io.support.ResourcePatternResolver;import java.util.Properties;/*** Created by kx on 17/4/2.*/public class MyBatisConfig {   @Bean(name = "sqlSessionFactory")   public SqlSessionFactory sqlSessionFactoryBean() {   SqlSessionFactoryBean bean = new SqlSessionFactoryBean();//  bean.setDataSource(dataSource());   bean.setTypeAliasesPackage("com.kx.springboot.bean");   //分页插件设置   PageHelper pageHelper = new PageHelper();   Properties properties = new Properties();   properties.setProperty("reasonable", "true");   properties.setProperty("supportMethodsArguments", "true");   properties.setProperty("returnPageInfo", "check");   properties.setProperty("params", "count=countSql");   pageHelper.setProperties(properties);   //添加分页插件   bean.setPlugins(new Interceptor[]{pageHelper});   ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();   try {       //基于注解扫描Mapper,不需配置xml路径       //bean.setMapperLocations(resolver.getResources("classpath:mapper/*.xml"));       return bean.getObject();   } catch (Exception e) {       e.printStackTrace();       throw new RuntimeException(e);   }   }}
           

新建配置扫描类

package com.kx.springboot.dao.mybatis;import org.springframework.boot.autoconfigure.AutoConfigureAfter;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import tk.mybatis.spring.mapper.MapperScannerConfigurer;import java.util.Properties;/*** Created by kx on 17/4/2.*/@Configuration//必须在MyBatisConfig注册后再加载MapperScannerConfigurer,否则会报错@AutoConfigureAfter(MyBatisConfig.class)public class MyBatisMapperScannerConfig {   @Bean   public MapperScannerConfigurer mapperScannerConfigurer() {   MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();   mapperScannerConfigurer.setSqlSessionFactoryBeanName("sqlSessionFactory");   mapperScannerConfigurer.setBasePackage("com.kx.springboot.dao.mybatis");    //初始化扫描器的相关配置,这里我们要创建一个Mapper的父类   Properties properties = new Properties();   properties.setProperty("mappers", "com.kx.springboot.dao.baseDao.MyMapper");   properties.setProperty("notEmpty", "false");   properties.setProperty("IDENTITY", "MYSQL");   mapperScannerConfigurer.setProperties(properties);    return mapperScannerConfigurer;   }}
           

新建对外暴露接口

package com.kx.springboot.dao.baseDao;import tk.mybatis.mapper.common.Mapper;import tk.mybatis.mapper.common.MySqlMapper;/*** Created by kx on 17/4/2.*/public interface MyMapper extends Mapper, MySqlMapper {   //TODO   //FIXME 特别注意,该接口不能被扫描到,否则会出错}
           

现在,修改我们的dao层实现类,继承我们的通用接口

@Mapperpublic interface UserDao extends MyMapper {}
           

在业务层中增加想要调用的方法

public interface TestInterFace {   public int testInterFace();   public UserInfo testUser();   public int insertUser(UserInfo userInfo);//新增加的方法   List selectALL();}@Servicepublic class TestInterFaceImpl implements TestInterFace {   @Autowired UserDao userDao;   @Override public int testInterFace() {   return 0;   }   @Override public UserInfo testUser() {   return new UserInfo();   }   @Override public int insertUser(UserInfo userInfo) {   return userDao.insert(userInfo);   }   //新增加的实现   @Override   public List selectALL(){   return userDao.selectAll();   }}
           

看到我们在调用userDao时已经有了通用的单表查询方法。将新接口暴露给http,修改controller

@[email protected]("user")public class UserController {   @Autowired   private TestInterFace testInterFace;   @RequestMapping("/get")   @ResponseBody UserInfo getUser() {   return testInterFace.testUser();   }   @RequestMapping("/add")   @ResponseBody String add() {       UserInfo user = new UserInfo();       user.setUsername("username123寇鑫");       user.setPassword("password123寇鑫");       testInterFace.insertUser(user);       return "插入成功";   }//新增的接口方法   @RequestMapping("/getall")   @ResponseBody List getall() {       return testInterFace.selectALL();   }}
           

访问http://localhost:8080/user/getall

程序抛出异常

### Error querying database.  Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Table 'ssmtest.user_info' doesn't exist### The error may exist in com/kx/springboot/dao/UserDao.java (best guess)### The error may involve defaultParameterMap### The error occurred while setting parameters### SQL: SELECT username,password  FROM user_info### Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Table 'ssmtest.user_info' doesn't exist; bad SQL grammar []; nested exception is com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Table 'ssmtest.user_info' doesn't exist] with root causecom.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Table 'ssmtest.user_info' doesn't exist   at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)   at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)   at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)   at java.lang.reflect.Constructor.newInstance(Constructor.java:526)
           

从异常可以看出来,我们的表和我们查询数据库的字段没有映射起来,查询资料后发现原因是通用插件的问题,默认的字段中有下划线,我们需要手动指定映射

修改userinfo类

package com.kx.springboot.bean;import javax.persistence.Column;import javax.persistence.Table;/*** Created by kx on 17/3/29.*///增加注解声明表名@Table(name = "userinfo")public class UserInfo {   //增加注解声明字段名   @Column(name = "username")   private String username = "username寇鑫123";   @Column(name = "password")   private String password = "password寇鑫123";   public String getUsername() {   return username;   }   public void setUsername(String username) {   this.username = username;   }   public String getPassword() {   return password;   }   public void setPassword(String password) {   this.password = password;   }   @Override public String toString() {   return "User{" + "username='" + username + ''' + ", password='" + password + ''' + '}';   }}
           

再次启动访问 http://loclhost:8080/user/getall

[   {       "username":"user123寇鑫",       "password":"password123寇鑫"   },   {       "username":"username123寇鑫",       "password":"password123寇鑫"   }]
           

得到如下结果,到此已经成功整合了通用mapper插件,现在加入定时任务测试 springboot 定时任务的启动和配置要简单很多,只需要增加一个注解即可

修改Application.java 启动类

@[email protected](basePackages={"com.kx.springboot"})@EnableScheduling//增加支持定时任务的注解public class Application {   public static void main(String[] args) throws Exception {   SpringApplication.run(Application.class, args);   }}
           

新建定时任务类

@Componentpublic class SchedulingTest {   private final Logger logger = LoggerFactory.getLogger(getClass());   @Scheduled(cron = "0/5 * * * * ?") // 每5秒执行一次   public void scheduler() {   logger.info(">>>>>>>>>>>>> scheduled test... ");   }}
           

启动测试

2017-04-05 00:19:00.002  INFO 6512 --- [pool-1-thread-1] c.kx.springboot.schedul.SchedulingTest   : >>>>>>>>>>>>> scheduled test... 2017-04-05 00:19:05.000  INFO 6512 --- [pool-1-thread-1] c.kx.springboot.schedul.SchedulingTest   : >>>>>>>>>>>>> scheduled test... 2017-04-05 00:19:10.000  INFO 6512 --- [pool-1-thread-1] c.kx.springboot.schedul.SchedulingTest   : >>>>>>>>>>>>> scheduled test... 2017-04-05 00:19:15.000  INFO 6512 --- [pool-1-thread-1] c.kx.springboot.schedul.SchedulingTest   : >>>>>>>>>>>>> scheduled test... 2017-04-05 00:19:20.001  INFO 6512 --- [pool-1-thread-1] c.kx.springboot.schedul.SchedulingTest   : >>>>>>>>>>>>> scheduled test... 2017-04-05 00:19:25.002  INFO 6512 --- [pool-1-thread-1] c.kx.springboot.schedul.SchedulingTest   : >>>>>>>>>>>>> scheduled test... 2017-04-05 00:19:30.003  INFO 6512 --- [pool-1-thread-1] c.kx.springboot.schedul.SchedulingTest   : >>>>>>>>>>>>> scheduled test... 2017-04-05 00:19:35.003  INFO 6512 --- [pool-1-thread-1] c.kx.springboot.schedul.SchedulingTest   : >>>>>>>>>>>>> scheduled test...
           

可以看到我们的定时任务执行结果没有问题,接下来要在框架中增加多数据源以及事务控制。