复习Spring, 正好这两个标签我分的也不是很清, 就写篇文章记录一下.
准备工作:
1.TestController.java:
public class TestController {
public void test() {
System.out.println("controller ok");
}
}
2.TestService.java:
public class TestService {
TestDao testDao;
public void setTestDao(TestDao testDao) {
this.testDao = testDao;
}
public void test() {
System.out.println("service ok");
testDao.test();
}
}
3.TestDao.java:
public class TestDao {
public void test() {
System.out.println("dao ok");
}
}
4.Test.java:
public class Test {
public static void main(String[] args) {
ApplicationContext context =
new ClassPathXmlApplicationContext("applicationContext.xml");
TestController controller = (TestController) context.getBean("testController");
controller.test();
TestService service = (TestService) context.getBean("testService");
service.test();
TestDao testDao = (TestDao) context.getBean("testDao");
testDao.test();
}
}
5.applicationContext.xml
<?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:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd">
<bean id="testController" class="com.ld.controller.TestController"></bean>
<bean id="testService" class="com.ld.service.TestService">
<property name="testDao" ref="testDao"/>
</bean>
<bean id="testDao" class="com.ld.dao.TestDao"></bean></beans>
--------------------------------------------- 假装是条分割线 ---------------------------------------------------------
● 我在配置文件中配置了3个bean,并且为TestService注入了testDao属性.
此时Test类的main方法可以正常运行,不会报错, 结果如下:
controller ok
service ok
dao ok
dao ok
● 若我想使用@Autowired注解将TestDao注入到TestService中, 则需要context:annotation-config这个标签来启用注解功能.
修改TestService类和配置文件:
<?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:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd">
<context:annotation-config></context:annotation-config>
<bean id="testController" class="com.ld.controller.TestController"></bean>
<bean id="testService" class="com.ld.service.TestService"></bean>
<bean id="testDao" class="com.ld.dao.TestDao"></bean>
</beans>
TestService.java:
public class TestService {
@Autowired
TestDao testDao;
public void test() {
System.out.println("service ok");
testDao.test();
}
}
运行Test的main方法, 结果不变:
controller ok
service ok
dao ok
dao ok
● 若我想自动注册bean,而不是在xml文件中使用bean标签来注册, 首先想到的是在被注册类上使用@Component注解,但这里模拟了controller层,service层和dao层, 所以分别使用@Controller, @Service, @Repository注解:
配置文件修改为:
<?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:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd">
<context:annotation-config></context:annotation-config>
</beans>
TestController.java:
@Controller
public class TestController {
public void test() {
System.out.println("controller ok");
}
}
TestService.java:
@Service
public class TestService {
@Autowired
TestDao testDao;
public void test() {
System.out.println("service ok");
testDao.test();
}
}
TestDao.java:
@Repository
public class TestDao {
public void test() {
System.out.println("dao ok");
}
}
现在运行一下,会发现报错了:
Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'testController' is defined
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanDefinition(DefaultListableBeanFactory.java:687)
at org.springframework.beans.factory.support.AbstractBeanFactory.getMergedLocalBeanDefinition(AbstractBeanFactory.java:1168)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:281)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:956)
at com.ld.test.Test.main(Test.java:16)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)
根据描述,报错信息为没有名为:"testController"的bean被定义, 其实包括service和dao的bean都没有被定义,只不过我们先调用了testController的test方法,所以在找不到testController后程序就终止了,所以没有打印出相应的错误信息.
● 那为什么会出现这样的异常呢? 我们明明启用了注解功能啊.
原来context:annotation-config标签的作用是只在开始了已被注册的bean上的注解功能, 也就是说之所以我们使用@Autowired注解能将testDao成功注入,是因为我们在配置文件中已经使用bean标签注册了那3个bean. 遇到未注册的bean上的注解,这个标签也无能为力, 它不能将标注了@Component,@Controller等注解的类注册为bean.
那该怎么办呢? 这是时候就该使用context:component-scan标签了.
● context:component-scan标签不仅可以识别@Autowired等注解, 还能将被@Component等注解标注的类注册到Spring的IOC容器中, 成为可供使用的bean. 该标签有一个base-package属性, 用来指定要扫描的包的包名.
修改配置文件:
<?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:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd">
<context:component-scan base-package="com.ld"></context:component-scan>
</beans>
运行结果:
controller ok
service ok
dao ok
dao ok
结果正确.
在SSM整合中的使用
大家都知道Spring和SpringMVC一起使用时, Spring容器包含了SpringMVC容器, 可称为Spring容器为父容器,SpringMVC容器为子容器.
我们在applicationContext.xml(SSM整合后一般叫做spring-mybatis.xml)中使用context:component-scan标签, base-package应该指定为除controller包以外的包,因为这样就不会把Controller也注册到Spring容器中. 我们通常会在spring-mvc.xml中也配置一个context:component-scan标签, base-package只指定为controller包, 只将controller注册到SpringMVC容器中.
● 想了解上述细节请看这篇文章: https://blog.csdn.net/s740556472/article/details/54879954
那么问题来了, 在spring的配置文件中,base-package该怎么配置呢? 难道要service,dao等包各对应一个context:component-scan标签吗? 这会显得配置文件非常臃肿. Spring当然考虑到了这一点, 提供了它的子标签:<context:include-filter/>和<context:exclude-filter/>, 顾名思义, 就是引入过滤器和排除过滤器.使用如下:
<context:component-scan base-package="com.ld">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
在spring的配置文件中, 只需要使用exclude-filter标签, 将过滤类型设置为annotation(注解), 将expression配置为Controller注解的全名即可实现过滤.
在使用<context:include-filter/>标签时需要注意, 如果你只想扫描几个包,剩下的过滤掉,这样写是不行的:
<context:component-scan base-package="com.ld">
<context:include-filter type="annotation"
expression="org.springframework.stereotype.Service" />
<context:include-filter type="annotation"
expression="org.springframework.stereotype.Repository" />
</context:component-scan>
尽管你使用了两个include-filter标签, 但实际上还是将"com.ld"下所有的包扫描了, 这时候需要指定context:component标签的另一个属性: use-default-filter, 将其指定为false即可.
● 具体解析请看这篇文章: https://blog.csdn.net/m0_37965853/article/details/72898490