一、基于注解方式实现bean管理
1、针对 Bean 管理中创建对象提供注解
1. @Component
2. @Service
3. @Controller
4. @Repository
四个注解的功能都是一样的,但是为了区分,一般要对这四个注解进行区分。
@Component
:主要时作用于实体类(Entity或POJO),把普通pojo实例化到spring容器中。
@Service
:用于标注服务层(Service层),主要用来进行业务的逻辑处理@Repository
:用于标注数据访问层(Dao层)
@Controller
:用于标注控制层(Controller层)
在此之前要先进行组件扫描,比如在com.scan包下的所有注解。
需要在xml配置文件中添加下面的标签,进行组件扫描
<context:component-scan base-package="com.scan"></context:component-scan>
比如在UserService添加@Service
注解,在xml配置文件中开启了Service包下的组件扫描后,只需要在测试代码中getBean
获取UserService的实例就可以了。
UserService:
/**
* 在注解里默认value属性值可以忽略不写
* 如果默认不写的话,那就取默认值,即类名称的首字母小写
* UserService --> userService
*/
@Service(value = "userService") // <bean id = "userServcie" class=""/>
public class UserService {
public void add(){
System.out.println("service add ...");
}
}
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: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">
<!--
开启组件扫描:
1、如果扫描多个包,多个包使用逗号隔开(下面扫描dao和entity包)
<context:component-scan base-package="com.scan.entity,com.scan.dao"></context:component-scan>
2、如果扫描多个包,可以包的上层目录
<context:component-scan base-package="com.scan"></context:component-scan>
-->
<context:component-scan base-package="com.scan"></context:component-scan>
<!--
过滤默认扫描文件夹下面的部分子文件夹:
use-default-filters="false":不适用默认扫描的情况
context:include-filter:带扫描的内容
expression="org.springframework.stereotype.Service":扫描包下面的Service注解
org.springframework.stereotype.Service:为import导入@Servcie的包,不是本地的包
-->
<!--
<context:component-scan base-package="com.scan" use-default-filters="false">
下面为只扫描com.scan包下面的Service和Controller注解
<context:include-filter type="annotation" expression="org.springframework.stereotype.Service"/>
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
-->
<!--
过滤默认扫描文件夹下面的部分子文件夹:
context:exclude-filter:不扫描该注解,其他都扫描(下面的例子即不扫描Service注解)
<context:component-scan base-package="com.scan">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service"/>
</context:component-scan>
-->
</beans>
测试:
/**
* 用于包下的注解全部扫描
*/
@Test
public void testUserService1(){
ApplicationContext context =
new ClassPathXmlApplicationContext("bean.xml");
UserService userService = context.getBean("userService", UserService.class);
userService.add();
}
结果:
service add ...
接下来我们可以想一下,上面那种方式并不能完全注解开发的功能,上面的例子时实现包下面的所有注解扫描,但是有时候我们只想扫描部分注解或者只扫描几个类,又或者时指定某个类不进行扫描,只扫描这个类包下面的其他同注解的类。
比如我们想扫描com.scan包下的@Repository
和@Controller
,这时候我们就需要引入context:include-filter
对应的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: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">
<!--
过滤默认扫描文件夹下面的部分子文件夹:
use-default-filters="false":不适用默认扫描的情况
context:include-filter:带扫描的内容
expression="org.springframework.stereotype.Service":扫描包下面的Service注解
org.springframework.stereotype.Service:为import导入@Servcie的包,不是本地的包
-->
<context:component-scan base-package="com.scan" use-default-filters="false">
<!--下面为只扫描com.scan包下面的Service和Controller注解 -->
<context:include-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
</beans>
此时还是对上UserService(@Service
)进行getBean获取时,就会报一下错误:
No bean named 'userService' available
org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'userService' available
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanDefinition(DefaultListableBeanFactory.java:814)
at org.springframework.beans.factory.support.AbstractBeanFactory.getMergedLocalBeanDefinition(AbstractBeanFactory.java:1282)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:297)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:207)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1114)
at com.scan.test.AnnotationTest.testUserService1(AnnotationTest.java:18)
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 org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:235)
at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)
下面介绍在包下哪些注解不扫描的情况,这里要用到context:exclude-filter
比如我们在扫描com.scan包下的组件时,不扫描带有@Servcie
的注解
<?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:exclude-filter:不扫描该注解,其他都扫描(下面的例子即不扫描Service注解)
-->
<context:component-scan base-package="com.scan">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service"/>
</context:component-scan>
</beans>
还是要上面的代码进行测试会发现报一样错。
如果我们要扫描多个包,可以用逗号隔开<context:component-scan base-package="com.scan.entity,com.scan.dao"></context:component-scan>
。
也可以使用扫描包共同的上层目录。比如我们扫描com.scan.entity和com.scan.dao包,那么可以直接扫描com.scan目录。
如果我们要特定的指定哪些类不扫描或者扫描的话,只需要将type
从annotation(指注解的意思)
改为assignable
。
例如我们在com.scan包的组件的时候,不扫描UserService
类,并且不扫描含有@Controller
注解的类,其他都扫描:
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: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">
<!--
过滤默认扫描文件夹下面的部分子文件夹:
type="assignable":按照类形式进行扫描
context:exclude-filter:设置哪些类不扫描
-->
<context:component-scan base-package="com.scan">
<!-- 指定com.scan.service.UserService类不进行扫描 -->
<context:exclude-filter type="assignable" expression="com.scan.service.UserService"/>
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
</beans>
此时还是用前面的UserServcie
和测试代码的话,会发现还是会报上面的错误
No bean named 'userService' available
如果看到这里我们发现还是不能实现完全注解开发,因为我们还需要注入属性。
这个时候就需要额外添加注解,下面介绍几个注解:
@Autowired
:根据类型(类的)注入
@Qualifier
:根据名称注入,要和Autowired一起使用
@Resource
:既可以根据类型,也可以根据名称注入
@Value
:给基本数据类型的属性赋值
注意:@Autowired,@Qualifier和@Resource都作用在对象上面,@Value作用在基本数据类型上面
下面我来一个一个解释:
首先@Autowired
是根据类型注入,这个有点像之前xml配置时讲的自动注入autowire
,在xml配置中如果使用了autowire
属性中的byType
,那么系统就会在所在的xml配置文件中进行查找对应类型的bean实例,如果有多个就会报错,提示多个。
其次@Qualifier
也类似于之前xml配置时讲的自动注入autowire
中的byName
,这里要和@Autowired
一起使用。
@Autowired
@Qualifier(value = "card")
再次就是@Resource
,这个注解可以根据类型也可以根据名称注入。
根据类型注入只需要加注解@Resource
,根据名称注入就要使用这种形式@Resource(name = "")
。@Value
的话就是对基本数据类型的注入(->_->)没啥花样。
为了解释这些注解,我添加了几个类和接口:UserDao
接口,UserDaoImpl
实现类,Eyes
和IdCard
实体类,可能不是很好的设计,我当时也时为了一下解释所有的注解,就东拼西凑随便写了几个实体类(->_->)见谅。
UserDao
接口:
public interface UserDao {
void add();
}
UserDaoImpl
实现类:
@Repository
public class UserDaoImpl implements UserDao{
/**
* Autowired:根据类型(类的)注入
* Qualifier:根据名称注入,要和Autowired一起使用
* 这里我把下面的Autowired删除之后运行结果发现输出的为null
*
* Resource:既可以根据类型,也可以根据名称注入
*
* @Resource(name = "card") 等价于 @Autowired + @Qualifier(value = "card")
*
*/
@Autowired
@Qualifier(value = "card")
private IdCard idCard;
/**
* 这里Resource根据类型诸如
*
* 若根据名称注入@Resource(name = "...")
*/
@Resource
private Eyes eyes;
@Override
public void add() {
System.out.println("userDao add ...");
System.out.println(idCard);
System.out.println(eyes);
}
}
实体类(Eyes
和IdCard
):
@Component(value = "eye")
public class Eyes {
@Value("蓝眼睛")
private String eyesType;
@Override
public String toString() {
return "Eyes{" +
"eyesType='" + eyesType + '\'' +
'}';
}
}
@Component(value = "card")
public class IdCard {
@Value("26181")
private String idCardNo;
@Override
public String toString() {
return "IdCard{" +
"idCardNo='" + idCardNo + '\'' +
'}';
}
}
测试:
/**
* 测试Autowired,Autowired,Value,Resource的使用
*/
@Test
public void testUserService5(){
ApplicationContext context =
new ClassPathXmlApplicationContext("bean.xml");
UserService userService = context.getBean("userService", UserService.class);
userService.addExtension();
}
结果展示:
userDao add ...
IdCard{idCardNo='26181'}
Eyes{eyesType='蓝眼睛'}
service add ...
到了这里我们还是没有实现完全注解开发,因为我们前面的操作都是基于在xml配置文件中添加组件扫描。所以要实现完全注解开发,我们还要有一个用于组件扫描的配置类用来代替xml配置文件中实现的组件扫描。
下面是一个配置类:
// Configuration:配置类
@Configuration
@ComponentScan(basePackages = "com.scan")
public class SpringConfig {
}
关于配置类也有很多知识点,所以待更新。。。
声明:写这个文章主要是用于自己对知识点的梳理,当然也有想让各位跟我一样的小白参考,作者学识浅薄,有可能有错误,所以欢迎纠错,一起学习讨论,一起进步。