我们在使用springboot 整合mybatis时,需要在启动类上添加上@mapperscan注解,并写入mapper接口的包路径,然后我们就能通过从spring ioc容器中取对应的mapper的bean来进行持久化操作了,那么@mapperscan是如何将mapper接口实例化并注入到spring ioc容器中的呢?
首先搭建一个spring boot项目,引入mybatis和mysql的相关maven包。在application.properties中配置好连接参数。
这些基本操作 我就不写了
新建mapper,当然我这里取名取成dao了,意思到了就行。
package com.p6spy.demop6spy.dao;
import org.apache.ibatis.annotations.select;
public interface testdao {
@select("select count(*) from subject_sae_detail")
string getinfo();
}
启动类上添加注解,指定扫描com.p6spy.demop6spy.dao包
@springbootapplication
@mapperscan({"com.p6spy.demop6spy.dao"})
public class demop6spyapplication {
public static void main(string[] args) {
springapplication.run(demop6spyapplication.class, args);
}
}
就能在service实现类中注入testdao 来进行持久化操作了。
一.@mapperscan
首先让我们来看下@mapperscan注解源码
(这里为了清晰的了解扫描注入过程,所以我只选取了部分代码,其余的删除了)。
@retention(retentionpolicy.runtime)
@target(elementtype.type)
@documented
@import(mapperscannerregistrar.class)
public @interface mapperscan {
string[] value() default {};
string[] basepackages() default {};
class<? extends annotation> annotationclass() default annotation.class;
class<?> markerinterface() default class.class;
class<? extends mapperfactorybean> factorybean() default mapperfactorybean.class;
}
1.首先是@import(mapperscannerregistrar.class)
从名字上看是一个扫描注册器,点击去看下,发现其继承于importbeandefinitionregistrar。
主要作用是:
importbeandefinitionregistrar接口的作用是当这个接口的实现类(类a)被@import接口引入某个被标记了@configuration的注册类(类b)时,可以得到这个类(类b)的所有注解,然后做一些动态注册bean的事儿。
我们看下@mapperscan,这也没有@configuration啊,别急,因为@mapperscan是被启动类引用的,所以还要去看下启动类,虽然启动类看上去是只有@springbootapplication和@mapperscan两个注解,实际上是这样的。
@springbootapplication如下
@target(elementtype.type)
@retention(retentionpolicy.runtime)
@documented
@inherited
@springbootconfiguration
@enableautoconfiguration
@componentscan(excludefilters = { @filter(type = filtertype.custom, classes = typeexcludefilter.class),
@filter(type = filtertype.custom, classes = autoconfigurationexcludefilter.class) })
@springbootconfiguration如下
@target({elementtype.type})
@retention(retentionpolicy.runtime)
@documented
@configuration
public @interface springbootconfiguration {
}
找到@configuration了。
2 . value和basepackages
string[] value() default {};
string[] basepackages() default {};
这两行应该比较好辨认,就是需要我们填写的mapper接口所在的包路径信息。
就像这样写,后面我们会发现这俩填哪个都行,结果是一样的。

或者这样,这样写的话默认是赋值给value的

