框架常见面试题

139 阅读7分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

@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();
    ​
        }
    }
    ​
    //servicepublic 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方法的参数是类的完整限定名(即包含包名)。

\