🏳️🌈bean 加载控制
🍸问题分析
我们先来看下目前我们的项目目录结构:
config 目录存入的是配置类,写过的配置类有:
ServletContainersInitConfig
SpringConfig
SpringMvcConfig
JdbcConfig
MybatisConfig
controller 目录存放的是 SpringMVC 的 controller 类
service 目录存放的是 service 接口和实现类
dao 目录存放的是 dao/Mapper 接口
controller、service 和 dao 这些类都需要被容器管理成 bean 对象,那么到底是该让 SpringMVC 加载还是让 Spring 加载呢?
SpringMVC 加载其相关 bean(表现层 bean),也就是 controller 包下的类
Spring 控制的 bean
业务bean(Service)
功能bean(DataSource,SqlSessionFactoryBean,MapperScannerConfigurer 等)
分析清楚谁该管哪些 bean 以后,接下来要解决的问题是如何让 Spring 和 SpringMVC 分开加载各自的内容。
在 SpringMVC 的配置类SpringMvcConfig中使用注解@ComponentScan,我们只需要将其扫描范围设置到 controller 即可,如
在 Spring 的配置类SpringConfig中使用注解@ComponentScan,当时扫描的范围中其实是已经包含了 controller,如:
从包结构来看的话,Spring 已经多把 SpringMVC 的 controller 类也给扫描到,所以针对这个问题该如何解决,就是咱们接下来要学习的内容。
概括的描述下咱们现在的问题就是因为功能不同,如何避免 Spring 错误加载到 SpringMVC 的 bean?
🍸思路分析
针对上面的问题,解决方案也比较简单,就是:
具体该如何排除:
方式一:Spring 加载的 bean 设定扫描范围为精准范围,例如 service 包、dao 包等
方式二:Spring 加载的 bean 设定扫描范围为 com.nefu,排除掉 controller 包中的 bean
方式三:不区分 Spring 与 SpringMVC 的环境,加载到同一个环境中[了解即可]
🍸环境准备
创建一个 Web 的 Maven 项目
pom.xml 添加 Spring 依赖
<?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> <groupId>com.itheima</groupId> <artifactId>springmvc_02_bean_load</artifactId> <version>1.0-SNAPSHOT</version> <packaging>war</packaging> <dependencies> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.2.10.RELEASE</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.16</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.6</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.2.10.RELEASE</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>1.3.0</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.tomcat.maven</groupId> <artifactId>tomcat7-maven-plugin</artifactId> <version>2.1</version> <configuration> <port>80</port> <path>/</path> </configuration> </plugin> </plugins> </build> </project>
复制代码
public class ServletContainersInitConfig extends AbstractDispatcherServletInitializer { protected WebApplicationContext createServletApplicationContext() { AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext(); ctx.register(SpringMvcConfig.class); return ctx; } protected String[] getServletMappings() { return new String[]{"/"}; } protected WebApplicationContext createRootApplicationContext() { return null; } } @Configuration @ComponentScan("com.itheima.controller") public class SpringMvcConfig { } @Configuration @ComponentScan("com.itheima") public class SpringConfig { }
复制代码
@Controller public class UserController { @RequestMapping("/save") @ResponseBody public String save(){ System.out.println("user save ..."); return "{'info':'springmvc'}"; } } public interface UserService { public void save(User user); } @Service public class UserServiceImpl implements UserService { public void save(User user) { System.out.println("user service ..."); } } public interface UserDao { @Insert("insert into tbl_user(name,age)values(#{name},#{age})") public void save(User user); } public class User { private Integer id; private String name; private Integer age; //setter..getter..toString略 }
复制代码
最终创建好的项目结构如下:
🍸设置 bean 加载控制
💊方法一
方式一:修改 Spring 配置类,设定扫描范围为精准范围。
@Configuration@ComponentScan({"com.nefu.service","com.nefu.dao"})public class SpringConfig {}
复制代码
注意:
上述只是通过例子说明可以精确指定让 Spring 扫描对应的包结构,真正在做开发的时候,因为 Dao 最终是交给MapperScannerConfigurer对象来进行扫描处理的,我们只需要将其扫描到 service 包即可。
💊方法二
修改 Spring 配置类,设定扫描范围为 com.nefu,排除掉 controller 包中的 bean
@Configuration@ComponentScan(value="com.nefu", excludeFilters=@ComponentScan.Filter( type = FilterType.ANNOTATION, classes = Controller.class ))public class SpringConfig {}
复制代码
这个地方要注意一点,你在写这个 Controller.class 的时候可能有多个类符合要求:
这里导的是第一个,如果导错了程序会报错!
excludeFilters 属性:设置扫描加载 bean 时,排除的过滤规则
我们可以看见两个过滤方法:excludeFilters 代表去除 includeFilters 代表包含
type 属性:设置排除规则,当前使用按照 bean 定义时的注解类型进行排除
ANNOTATION:按照注解排除
ASSIGNABLE_TYPE:按照指定的类型过滤
ASPECTJ:按照 Aspectj 表达式排除,基本上不会用
REGEX:按照正则表达式排除
CUSTOM:按照自定义规则排除
大家只需要知道第一种 ANNOTATION 即可
classes 属性:设置排除的具体注解类,当前设置排除 @Controller 定义的 bean
如何测试 controller 类已经被排除掉了?
public class App{ public static void main (String[] args){ AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class); System.out.println(ctx.getBean(UserController.class)); }}
复制代码
如果被排除了,该方法执行就会报 bean 未被定义的错误
注意:测试的时候,需要把 SpringMvcConfig 配置类上的 @ComponentScan 注解注释掉,否则不会报错
这是因为:Spring 配置类扫描的包是com.nefu,SpringMVC 的配置类,SpringMvcConfig上有一个 @Configuration 注解,也会被 Spring 扫描到,SpringMvcConfig 上又有一个 @ComponentScan,把 controller 类又给扫描进来了。所以如果不把 @ComponentScan 注释掉,Spring 配置类将 Controller 排除,但是因为扫描到 SpringMVC 的配置类,又将其加载回来,演示的效果就出不来。
解决方案,也简单,把 SpringMVC 的配置类移出 Spring 配置类的扫描范围即可。
最后一个问题,有了 Spring 的配置类,要想在 tomcat 服务器启动将其加载,我们需要修改 ServletContainersInitConfig
public class ServletContainersInitConfig extends AbstractDispatcherServletInitializer { protected WebApplicationContext createServletApplicationContext() { AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext(); ctx.register(SpringMvcConfig.class); return ctx; } protected String[] getServletMappings() { return new String[]{"/"}; } protected WebApplicationContext createRootApplicationContext() { AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext(); ctx.register(SpringConfig.class); return ctx; }}
复制代码
对于上述的配置方式,Spring 还提供了一种更简单的配置方式,可以不用再去创建AnnotationConfigWebApplicationContext对象,不用手动register对应的配置类。
我们打开AnnotationConfigWebApplicationContext发现,在他的下面还有一个子类AbstractAnnotationConfigDispatcherServletInitializer:
借助它我们就可以简化配置类:
public class ServletContainersInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
protected Class<?>[] getRootConfigClasses() { return new Class[]{SpringConfig.class}; }
protected Class<?>[] getServletConfigClasses() { return new Class[]{SpringMvcConfig.class}; }
protected String[] getServletMappings() { return new String[]{"/"}; }}
复制代码
评论