3.annotationclass
在这里的作用是:
配置了该注解的mapper才会被扫描器扫描,与basepackage是与的作用。
默认值是annotation.class。
class<? extends annotation> annotationclass() default annotation.class;
4.markerinterface
在这里的作用是:
基于接口的过滤器,实现了该接口的mapper才会被扫描器扫描,与basepackage是与的作用。
默认值是class.class。
class<?> markerinterface() default class.class;
5.构造工厂
class<? extends mapperfactorybean> factorybean() default mapperfactorybean.class;
二.mapperscannerregistrar
在一中我们知道了实现importbeandefinitionregistrar的大致作用。
public class mapperscannerregistrar implements importbeandefinitionregistrar, resourceloaderaware {
private resourceloader resourceloader;
/**
* {@inheritdoc}
*/
@override
public void registerbeandefinitions(annotationmetadata importingclassmetadata, beandefinitionregistry registry) {
annotationattributes annoattrs = annotationattributes.frommap(importingclassmetadata.getannotationattributes(mapperscan.class.getname()));
classpathmapperscanner scanner = new classpathmapperscanner(registry);
// this check is needed in spring 3.1
if (resourceloader != null) {
scanner.setresourceloader(resourceloader);
}
class<? extends annotation> annotationclass = annoattrs.getclass("annotationclass");
if (!annotation.class.equals(annotationclass)) {
scanner.setannotationclass(annotationclass);
}
class<?> markerinterface = annoattrs.getclass("markerinterface");
if (!class.class.equals(markerinterface)) {
scanner.setmarkerinterface(markerinterface);
}
class<? extends beannamegenerator> generatorclass = annoattrs.getclass("namegenerator");
if (!beannamegenerator.class.equals(generatorclass)) {
scanner.setbeannamegenerator(beanutils.instantiateclass(generatorclass));
}
class<? extends mapperfactorybean> mapperfactorybeanclass = annoattrs.getclass("factorybean");
if (!mapperfactorybean.class.equals(mapperfactorybeanclass)) {
scanner.setmapperfactorybean(beanutils.instantiateclass(mapperfactorybeanclass));
}
scanner.setsqlsessiontemplatebeanname(annoattrs.getstring("sqlsessiontemplateref"));
scanner.setsqlsessionfactorybeanname(annoattrs.getstring("sqlsessionfactoryref"));
list<string> basepackages = new arraylist<string>();
for (string pkg : annoattrs.getstringarray("value")) {
if (stringutils.hastext(pkg)) {
basepackages.add(pkg);
}
}
for (string pkg : annoattrs.getstringarray("basepackages")) {
if (stringutils.hastext(pkg)) {
basepackages.add(pkg);
}
}
for (class<?> clazz : annoattrs.getclassarray("basepackageclasses")) {
basepackages.add(classutils.getpackagename(clazz));
}
scanner.registerfilters();
scanner.doscan(stringutils.tostringarray(basepackages));
}
/**
* {@inheritdoc}
*/
@override
public void setresourceloader(resourceloader resourceloader) {
this.resourceloader = resourceloader;
}
}
1.首先在registerbeandefinitions()方法
第一行打个断点,看下是不是真的得到引用类的所有注解信息了

点开看下详细信息:
可以看到只含有两个注解@springbootapplication和@mapperscan,然后看下@mapperscan注解的value值,没错,也是我们填的值。

2.这里会通过getannotationattributes方法
获取mapperscan的所有信息,上面我们知道importingclassmetadata包含启动类的所有注解信息,所以这里获取mapperscan.class注解的信息也就很轻松了。
annotationattributes annoattrs = annotationattributes .frommap(importingclassmetadata.getannotationattributes(mapperscan.class.getname()));
打个断点就能很清晰的看到annoattrs 的值了:

