第五章 注释方法注入

来源:互联网 发布:章燎原的学历知乎 编辑:程序博客网 时间:2024/05/16 05:37

l          注释方法注入

采用注释方式的前提条件,

1.XML配置文件的改动

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

 

蓝色部分为添加的内容,这一部分内容隐式注册注释处理器

 AutowiredAnnotationBeanPostProcessorCommonAnnotationBeanPostProcessorPersistenceAnnotationBeanPostProcessor

 

2.依赖包的导入

见下图:

 

只需要选中 Spring2.5 J2EE 此选项后, 其他的项会自动选中,包括后面要学习的AOP的包也导入了.

 

java代码中,可以使用@Autowired @Resource注释方式进行装配,这两个注解的区别是:

@Autowired默认按类型装配

@Resource 默认按名称装配,当找不到与名称匹配的bean,再按类型装配.

 

@Autowired @Resource两者的区别:

@Autowired: Spring提供,使用后程序会具有侵入性.

@Resource: JDK提供,移植性强,推荐使用, 可以看到支撑类是: import javax.annotation.Resource; 只要是JDK1.5以上版本,JDK内置了此注释支撑类.

 

用法:

用在属性上

public class FruitServiceImpl implements FruitService {

     @Resource

     private FruitDao fruitDao;

 

     @Override

     public void create() {

         fruitDao.create();

     }

 

     public void setFruitDao(FruitDao fruitDao) {

         this.fruitDao = fruitDao;

     }

}

配置文件:

<?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-2.5.xsd

           http://www.springframework.org/schema/context

           http://www.springframework.org/schema/context/spring-context-2.5.xsd">

     <context:annotation-config />

     <bean id="fruitDao" class="dao.impl.FruitDaoImpl" />

     <bean id="fruitService" class="service.impl.FruitServiceImpl" />

</beans>

可以看到这里只是在容器中配置了两个bean对象, 他们之间并没有任何关系.但是在Java文件中,我们采用了注释语法,

这个时候@Resource注释语法会根据属性名称在Spring容器中去寻找和属性名称同名idbean对象,然后将其注入到属性中.

Test:

public class Test {

     public static void main(String[] args) {

         ApplicationContext acx = new ClassPathXmlApplicationContext(

                   "chapter2.xml");

         FruitService fruitService = (FruitService) acx.getBean("fruitService");

         fruitService.create();

     }

}

 

更改配置文件:

<bean id="myFruitDao" class="dao.impl.FruitDaoImpl" />

这个时候可以看到myFruitDao和属性名称并没有对应,但是执行Test文件后,可以看到还是成功执行了方法,为什么呢?

验证了我们刚才所说的,如果名称没有对应上,就会按类型自动对应,所以会执行成功.

 

我们还可以给@Resource加上name,请看下面的语法

public class FruitServiceImpl implements FruitService {

     @Resource(name="myFruitDao")

     private FruitDao fruitDao;

 

     @Override

     public void create() {

         fruitDao.create();

     }

 

     public void setFruitDao(FruitDao fruitDao) {

         this.fruitDao = fruitDao;

     }

}

再次执行Test,同样可以执行成功.

 

用上属性的set方法上

public class FruitServiceImpl implements FruitService {

    

     private FruitDao fruitDao;

 

     @Override

     public void create() {

         fruitDao.create();

     }

 

     @Resource(name="myFruitDao")

     public void setFruitDao(FruitDao fruitDao) {

         this.fruitDao = fruitDao;

     }

}

可以看到,用在属性上,或者用上属性的set方法上,效果是一模一样的,没有任何区别

 

注意:如果我这样配置:

@Resource(name="myFruitDaosss")

     public void setFruitDao(FruitDao fruitDao) {

         this.fruitDao = fruitDao;

     }

而配置文件中又没有提供idmyFruitDaosssbean,这个时候是会报错的:

Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'fruitService': Injection of resource methods failed; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'myFruitDaosss' is defined

 

@Autowired

请看下面的代码:

public class FruitServiceImpl implements FruitService {

    

     private FruitDao fruitDao;

 

     @Override

     public void create() {

         fruitDao.create();

     }

 

     @Autowired

     public void setFruitDao(FruitDao fruitDao) {

         this.fruitDao = fruitDao;

     }

}

运行Test文件,可以看到效果一样.

 

同样,我们也可以把@Autowired改成按名称装配

public class FruitServiceImpl implements FruitService {

     @Autowired()

     @Qualifier("myaFruitDao")

     private FruitDao fruitDao;

        

     public void create() {

         fruitDao.create();

     }

 

     public void setFruitDao(FruitDao fruitDao) {

         this.fruitDao = fruitDao;

     }

}

但是这里需要注意的是:

@Autowired()

@Qualifier("myaFruitDao")

private FruitDao fruitDao;

这两句只能用在属性上,不能用上方法上.

 

@Autowiredrequired属性

@Autowired(required=true)

@Qualifier("myFruitDao")

required=true : 必须为属性注入值,

required=false: 不是必须注入值,如果找不到对应的值注入,会注入null

 

示例:

@Autowired(required=false) @Qualifier("myXXXXXXFruitDao")

当配置文件中没有对应IDBean,会出现java.lang.NullPointerException空指针异常

 

l          通过在classpath自动扫描方式把组件纳入到spring容器中管理.

