Spring:基于注解管理Bean

开启组件扫描

Spring 默认不使用注解装配 Bean,因此我们需要在 Spring 的 XML 配置中,通过 <context:component-scan> 元素开启 Spring Beans的自动扫描功能。开启此功能后,Spring 会自动从扫描指定的包(base-package 属性设置)及其子包下的所有类,如果类上使用了 @Component 注解,就将该类装配到容器中。

注意:在使用 <context:component-scan> 元素开启自动扫描功能前,首先需要在 XML 配置的一级标签 <beans> 中添加 context 相关的约束。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
    http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context.xsd">
   
</beans>

最基本的扫描方式

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

意味着spring会自动扫描com.lin的包以及其子包下的所有类,如果类上使用了 @Component 注解,就将该类装配到容器中。

指定要排除的组件

<context:component-scan base-package="com.lin">
    <!-- context:exclude-filter标签:指定排除规则 -->
    <!-- 
         type:设置排除或包含的依据
        type="annotation",根据注解排除,expression中设置要排除的注解的全类名
        type="assignable",根据类型排除,expression中设置要排除的类型的全类名
    -->
    <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
<!--<context:exclude-filter type="assignable" expression="com.lin.controller.UserController"/>-->
</context:component-scan>

仅扫描指定组件

<context:component-scan base-package="com.lin" use-default-filters="false">
    <!-- context:include-filter标签:指定在原有扫描规则的基础上追加的规则 -->
    <!-- use-default-filters属性:取值false表示关闭默认扫描规则 -->
    <!-- 此时必须设置use-default-filters="false",因为默认规则即扫描指定包下所有类 -->
    <!-- 
         type:设置排除或包含的依据
        type="annotation",根据注解排除,expression中设置要排除的注解的全类名
        type="assignable",根据类型排除,expression中设置要排除的类型的全类名
    -->
    <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
<!--<context:include-filter type="assignable" expression="com.lin.controller.UserController"/>-->
</context:component-scan>

使用注解定义 Bean

注解 说明
@Component 该注解用于描述 Spring 中的 Bean,它是一个泛化的概念,仅仅表示容器中的一个组件(Bean),并且可以作用在应用的任何层次,例如 Service 层、Dao 层等。 使用时只需将该注解标注在相应类上即可。
@Repository 该注解用于将数据访问层(Dao 层)的类标识为 Spring 中的 Bean,其功能与 @Component 相同。里面包含了@Component
@Service 该注解通常作用在业务层(Service 层),用于将业务层的类标识为 Spring 中的 Bean,其功能与 @Component 相同。里面包含了@Component
@Controller 该注解通常作用在控制层(如SpringMVC 的 Controller),用于将控制层的类标识为 Spring 中的 Bean,其功能与 @Component 相同。里面包含了@Component

@Autowired注入

单独使用@Autowired注解,默认根据类型装配。【默认是byType】

查看源码:

package org.springframework.beans.factory.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;

// 该注解可以标注在: 构造方法上、方法上、形参上、属性上、注解上
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
    // 该注解有一个required属性,默认值是true,表示在注入的时候要求被注入的Bean必须是存在的,如果不存在则报错。
    /// 如果required属性设置为false,表示注入的Bean存在或者不存在都没关系,存在的话就注入,不存在的话,也不报错。
    boolean required() default true;
}

属性注入

创建UserDao接口

public interface UserDao {

    public void print();
}

创建UserDaoImpl实现

@Repository // 创建Bean
public class UserDaoImpl implements UserDao {

    @Override
    public void print() {
        System.out.println("Dao层执行结束");
    }
}

创建UserService接口

public interface UserService {

    public void out();
}

创建UserServiceImpl实现类

@Service// 创建Bean
public class UserServiceImpl implements UserService {

    @Autowired // 自动注入,注入Bean
    private UserDao userDao;

    @Override
    public void out() {
        userDao.print();
        System.out.println("Service层执行结束");
    }
}

创建UserController类

@Controller // 创建Bean
public class UserController {