这里我们能看到value的值就是我上面填的。
3.然后是比较重要的一点,新建扫描器scanner
对mapper接口的扫描在这里实现。
classpathmapperscanner scanner = new classpathmapperscanner(registry);
4.然后是把一些参数赋给scanner扫描器
class<? extends annotation> annotationclass = annoattrs.getclass("annotationclass");
if (!annotation.class.equals(annotationclass)) {
scanner.setannotationclass(annotationclass);
}
class<?> markerinterface = annoattrs.getclass("markerinterface");
if (!class.class.equals(markerinterface)) {
scanner.setmarkerinterface(markerinterface);
}
......
这里annotationclass对应的是只会扫描到带该注解的mapper,而markerinterface对应的是只会扫描到实现该接口的mapper。
因为我们并没有在启动器上的@mapperscan中添加annotationclass 和markerinterface 参数,所以这里会使用默认值annotation.class和class.class,并且不会给scanner扫描器赋值,而之后scanner扫描器也就不会对扫描到的mapper进行过滤。
5.这里开始获取我们配置的要扫描的包了
可以看到会从value,basepackages和basepackageclasses中获取,所以我上面说结果是一样的了。
list<string> basepackages = new arraylist<string>();
for (string pkg : annoattrs.getstringarray("value")) {
if (stringutils.hastext(pkg)) {
basepackages.add(pkg);
}
}
for (string pkg : annoattrs.getstringarray("basepackages")) {
if (stringutils.hastext(pkg)) {
basepackages.add(pkg);
}
}
for (class<?> clazz : annoattrs.getclassarray("basepackageclasses")) {
basepackages.add(classutils.getpackagename(clazz));
}
6.在这里scanner扫描器会开始扫描
并返回bean定义集合(set<beandefinitionholder>)
scanner.registerfilters();
scanner.doscan(stringutils.tostringarray(basepackages));
在第一句scanner.registerfilters();中会用到上面3中设置的过滤参数,从方法名也可以看出来,这里会对扫描出的mapper设置过滤条件,只留下带有annotationclass 注解和实现了markerinterface接口的mapper,我们知道这里实际并没有给scanner赋这些值,所以相当于没有过滤这俩条件。
在第二句scanner.doscan(stringutils.tostringarray(basepackages));中,会扫描出所有符合条件的mapper。
7.所有流程走完后会生成一个
bean定义集合(set<beandefinitionholder>,内含beandefinition),之后
beandefinitionregistry接口(applicationcontext会实现此接口)会将新的beandefinition注册到spring ioc容器中。
主要实现方法
void registerbeandefinition(string beanname, beandefinition beandefinition)
三.classpathmapperscanner扫描器
因为源码相对有点长,这里就不放所有的了,只截取重点部分说明,请自行查看所有源码。
1.首先看下继承关系,classpathmapperscanner 继承于classpathbeandefinitionscanner。
public class classpathmapperscanner extends classpathbeandefinitionscanner
熟悉spring ioc容器的应该会知道classpathbeandefinitionscanner,我们知道有多种方式将bean注入ioc容器,可以通过在xml中配置<bean>,也可以通过@bean注解,两种方式最终都会调用classpathbeandefinitionscanner来实现扫描并生成beandefinition。
2.registerfilters(),在上面二.mapperscannerregistrar中有讲到是用来配置扫描过滤条件的,来看下源码:
public void registerfilters() {
boolean acceptallinterfaces = true;
// if specified, use the given annotation and / or marker interface
if (this.annotationclass != null) {
addincludefilter(new annotationtypefilter(this.annotationclass));
acceptallinterfaces = false;
}
// override assignabletypefilter to ignore matches on the actual marker interface
if (this.markerinterface != null) {
addincludefilter(new assignabletypefilter(this.markerinterface) {
@override
protected boolean matchclassname(string classname) {
return false;
}
});
acceptallinterfaces = false;
}
if (acceptallinterfaces) {
// default include filter that accepts all classes
addincludefilter(new typefilter() {
@override
public boolean match(metadatareader metadatareader, metadatareaderfactory metadatareaderfactory) throws ioexception {
return true;
}
});
}
// exclude package-info.java
addexcludefilter(new typefilter() {
@override
public boolean match(metadatareader metadatareader, metadatareaderfactory metadatareaderfactory) throws ioexception {
string classname = metadatareader.getclassmetadata().getclassname();
return classname.endswith("package-info");
}
});
}
从上面可以看到会判断annotationclass 和markerinterface ,然后通过annotationtypefilter()和assignabletypefilter()来设置过滤器。
如果这俩都没设置的话就会默认扫描所有的:
addincludefilter(new typefilter() {
@override
public boolean match(metadatareader metadatareader, metadatareaderfactory metadatareaderfactory) throws ioexception {
return true;
}
});
最后会过滤掉package-info.java。
// exclude package-info.java
addexcludefilter(new typefilter() {
@override
public boolean match(metadatareader metadatareader, metadatareaderfactory metadatareaderfactory) throws ioexception {
string classname = metadatareader.getclassmetadata().getclassname();
return classname.endswith("package-info");
}
});
3.doscan(),终于到最重要的doscan方法了。
看下源码:
public set<beandefinitionholder> doscan(string... basepackages) {
set<beandefinitionholder> beandefinitions = super.doscan(basepackages);
if (beandefinitions.isempty()) {
logger.warn("no mybatis mapper was found in '" + arrays.tostring(basepackages) + "' package. please check your configuration.");
} else {
processbeandefinitions(beandefinitions);
}
return beandefinitions;
}
第一眼看上去是不是瞬间无语,怎么源码有点少,这里实际上是调用父类classpathbeandefinitionscanner的doscan方法进行扫描(内容有点多=.=)。
这里我们只要知道会对basepackages参数包含的所有包进行扫描,并且会使用之前设置的过滤器。
最后返回扫描到的所有类——的beandefinitionholder集合(当成类定义集合就行)。因为这里扫描的是mybatis的mapper,我们知道mapper都是接口,而接口是无法实例化的,所以这样直接返回的话是没办法生成对应的实例的,就更不用说注入spring ioc容器了。
所以需要使用processbeandefinitions方法再加工一下。
4.processbeandefinitions
private void processbeandefinitions(set<beandefinitionholder> beandefinitions) {
genericbeandefinition definition;
for (beandefinitionholder holder : beandefinitions) {
definition = (genericbeandefinition) holder.getbeandefinition();
if (logger.isdebugenabled()) {
logger.debug("creating mapperfactorybean with name '" + holder.getbeanname()
+ "' and '" + definition.getbeanclassname() + "' mapperinterface");
}
// the mapper interface is the original class of the bean
// but, the actual class of the bean is mapperfactorybean
definition.getconstructorargumentvalues().addgenericargumentvalue(definition.getbeanclassname()); // issue #59
definition.setbeanclass(this.mapperfactorybean.getclass());
definition.getpropertyvalues().add("addtoconfig", this.addtoconfig);
boolean explicitfactoryused = false;
if (stringutils.hastext(this.sqlsessionfactorybeanname)) {
definition.getpropertyvalues().add("sqlsessionfactory", new runtimebeanreference(this.sqlsessionfactorybeanname));
explicitfactoryused = true;
} else if (this.sqlsessionfactory != null) {
definition.getpropertyvalues().add("sqlsessionfactory", this.sqlsessionfactory);
explicitfactoryused = true;
}
if (stringutils.hastext(this.sqlsessiontemplatebeanname)) {
if (explicitfactoryused) {
logger.warn("cannot use both: sqlsessiontemplate and sqlsessionfactory together. sqlsessionfactory is ignored.");
}
definition.getpropertyvalues().add("sqlsessiontemplate", new runtimebeanreference(this.sqlsessiontemplatebeanname));
explicitfactoryused = true;
} else if (this.sqlsessiontemplate != null) {
if (explicitfactoryused) {
logger.warn("cannot use both: sqlsessiontemplate and sqlsessionfactory together. sqlsessionfactory is ignored.");
}
definition.getpropertyvalues().add("sqlsessiontemplate", this.sqlsessiontemplate);
explicitfactoryused = true;
}
if (!explicitfactoryused) {
if (logger.isdebugenabled()) {
logger.debug("enabling autowire by type for mapperfactorybean with name '" + holder.getbeanname() + "'.");
}
definition.setautowiremode(abstractbeandefinition.autowire_by_type);
}
}
}
别看洋洋洒洒写了一大堆东西,核心只有beandefinition一点。上面我们知道beandefinitions集合中的bean定义都是针对mapper接口的,而接口无法实例化。
先来认知下genericbeandefinition ,大致讲下作用:类的定义,创建完后,beandefinitionregistry接口(applicationcontext会实现此接口)会将新的beandefinition注册到spring ioc容器中(genericbeandefinition继承于abstractbeandefinition,abstractbeandefinition实现了beandefinition)。
说几个用genericbeandefinition注入bean的例子吧:
新建userinfo.java类,之后会通过几种方式实例化并注入spring ioc容器。
public class userinfo {
private string username;
public string getusername() {
return username;
}
public void setusername(string username) {
this.username = username;
}
}
(1)通过实现beanfactorypostprocessor(bean工厂的后置处理器)注入。
该接口在spring ioc容器注册bean定义的逻辑都跑完后,但是所有的bean都还没真正实例化之前调用。
void postprocessbeanfactory(configurablelistablebeanfactory beanfactory)
这个方法的主要是通过注入进来的beanfactory,在真正初始化bean之前,再对spring
ioc容器做一些动态修改。增加或者修改某些bean定义的值,甚至再动态创建一些beandefinition。
新建mybeanfactorypostprocessor 并实现beanfactorypostprocessor 。
@component
public class mybeanfactorypostprocessor implements beanfactorypostprocessor {
@override
public void postprocessbeanfactory(configurablelistablebeanfactory configurablelistablebeanfactory) throws beansexception {
genericbeandefinition genericbeandefinition = new genericbeandefinition();
genericbeandefinition.setbeanclass(userinfo.class);
genericbeandefinition.getpropertyvalues().add("username","123");
((defaultlistablebeanfactory) configurablelistablebeanfactory)
.registerbeandefinition("userinfo", genericbeandefinition);
}
}
测试下
@requestmapping("/testuser")
public void testuser(){
try{
userinfo s4 = (userinfo)applicationcontext.getbean("userinfo");
system.out.println(s4.getusername());
}catch (exception ex){
system.out.println("4.there is something error:"+ex.getmessage());
}
}
结果,成功注入。

