Spring基础-基础注解

1、基础的基础概念

是指在类上或者方法上加上@XX的,完成特定的功能

2、注解的作用

  • 替代xml配置,简化配置

从配置文件的方式,转化为注解的方式创建对象

下面的xml的创建bean方式等价于上面的注解创建方式

3、Spring注解开发的问题

我们使用配置文件,是为了解耦。现在,又将注解写入到了代码中,那到底是增加了耦合还是解耦了呢?

注解只是替代了配置文件,让我们更加方便的创建对象,本质上还是解耦

4、Spring的基础注解

我们要想使用注解,首先应该告诉Spring,我们的注解在哪个包下,他需要扫描哪个包路径。这样我们的写的注解才可以生效

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

4.1、Spring创建对象的注解

4.1.1、@Component注解

@Component注解可以替代我们配置文件中的<bean>标签,class就是配置注解类的全限定类名,id就是首字母小写,如User类就是user

实体类

@Component
public class User implements Serializable {
    private String name;
    private Integer age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

配置文件

<?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.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="com.wx.annotation"></context:component-scan>
</beans>

测试类

public class UserTest {

    @Test
    public void testAnnoBean(){
        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext.xml");
        User user = (User) ctx.getBean("user");
        System.out.println(user);
    }
}

测试结果

注解@Component也可以指定id,如@Component(value = "user2"),那么getBean("user2")就需要通过这个id来获取

如果对配置不满意,我们也可以通过配置文件来覆盖我们的注解配置,但是id必须要保持一致

配置文件

    <bean id="user" class="com.wx.annotation.entiy.User"></bean>

测试日志输出的结果

2021-05-09 20:06:02 DEBUG DefaultListableBeanFactory:906 - Overriding bean definition for bean 'user' with a different definition: replacing [Generic bean: class [com.wx.annotation.entiy.User]; scope=singleton; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in file [D:\IdeaProjects\myspring-annotation\target\classes\com\wx\annotation\entiy\User.class]] with [Generic bean: class [com.wx.annotation.entiy.User]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in class path resource [applicationContext.xml]]

通过日志输出,我们可以看到,配置文件中的配置覆盖了我们的注解配置

注意,我们在这么做的时候,一定要注意配置文件中的id是否写的正确,如果不正确,spring会帮我们创建两个bean对象出来,这样在注入的时候,可以会报冲突

4.1.2、@Service,@Controller,@Repository注解

这三个注解都是@Component的衍生注解,通过源码可以发现,他们也是使用的@Component注解

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

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

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

那为什么还需要这三个注解呢?这是因为方便我们区分这些注解的类的作用

@Repository,我们建议用在Dao层

@Controller,建议用在控制层Controller

@Service,建议用在业务层Service上,这些都是Spring建议给我们的。

由于我们的dao层都是使用动态代理生成的,所以@Repository这个注解几乎不会使用

4.1.3、@Scope注解

用来设置创建对象的次数,等同于<bean scope="singleton">配置

这个是控制spring帮助我们创建一次,还是每次调用getBean都生成一个新的对象

实体类

@Component
@Scope("singleton")
public class User implements Serializable {
    private String name;
    private Integer age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
    
}

测试类

Spring默认bean对象就是单例的,我们可以通过@Scope("prototype")注解将对象设置为多例

4.1.4、@Lazy注解

延迟创建单例对象

我们知道,单例对象使在对象功能创建的时候创建好的,如果需要修改到getBean的时候才创建,要在配置文件<bean>中添加lazy-init="true"这个配置。而@Lazy就是代替这个配置的注解

    <bean id="user" class="com.wx.annotation.entiy.User" scope="prototype" lazy-init="true"></bean>

@Lazy注解出现后,默认值是true,只需要加上这个注解,就会在getBean的时候才去创建对象

实体类

测试

4.2、声明周期相关注解

spring中bean对象生命周期中,有两个比较重要的方法,一个是初始化相关的方法,一个是销毁相关的方法

对于初始化方法,我们需要先实现InitializingBean接口,并在配置文件<bean>中添加配置init-method="myInit"

对于销毁方法,也需要实现DisposableBean接口,并在配置文件<bean>中添加配置destroy-method="myDestory"

使用注解后,我们可以通过注解来代替这些操作

  • @PostConstruct--------初始化
  • @PreDestroy----销毁

实体类

@Component
@Scope("singleton")
@Lazy
public class User implements Serializable {
    private String name;
    private Integer age;

    @PostConstruct
    public void myInit(){
        System.out.println("User.myinit");
    }

    @PreDestroy
    public void myDestroy(){
        System.out.println("User.myDestroy");
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

}

测试类

实现了通过注解在对象初始化和销毁的时候,做出了信息输出

4.3、注入相关的注解

上面将的都是对象的创建,现在来说一下对象的注入

对象的注入有两种方式,一种是用户自定义注解(@Autowired),一种是JDK类型的注入

使用配置文件注入的时候,需要先将两个对象<bean>创建出来,然后在进行set注入。现在,我们让spring来帮我们进行注入,是不是只需要将@Autowired加在我们的set方法上即可呢?

4.3.1、@Autowired注解

service实现

public interface UserService {

    public void save();
}

@Service
public class UserServiceImpl implements UserService {