    @Autowired // 自动注入,注入Bean
    private UserService userService;

    public void out() {
        userService.out();
        System.out.println("Controller层执行结束。");
    }

}

测试:

public class UserTest {
    private Logger logger = LoggerFactory.getLogger(UserTest.class);
    @Test
    public void testAnnotation(){
        ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
        UserController userController = context.getBean("userController", UserController.class);
        userController.out();
        logger.info("执行成功");
    }

}

set注入

修改UserServiceImpl类

@Service
public class UserServiceImpl implements UserService {

    private UserDao userDao;

    @Autowired // 自动注入,注入Bean
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    @Override
    public void out() {
        userDao.print();
        System.out.println("Service层执行结束");
    }
}

修改UserController类

@Controller
public class UserController {

    private UserService userService;

    @Autowired // 自动注入,注入Bean
    public void setUserService(UserService userService) {
        this.userService = userService;
    }

    public void out() {
        userService.out();
        System.out.println("Controller层执行结束。");
    }

}

构造方法注入

修改UserServiceImpl类

@Service
public class UserServiceImpl implements UserService {

    private UserDao userDao;

    @Autowired// 自动注入,注入Bean
    public UserServiceImpl(UserDao userDao) {
        this.userDao = userDao;
    }

    @Override
    public void out() {
        userDao.print();
        System.out.println("Service层执行结束");
    }
}

修改UserController类

@Controller
public class UserController {

    private UserService userService;
    // 在使用构造器注入时,如果同时有两个构造器,比如一个有参构造器,一个无参构造器
    // 会优先使用这个有参的构造器,因为使用了 @Autowired
    @Autowired// 自动注入,注入Bean
    public UserController(UserService userService) {
        this.userService = userService;
    }

    public void out() {
        userService.out();
        System.out.println("Controller层执行结束。");
    }

}

形参上注入

修改UserServiceImpl类

@Service
public class UserServiceImpl implements UserService {

    private UserDao userDao;
    // 使用这个的前提是:这里不能有无参构造,否则会使用无参构造器
    // 这样就会注入失败
    public UserServiceImpl(@Autowired UserDao userDao) {
        this.userDao = userDao;
    }

    @Override
    public void out() {
        userDao.print();
        System.out.println("Service层执行结束");
    }
}

修改UserController类

@Controller
public class UserController {

    private UserService userService;
    
    public UserController(@Autowired UserService userService) {
        this.userService = userService;
    }

    public void out() {
        userService.out();
        System.out.println("Controller层执行结束。");
    }
}

一个有参构造器,无注解

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserDao userDao;

    public UserServiceImpl(UserDao userDao) {
        this.userDao = userDao;
    }

    @Override
    public void out() {
        userDao.print();
        System.out.println("Service层执行结束");
    }
}

当有参数的构造方法只有一个时,@Autowired注解可以省略。

@Autowired+@Qualifier:根据名称注入

接口UserDao有两个实现类

@Repository
public class UserDaoImpl  implements UserDao{
    @Override
    public void add() {
        System.out.println("dao........");
    }
}
@Repository
public class UserRedisDaoImpl implements UserDao{
    @Override
    public void add() {
        System.out.println("dao redis.........");
    }
}

如果这个时候我们还根据类型注入,就会报错

@Autowired
//@Qualifier(value = "userRedisDaoImpl")
private UserDao userDao;

@Override
public void add() {
    System.out.println("service.....");
    userDao.add();
}

Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.lin.autowired.dao.UserDao' available: expected single matching bean but found 2: userDaoImpl,userRedisDaoImpl

这个时候,就需要另外一个注解的帮忙

@Autowired
@Qualifier(value = "userRedisDaoImpl")
private UserDao userDao;

@Override
public void add() {
    System.out.println("service.....");
    userDao.add();
}

@Qualifier(value = "userRedisDaoImpl") 这里的value填写类的名字。默认是类的首字母小写

@Resource注入