(2)通过实现beandefinitionregistrypostprocessor接口(bean注册器的后置处理器)注入。
新建mybeandefinitionregistrypostprocessor实现beandefinitionregistrypostprocessor 接口
@component
public class mybeandefinitionregistrypostprocessor implements beandefinitionregistrypostprocessor {
@override
public void postprocessbeandefinitionregistry(beandefinitionregistry beandefinitionregistry) throws beansexception {
genericbeandefinition genericbeandefinition = new genericbeandefinition();
genericbeandefinition.setbeanclass(userinfo.class);
genericbeandefinition.getpropertyvalues().add("username","456");
beandefinitionregistry.registerbeandefinition("userinfo2",genericbeandefinition);
}
@override
public void postprocessbeanfactory(configurablelistablebeanfactory configurablelistablebeanfactory) throws beansexception {
}
}
我们能看到这里竟然也有postprocessbeanfactory方法。原来beandefinitionregistrypostprocessor 继承了beanfactorypostprocessor。然后注入一个userinfo实例,beanname为userinfo2。测试下
@requestmapping("/testuser")
public void testuser(){
try{
userinfo s4 = (userinfo)applicationcontext.getbean("userinfo");
system.out.println("userinfo username:"+s4.getusername());
}catch (exception ex){
system.out.println("4.there is something error:"+ex.getmessage());
}
try{
userinfo s5 = (userinfo)applicationcontext.getbean("userinfo2");
system.out.println("userinfo2 username:"+s5.getusername());
}catch (exception ex){
system.out.println("5.there is something error:"+ex.getmessage());
}
}
结果如下,也注入进来了。

