SSM框架整合:完成登录,解释配置文件为何这么使用,父子容器问题
前言
简单学习了SSM框架,现在开始完成第一个任务:SSM框架整合,以及搞明白为什么要这么配置文件~第一次整合,搞明白里面如何运作是很关键的。
项目目录
名称 | 作用 |
---|---|
mapper | mybatis映射文件 |
spring | spring家族配置文件=context为父(全局性)+mybatis整合+transaction事务+mvc |
jdbc-druid-config | druid配置文件 |
log4j2 | log文件 |
mybatis-config | mybatis的配置文件 |
项目依赖
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-beans -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.1.0.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-core -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.1.0.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.0.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-test -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.1.0.RELEASE</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-web -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.1.0.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.0.RELEASE</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.12</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.11.1</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.11.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.11</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.1.0.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.servlet.jsp/javax.servlet.jsp-api -->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.3</version>
<scope>provided</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.taglibs/taglibs-standard-impl -->
</dependencies>
依赖没进行整理,有的用不到是尝试用的,可以根据需要进行删减。
配置文件
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!--告诉它spring配置文件在哪-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:/spring/spring-context.xml</param-value>
</context-param>
<!--这个比较关键:通过Listener来加载IOC容器上下文,就是一个桥梁-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!--编码过滤器-->
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!--分发任务Servlet-->
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--制定mvc配置文件在哪-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/spring-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<!--这里是'/'而不是'/*'-->
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
理解一哈:
- Listener: 这里的Listener是比较意思的地方,当tomcat加载完servletContext上下文之后,它怎么知道要加载spring的呢?还记得Listener监听器的作用,这里可以认为是servletContext起来了之后,监听器监听到了,就开始启动spring加载IOC容器了。知道它的作用就可以了。
- dispatcherServlet: 是一个管家。它就像快递的中转站一样,来了一个请求,我就把它送给某一个Servlet去处理。
druid 数据库连接池
# database configure
jdbc.url = jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&autoReconnect=true&useSSL=true&rewriteBatchedStatements=true
jdbc.username = root
jdbc.password = lyy1314520
# druid datasource
druid.initialSize = 10
druid.minIdle = 10
druid.maxActive = 50
druid.maxWait = 60000
druid.timeBetweenEvictionRunsMillis = 60000
druid.minEvictableIdleTimeMillis = 300000
druid.validationQuery = SELECT 1
druid.testWhileIdle = true
druid.testOnBorrow = false
druid.testOnReturn = false
druid.poolPreparedStatements = false
druid.maxPoolPreparedStatementPerConnectionSize = 20
log4j2
status = error
name = PropertiesConfig
filters = threshold
filter.threshold.type = ThresholdFilter
filter.threshold.level = debug
appenders = console
appender.console.type = Console
appender.console.name = STDOUT
appender.console.layout.type = PatternLayout
appender.console.layout.pattern = %d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
rootLogger.level = debug
rootLogger.appenderRefs = stdout
rootLogger.appenderRef.stdout.ref = STDOUT
重点!spring配置文件整合
老大哥:全局context文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="dao"/>
<context:component-scan base-package="service"/>
<import resource="spring-context-mybatis.xml"/>
<import resource="spring-context-transaction.xml"/>
</beans>
完成了2个任务,一个扫描dao,一个扫描service,然后就是导入另外两个配置文件,这样的作用是,让整个配置条理很清晰,不要把它们挤在一起。
二哥:mybatis 整合
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!--配置properties文件的位置-->
<context:property-placeholder location="classpath:jdbc-druid-config.properties"/>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<!-- 基本属性 url、user、password -->
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<!-- 配置初始化大小、最小、最大 -->
<property name="initialSize" value="${druid.initialSize}"/>
<property name="minIdle" value="${druid.minIdle}"/>
<property name="maxActive" value="${druid.maxActive}"/>
<!-- 配置获取连接等待超时的时间 -->
<property name="maxWait" value="${druid.maxWait}"/>
<!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
<property name="timeBetweenEvictionRunsMillis" value="${druid.timeBetweenEvictionRunsMillis}"/>
<!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
<property name="minEvictableIdleTimeMillis" value="${druid.minEvictableIdleTimeMillis}"/>
<property name="validationQuery" value="${druid.validationQuery}"/>
<property name="testWhileIdle" value="${druid.testWhileIdle}"/>
<property name="testOnBorrow" value="${druid.testOnBorrow}"/>
<property name="testOnReturn" value="${druid.testOnReturn}"/>
<!-- 打开PSCache,并且指定每个连接上PSCache的大小 如果用Oracle,则把poolPreparedStatements配置为true,mysql可以配置为false。-->
<property name="poolPreparedStatements" value="${druid.poolPreparedStatements}"/>
<property name="maxPoolPreparedStatementPerConnectionSize"
value="${druid.maxPoolPreparedStatementPerConnectionSize}"/>
<!--End 慢sql监控及log4j设置,不想监控的话将proxyFilters属性删除即可 -->
</bean>
<!--配置sqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="mapperLocations" value="classpath:mapper/*.xml"/>
<property name="configLocation" value="classpath:mybatis-config.xml"/>
</bean>
<!--配置mapper扫描-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
<property name="basePackage" value="dao"/>
</bean>
</beans>
这个没啥好说的,就是这么配置咯~然后properties配置字面量,引用等
三哥:事务处理
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<tx:annotation-driven transaction-manager="dataSourceTransactionManager"/>
</beans>
三哥也简单
儿子:mvc
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 配置jsp显示ViewResolver -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>-->
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
<!--开启mvc注解-->
<mvc:annotation-driven/>
<!--让它只扫描Controller-->,其实这里不需要添加filter的因为我们只是在controller中扫描
<context:component-scan base-package="controller" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
</beans>
为什么要这么配?->父子容器问题
理论
有没有发现一个巨大的问题:为什么要把controller单独拉出来,单独进行扫描呢?一次性扫描完了,不就完事了?还这么费劲?这里一个比较重要的概念:父子容器,父亲(Spring) 儿子(mvc)。
在spring和spring-mvc中,分别是父容器和子容器,父容器不能访问子容器的bean,子容器可以访问父容器的bean,想一下,如果spring上来就把所有的bean 都装到自己的bean里面了。默认情况下,SpringMVC会在自己的容器里面找Controller,于是它找呀找呀,发现就是没有,那不得急死了,Controller啊你在哪里,mmp被Spring抢走了,于是gg了。于是我就想到了一个问题。还记得我们写代码的时候在Controller里面装配Service,这似乎就是子容器访问父容器。水到渠成~操作
- 第一种就是我们固定包扫描不全局扫描
- 第二种,我已经启用了全局扫描,这个时候可以通过include-filter和exclude-filter进行包含和剔除,就像儿子mvc中写的那样也能完成~
配置到此结束~来个例子练练手:完成登录
实现登录
Controller
@Controller
@SessionAttributes(value = {"message"})
public class UserController {
@Autowired
private UserService userService;
@Transactional
@RequestMapping("/login")
public String login(User2 user2, Model model) {
User2 user = userService.loginUser(user2);
if (user != null) {
model.addAttribute("message", "登录成功");
return "success";
}
return null;
}
}
Service
public interface UserService {
User2 loginUser(User2 user2);
}
@Service
public class UserServiceImpl implements service.UserService {
@Autowired
private UserDao dao;
@Override
public User2 loginUser(User2 user2) {
return dao.login(user2);
}
}
dao
public interface UserDao {
User2 login(User2 user);
}
mapper
<mapper namespace="dao.UserDao">
<select id="login" resultType="bean.User2">
select *
from user
where username = #{username} and password = #{password}
</select>
</mapper>
这样简单的配置就完成了。
可能遇到的问题
1、bean找不到,Service等。
主要原因:
- 配置文件是否配置正确,尤其是在web.xml里面,这里面定义的是配置文件的位置。扫描不到有一定程度因为它。比如,你只是指定了Context的位置,却没有import其它。可以用-*.xml指定全部位置。
- 注解是否加对
2、某个类找不到,依赖没弄好
3、…忘记了,出了问题可以留言,或者google把~
总结
此次最大的莫过于,父子容器问题。知道了为什么两个扫描要分开的原因在哪。加上配置文件的一些细节部分,收获挺大。