04.Spring的常用注解

2019-09-26  本文已影响0人  吃伏冒有礼貌

spring中ioc常用注解

曾经XML的配置

 <bean id="accountService" class="itheima.service.impl.AccountServiceImpl" scope="" init-method="" destory-method=" ">
  <property name="" value="" |ref =""><property>
</bean>
@Component
public class AccountServiceImpl implements IAccountService {
    public AccountServiceImpl() {
        System.out.println("AccountServiceImpl 创建了");
    }
    public void saveAccount() {
        System.out.println("service中的saveAccount执行了");
    }
}

bean.xml中的配置需要变动一下,之前是<bean>标签,现在需要告知spring在创建容器时要扫描的包,配置所需要的标签不是在bean的约束中,而是一个名称为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.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">
 <context:component-scan base-package="itheima.service.impl"></context:component-scan>

在Cilent中执行

    public static void main(String[] args) {
        //获取核心容器对象
        //ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("beans.xml");
        ApplicationContext ac = new ClassPathXmlApplicationContext("beans.xml");
        //获取Bean对象
        IAccountService accountService = (IAccountService) ac.getBean("accountServiceImpl");
        accountService.saveAccount();
    }

当component的value有值时


component的value
Cilent中获取bean id

一个细节,如果一个注解中有value属性,value属性的name是可以不写的.

由Compoent衍生的注解

在dao上打上Repository的注解

@Repository
public class AccountDaoImpl implements IAccountDao {
    @Override
    public void saveAccount() {
        System.out.println("dao保存了");
    }
}

在Client中执行

 public static void main(String[] args) {
        //获取核心容器对象
        //ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("beans.xml");
        ApplicationContext ac = new ClassPathXmlApplicationContext("beans.xml");
        //获取Bean对象
        IAccountService accountService = (IAccountService) ac.getBean("accountService");
        IAccountDao accountDao = (IAccountDao) ac.getBean("accountDaoImpl");
        accountService.saveAccount();
    }

结果

AccountServiceImpl 创建了
Service的saveAccount执行了

仍然存疑的点是,如果我在Service的saveAccount换上dao.saveAccount方法 就会空指针异常.

@Component(value = "accountService")
public class AccountServiceImpl implements IAccountService {
    private AccountDaoImpl dao;
    public AccountServiceImpl() {
        System.out.println("AccountServiceImpl 创建了");
    }
    public void saveAccount() {
    dao.saveAccount();
    }
}

执行结果

AccountServiceImpl 创建了
Exception in thread "main" java.lang.NullPointerException
    at itheima.service.impl.AccountServiceImpl.saveAccount(AccountServiceImpl.java:18)
    at itheima.ui.Client.main(Client.java:28)

不过老师也说过这个问题待解决

自动按照类型注入

跟<bean>标签作用类似的是Component以及它的三个衍生注解,主要作用是创建Bean对象到Spring容器.
那么作用与<property>标签的作用类似的是Autowired,它的作用是注入数据
之前那个待解决的问题,dao总是为空,是因为无数据注入,自然是空的.

@Component(value = "accountService")
public class AccountServiceImpl implements IAccountService {
    @Autowired
    private IAccountDao accountDao;
    public AccountServiceImpl() {
        System.out.println("AccountServiceImpl 创建了");
    } 
    public void saveAccount() {
        dao.saveAccount();
    }
}

Autowired的作用是自动按照类型注入,IAccountDao这个数据类型去SpringIOC容器寻找相应的Value,看哪个类型与他对应,于是它找到了找到AccountDaoImpl,就算是实现类,也能看作它的数据类型,如果有唯一的一个匹配,就能注入成功.


自动按照类型注入.png

此时把AccountDaoImpl这个类的实现去掉,它将不再是IAccountDao 这个类型,此时再执行

@Repository("accountDao")
public class AccountDaoImpl {
    public void saveAccount() {
        System.out.println("dao保存了");
    }
}

执行结果是失败的,如果IOC容器中没有任何bean类型和要注入的类型匹配,则报错.

aused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'itheima.dao.IAccountDao' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1646)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1205)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1166)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:593)
    ... 15 more

如果不是唯一匹配的数据类型,比如说存在两个AccountServiceImpl,比如AccountServiceImpl1和AccountServiceImpl2.

@Repository("accountDao1")
public class AccountDaoImpl implements IAccountDao{
    public void saveAccount() {
        System.out.println("accountDao1保存了");
    }
}
@Repository("accountDao2")
public class AccountDaoImpl2 implements IAccountDao{
    public void saveAccount() {
        System.out.println("accountDao2保存了");
    }
}

此时Autowired注解将无法找到唯一的bean对象,将报错:expected single matching bean but found 2: accountDao1,accountDao2,

Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'itheima.dao.IAccountDao' available: expected single matching bean but found 2: accountDao1,accountDao2
    at org.springframework.beans.factory.config.DependencyDescriptor.resolveNotUnique(DependencyDescriptor.java:217)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1217)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1166)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:593)
    ... 15 more

此时,如果把注入的变量名称改动
改为

    private IAccountDao accountDao2 = null;

或者

 private IAccountDao accountDao = null;

