[SpringMVC]bean加载控制

👩🏻‍🚀博客主页:⚠️十八岁讨厌编程⚠️
📖所属专栏:SpringMVC专栏💤
🌌写文目的:记录学习中的知识点
🛕目前已更新内容涵盖:🔥【前端】、🔥【后端】、🔥【人工智能】、🔥【数据分析】、🔥【网络爬虫】、🔥【数据结构与算法】、🔥【PS】、🔥【计算机数学】等几个大方面

如果这篇文章对你有帮助,欢迎❤️关注、👍🏻点赞、🤞🏻收藏、👏🏻留言,看到我会积极回复。
在这里插入图片描述


在这里插入图片描述

🏳️‍🌈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的时候排除掉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,Service,Dao,Domain类

    @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[]{"/"};
    }
}

🏳️‍🌈总结

名称@ComponentScan
类型类注解
位置类定义上方
作用设置spring配置类扫描路径,用于加载使用注解格式定义的bean
相关属性excludeFilters:排除扫描路径中加载的bean,需要指定类别(type)和具体项(classes)
includeFilters:加载指定的bean,需要指定类别(type)和具体项(classes)
  • 8
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

十八岁讨厌编程

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值