本文已参与「新人创作礼」活动,一起开启掘金创作之路。
@autowired和@resource注解的区别是什么?
-
@Autowired注解由Spring提供,只按照byType注入;@resource注解由J2EE提供,默认按照byName自动注入。2、@Autowired默认按类型进行装配,@Resource默认按照名称进行装配。
-
-
1、@Autowired
由Spring提供,只按照byType注入
-
2、@Resource
由J2EE提供,默认按照byName自动注入
@Resource有两个重要的属性:name和type
Spring将@Resource注解的name属性解析为bean的名字,type属性则解析为bean的类型。所以如果使用name属性,则使用byName的自动注入策略,而使用type属性则使用byType自动注入策略。如果既不指定name也不指定type属性,这时将通过反射机制使用byName自动注入策略。
@Resource装配顺序:
(1)如果同时指定了name和type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常
(2)如果指定了name,则从Spring上下文中查找名称(id)匹配的bean进行装配,找不到则抛出异常
(3)如果指定了type,则从Spring上下文中找到类型匹配的唯一bean进行装配,找不到或找到多个,都抛出异常
(4)如果既没指定name,也没指定type,则自动按照byName方式进行装配。如果没有匹配,则回退为一个原始类型进行匹配,如果匹配则自动装配。
@Resource的作用相当于@Autowired,只不过@Autowired按byType自动注入。
-
3、使用区别
(1)@Autowired与@Resource都可以用来装配bean,都可以写在字段或setter方法上
(2)@Autowired默认按类型装配,默认情况下必须要求依赖对象存在,如果要允许null值,可以设置它的required属性为false。如果想使用名称装配可以结合@Qualifier注解进行使用。
(3)@Resource,默认按照名称进行装配,名称可以通过name属性进行指定,如果没有指定name属性,当注解写在字段上时,默认取字段名进行名称查找。如果注解写在setter方法上默认取属性名进行装配。当找不到与名称匹配的bean时才按照类型进行装配。但是需要注意的是,如果name属性一旦指定,就只会按照名称进行装配。
推荐使用@Resource注解在字段上,这样就不用写setter方法了,并且这个注解是属于J2EE的,减少了与Spring的耦合。
-
-
使用
//controller @RestController @Slf4j public class TestController { // testService 有两个实现了 bean 注入失败 所以使用Autowired和Qualifier结合使用 //也可以使用Resource 指定使用哪一个 @Autowired @Qualifier("TestServiceImpl") //@Resource(name = "TestServiceImpl2") private TestService testService; @GetMapping("/v1/get/test01") public void test01(){ testService.test01(); } } //service public interface TestService { void test01(); } //service 实现 //使用component分别给两个实现类 起别名 @Service @Component("TestServiceImpl2") public class TestServiceImpl2 implements TestService { @Override public void test01() { System.out.println("22222222222222222222222"); } } //或者直接使用 //@Service("TestServiceImpl") @Service @Component("TestServiceImpl") public class TestServiceImpl implements TestService { @Override public void test01() { System.out.println("111111111111111111111"); } }-
@Autowired//默认按type注入 @Qualifier("cusInfoService")//一般作为@Autowired()的修饰用 @Resource(name="cusInfoService")//默认按name注入,可以通过name和type属性进行选择性注入
-
一般@Autowired和@Qualifier一起用,@Resource单独用。
当然没有冲突的话@Autowired也可以单独用
-
@Transactional注有哪些属性?
propagation属性
propagation 代表事务的传播行为,默认值为 Propagation.REQUIRED,其他的属性信息如下:\
① PROPAGATION_REQUIRED:如果当前没有事务,就创建一个新事务,如果当前存在事务,就加入该事务,该设置是最常用的设置。
② PROPAGATION_SUPPORTS:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就以非事务执行。
③ PROPAGATION_MANDATORY:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就抛出异常。
④ PROPAGATION_REQUIRES_NEW:创建新事务,无论当前存不存在事务,都创建新事务。
⑤ PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
⑥ PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
⑦ PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则按REQUIRED属性执行。
事务的特征
- 原子性
- 一致性
- 隔离性
- 持久性
事务产生的问题
场景:同一个事务内(同一个服务内)
| 名称 | 数据的状态 | 实际行为 | 产生原因 |
|---|---|---|---|
| 脏读 | 未提交 | 打算提交但是数据回滚了,读取了提交的数据 | 数据的读取 |
| 不可重复读 | 已提交 | 读取了修改前的数据 | 数据的修改 |
| 幻读 | 已提交 | 读取了插入前的数据 | 数据的插入 |
事务的隔离级别
| 名称 | 结果 | 脏读 | 不可重复读 | 幻读 |
|---|---|---|---|---|
| Read UnCommitted(读未提交) | 什么都不解决 | √ | √ | √ |
| Read Committed(读已提交) | 解决了脏读的问题 | – | √ | √ |
| Repeatable Read(重复读) | (mysql的默认级别)解决了不可重复读 ) | – | – | √ |
| Serializable(序列化) | 解决所有问题 | – | – | – |
- READ UNCOMMITTED(读未提交数据):允许事务读取未被其他事务提交的变更数据,会出现脏读、不可重复读和幻读问题。
- READ COMMITTED(读已提交数据):只允许事务读取已经被其他事务提交的变更数据,可避免脏读,仍会出现不可重复读和幻读问题。
- REPEATABLE READ(可重复读):确保事务可以多次从一个字段中读取相同的值,在此事务持续期间,禁止其他事务对此字段的更新,可以避免脏读和不可重复读,仍会出现幻读问题。
- SERIALIZABLE(序列化):确保事务可以从一个表中读取相同的行,在这个事务持续期间,禁止其他事务对该表执行插入、更新和删除操作,可避免所有并发问题,但性能非常低。
@Transactional失效场景
-
@Transactional 应用在非 public 修饰的方法上
如果Transactional注解应用在非public修饰的方法上,Transactional将会失效。之所以会失效是因为在Spring AOP 代理时,TransactionInterceptor (事务拦截器)在目标方法执行前后进行拦截,DynamicAdvisedInterceptor(CglibAopProxy 的内部类)的 intercept 方法或 JdkDynamicAopProxy 的 invoke 方法会间接调用 AbstractFallbackTransactionAttributeSource的 computeTransactionAttribute方法,获取Transactional 注解的事务配置信息。
此方法会检查目标方法的修饰符是否为 public,不是 public则不会获取@Transactional 的属性配置信息。
注意:protected、private修饰的方法上使用 @Transactional 注解,虽然事务无效,但不会有任何报错,这是我们很容犯错的一点
- 不是运行时异常
反射
当我们编写完一个Java项目之后,每个java文件都会被编译成一个.class文件,这些Class对象承载了这
个类的所有信息,包括父类、接口、构造函数、方法、属性等,这些class文件在程序运行时会被
ClassLoader加载到虚拟机中。
当一个类被加载以后,Java虚拟机就会在内存中自动产生一个Class对象。我们通过new的形式创建对象
实际上就是通过这些Class来创建,只是这个过程对于我们是不透明的而已。
反射的工作原理就是借助Class.java、Constructor.java、Method.java、Field.java这四个类在程序运行
时动态访问和修改任何类的行为和状态。
java中反射的三种方法
- 类名.class
- 对象名.getClass
- Class.forName("org.whatisjava.reflect.Foo")首先会将reflection.Foo类装入JVM,并返回与之关联的Class对象。JVM装入Foo类后对其进行初始化,调用了其static块中的代码。需要注意的是:forName方法的参数是类的完整限定名(即包含包名)。
\