此时,变能执行成功
执行结果为

accountDao2保存了

这样能执行的原因是,IAccountDao在spring的Ioc容器中寻找相匹配的数据类型,找到以后会根据变量名称作为bean的id在相匹配的数据类型中继续查找,如果有一样的就成功了,如果不一样则还会继续报错.NoUniqueBeanDefinitionException


自动按照类型注入.png

用于注入数据的注解

@Autowired的注解在上面的问题是,遇到了相同数据类型的对象,却没有遇到能匹配的id,就无法注入成功,@Qualifer能解决这个问题

@Repository("accountDao2")
public class AccountDaoImpl2 implements IAccountDao{
    public void saveAccount() {
        System.out.println("accountDao2保存了");
    }
}

此时

@Service("accountService")
public class AccountServiceImpl implements IAccountService {
    @Autowired
    @Qualifier("accountDao2")
    private IAccountDao accountDao = null;
    public void saveAccount() {
        accountDao.saveAccount();
    }
public class AccountServiceImpl implements IAccountService {
    /*@Autowired
    @Qualifier("accountDao1")*/
    @Resource(name="accountDao2")
    private IAccountDao accountDao = null;
    public void saveAccount() {
        accountDao.saveAccount();
    }
}

java.lang.NoSuchMethodError

在执行的时候遇到报错 java.lang.NoSuchMethodError: javax.annotation.Resource.lookup()Ljava/lang/String;,应该是javax.annotation.的问题.所以在Maven Repository中用了另外一个dependency,此时便运行无问题了

        <dependency>
            <groupId>javax.annotation</groupId>
            <artifactId>javax.annotation-api</artifactId>
            <version>1.3.2</version>
        </dependency>

改变作用范围及生命周期的注解

改变作用范围的注解,他的作用就bean在xml使用scope标签的作用是一样的.

给AccountServiceImpl打上Scope的注解

@Service("accountService")
@Scope("singleton")
public class AccountServiceImpl implements IAccountService {
    @Autowired
    private IAccountDao accountDao2 = null;
    public void saveAccount() {
        accountDao2.saveAccount();
    }
}

创建出两个accountService对象,比较内存地址值,在Client中执行,结果为true

 public static void main(String[] args) {
     
        ApplicationContext ac = new ClassPathXmlApplicationContext("beans.xml");
        //获取Bean对象
        IAccountService accountService = (IAccountService) ac.getBean("accountService");
        IAccountService accountService2 = (IAccountService) ac.getBean("accountService");
        System.out.println(accountService == accountService2);
        accountService.saveAccount();
    }

把AccountServiceImpl的注解改成prototype,在Client中执行,结果为false

@Service("accountService")
@Scope("prototype")
public class AccountServiceImpl implements IAccountService {
    @Autowired
    private IAccountDao accountDao2 = null;cco
    public void saveAccount() {
        accountDao2.saveAccount();
    }
}

和生命周期相关 (了解)
在这个地方我遇到了@PostConstruct 与 @ PreDestory不生效的问题,参考[@PostConstruct 与 @ PreDestory不生效的问题]
(https://blog.csdn.net/zrcode/article/details/77769372)
这个问题是因为原因:使用maven创建工程的时候jdk默认版本是1.5而jdk1.5版本不支持javax.annotation包造成的(这个时候还没碰上@Resource注解找不到的问题,dependency的配置还是按照@Resource时配置,这样的话,@PostConstruct与@ PreDestory也能使用)
整个pom.xml配置如下

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>itheima.com</groupId>
    <artifactId>spring02_eesy_ano_ioc</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

    <dependencies>
      <!-- <dependency>
            <groupId>javax.annotation</groupId>
            <artifactId>jsr250-api</artifactId>
            <version>1.0</version>
        </dependency>-->
        <!-- https://mvnrepository.com/artifact/javax.annotation/javax.annotation-api -->
        <dependency>
            <groupId>javax.annotation</groupId>
            <artifactId>javax.annotation-api</artifactId>
            <version>1.3.2</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.1.2.RELEASE</version>
        </dependency>
    </dependencies>

</project>

在AccountServiceImpl中写init和desrory方法,分别打上@PreConstruct和@PreDestoory注解

public class AccountServiceImpl implements IAccountService {
    @Autowired
    @Qualifier("accountDao1")
    private IAccountDao accountDao = null;
    public void saveAccount() {
        accountDao.saveAccount();
    }
    @PostConstruct
    public void init(){
        System.out.println("service初始化了");
    }
    @PreDestroy
    public void Destory(){
        System.out.println("service销毁了");
    }
}

在Cilent中执行,此时获取Ioc容器使用了ClassPathXmlApplicationContext ,以便用close方法.

 public static void main(String[] args) {
        //获取核心容器对象
        ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("beans.xml");
        //ApplicationContext ac2 = new ClassPathXmlApplicationContext("beans.xml");
        //获取Bean对象
        IAccountService accountService = (IAccountService) ac.getBean("accountService");
        accountService.saveAccount();
        ac.close();
        ac.close();
    }

执行

service初始化了
accountDao1保存了
service销毁了
上一篇 下一篇

猜你喜欢

热点阅读