我们只要知道该步骤是完善beandefinition,使之后能够实例化并注入spring ioc即可。
让我们回到源码中来。打个断点看下,在对beandefinitions进行加工前,能看到beandefinition中的beanclass是我们写我的mybatis的mapper testdao,而testdao是一个接口,是无法实例化的,所以之后会修改该值。

之后会重新设置beanclass
definition.setbeanclass(this.mapperfactorybean.getclass());
这个mapperfactorybean又是什么?
private mapperfactorybean<?> mapperfactorybean = new mapperfactorybean<object>();
public class mapperfactorybean<t> extends sqlsessiondaosupport implements factorybean<t> {
//......
}
翻看源码后我们发现该类继承了sqlsessiondaosupport并实现了factorybean<t>,sqlsessiondaosupport主要是可以提供sqlsessiontemplate,我们可以通过继承其来使用sqlsessiontemplate连接数据库并进行持久化操作,当然这个不是本篇的重点,重点是factorybean<t>。
factorybean是spring对外提供的对接接口,当向spring对象使用getbean("…")方法时,spring会使用factorybean的getobject方法返回对象。所以当一个类实现了 factorybean接口时,那么每次向spring要这个类时,spring就返回t对象。
mapperfactorybean<t>中有一个成员变量
private class<t> mapperinterface;
这个是什么时候传入的呢?
让我们往回一步,在重新设置beanname的上一步。
设置了constructorargumentvalues
definition.getconstructorargumentvalues().addgenericargumentvalue(definition.getbeanclassname());
这里会将definition.getbeanclassname()添加进constructorargumentvalues中。

