日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網站首頁 編程語言 正文

Spring-IOC—基于注解配置Bean

作者:llp1110 更新時間: 2022-05-20 編程語言

Spring-IOC—基于注解配置Bean

1.基本使用

1.說明

● 基本介紹

基于注解的方式配置 bean, 主要是項目開發中的組件,比如 Controller、Service、和 Dao.

● 組件注解的形式有

  1. @Component 表示當前注解標識的是一個組件

  2. @Controller 表示當前注解標識的是一個控制器,通常用于 Servlet

  3. @Service 表示當前注解標識的是一個處理業務邏輯的類,通常用于 Service 類

  4. @Repository 表示當前注解標識的是一個持久化層的類,通常用于 Dao 類

2.快速入門

● 應用實例

使用注解的方式來配置 Controller / Service / Respository / Component

● 代碼實現

  1. 引入 spring-aop-5.3.8.jar , 在 spring/libs 下拷貝即可
  2. 創建 UserAction.java UserService.java, UserDao.java MyComponent.java

image-20220515124538230

/**
 * 用來標識該類是一個組件,是一個通用的注解
 */
@Component
public class MyComponent {
}
/**
 * 標識該類是一個控制器
 */
@Controller
public class UserController {
}
/**
 * 使用@Repository標識該類是一個持久層的類/對象
 */
@Repository
public class UserDao {
}
/**
 * 標識是一個Service類
 */
@Service
public class UserService {
}

beans.xml


<context:component-scan base-package="com.llp.spring.component"/>

測試

/**
 * 通過注解方式配置bean
 */
@Test
public void setBeanByAnnotation(){
    ApplicationContext ioc = new ClassPathXmlApplicationContext("beans05.xml");
    UserDao userDao = ioc.getBean(UserDao.class);
    UserService userService = ioc.getBean(UserService.class);
    UserController userController = ioc.getBean(UserController.class);
    MyComponent myComponent = ioc.getBean(MyComponent.class);
    System.out.println(userDao);
    System.out.println(userService);
    System.out.println(userController);
    System.out.println(myComponent);
}

image-20220515124714275

3.注意事項和細節說明

1.需要導入 spring-aop-5.3.8.jar

2.必須在 Spring 配置文件中指定"自動掃描的包",IOC 容器才能夠檢測到當前項目中哪些類被標識了注解, 注意到導入 context 名稱空間

xmlns:context=“http://www.springframework.org/schema/context”

    <context:component-scan base-package="com.llp.spring.component"/>

可以使用通配符 * 來指定 ,比如 com.llp.spring.* 表示

提問: com.hspedu.spring.component 會不會去掃描它的子包? 會的

3.Spring 的 IOC 容器不能檢測一個使用了@Controller 注解的類到底是不是一個真正的控制器。注解的名稱是用于程序員自己識別當前標識的是什么組件。

其它的@Service、@Repository 也是一樣的道理 [也就是說spring的IOC容器只要檢查到注解就會生成對象,但是這個注解的含義 spring 不會識別,注解是給程序員編程方便看的]

4.resource-pattern=“User*.class”: 表示只掃描滿足User打頭的類.[使用的少,不想掃描,不寫注解就可以, 知道這個知識點即可]

<context:component-scan base-package="com.llp.spring.component" resource-pattern="User*.class"/>

5.排除哪些類 , 以 annotaion 注解為例,如下就是排除@Service注解和@Repository類


<context:component-scan base-package="com.llp.spring.component">
    <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service"/>
    <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
context:component-scan>

6.指定自動掃描哪些注解類


<context:component-scan base-package="com.llp.spring.component" use-default-filters="false">
    <context:include-filter type="annotation" expression="org.springframework.stereotype.Service"/>
    <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    <context:include-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
context:component-scan>

debug查看beanFactory中SingletonObjects對象中table屬性的值

7.默認情況:標記注解后,類名首字母小寫作為 id 的值。也可以使用注解的 value 屬性指定 id 值,并且 value 可以省略。[代碼演示]

/**
 * 標識該類是一個控制器
 */
@Controller(value = "user")
public class UserController {
}
    /**
     * 通過注解方式配置bean
     */
    @Test
    public void setBeanByAnnotation(){
        ApplicationContext ioc = new ClassPathXmlApplicationContext("beans05.xml");
        //在獲取userController時 id變成對應的value值
        UserService userService = ioc.getBean("user",UserService.class);
        System.out.println(userController);
    }