@Resource注解与@Autowired注解有什么区别?

  • @Resource注解是JDK扩展包中的,也就是说属于JDK的一部分。所以该注解是标准注解,更加具有通用性。(JSR-250标准中制定的注解类型。JSR是Java规范提案。) 。@Autowired注解是Spring框架自己的。
  • @Resource注解默认根据名称装配byName,未指定name时,使用属性名作为name。通过name找不到的话会自动启动通过类型byType装配。@Autowired注解默认根据类型装配byType,如果想根据名称装配,需要配合@Qualifier注解一起用。
  • @Resource注解用在属性上、setter方法上。@Autowired注解用在属性上、setter方法上、构造方法上、构造方法参数上。

依赖

@Resource注解属于JDK扩展包,所以不在JDK当中,需要额外引入以下依赖:【如果是JDK8的话不需要额外引入依赖。高于JDK11或低于JDK8需要引入以下依赖。

<dependency>
    <groupId>jakarta.annotation</groupId>
    <artifactId>jakarta.annotation-api</artifactId>
    <version>2.1.1</version>
</dependency>

源码

@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(Resources.class)
public @interface Resource {
    String name() default "";

    String lookup() default "";

    Class<?> type() default Object.class;

    Resource.AuthenticationType authenticationType() default Resource.AuthenticationType.CONTAINER;

    boolean shareable() default true;

    String mappedName() default "";

    String description() default "";

    public static enum AuthenticationType {
        CONTAINER,
        APPLICATION;

        private AuthenticationType() {
        }
    }
}

根据name注入

@Controller("myUserController")
public class UserController {

    //根据名称进行注入
    @Resource(name = "myUserService")
    private UserService userService;

    public void add() {
        System.out.println("controller........");
        userService.add();
    }
}


@Service( "myUserService") //value 可以省略 @Service(value = "myUserService") // 起名字
public class UserServiceImpl  implements UserService {

    //不指定名称,根据属性名称进行注入
    @Resource
    private UserDao myUserDao;

    @Override
    public void add() {
        System.out.println("service.....");
        myUserDao.add();
    }
}

根据属性名称注入

@Service( "myUserService") //value 可以省略 @Service(value = "myUserService") // 起名字
public class UserServiceImpl  implements UserService {

    //不指定名称,根据属性名称进行注入
    @Resource
    private UserDao myUserDao;

    @Override
    public void add() {
        System.out.println("service.....");
        myUserDao.add();
    }
}


@Repository("myUserDao")
public class UserDaoImpl  implements UserDao {
    @Override
    public void add() {
        System.out.println("dao........");
    }
}

根据类型进行匹配

@Controller("myUserController")
public class UserController {

    //根据类型配置
    @Resource
    private UserService userService;

    public void add() {
        System.out.println("controller........");
        userService.add();
    }
}

@Service( "myUserService") //value 可以省略 @Service(value = "myUserService") // 起名字
public class UserServiceImpl  implements UserService {

    //不指定名称,根据属性名称进行注入
    @Resource
    private UserDao myUserDao;

    @Override
    public void add() {
        System.out.println("service.....");
        myUserDao.add();
    }
}

总结

@Resource注解:

  1. 默认byName注入,
  2. 没有指定name时把属性名当做name,
  3. 根据name找不到时,才会byType注入。
  4. byType注入时,某种类型的Bean只能有一个

Spring全注解开发

全注解开发就是不再使用spring配置文件了,写一个配置类来代替配置文件

@Configuration //配置类
@ComponentScan("com.lin") //开启组件扫描
public class SpringConfig {
}

测试类

import com.lin.config.SpringConfig;

public class TestUserControllerAnno {
    public static void main(String[] args) {
        //加载配置类 : 其中 SpringConfig.class 就是要加载的配置类
        ApplicationContext context =new AnnotationConfigApplicationContext(SpringConfig.class);
        UserController controller = context.getBean(UserController.class);
        controller.add();
    }
}

转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 1909773034@qq.com

×

喜欢就点赞,疼爱就打赏