    private UserDao userDao;

    @Autowired
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    public void save() {
        userDao.save();
    }
}

dao层实现

public interface UserDao {

    void save();
}

@Repository
public class UserDaoImpl implements UserDao {
    public void save() {
        System.out.println("UserDaoImpl.save");
    }
}

测试类

我们在set方法上添加了注解,spring会帮助我们进行注入,本质也是set注入

@Autowired注入是通过类型进行注入,必须和成员变量相同的类型,或者是子类、接口的实现类

@Autowired注入也可以通过名字di进行注入,但是需要配合一个注解@Qualifier一起使用

@Autowired不仅可以放在set方法上,也可以放在成员变量上。放在set方法上,是直接调用set方法进行注入,放在成员变量上,是通过反射进行注入。如果set方法和成员变量都加了@Autowired注解,优先走set方法。

4.3.2、@Resource注解

可以通过@Autowired、@Qualifier这两个注解配合使用,达到按照名称id注入的目的。因为我们注入id默认是首字母小写,所以这里@Qualifier的值填写userDaoImpl

@Service
public class UserServiceImpl implements UserService {

    private UserDao userDao;

    @Autowired
    @Qualifier("userDaoImpl")
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    public void save() {
        userDao.save();
    }
}

@Repository
public class UserDaoImpl implements UserDao {
    public void save() {
        System.out.println("UserDaoImpl.save");
    }
}

推荐使用@Autowired按照类型注入

@Autowired、@Qualifier("userDaoImpl")这两个注解的效果等价于@Resource(name = "userDaoImpl")这个注解

//这两段代码等价
@Autowired
@Qualifier("userDaoImpl")
public void setUserDao(UserDao userDao) {
	this.userDao = userDao;
}

@Resource(name = "userDaoImpl")
public void setUserDao(UserDao userDao) {
	this.userDao = userDao;
}

如果@Resource注解没有指定name值或者没有找到指定的name,那么会自动转换为根据类型注入

@Service
public class UserServiceImpl implements UserService {

    private UserDao userDao;

    @Resource
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    public void save() {
        userDao.save();
    }
}

4.4、JDK相关的注解

上面说到,我们的配置文件中的<bean>标签被我们的@Component注解进行替代了,帮助我们创建了bean对象,又通过@Autowired帮助我们注入了复杂类对象。那么对象中的JDK类型的属性如何进行注入呢?

这个时候我们可以创建一个xxx.properties配置文件,通过key,value的形式来进行赋值,那么Spring如何来读取这个配置文件呢?

实体类

public class User implements Serializable {
    private String name;
    private Integer age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

}

可以通过这个配置将自定义配置文件读取

    <context:property-placeholder location="classpath:xxx.properties"></context:property-placeholder>

现在spring就可以读取到我们的配置文件了,还需要通过注解的方式,将配置文件中的值注入到属性中

Spring为我们提供了@Value注解,来进行注入

public class User implements Serializable {
    @Value("${name}")
    private String name;
    @Value("${age}")
    private Integer age;
    
    //省略getset方法
}

配置文件application.properties

name=zhangsan
age=18

测试

其实我们还是在依赖配置文件,用来加载自定义的配置文件,那么Spring有为我们提供注解来解决这个问题吗?@PropertySource()注解就是帮助我们加载自定义配置文件的

@PropertySource("classpath:/application.properties")
public class User implements Serializable {
    @Value("${name}")
    private String name;
    @Value("${age}")
    private Integer age;
    //省略getset方法
}

注意

@Vlaue不能作用于静态变量

@Vlaue不能注入集合类型

4.5、扫描类注解

我们一直是通过一个配置来告诉Spring需要扫描的包,让Spring帮助我们扫描

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

Spring会根据这个路径,扫描该目录及子目录下的所有注解。帮助我们进行注入,那如果我们只希望Spring只扫描这个包下指定的一些包该咋办呢?

4.5.1、排除方式

我们配置文件中有一个exclude-filter标签,帮助我们排除指定的一些包。type值有5种

  • annotation:排除特定的注解,不扫描
  • aspectj:通过切入点表达式进行过滤
  • assignable:排除特定的类,不扫描
  • custom:自定义排除
  • regex:正则表达式

排除指定的类,不进行扫描

    <context:component-scan base-package="com.wx.annotation">
        <context:exclude-filter type="assignable" expression="com.wx.annotation.entiy.User"/>
    </context:component-scan>

排除指定的类,和指定的注解,不进行扫描

    <context:component-scan base-package="com.wx.annotation">
        <context:exclude-filter type="assignable" expression="com.wx.annotation.entiy.User"/>
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Component"/>
    </context:component-scan>

4.5.2、包含方式

包含方式决定了spring要扫描哪些注解

use-default-filters="false",spring默认是扫描包下所有的注解,所以需要加上这个配置,不适用默认策略,扫描所有

    <context:component-scan base-package="com.wx.annotation" use-default-filters="false">
        <context:include-filter type="assignable" expression="com.wx.annotation.entiy.User"/>
    </context:component-scan>

这个和上面那个相反

注解只能作用于程序员开发的类上,如SqlsessionFactoryBean这种复杂对象,我们还是需要使用配置文件进行注入。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值