黑马程序员_细说接口

本文深入解析Java接口的概念、特点及应用,包括如何通过接口实现多继承,解决多继承带来的弊端,并探讨接口如何帮助降低代码耦合度。

---------------------- ASP.Net+Unity开发.Net培训、期待与您交流! ----------------------


一、接口的产生:

抽象类中可以定义抽象方法,当一个类中的方法全是抽象的,这时,可以通过另外一种特殊的形式来体现。

即接口:接口是一个特殊的抽象类(但是它不是类,因为不会继承Object类)。

接口用关键字  interface  来定义:

<span style="font-size:18px;"><span style="white-space:pre">	</span>abstract class Demo {
		abstract void show1();
		abstract void show2();
	}</span>

二、接口的定义格式:

1、定义变量,但是变量必须要有固定的修饰符修饰,public static final   所以接口中的变量也称为常量。

2、定义方法,方法的固定修饰符,public abstract。

3、接口中的成员都是公共的。

4、接口中的修饰符,如果自己没有写,编译器会自动添加。


三、接口的特点:

1、接口不可以创建对象;

2、子类必须覆盖掉接口中所有的抽象方法后,子类才可以实例化,否则子类是一个抽象类,不可以进行实例化;

3、子类必须与接口产生关系,类与接口之间的关系是实现,同过关键字  implements  来实现;

4、接口的产生解决了多继承的弊端。将多继承的这种机制在Java 中通过实现完成了。


接口的产生解决多继承弊端的代码示例:

<span style="font-size:18px;"><span style="white-space:pre">	</span>interface A {
		void show1();
	}
	interface B {
		void show2();
	}
	class C implements A,B {	// 多实现。同时实现多个接口。
		public void show1(){}
		public void show2(){}
	}</span>

接口是如何解决多继承的弊端呢?

弊端:多继承时,当多个父类有相同功能时,子类调用父类功能会产生不确定性。核心原因就是在于多继承父类中功能有主题,而导致调用运行时,uqueding运行那个主体内容。

接口中的功能没有方法体(主体),方法体由子类来确定,因此接口的多实现就解决了多继承。

<span style="font-size:18px;"><span style="white-space:pre">	</span>interface A {
<span style="white-space:pre">	</span>	void show();	//接口中的功能都没有方法体
<span style="white-space:pre">	</span>}
<span style="white-space:pre">	</span>interface B {
<span style="white-space:pre">	</span>	void show();	//接口中的功能都没有方法体
<span style="white-space:pre">	</span>}
<span style="white-space:pre">	</span>class C implements A,B {	// 多实现。同时实现多个接口。
<span style="white-space:pre">	</span>	public void show();		//方法体是由子类来明确
<span style="white-space:pre">	</span>}

<span style="white-space:pre">	</span>C c = new C();
<span style="white-space:pre">	</span>c.show();	//对象调用方法,方法体是确定的,可以调用。</span>

四、接口的应用:

子类通过继承父类扩展功能,通过继承扩展的功能都是子类应该具备的基础功能。如果子类想要继续扩展其他类中的功能,这时可以通过实现接口来完成。

<span style="font-size:18px;"><span style="white-space:pre">	</span>class Fu {
<span style="white-space:pre">	</span>	public void show(){}
<span style="white-space:pre">	</span>}
<span style="white-space:pre">	</span>interface Inter	{	//接口一般定义事物的扩展功能
<span style="white-space:pre">	</span>	pulbic void show1();
<span style="white-space:pre">	</span>}
<span style="white-space:pre">	</span>/*
<span style="white-space:pre">	</span> * 让子类继承父类实现父类功能,
<span style="white-space:pre">	</span> * 子类想要继续扩展其他类中的功能
<span style="white-space:pre">	</span> * 只需要实现接口
<span style="white-space:pre">	</span> */
<span style="white-space:pre">	</span>class Zi extends Fu implements Inter {
<span style="white-space:pre">	</span>	public void show1() {
<span style="white-space:pre">	</span>		System.out.println("重写父类方法");
<span style="white-space:pre">	</span>	}
<span style="white-space:pre">	</span>}</span>
a、接口的出现避免了单继承的局限性

b、父类中定义事物的基本功能

