美文网首页
04.Spring的常用注解

04.Spring的常用注解

作者: 吃伏冒有礼貌 | 来源:发表于2019-09-26 23:23 被阅读0次

    spring中ioc常用注解

    曾经XML的配置

     <bean id="accountService" class="itheima.service.impl.AccountServiceImpl" scope="" init-method="" destory-method=" ">
      <property name="" value="" |ref =""><property>
    </bean>
    
    • 用于创建对象的
      他们的作用就和在XML中写一个<bean>标签实现的功能是一样的

    • 用于注入数据的
      他们的作用就和XML中写<property>标签是的作用一样的

    • 用于改变作用范围的
      他们的作用就像<scope>标签的作用是一样的

    • 和生命周期相关
      他们的作用就像<init-method>|<destory-method>标签作用是一样的

    • @Component 注解
      作用:用于把当前类对象存入spring容器中
      属性:
      value 用于指定bean的id,当我们不写时.它默认值是当前类名,且首字母改小写
      给AccountserviceImpl打上Componet注解

    @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衍生的注解

    • Service 一般用在业务层
    • Controller 一般用在表现层
    • Repository:一般用于持久层
      功能与Compoent是一样的 是Spring为我们提供的明确的三层注解,使我们的三层对象更加清晰.如果有对象三层注解都不适用,那就直接使用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总是为空,是因为无数据注入,自然是空的.

    • @Autowired
      • 作用:自动按照类型注入,只要容器中有唯一一个bean对象类型和要注入的类型匹配,就可以注入成功.
      • 出现位置:可以是变量上,也可以是方法上.
        所以给dao打上Autowried的标签.
    @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能解决这个问题

    • @Qualifier
    • 作用:在按照类型注入的基础之上,再按照名称注入.它在给类成员注入的时候不能单独使用,但是在给参数方法注入时,可以.它不能独立使用,必须要和@Autowired一起使用
      他有一个个属性,Value
    • value ,用于指定注入的ID
      在AccountServiceImpl中,需要注入的数据类型是 IAccountDao,注入数据时,无论AccountDaoImpl或者是想给AccountDaoImpl1,只要写上他们的id就可以AccountDaoImpl2的id为accountDao2
    @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();
        }
    
    • @Resource
    • 作用:直接按照bean的id注入,可以独立使用,不依赖于@Autowired,他的属性是name,用于指定id,而不是id.
    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标签的作用是一样的.

    • @Scope
      作用:用于指定bean的作用范围
      常用取值:singleton 和 prototype

    给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>
    
    • PreDestory
      作用,指定销毁方法
    • PostConstruct
      作用:指定初始化方法

    在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销毁了
    

    相关文章

      网友评论

          本文标题:04.Spring的常用注解

          本文链接:https://www.haomeiwen.com/subject/dbevuctx.html