之后在实例化并注入spring ioc容器时。程序会获取beandefinition的beanname,然后调用beanname对应的构造方法实例化beanname。
用到的参数就是这里添加的constructorargumentvalues>
让我们看看mapperfactorybean<t>的构造方法:
public mapperfactorybean(class<t> mapperinterface) {
this.mapperinterface = mapperinterface;
}
是不是整个流程都清晰了呢。
我们知道实现了factorybean<t>接口的类,想要从spring ioc容器中获取该bean,会调用getobject方法。看下源码
@override
public t getobject() throws exception {
return getsqlsession().getmapper(this.mapperinterface);
}
从表面上看好像mybatis也自己实现了一个容器,然后会从容器中取出bean,之后spring 的ioc容器会缓存该bean,所以第二次之后就不会再去取了。
以上,就是mapper扫描,实例化到注入容器的整个过程了。
这里我们可以自己写一个demo来验证下。当然我这里就不用接口了,为了简单起见,直接注入类。
四.自动注入带自定义注解的类
1.首先让我们模仿mapperscan写一个自定义注解,作用是启动自动扫描。
myscanannotation.java
@retention(retentionpolicy.runtime)
@target({elementtype.type})
@documented
@import(mytestscannerregistrar.class)
public @interface myscanannotation {
string[] basepackages() default {};
class<? extends annotation> annotationclass() default annotation.class;
class<?> markerinterface() default class.class;
}
可以看到有三个参数,相信看完文章的各位一定都知道意义,就不多讲了。
2.新建mytestscannerregistrar,扫描注册器。
mytestscannerregistrar.java
public class mytestscannerregistrar implements importbeandefinitionregistrar {
@override
public void registerbeandefinitions(annotationmetadata annotationmetadata, beandefinitionregistry beandefinitionregistry) {
annotationattributes annoattrs = annotationattributes.frommap(annotationmetadata.getannotationattributes(myscanannotation.class.getname()));
mytestscanner scanner = new mytestscanner(beandefinitionregistry);
class<? extends annotation> annotationclass = (class<? extends annotation>) annoattrs.getclass("annotationclass");
if (!annotation.class.equals(annotationclass)) {
scanner.setannotationclass(annotationclass);
}
class<?> markerinterface = annoattrs.getclass("markerinterface");
if (!class.class.equals(markerinterface)) {
scanner.setmarkerinterface(markerinterface);
}
list<string> basepackages = new arraylist<string>();
for (string pkg : annoattrs.getstringarray("basepackages")) {
if (stringutils.hastext(pkg)) {
basepackages.add(pkg);
}
}
scanner.registerfilters();
scanner.doscan(stringutils.tostringarray(basepackages));
}
}
这里也是很简单,获取注解参数,新建扫描器,然后把注解参数赋值给新建的扫描器。然后运行扫描。获取set<beandefinitionholder>,内含beandefinition,之后spring的上下文applicationcontext实现的beandefinitionregistry接口的
void registerbeandefinition(string beanname, beandefinition beandefinition)
方法实例化和注入这些beandefinition。
3.新建mytestscanner。
mytestscanner.java
public class mytestscanner extends classpathbeandefinitionscanner {
private class<? extends annotation> annotationclass;
private class<?> markerinterface;
public class<?> getmarkerinterface() {
return markerinterface;
}
public void setmarkerinterface(class<?> markerinterface) {
markerinterface = markerinterface;
}
public class<? extends annotation> getannotationclass() {
return annotationclass;
}
public void setannotationclass(class<? extends annotation> annotationclass) {
this.annotationclass = annotationclass;
}
public mytestscanner(beandefinitionregistry registry) {
super(registry);
}
public void registerfilters(){
if(!this.annotationclass.isinstance(annotation.class)){
addincludefilter(new annotationtypefilter(this.annotationclass));
}
}
@override
protected set<beandefinitionholder> doscan(string... basepackages) {
set<beandefinitionholder> beandefinitions = super.doscan(basepackages);
if (beandefinitions.isempty()) {
logger.info("no myscanannotation class was found in '" + arrays.tostring(basepackages) + "' package. please check your configuration.");
}
return beandefinitions;
}
}
因为扫描的都是类,可以实例化,所以就直接返回了。
4.新建注解mytestanntation,之后会自动注入带有该注解的类。
mytestanntation.java
@retention(retentionpolicy.runtime)
@target({elementtype.type})
public @interface mytestanntation {
}
没有参数,最简单的方式。
5.新建baseservice接口
baseservice.java
public interface baseservice {
void getinfo();
}
5.新增包和类
新增com.p6spy.demop6spy.basei和com.p6spy.demop6spy.baseii包,用来对比参照
(1)在com.p6spy.demop6spy.basei中新建baseitesti,baseitestii和baseitestiii,都实现baseservice 接口,baseitesti.java和baseitestii.java加上@mytestanntation注解。
baseitesti.java
@mytestanntation
public class baseitesti implements baseservice {
public void getinfo(){
system.out.println("hello,baseitesti");
}
}
baseitestii.java
@mytestanntation
public class baseitestii implements baseservice {
public void getinfo(){
system.out.println("hello,baseitestii");
}
}
baseitestiii.java
public class baseitestiii implements baseservice {
public void getinfo(){
system.out.println("hello,baseitestiii");
}
}
(2)在com.p6spy.demop6spy.baseii中新建baseiitesti,实现baseservice接口,添加@mytestanntation注解。
baseiitesti.java
@mytestanntation
public class baseiitesti implements baseservice {
public void getinfo(){
system.out.println("hello,baseiitesti");
}
}
6.在启动类上添加@myscanannotation注解
demop6spyapplication.java
@springbootapplication
@mapperscan({"com.p6spy.demop6spy.dao"})
@myscanannotation(basepackages = {"com.p6spy.demop6spy.basei"},
annotationclass = mytestanntation.class)
public class demop6spyapplication {
public static void main(string[] args) {
springapplication.run(demop6spyapplication.class, args);
}
}
这里我设置了basepackages 和annotationclass 两个参数,意思为只扫描com.p6spy.demop6spy.basei包中带mytestanntation注解的类。
@myscanannotation(basepackages = {"com.p6spy.demop6spy.basei"},
annotationclass = mytestanntation.class)
7.测试
测试一下
@requestmapping("/testann")
public void testannotation(){
try{
baseservice s1 = (baseservice)applicationcontext.getbean("baseitesti");
s1.getinfo();
}catch (exception ex){
system.out.println("1.there is something error:"+ex.getmessage());
}
try{
baseservice s2 = (baseservice)applicationcontext.getbean("baseitestii");
s2.getinfo();
}catch (exception ex){
system.out.println("2.there is something error :"+ex.getmessage());
}
try{
baseservice s3 = (baseservice)applicationcontext.getbean("baseitestiii");
s3.getinfo();
}catch (exception ex){
system.out.println("3.there is something error:"+ex.getmessage());
}
try{
baseservice s4 = (baseservice)applicationcontext.getbean("baseiitesti");
s4.getinfo();
}catch (exception ex){
system.out.println("4.there is something error:"+ex.getmessage());
}
}
结果

可以看到只有i和ii成功注入了,这也符合我们定的扫描策略。
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。
发表评论