2.手動開發-簡單的Spring基于注解配置的程序

1.需求說明

  1. 自己寫一個簡單的 Spring 容器, 通過讀取類的注解(@Component @Controller @Service @Reponsitory),將對象注入到 IOC 容器
  2. 也就是說,不使用 Spring 原生框架,我們自己使用 IO+Annotaion+反射+集合 技術實現, 打通 Spring 注解方式開發的技術痛點

2.思路分析

  1. 思路分析+程序結構

    1)我們使用注解方式完成, 這里2不用 xml 來配置

    2) 程序框架圖

image-20220515135821737

● 應用實例

  1. 手動實現注解的方式來配置 Controller / Service / Respository / Component
  2. 我們使用自定義注解來完成.

3.代碼實現

1.搭建基本結構并獲取掃描包

2.獲取掃描包下所有.class文件

3.獲取全類名 反射對象放入容器

自定義包掃描注解 @ComponentScan

//注解指定運行的類型
@Target(ElementType.TYPE)
//應用范圍
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ComponentScan {
    String value() default "";
}

配置類

//配置類, 作用類似我們原生spring的 beans.xml 容器配置文件
@ComponentScan(value = "com.llp.spring.component")
public class LlpSpringConfig {
}

自定義容器-LlpSpringApplicationContext

public class LlpSpringApplicationContext {
    private Class aClass;
    //ioc我存放的就是通過反射創建的對象(基于注解方式)
    private final ConcurrentHashMap<String, Object> ioc = new ConcurrentHashMap<>();

    public LlpSpringApplicationContext(Class aClass) {
        //傳入LlpSpringConfig
        this.aClass = aClass;
        //根據LlpSpringConfig類獲取到@ComponentScan注解
        ComponentScan componentScan = (ComponentScan) aClass.getDeclaredAnnotation(ComponentScan.class);
        //獲取@ComponentScan注解配置的包路徑
        //basePackage=com.llp.spring.component
        String basePackage = componentScan.value();
        System.out.println("basePackage=" + basePackage);
        ClassLoader classLoader = LlpSpringApplicationContext.class.getClassLoader();
        //path=com/llp/spring/component
        String path = basePackage.replace(".", "/");
        System.out.println("path=" + path);
        //根據包路徑獲取目錄的絕對路徑
        // file:/C:/ide/IdeaProjects/llp-spring/out/production/llp-spring/com/llp/spring/component
        URL resource = classLoader.getResource(path);
        System.out.println(resource);
        //從URL對象中獲取file 目錄
        File file = new File(resource.getFile());
        //判斷文件是否時一個目錄
        if (file.isDirectory()) {
            //獲取目錄下所有的文件
            File[] files = file.listFiles();
            //遍歷
            for (File f : files) {
                System.out.println("=========");
                //獲取目錄下每個文件的絕對路徑
                String absolutePath = f.getAbsolutePath();
                //C:\ide\IdeaProjects\llp-spring\out\production\llp-spring\com\llp\spring\component\MyComponent.class
                System.out.println(f.getAbsolutePath());
                //1.獲取類名
                String className = absolutePath.substring(absolutePath.lastIndexOf("\\") + 1, absolutePath.indexOf(".class"));
                System.out.println(className);
                //2.獲取類的全類名
                //com.llp.spring.component
                String classFullName = path.replace("/", ".") + "." + className;
                System.out.println(classFullName);
                //3.進行過濾,只處理文件目錄下的.class文件
                if (absolutePath.endsWith(".class")) {
                    //4.判斷是不是需要注入到Spirng容器中,看該類是不是有注解@Component @Service
                    try {

                        //1. Class clazz = Class.forName(classFullName) 可以反射加載類
                        //2. classLoader.loadClass(classFullName); 可以反射類的Class
                        //3. 區別是 : 上面方式后調用來類的靜態方法, 下面方法不會
                        //4. aClass.isAnnotationPresent(Component.class) 判斷該類是否有 @Component

                        //得到com.llp.spring.component包下的類的Class對象
                        Class<?> cls = classLoader.loadClass(classFullName);
                        //判斷該類上是否有@Component、@Controller、@Service、@Repository注解
                        if (cls.isAnnotationPresent(Component.class) ||
                                cls.isAnnotationPresent(Controller.class) ||
                                cls.isAnnotationPresent(Service.class) ||
                                cls.isAnnotationPresent(Repository.class)) {
                                   Class<?> clazz = Class.forName(classFullName);
                                    Object o = clazz.newInstance();
                                    String id = null;
                                    if(cls.isAnnotationPresent(Component.class)){
                                        Component component = cls.getAnnotation(Component.class);
                                        id = component.value();
                                        className = id;
                                    }
                            		//StringUtils.uncapitalize(className)將類的首字母小寫
                                    ioc.put(id==null? StringUtils.uncapitalize(className):id,o);
                                        System.out.println(ioc);
                                    }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    public Object getBean(String name){
         return ioc.get(name);
    }
}

測試類

public class LlpSpringApplicationContextTest {
    public static void main(String[] args) {
        LlpSpringApplicationContext ioc = new LlpSpringApplicationContext(LlpSpringConfig.class);
        UserService userService = (UserService)ioc.getBean("userService");
        UserDao userDao = (UserDao)ioc.getBean("userDao");
        UserController userController = (UserController)ioc.getBean("userController");
        MyComponent myComponent = (MyComponent)ioc.getBean("myComponent");
    }
}

測試結果

image-20220515164946550

3.自動裝配

1.基本說明

● 基本說明

  1. 基于注解配置 bean,也可實現自動裝配,使用的注解是:@AutoWired 或者 @Resource

  2. @AutoWired 的規則說明

    1)在 IOC 容器中查找待裝配的組件的類型,如果有唯一的 bean 匹配,則使用該 bean 裝配

    2)如待裝配的類型對應的 bean 在 IOC 容器中有多個,則使用待裝配的屬性的屬性名作為 id 值再進行查找, 找到就裝配,找不到就拋異常

  3. @Resource 的規則說明

    1)@Resource 有兩個屬性是比較重要的,分別是 name 和 type,Spring 將@Resource 注解的 name 屬性解析為 bean 的名字,而 type 屬性則解析為 bean 的類型.所以如果使用 name 屬 性,則使用 byName 的自動注入策略,而使用 type 屬性時則使用 byType 自動注入策略

    2)如果@Resource 沒有指定 name 和 type ,則先使用byName注入策略, 如果匹配不上, 再使用 byType 策略, 如果都不成功,就會報錯

