代理模式
应用场景:RPC调用、Spring AOP、Spring @Transactional事务注解
代理模式是常见的设计模式之一,Java我们通常通过new一个对象然后调用其对应的方法来访问我们需要的服务。代理模式则是通过创建代理类(proxy)的方式来访问服务,代理类通常会持有一个委托类对象,代理类不会自己实现真正服务,而是通过调用委托类对象的相关方法,来提供服务,所以其实我们调用的还是委托类的服务,但是中间隔了一个代理类。这么做是有好处的,我们可以在访问服务之前或者之后加一下我们需要的操作。例如Spring的面向切面编程,我们可以在切入点之前执行一些操作,切入点之后执行一些操作。这个切入点就是一个个方法。这些方法所在类肯定就是被代理了,在代理过程中切入了一些其他操作。
Real Subject:目标类,也就是被代理类、委托类。用来真正完成业务服务功能; Proxy:代理类,将自身的请求用 Real Subject 对应的功能来实现,代理类对象并不真正的去实现其业务功能; Subject:定义 RealSubject 和 Proxy 角色都应该实现的接口,使用者直接调用接口,然后调Proxy代理类,最终调委托类。
1、静态代理
静态代理就是程序员在编写代码的时候就已经把代理类的源码写好了,编译后就会生成.class文件。静态代理在使用时,需要定义接口或者父类,被代理对象与代理对象一起实现相同的接口或者是继承相同父类。举一个租客通过中介租房子的例子。
(1)首先创建一个person接口,这个接口就是租客和中介的公共接口,这个接口有一个rentHouse()方法
public interface Person { //租房 public void rentHouse(); }
(2)创建租客Renter类,实现上述接口
public class Renter implements Person{ @Override public void rentHouse() { System.out.println("租客租房成功!"); } }
(3)创建中介类RenterProxy,同样实现Person接口,但是还另外持有一个租客类对象
public class RenterProxy implements Person{ private Person renter; public RenterProxy(Person renter){ this.renter = renter; } @Override public void rentHouse() { System.out.println("中介找房东租房,转租给租客!"); renter.rentHouse(); System.out.println("中介给租客钥匙,租客入住!"); } }
(4)新建测试类测试
public class StaticProxyTest { public static void main(String[] args) { Person renter = new Renter(); RenterProxy proxy = new RenterProxy(renter); proxy.rentHouse(); } }
运行结果: 中介找房东租房,转租给租客! 租客租房成功! 中介给租客钥匙,租客入住!
2、动态代理
代理类在程序运行时创建的代理方式被成为动态代理。在静态代理中,代理类(RenterProxy)是自己已经定义好了的,在程序运行之前就已经编译完成。而动态代理是在运行时根据我们在Java代码中的“指示”动态生成的。动态代理相较于静态代理的优势在于可以很方便的对代理类的所有函数进行统一管理,如果我们想在每个代理方法前都加一个方法,如果代理方法很多,我们需要在每个代理方法都要写一遍,很麻烦。而动态代理则不需要 代码实现:在java的java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过这个类和这个接口可以生成JDK动态代理类和动态代理对象。还是上面的例子:
(1)和静态代理相同,首先定义一个person接口
public interface Person { //租房 public void rentHouse(); }
(2)创建被代理的类
public class Renter implements Person{ @Override public void rentHouse() { System.out.println("租客租房成功!"); } }
(3)创建RenterInvocationHandler类,这个类实现了InvocationHandler接口,并持有一个被代理类的对象,InvocationHandler中有一个invoke方法,所有执行代理对象的方法都会被替换成执行invoke方法。然后通过反射在invoke方法中执行代理类的方法。在代理过程中,在执行代理类的方法前或者后可以执行自己的操作,这就是spring aop的主要原理。
public class RenterInvocationHandlerimplements InvocationHandler{ //被代理类的对象 private T target; public RenterInvocationHandler(T target){ this.target = target; } /** * proxy:代表动态代理对象 * method:代表正在执行的方法 * args:代表调用目标方法时传入的实参 */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //代理过程中插入其他操作 System.out.println("租客和中介交流"); Object result = method.invoke(target, args); return result; } }
(4)创建动态代理对象
public class ProxyTest { public static void main(String[] args) { //创建被代理的实例对象 Person renter = new Renter(); //创建InvocationHandler对象 InvocationHandler renterHandler = new RenterInvocationHandler(renter); //创建代理对象,代理对象的每个执行方法都会替换执行Invocation中的invoke方法 Person renterProxy = (Person)Proxy.newProxyInstance(Person.class.getClassLoader(),new Class>[]{Person.class}, renterHandler); renterProxy.rentHouse(); //也可以使用下面的方式创建代理类对象,Proxy.newProxyInstance其实就是对下面代码的封装 /*try { //使用Proxy类的getProxyClass静态方法生成一个动态代理类renterProxy Class> renterProxyClass = Proxy.getProxyClass(Person.class.getClassLoader(), new Class>[]{Person.class}); //获取代理类renterProxy的构造器,参数为InvocationHandler Constructor> constructor = renterProxyClass.getConstructor(InvocationHandler.class); //使用构造器创建一个代理类实例对象 Person renterProxy = (Person)constructor.newInstance(renterHandler); renterProxy.rentHouse(); // } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); }*/ } }
执行结果: 租客和中介交流 租客租房成功!
java自动生成了一个$Proxy0代理类,这个类在内存中,所以可以通过反射获取这个类的构造方法,然后创建的代理类实例对象。分析这个类源码,可以知道当调用代理类对象的rentHouse方法时的大概流程为:调用RenterInvocationHandler类的invoke方法,而RenterInvocationHandler类的invoke方法中又用反射调用了被代理类的rentHouse方法。 RenterInvocationHandler可以看成是中间类,它持有被代理对象,把外部对invoke的调用转为对被代理对象的调用。而代理类通过持有中间类,调用中间类的invoke方法,来达到调用被代理类的方法的目的
3、CGLIB代理
动态代理需要被代理类实现接口,如果被代理类没有实现接口,那么这么实现动态代理?这时候就需要用到CGLib了。这种代理方式就叫做CGlib代理。 Cglib代理也叫作子类代理,他是通过在内存中构建一个子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,然后加入自己需要的操作。因为使用的是继承的方式,所以不能代理final 类。
代码实现:导入cglib的jar包,另外Spring的核心包中已经包括了Cglib功能,也可以导入spring-core-3.2.5.jar
(1)创建被代理类UserService
public class UserService { public void getName(){ System.out.println("张三!"); } }
不能为final (2)创建代理工厂类ProxyFactory
public class ProxyFactoryimplements MethodInterceptor { private T target; public ProxyFactory(T target) { this.target = target; } // 创建代理对象 public Object getProxyInstance() { // 1.cglib工具类 Enhancer en = new Enhancer(); // 2.设置父类 en.setSuperclass(this.target.getClass()); // 3.设置回调函数 en.setCallback(this); return en.create(); } //拦截方法 @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { System.out.println("开始事务..."); // 执行目标对象的方法 Object result = method.invoke(target, args); System.out.println("提交事务..."); return result; } }
动态代理与CGLib动态代理都是实现Spring AOP的基础。如果加入容器的目标对象有实现接口,用动态代理,如果目标对象没有实现接口,用Cglib代理。
Spring中什么时候需要注入bean,有哪些类需要注入,哪些类不需要呢?
众所周知,Spring 做的两个事情:IOC,AOP。这两个特性里面,其实就是两个事; 如果A类有成员变量B类,B有成员变量C类。就是A依赖B,B依赖C。实际上,就是A依赖BCD,D依赖EFGH…更复杂的依赖。如果全部成员变量都需要自己手动去new的话,管理依赖实在太复杂。所以,把ABCDEFGH…这些组件全部交由Spring管理,然后用依赖注入。就省了自己new对象的这些过程。AOP,AOP其实只是一个特性。我觉得,总体来讲就是,对象交由Spring管理的话,因为对象由Spring的动态代理。从而可以做相应的增强!AOP只是其中一个,其余还包括,注解Spring缓存,Spring的事物,甚至是监听器等等。Spring都有其便利的实现方式。但是实现的前提就是,注册为Spring的组件。也就是Service,Controller这些。我觉得这两个,就是把Bean交由Spring管理,使用时再注入的主要原因。
假如,一个对象需要依赖Spring的其他依赖,或者Spring的特性,包括但不限于 AOP,事务,缓存等。甚至,如果你要读取Spring等配置文件里的内容,也需要将其交由Spring管理。在我来看其他情况,就没有必要交由Spring管理。 再举个需要交由Spring管理的例子:比如,我要在本地上传一个文件,需要一个文件路径,然而我的路径配置信息存放在配置文件中,这时该属性通过 @Value 注解从配置文件获取。这种情况,就需要将Consts(常量类)注册为Bean。然后通过 @Autowired注入。
配置文件:
file: upload: dir: ${user.dir}/upload
常量类:
/** * 常量类 * @author lj * @since 2022-04-30 */ @Data @Component public class Consts { @Value("${file.upload.dir}") private String uploadDir; }
图片上传工具:
/**
图片上传工具
@author lj
@since 2022-04-30 */ @Slf4j @Component public class UploadUtil {
@Autowired Consts consts;
...后续上传图片逻辑
}
### 注解 **1、@PostConstruct** `@PostConstruct`注解的方法在项目启动的时候执行这个方法,也可以理解为在spring容器启动的时候执行,可作为一些数据的常规化加载。 Spring中Constructor > @Autowired > @PostConstruct,构造方法 >> 依赖注入 >> 后期构造三者的顺序,A类中引用B类对象,生成A类对象进行依赖注入时,引用的B类对象必须存在才能注入A类中;例如A类中有B类对象,生成A类实例前必须先注入B类实例;现在A类中成员变量b被@Autowried注解,实例b注入是发生在A的构造方法执行完之后的。如果在A的构造方法中需要用到实例b,那么就无法在构造函数中进行;此时可以采用@PostConstruct注解,@PostConstruct注解的作用是在依赖注入完成后调用被注解的方法; **2、元注解** **@Retention** @Retention是用来修饰注解的,注解的注解,也称为元注解。@Retention修饰注解,用来表示注解的**生命周期**,生命周期的长短取决于@Retention的属性RetentionPolicy指定的值,例如@Retention(RetentionPolicy.RUNTIME) | 取值 | 描述 | 作用范围 | 使用场景 | | ----------------------- | ------------------------------------------------------------ | ----------------- | ------------------------------------------------------------ | | RetentionPolicy.SOURCE | 表示注解只保留在源文件,当java文件编译成class文件,就会消失 | 源文件 | 只是做一些检查性的操作,比如 @Override 和 @SuppressWarnings | | RetentionPolicy.CLASS | 注解被保留到class文件,但jvm加载class文件时候被遗弃,这是默认的生命周期 | class文件(默认) | 要在编译时进行一些预处理操作,比如生成一些辅助代码(如 ButterKnife) | | RetentionPolicy.RUNTIME | 注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在 | 运行时也存在 | 需要在运行时去动态获取注解信息 | **@Target** `@Target`:注解的作用目标、类型范围 **`@Target(ElementType.TYPE)`——接口、类、枚举、注解 `@Target(ElementType.FIELD)`——字段、枚举的常量 `@Target(ElementType.METHOD)`——方法 `@Target(ElementType.PARAMETER)`——方法参数 `@Target(ElementType.CONSTRUCTOR)` ——构造函数 `@Target(ElementType.LOCAL_VARIABLE)`——局部变量 `@Target(ElementType.ANNOTATION_TYPE)`——注解 `@Target(ElementType.PACKAGE)`——包** **@Document**:元注解,说明该注解将被包含在javadoc中 **@Inherited**:元注解,如果一个类用上了@Inherited修饰的注解,那么其子类也会继承这个注解 **3、@Import** 1. 引入其他的`@Configuration` 2. 直接初始化其他类的`Bean` 3. 指定实现`ImportSelector`(以及`DefferredServiceImportSelector`)的类,用于个性化加载 新建TestC.class ```java public class TestC { public void fun(String str) { System.out.println(str); } public void printName() { System.out.println("类名 :" + Thread.currentThread().getStackTrace()[1].getClassName()); } }
新建SelfImportSelector.class 实现ImportSelector 接口,注入TestC.class
public class SelfImportSelector implements ImportSelector { @Override public String[] selectImports(AnnotationMetadata importingClassMetadata) { return new String[]{"com.test.importdemo.TestC"}; } }
ImportConfig上面引入SelfImportSelector.class
@Import({TestA.class,TestB.class,SelfImportSelector.class}) @Configuration public class ImportConfig { }
测试
@Autowired TestC testC; @Test public void TestC() { testC.printName(); }
打印:
ImportAnnotionTest in 7.23 seconds (JVM running for 9.065) 类名 :com.test.importdemo.TestC
指定实现ImportBeanDefinitionRegistrar
的类,用于个性化加载
4、@EnableTransactionManagement
使用spring事务的时候有一个总开关@EnableTransactionManagement
Spring Boot 使用事务非常简单
使用注解 @EnableTransactionManagement 开启事务支持后
在访问数据库的Service方法上添加注解 @Transactional 便可
5、测试相关注解
@RunWith
@RunWith(SpringJUnit4ClassRunner.class),让测试运行于Spring测试环境
在使用 spring-test 的过程中,有两个 runner 可以选择,分别是 SpringRunner 和 SpringJUnit4ClassRunner。 如果是在 4.3 之前,只能选择 SpringJUnit4ClassRunner,如果是 4.3 之后,建议选择 SpringRunner。 SpringRunner 对 junit 的版本有要求,需要 4.12 及以上。
@RunWith(SpringJUnit4ClassRunner.class)使用了Spring的SpringJUnit4ClassRunner,以便在测试开始的时候自动创建Spring的应用上下文。其他的想创建spring容器的话,就得在web.xml配置classloder。 注解了@RunWith就可以直接使用spring容器,直接使用@Test注解,不用启动spring容器
@MockBean
springboot提供了spirng-boot-starter-test以供开发者使用单元测试,其中org.springframework.boot.test的mockito包,是springboot对org.mockito的增强替换,@MockBean和@SpyBean是主要的mock注解。
在实际开发中,我们自己写的Controller、Service很可能去调用别的同事或别的项目组写的Service、Mapper,对方可能只写了一个接口,没有实现,这样是没法进行测试的。或者测试service时不想关心持久层或真的去操作数据库,因此利用mock
Mock的作用:创建一个虚拟的对象替代那些不易构造或不易获取的对象。
6、@SpringBootApplication
@SpringBootApplication
:申明让Spring Boot
自动给程序进行必要的配置,这个配置等同于:@Configuration
,@EnableAutoConfiguration
和 @ComponentScan
三个配置。
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication // same as @Configuration @EnableAutoConfiguration @ComponentScan public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
7、@ResponseBod
@ResponseBody
:表示该方法的返回结果直接写入HTTP Response Body
中,一般在异步获取数据时使用,用于构建RESTful
的api
。
在使用@RequestMapping
后,返回值通常解析为跳转路径,加上@ResponseBody
后返回结果不会被解析为跳转路径,而是直接写入HTTP Response Body
中。
比如异步获取json
数据,加上@ResponseBody
后,会直接返回json
数据。
该注解一般会配合@RequestMapping
一起使用。
示例代码:
@RequestMapping(“/test”) @ResponseBody public String test(){ return ”ok”; }
8、@Controller
@Controller
:用于定义控制器类,在spring
项目中由控制器负责将用户发来的URL
请求转发到对应的服务接口(service
层)
一般这个注解在类中,通常方法需要配合注解@RequestMapping
。
示例代码:
@Controller @RequestMapping(“/demoInfo”) publicclass DemoController { @Autowired private DemoInfoService demoInfoService; @RequestMapping("/hello") public String hello(Map map){ System.out.println("DemoController.hello()"); map.put("hello","from TemplateController.helloHtml"); // 会使用hello.html或者hello.ftl模板进行渲染显示. return"/hello"; } }
9、@RestController
@RestController
:用于标注控制层组件(如struts
中的action
),@ResponseBody
和@Controller
的合集。
示例代码:
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping(“/demoInfo2”) publicclass DemoController2 { @RequestMapping("/test") public String test(){ return"ok"; } }
10、@RequestMapping
@RequestMapping
:提供路由信息,负责URL
到Controller
中的具体函数的映射。
11、@EnableAutoConfiguration
@EnableAutoConfiguration
:Spring Boot
自动配置(auto-configuration
):尝试根据你添加的jar
依赖自动配置你的Spring
应用。
例如,如果你的classpath
下存在HSQLDB,并且你没有手动配置任何数据库连接beans
,那么我们将自动配置一个内存型(in-memory
)数据库”。
你可以将@EnableAutoConfiguration
或者@SpringBootApplication
注解添加到一个@Configuration
类上来选择自动配置。
如果发现应用了你不想要的特定自动配置类,你可以使用@EnableAutoConfiguration
注解的排除属性来禁用它们。
12、@ComponentScan
@ComponentScan
:表示将该类自动发现扫描组件。
个人理解相当于,如果扫描到有@Component
、@Controller
、@Service
等这些注解的类,并注册为Bean
,可以自动收集所有的Spring
组件,包括@Configuration
类。
我们经常使用@ComponentScan
注解搜索beans
,并结合@Autowired
注解导入。可以自动收集所有的Spring
组件,包括@Configuration
类。
如果没有配置的话,Spring Boot
会扫描启动类所在包下以及子包下的使用了@Service
、@Repository
等注解的类。
13、@Configuration
@Configuration
:相当于传统的xml
配置文件,如果有些第三方库需要用到xml
文件,建议仍然通过@Configuration
类作为项目的配置主类——可以使用@ImportResource
注解加载xml
配置文件。@Configuration注解的作用:声明一个类为配置类,用于取代bean.xml配置文件注册bean对象
14、@Import
:用来导入其他配置类。
15、@ImportResource
:用来加载xml
配置文件。
16、@Autowired
:自动导入依赖的bean
,不能注入static属性
17、@Service
:一般用于修饰service
层的组件
18、@Repository
:使用@Repository
注解可以确保DAO
或者repositories
提供异常转译,这个注解修饰的DAO
或者repositories
类会被ComponetScan
发现并配置,同时也不需要为它们提供XML
配置项。
19、@Bean
:用@Bean
标注方法等价于XML
中配置的bean
。
20、@Value
:注入Spring boot
application.properties
配置的属性的值。示例代码:
@Value(value = “#{message}”) private String message;
21、@Inject
:等价于默认的@Autowired
,只是没有required
属性;
22、@Component
:泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注。
23、@Bean
:相当于XML
中的,放在方法的上面,而不是类,意思是产生一个bean
,并交给spring
管理。
24、@AutoWired
:自动导入依赖的bean
。byType
方式。把配置好的Bean
拿来用,完成属性、方法的组装,它可以对类成员变量、方法及构造函数进行标注,完成自动装配的工作。当加上(required=false
)时,就算找不到bean
也不报错。
25、@Qualifier
:当有多个同一类型的Bean
时,可以用@Qualifier(“name”)
来指定。与@Autowired
配合使用。@Qualifier
限定描述符除了能根据名字进行注入,但能进行更细粒度的控制如何选择候选者,具体使用方式如下:
@Autowired @Qualifier(value = “demoInfoService”) private DemoInfoService demoInfoService;
26、@Resource(name=”name”,type=”type”)
:没有括号内内容的话,默认byName
。与@Autowired
干类似的事。
27、@Transactional
@Tranasctional注解是Spring 框架提供的声明式注解事务解决方案,我们在开发中使用事务保证方法对数据库操作的原子性,要么全部成功,要么全部失败,在使用@Transactional注解时需要注意以下问题:
1)@Transactional 注解只能用在public 方法上,如果用在protected或者private的方法上,不会报错,但是该注解不会生效。
2)@Transactional注解只能回滚非检查型异常,具体为RuntimeException及其子类和Error子类,可以从Spring源码的DefaultTransactionAttribute类里找到判断方法rollbackOn。
@Override public boolean rollbackOn(Throwable ex) { return (ex instanceof RuntimeException || ex instanceof Error); }
3)使用rollbackFor 属性来定义回滚的异常类型,使用 propagation 属性定义事务的传播行为。如: 回滚Exception类的异常,事务的传播行为支持当前事务,当前如果没有事务,那么会创建一个事务。
4)@Transactional注解不能回滚被try{}catch() 捕获的异常。
5)@Transactional注解只能对在被Spring 容器扫描到的类下的方法生效。
其实Spring事务的创建也是有一定的规则,对于一个方法里已经存在的事务,Spring 也提供了解决方案去进一步处理存在事务,通过设置@Tranasctional的propagation 属性定义Spring 事务的传播规则。 6)同一个类中方法调用,导致@Transactional失效
开发中避免不了会对同一个类里面的方法调用,比如有一个类Test,它的一个方法A,A再调用本类的方法B(不论方法B是用public还是private修饰),但方法A没有声明注解事务,而B方法有。则外部调用方法A之后,方法B的事务是不会起作用的。这也是经常犯错误的一个地方。 那为啥会出现这种情况?其实这还是由于使用Spring AOP代理造成的,因为只有当事务方法被当前类以外的代码调用时,才会由Spring生成的代理对象来管理。
7)最重要的是,使用@Transactional的类必须是一个Bean,因为@Transactional需要用到代理。
注:如果加入容器的目标对象有实现接口,用动态代理,如果目标对象没有实现接口,用Cglib代理。
28、@Bean和@Autowired的区别
常用的依赖注入有3种方式:构造器注入、setter注入和注解注入 注解注入:@Resource、@Autowired
@Resource默认按名称来装配注入,只有找不到与名称匹配的bean才会按照类型来装配注入
@Autowired默认是按照类型装配注入的,如果像按照名称来装配注入则需要结合@Qualifier一起使用
@Resource注解是由J2EE提供的,而@Autowired是由Spring提供;
@Bean和@Autowired的区别: @Bean告诉Spring:"这是这个类的实例,请保留它,并在我请求时将他给我"
@Autowired说:"请给我一个这个类的实例,例如,一个我之前用@Bean注解创建的实例"
29、@Component 和@Bean区别
@Component 和@Bean都是声明一个bean交给spring管理,但他们在使用上有一些区别
1)作用对象不同: @Component 注解作用于类,而@Bean注解作用于方法。
2)@Component通常是通过类路径扫描来自动侦测以及自动装配到Spring容器中(我们可以使用 @ComponentScan 注解定义要扫描的路径从中找出标识了需要装配的类自动装配到 Spring 的 bean 容器中)。@Bean 注解通常是我们在标有该注解的方法中定义产生这个 bean,@Bean告诉了Spring这是某个类的示例,当我需要用它的时候还给我。
3)@Bean 注解比 Component 注解的自定义性更强,而且很多地方我们只能通过 @Bean 注解来注册bean。比如当我们引用第三方库中的类需要装配到 Spring容器时,则只能通过 @Bean来实现。
未正确绑定
报错:org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): com.huatai.cube.mgt.common.udmmetadata.mapper.UdmMetadataMapper.selectByCondition
一般的原因
Mapper interface和xml文件的定义对应不上,需要检查包名,namespace,函数名称等能否对应上。 按以下步骤一一执行: 1、检查xml文件所在的package名称是否和interface对应的package名称一一对应 2、检查xml文件的namespace是否和xml文件的package名称一一对应 3、检查函数名称能否对应上 4、去掉xml文件中的中文注释 5、随意在xml文件中加一个空格或者空行然后保存
但是竟然都不起作用!崩溃!终于在一篇帖子里看到了想要的答案,在使用IDEA开发时,如果打包时*Mapper.xml没有自动复制到class输出目录的mapper类包下,则需要在pom文件中添加mybatis加载配置文件的配置! 如下所示:
src/main/java **/*.xml src/main/resources
调用mapper为空指针
异常情况
有A,B两个类; 其中A类中使用注解的方式引入了Mapper类; B类使用new对象的方式引入A类, 例如:A a = new A(); ***但是执行到A类里面后,就会抛出Mapper对象空指针异常。(即A类中的Mapper对象注解失败)***
解决方案
A类:在类名上面使用@Service("类名") 例如@Service("a"); B类:使用注解的方式引入A类,例如: @Autowired A a; 这样B类中的A类能被自动注解了,A类中的Mapper也能被自动注解了
原因分析
因为mapper需要被自动注入,而使用A a = new A();方式与Spring的IOC容器无关,即如果A不是自动注入,则A中声明的@Autowired也会失效
编译打包时mvn install包不存在
一般是引用的包修改后没有重新install
Dubbo的服务提供者,service类名有要求