Spring2.5引入了组件自动扫描机制,他可以在类路径下寻找标注了@Component, @Service, @Controller, @Repository注解的类, 并把这些类纳入进Spring容器中管理,它的作用,和在XML文件中使用Bean节点配置组件是一样的,要使用自动扫描机制,我们需要打开以下配置信息

<?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-2.5.xsd

           http://www.springframework.org/schema/context

           http://www.springframework.org/schema/context/spring-context-2.5.xsd">

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

</beans>

<context:component-scan base-package="com.wdpc.springdemo" />这一项配置了需要扫描的包,包含子包.

 

@Service: 用于标注业务层的类

@Controller: 用于标注控制层,Action

@Repository: 用于标注Dao

@Component: 用于标注组件层, 当组件不好归类的时候,可以用此标注

这四个注释功能一样,只不过是在逻辑层次上达到一个区分的目的.其实,用哪一个都可以.只要类上用到了这些注释,就是告诉Spring,我这个类要交给Spring管理.效果相当于在配置文件中配置的<bean id=”**” class=”**”/>

 

通过扫描机制, 基本上可以达到配置文件基本为空的效果, 这是Spring2.5新推出的功能, 老系统不能运用, 但是新系统中, 此功能运用的非常多.

 

示例:

Dao:

package com.wdpc.springdemo.dao.impl;

 

import org.springframework.stereotype.Repository;

 

import com.wdpc.springdemo.dao.FruitDao;

 

@Repository

public class FruitDaoImpl implements FruitDao {

     public void create() {

         System.out.println("Dao层方法create被调用");

     }

}

服务层:

package com.wdpc.springdemo.service.impl;

 

import org.springframework.stereotype.Service;

 

import com.wdpc.springdemo.dao.FruitDao;

import com.wdpc.springdemo.service.FruitService;

 

@Service

public class FruitServiceImpl implements FruitService {

    

     private FruitDao fruitDao;

 

     public void create() {

         fruitDao.create();

     }

 

     public void setFruitDao(FruitDao fruitDao) {

         this.fruitDao = fruitDao;

     }

}

这个时候,我将Dao,服务层的两个实现类交给Spring管理了,相当于以前配置了两个Bean,这个时候我们可以验证一下,我们是否可以从容器中获取对象

Test:

package com.wdpc.springdemo.service;

 

import org.springframework.context.ApplicationContext;

import org.springframework.context.support.ClassPathXmlApplicationContext;

 

import com.wdpc.springdemo.dao.FruitDao;

 

public class Test {

 

     public static void main(String[] args) {

         ApplicationContext acx = new ClassPathXmlApplicationContext(

                   "chapter2.xml");

         FruitDao fruitDao = (FruitDao)acx.getBean("fruitDaoImpl");

         FruitService fruitService = (FruitService) acx.getBean("fruitServiceImpl");

         System.out.println(fruitDao);

         System.out.println(fruitService);

     }

}

这里需要注意的是,当对象交给Spring管理后,Spring给对象起的ID名称是类的简单名称, 即类全名,但是首字母小写

 

自定义名称:

Dao:

package com.wdpc.springdemo.dao.impl;

 

import org.springframework.stereotype.Repository;

 

import com.wdpc.springdemo.dao.FruitDao;

 

@Repository("fruitDao")

public class FruitDaoImpl implements FruitDao {

     public void create() {

         System.out.println("Dao层方法create被调用");

     }

}

 

服务层:

package com.wdpc.springdemo.service.impl;

 

import org.springframework.stereotype.Service;

 

import com.wdpc.springdemo.dao.FruitDao;

import com.wdpc.springdemo.service.FruitService;

 

@Service("fruitService")

public class FruitServiceImpl implements FruitService {

    

     private FruitDao fruitDao;

 

     public void create() {

         fruitDao.create();

     }

 

     public void setFruitDao(FruitDao fruitDao) {

         this.fruitDao = fruitDao;

     }

}

Test:

public class Test {

     public static void main(String[] args) {

         ApplicationContext acx = new ClassPathXmlApplicationContext(

                   "chapter2.xml");

         FruitDao fruitDao = (FruitDao)acx.getBean("fruitDao");

         FruitService fruitService = (FruitService) acx.getBean("fruitService");

         System.out.println(fruitDao);

         System.out.println(fruitService);

     }

}

 

l          改变对象的范围(单例或非单例)

@Repository("fruitDao")

@Scope("prototype")

加上@Scope("prototype")此注释即可,这个时候每次获取对象都会是一个新对象

 

l          对象初始化方法和销毁方法的配置

@Repository("fruitDao")

@Scope("prototype")

public class FruitDaoImpl implements FruitDao {

     public void create() {

         System.out.println("Dao层方法create被调用");

     }

    

     @PostConstruct

     public void init(){

         System.out.println("调用init");

     }

    

     @PreDestroy

     public void destory(){

         System.out.println("调用destory");

     }

}

如果对象是非单例模式,这个时候可以看到init方法会调用两次,destroy方法不会调用.

 

Dao层注入到服务层:

@Service("fruitService")

public class FruitServiceImpl implements FruitService {

     @Resource(name="fruitDao")

     private FruitDao fruitDao;

 

     public void create() {

         fruitDao.create();

     }

 

     public void setFruitDao(FruitDao fruitDao) {

         this.fruitDao = fruitDao;

     }

}