  4. 建議,不管是@Autowired 還是 @Resource 都保證屬性名是規范的寫法就可以 注入

2.注意事項和細節說明

  1. 如待裝配的類型對應的 bean 在 IOC 容器中有多個,則使用待裝配的屬性的屬性名作 為 id 值再進行查找, 找到就裝配,找不到就拋異常

4.泛型依賴注入

1.泛型依賴解釋

● 基本說明

  1. 為了更好的管理有繼承和相互依賴的 bean 的自動裝配,spring 還提供基于泛型依賴的 注入機制
  2. 在繼承關系復雜情況下,泛型依賴注入就會有很大的優越性

2.應用實例

● 應用實例需求

  1. 各個類關系圖

image-20220515174554806

  1. 傳統方法是將 PhoneDao /BookDao 自動裝配到 BookService/PhoneSerive 中,當這 種繼承關系多時,就比較麻煩,可以使用 spring 提供的泛型依賴注入

● 應用實例-代碼實現

//自定義的泛型類
public abstract class BaseDao<T> {
    public abstract void save();
}
//自定義泛型類
public class BaseService<T> {
    @Autowired
    private BaseDao<T> baseDao;

    public void save() {
        baseDao.save();
    }
}
public class Book {
}

public class Phone {
}

@Repository
public class BookDao extends BaseDao<Book>{
    @Override
    public void save() {
        System.out.println("BookDao 的 save()..");
    }
}

@Repository
public class PhoneDao extends BaseDao<Phone>{
    @Override
    public void save() {
        System.out.println("PhoneDao save()");
    }
}

@Service
public class BookService extends BaseService<Book>{
    //并沒有寫屬性
}

@Service
public class PhoneService extends BaseService<Phone>{
    
}
//通過泛型依賴來配置Bean
@Test
public void setProByDependencyInjection() {

    ApplicationContext ioc =
            new ClassPathXmlApplicationContext("beans07.xml");
    PhoneService phoneService = ioc.getBean("phoneService", PhoneService.class);
    phoneService.save();
    System.out.println("ok");

}

原文鏈接:https://blog.csdn.net/qq_44981526/article/details/124831734

欄目分類
最近更新