c、接口中定义事物的扩展功能


五、接口与接口的关系:

1、类与类之间的关系是继承

2、类与接口之间的关系是实现

3、接口与接口之间的关系是继承

<span style="font-size:18px;"><span style="white-space:pre">	</span>interface A {
<span style="white-space:pre">	</span>	void show1();
<span style="white-space:pre">	</span>}
<span style="white-space:pre">	</span>interface AA {
<span style="white-space:pre">	</span>	void show11();
<span style="white-space:pre">	</span>}
<span style="white-space:pre">	</span>interface B extends A,AA {	//接口与接口之间的关系是继承,可以多继承。 
<span style="white-space:pre">	</span>	void show2();
<span style="white-space:pre">	</span>}

<span style="white-space:pre">	</span>class Test implements B {	//类与接口之间的关系是实现。
<span style="white-space:pre">	</span>	public void show1(){}
<span style="white-space:pre">	</span>	public void show2(){}
<span style="white-space:pre">	</span>	public void show11(){}
<span style="white-space:pre">	</span>}

<span style="white-space:pre">	</span>class InterfaceDemo  {
	<span style="white-space:pre">	</span>public static void main(String[] args) {
	<span style="white-space:pre">	</span>	DemoImpl d = new DemoImpl();
	<span style="white-space:pre">	</span>	d.show1();
	<span style="white-space:pre">	</span>	d.show2();
	<span style="white-space:pre">	</span>}
<span style="white-space:pre">	</span>}</span>


六、接口的思想:

1,、接口的出现对功能是实现了扩展

2、接口的出现定义了规则

3、接口的出现降低了耦合性(解耦 -- 让事物之间的耦合性降低)

接口的出现完成了解耦,说明有两方:一方在使用这个规则;一方在实现这个规则。

比如电脑使用USB接口这个规则,而外围设备则是在实现则个规则。



---------------------- ASP.Net+Unity开发.Net培训、期待与您交流! ----------------------

详细请查看:www.itheima.com

