分层解耦-04.IOC&DI-IOC详解

一.Bean的声明 

springboot为了更好地标识web应用开发中bean对象到底归属于哪一层,在@Component注解的基础上又衍生出了三个注解@Controller、@Service和@Repository。分别应用于Controller层,Service层和Dao层。推荐使用衍生注解,当无法判断该bean对象到底是属于哪一层时,使用@Component注解。

package com.gjw.dao.impl;

import com.gjw.dao.EmpDao;
import com.gjw.pojo.Emp;
import com.gjw.utils.XmlParserUtils;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Repository;

import java.util.List;

//@Component  // IOC:控制反转,将实现类对象交给容器。将当前类交给IOC容器管理,成为IOC容器中的bean
@Repository     // Dao层
public class EmpDaoA implements EmpDao {
    @Override
    public List<Emp> listEmp() {
        String file = this.getClass().getClassLoader().getResource("emp.xml").getFile();
        System.out.println(file);
        List<Emp> empList = XmlParserUtils.parse(file, Emp.class);
        return empList;
    }
}
package com.gjw.service.impl;

import com.gjw.dao.EmpDao;
import com.gjw.pojo.Emp;
import com.gjw.service.EmpService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

import java.util.List;

//@Component  // IOC:控制反转,将实现类对象交给容器。将当前类交给IOC容器管理,成为IOC容器中的bean
@Service
public class EmpServiceA implements EmpService {
    @Autowired  // DI:依赖注入,service依赖于dao,运行时IOC容器会提供该类型的bean对象,并赋值给该变量
    private EmpDao empDao;
    @Override
    public List<Emp> listEmp() {
        List<Emp> empList = empDao.listEmp();
        empList.stream().forEach(emp ->
        {
            if ("1".equals(emp.getGender())) {
                emp.setGender("男");
            } else if ("2".equals(emp.getGender())) {
                emp.setGender("女");
            }
            if ("1".equals(emp.getJob())) {
                emp.setJob("讲师");
            } else if ("2".equals(emp.getJob())) {
                emp.setJob("班主任");
            } else if ("3".equals(emp.getJob())) {
                emp.setJob("就业指导");
            }
        });
        return empList;
    }
}

在controller代码中就不用加@Controller注解了,因为@RestController注解中已经有了@Controller注解 

@RestController注解源码:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.springframework.web.bind.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.annotation.AliasFor;
import org.springframework.stereotype.Controller;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller
@ResponseBody
public @interface RestController {
    @AliasFor(
        annotation = Controller.class
    )
    String value() default "";
}

@RestController = @Controller + @ResponseBody

将原来的@Component注解替换为对应层的注解,刷新后依然可以看到数据,证明成功

二.Bean的名字

在我们声明一个bean时,每一个bean都有一个名字。在声明一个bean时,我们可以通过注解当中的value属性来指定一个bean的名字,如果不指定,默认就是类名的首字母小写。

 将程序运行起来之后,在控制台中进入Actuator,点击右边按键。其中非黄色部分就是自己定义的bean对象的类,而点击三角显示出来的就是其bean的名字。

@Service和@Repository源码

// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)

package org.springframework.stereotype;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.annotation.AliasFor;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Service {
    @AliasFor(
        annotation = Component.class
    )
    String value() default "";
}
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.springframework.stereotype;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.annotation.AliasFor;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Repository {
    @AliasFor(
        annotation = Component.class
    )
    String value() default "";
}

我们可以通过使用注解中的value方法来为bean对象重新起一个名字。

@Service(value = "serviceA")

通过value指定的bean对象的名字,可以将value省略。

@Service("serviceA")

 已修改。一般不会修改名字。

三.Bean组件扫描

我们将dao移出来,放在java包下,与com.gjw平级。 再次启动程序。

程序报错,报错原因是EmpServiceA这个类中要找的EmpDao这个接口的实现类发现找不到了。

为什么会找不到呢?原因一定在将dao包移动到和com.gjw包平级的位置有关。这就涉及到bean的组件扫描问题。

 也就是说,bean组件扫描只会扫描com.gjw包下的所有包及其子包。而dao包与其平级,自然不会被扫描到。因此也就找不到。

四.解决办法

方法一:手动设置包扫描@ComponentScan(指定扫描包)(不推荐)

通过value属性和basePackages属性都可以来指定要扫描哪个包。返回类型为字符串数组。

@ComponentScan({"dao","com.gjw"})
@SpringBootApplication  // 默认扫描当前包及其子包
public class SpringbootWebReqRespApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringbootWebReqRespApplication.class, args);
    }

}
    

手动指定,一旦手动指定,那么默认的com.gjw包就会没有,需要手动添加上去。

成功

方法二:将所写代码全部放在启动类所在包及其子包下,按照规范来(推荐)

五.总结

上一篇:自闭症学校全寄宿制度对孩子发展的影响


下一篇:安全运营中心 (SOC) 团队对其安全工具感到失望