<think>我们讨论的是在MyBatis中是否可以直接在mapper接口上配置Bean名称,以及具体实现方式和注意事项。根据用户的问题,我们需要关注两点:1.是否可以在mapper接口上直接配置Bean名称(而不通过@Configuration类)?2.具体如何实现,以及优缺点和注意事项。回顾之前的引用:引用[1]提到了通过MapperScannerConfigurer来扫描指定注解的Mapper接口,并可以指定basePackage。引用[2]展示了使用@MapperScan注解来扫描Mapper接口,并在配置类中通过@Autowired注入Mapper。同时,根据MyBatis-Spring的官方文档,我们可以通过以下方式为Mapper接口指定Bean名称:方案1:使用@MapperScan注解的nameGenerator属性方案2:在Mapper接口上使用Spring的注解(如@Component)并指定名称但是,直接在Mapper接口上使用@Component(或@Repository)并指定名称,然后通过包扫描,这种方式确实可以,但需要注意避免冲突。具体分析如下:一、在Mapper接口上直接使用注解指定Bean名称(无需@Configuration)步骤:1.在Mapper接口上添加@Component或@Repository注解,并指定value属性(即Bean名称)2.确保配置了包扫描(比如在配置类上使用@ComponentScan)或者使用@MapperScan(它实际上也会将接口注册为Bean)示例:```java@Repository("customUserMapper")publicinterfaceUserMapper{//...}```然后在配置中,我们需要确保这个Mapper接口被扫描到:-如果使用@ComponentScan,需要确保扫描到该包,并且MyBatis的扫描也要处理(因为MyBatis还需要创建代理类)-或者使用@MapperScan,但是注意@MapperScan默认会使用接口的简单类名(首字母小写)作为Bean名称,如果同时我们又用@Repository指定了名称,那么就会有两个Bean(一个是由MyBatis扫描生成的,一个是由@ComponentScan扫描生成的),这样就会冲突。因此,为了在Mapper接口上直接指定Bean名称,我们需要避免重复注册。解决冲突的方法:1.使用@MapperScan并设置其属性nameGenerator,通过自定义BeanNameGenerator来指定名称。2.或者不使用@ComponentScan扫描Mapper接口,只使用@MapperScan,但@MapperScan默认不会使用我们指定的注解里的名称。这里,我们更推荐使用@MapperScan的nameGenerator属性。二、使用@MapperScan的nameGenerator属性我们可以通过实现BeanNameGenerator接口来自定义Mapper的Bean名称,然后将这个生成器设置到@MapperScan上。示例:```java@Configuration@MapperScan(basePackages="com.example.mapper",nameGenerator=CustomNameGenerator.class)publicclassMybatisConfig{//...}//自定义名称生成器publicclassCustomNameGeneratorimplementsBeanNameGenerator{@OverridepublicStringgenerateBeanName(BeanDefinitiondefinition,BeanDefinitionRegistryregistry){//这里可以根据需要自定义名称//比如,我们可以从注解上获取自定义的名称StringbeanClassName=definition.getBeanClassName();try{Class<?>clazz=Class.forName(beanClassName);//检查类上是否有自定义名称的注解Repositoryrepository=clazz.getAnnotation(Repository.class);if(repository!=null&&!repository.value().isEmpty()){returnrepository.value();}//或者根据其他规则生成//...}catch(ClassNotFoundExceptione){//异常处理}//默认使用类名首字母小写returnIntrospector.decapitalize(clazz.getSimpleName());}}```这样,我们就可以在Mapper接口上使用@Repository("customName")来指定Bean名称。三、直接在接口上使用注解指定名称的优缺点优点:1.直观:直接在Mapper接口上指定名称,一目了然。2.避免在配置类中显式定义每个Mapper的Bean(如果有很多Mapper,配置类会很臃肿)。缺点:1.容易导致冲突:如果同时使用@ComponentScan和@MapperScan,可能注册两次(一次由Spring的组件扫描,一次由MyBatis扫描),因此需要确保只扫描一次。2.需要特别注意包扫描的设置,避免重复注册。3.自定义名称可能会影响默认的按类型注入,如果使用@Autowired按类型注入没有问题,但如果按名称注入则需要明确指定名称。四、注意事项1.重复注册问题:避免同时使用@ComponentScan和@MapperScan扫描相同的包(除非明确知道不会重复)。2.代理问题:MyBatis为Mapper接口生成的代理对象是SpringBean,所以我们在接口上指定的名称实际上是代理Bean的名称,而不是接口本身的名称(在Spring容器中,接口本身不会作为一个Bean)。3.避免多个同名的Bean:如果多个Mapper接口指定了相同的名称,在Spring容器启动时会报BeanDefinitionOverrideException(默认情况下不允许覆盖),除非设置`spring.main.allow-bean-definition-overriding=true`。结论:是可以在Mapper接口上直接配置Bean名称的,但要注意避免重复注册和命名冲突。推荐使用@MapperScan配合自定义的BeanNameGenerator,这样既能利用接口上的注解指定名称,又避免重复注册。根据以上讨论,我们总结如下:###在Mapper接口上直接配置Bean名称的方法**方法1:使用Spring的@Component或@Repository注解(需配合避免重复注册策略)**```java@Repository("customUserMapper")publicinterfaceUserMapper{//...}```**配置方式1:禁用MyBatis的扫描,仅使用Spring组件扫描**-不使用@MapperScan,而是让Spring扫描到@Repository,然后手动为每个Mapper创建代理(繁琐,不推荐)**配置方式2:使用@MapperScan并禁用默认的组件扫描**-在配置类上使用@MapperScan,并设置自定义的nameGenerator,如上所述。这样我们可以读取接口上的@Repository注解来设置名称。-同时,确保@ComponentScan不要扫描Mapper接口所在的包(或者排除Mapper接口)。**方法2:自定义BeanNameGenerator(更灵活,无需在接口上添加注解)**-在自定义的BeanNameGenerator中,根据我们自己的规则生成名称(例如:在接口名后加“Impl”等)。优缺点比较:|方法|优点|缺点|适用场景||------|------|------|---------||接口上加注解|简单直观|需处理重复注册;需自定义BeanNameGenerator配合|名称需个性化定制的场景||自定义BeanNameGenerator|集中管理名称生成规则,无需修改接口|需要编写额外代码|统一命名规则或复杂命名规则场景|根据实际项目需要选择合适的方式。###相关问题§§相关问题§§1.如何在MyBatis中避免Mapper重复注册?2.自定义BeanNameGenerator的实现细节有哪些?3.如何为不同数据源的Mapper指定不同的命名规则?</think> # 在MyBatis中直接在Mapper接口上配置Bean名称的方法与注意事项 在MyBatis中,**可以直接在Mapper接口上配置Bean名称**,无需强制在`@Configuration`文件中定义。以下是具体实现方法和注意事项: ## 直接配置Bean名称的方法 ### 方法1:使用`@Component`或`@Repository`注解 ```java @Repository("customUserMapper") // 在此直接指定Bean名称 public interface UserMapper { @Select("SELECT * FROM users") List<User> findAll(); } ``` - **实现原理**:通过Spring的包扫描机制(`@ComponentScan`)自动注册Bean - **需要配置**:在启动类或配置类中添加`@MapperScan`并开启组件扫描 ```java @SpringBootApplication @MapperScan("com.example.mapper") // 扫描Mapper所在包 public class Application { // ... } ``` ### 方法2:使用`@MapperScan`的nameGenerator属性 ```java @Configuration @MapperScan( basePackages = "com.example.mapper", nameGenerator = CustomNameGenerator.class // 自定义名称生成器 ) public class MybatisConfig {} // 自定义Bean名称生成器 public class CustomNameGenerator extends BeanNameGenerator { @Override public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) { String className = definition.getBeanClassName(); // 实现自定义命名逻辑,如添加前缀/后缀 return "my" + className.substring(className.lastIndexOf('.') + 1); } } ``` ## 直接配置的优点 1. **简化配置**:无需在配置文件中逐个定义Mapper Bean 2. **集中管理**:所有Mapper命名规则在接口源文件直接可见 3. **减少冗余代码**:避免在配置类中重复定义相同的Mapper接口 ## 潜在问题与注意事项 1. **Bean名称冲突风险**: ```java // 包com.mapper.a @Repository("userMapper") public interface UserMapper { /* ... */ } // 包com.mapper.b @Repository("userMapper") // 相同Bean名称! public interface UserMapper { /* ... */ } ``` - 解决方案:使用`@MapperScan`的nameGenerator统一管理命名规则[^2] 2. **扫描范围冲突**: - 避免同时使用`@ComponentScan`和`@MapperScan`扫描相同包路径 - 推荐方案:`@MapperScan`扫描Mapper包,`@ComponentScan`扫描其他组件包 3. **代理类问题**: - MyBatis实际注册的是接口的**动态代理实现类**,非接口本身 - 名称冲突错误信息可能较难定位实际冲突点 4. **XML配置兼容性**: - 与MyBatis XML配置文件共存时,需确保命名空间(namespace)与接口全限定名一致 ```xml <!-- UserMapper.xml --> <mapper namespace="com.example.mapper.UserMapper"> <!-- SQL映射 --> </mapper> ``` 5. **多数据源场景**: ```java @Configuration public class MultiDataSourceConfig { @Bean @MapperScan( basePackages = "com.mapper.primary", sqlSessionFactoryRef = "primarySqlSessionFactory", nameGenerator = PrimaryNameGenerator.class // 主库命名规则 ) public SqlSessionFactory primarySqlSessionFactory() { /* ... */ } @Bean @MapperScan( basePackages = "com.mapper.secondary", sqlSessionFactoryRef = "secondarySqlSessionFactory", nameGenerator = SecondaryNameGenerator.class // 从库命名规则 ) public SqlSessionFactory secondarySqlSessionFactory() { /* ... */ } } ``` ## 最佳实践建议 1. **中小项目**:直接使用`@Repository("beanName")`在接口声明名称 2. **大型项目**:通过`@MapperScan`配合自定义BeanNameGenerator统一命名规则 3. **多模块项目**:在公共模块定义命名生成器,各子模块统一引用 > 在Spring Boot 2.6+版本中,默认禁用Bean定义覆盖(BeanDefinitionOverride),需显式设置`spring.main.allow-bean-definition-overriding=true`才能允许同名Bean覆盖[^1]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值