Spring入门详解(三)Web项目整合Spring及注解的使用

通过前面两章,我们了解了Spring中通过控制反转和依赖注入,将bean的创建及初始化交给Spring容器,需要时直接从容器中获取。不难发现,之前我们获取bean之前都要初始化Spring容器。

但是,每次初始化Spring容器得到的容器对象是不同的,不同容器对象创建的bean也是不同的,这不仅不太符合我们预期,也消耗了资源,降低了性能。

所以,当我们在Web项目中整合Spring时,也需要想办法保证Spring容器对象的唯一。

一、Web项目整合Spring

1、分析

在Web服务运行中,ServletContext是唯一的,所以我们可以在监听到ServletContext创建的时候就创建Spring容器,并将Spring容器对象放在ServletContext的属性中。这样,我们就将Spring容器与Web Servlet容器绑定在一起,由Web容器管理Spring容器的创建和销毁。

2、配置

通过分析我们知道关键在于监听ServletContext,而这个监听器并不需要我们手动创建,Spring中已经提供了一个名为ContextLoaderListener的监听器,它位于spring-web包中。

(1)导入jar包
需要导入的jar包
(2)配置监听器

在web.xml中配置监听器。(注意是在web.xml中配置,而不是spring的xml配置文件)

<listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

(3)构建dao层、service层及相应的dao和service类

public interface UserDao {
    void save();
}

public class UserDaoImpl implements UserDao {
    @Override
    public void save() {
        System.out.println("Dao层保存一条user记录");
    }
}

在service类中,由于需要将dao类bean注入,并且我们用setter注入的方式注入,所以这里需要加上userDao属性的setter方法。

public interface UserService {
    void addUser();
}

public class UserServiceImpl implements UserService {

    private UserDao userDao;
	//为了setter方式注入userDao,所以加上了setter方法
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    @Override
    public void addUser() {
        System.out.println("service层新增一个user");
        userDao.save();
    }

}

(4)在spring配置文件中配置bean

    <bean id="userDao" class="dao.impl.UserDaoImpl" ></bean>
    <bean id="userService" class="service.impl.UserServiceImpl">
        <property name="userDao" ref="userDao"></property>
    </bean>

(5)编写sevlet测试

@WebServlet("/api/user")
public class UserServlet extends HttpServlet {

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response){
        //使用这种方式获取容器对象
        WebApplicationContext webApplicationContext = WebApplicationContextUtils
                .getWebApplicationContext(request.getServletContext());
        UserService userService = (UserService) webApplicationContext.getBean("userService");
        userService.addUser();
    }

}

如果这时运行tomcat发现报错了,且是下面图中的报错内容:
运行tomcat报错
不要慌,报错内容其实已经说的很明白,原来会默认寻找位于WEB-INF下名为applicationContext.xml的配置文件。而此时有两种解决办法:

方法一: 直接按报错提示解决

将配置文件名改为applicationContext.xml,并移动至WEB-INF目录下。

这个解决办法缺点很明显,必须使用固定的配置文件名,并放在固定位置。

方法二: 通过web.xml中全局参数指定配置文件所在的路径

   <!-- 当配置文件在src目录下时,在classpath:后面加上相对路径即可 -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:Application.xml</param-value>
    </context-param>
    
    或者
     
    <!-- 当配置文件在web目录下时,直接写配置文件的相对路径即可 -->
    <context-param>
	    <param-name>contextConfigLocation</param-name>
	    <param-value>WEB-INF/Application.xml</param-value>
    </context-param>

这样就能正常启动tomcat了。然后我们访问servlet路径/api/user,可以看到打印了dao层和service层的内容。
访问servlet控制台输出内容

二、注解的使用

上面的案例中我们是将dao和service在xml配置文件中注册bean的,但一个大型项目中会有很多的dao和很多的service,如果都像上面一样在xml中注册bean的话,我们会得到一个异常臃肿、不方便维护的配置文件。针对这种情况,我们可以采用注解的方式来配置bean。

1、注解方式配置bean

spring2.5引入 @Component 注解,如果放置到类的上面,相当于在spring容器注册了bean。除了@Component注解,还有三个和它用法相同的注解,功能基本是相同的:

@Service用于标注业务层组件、(如Service层);
@Controller用于标注控制层组件(如struts中的action层);
@Repository用于标注数据访问组件,(如DAO层组件);

而@Component泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注。这里我们用@Component做实例,说一下这几个注解的用法。

(1)在接口实现类上加上注解

@Component注解有默认属性value:
没有值时,bean的名字就是首字母小写后的实现类的类名,即userDaoImpl;
有值时,相当于用这个值给bean取了名字,即userDao。

@Component
public class UserDaoImpl implements UserDao {

或

@Component("userDao")
public class UserDaoImpl implements UserDao {

(2)在spring配置文件中开启注解扫描

    <!-- base-package表示注解扫描的范围,这里表示扫描com.tq这个包下的组件 -->
    <context:component-scan base-package="com.tq"></context:component-scan>

(3)在servlet中测试

注意获取bean时,bean的名字要与上面@Componnet注解时相匹配。

        WebApplicationContext webApplicationContext = WebApplicationContextUtils.getWebApplicationContext(request.getServletContext());
        //当@Component注解中没有value值时,bean的名字就是首字母小写后的实现类的类名,即userDaoImpl
        //UserDao userDao = webApplicationContext.getBean("userDaoImpl", UserDaoImpl.class);

		//当@Component注解中有value值时,相当于用这个值给bean取了名字,即userDao
        UserDao userDao = webApplicationContext.getBean("userDao", UserDaoImpl.class);
        userDao.save();

启动tomcat后,如果出现下面的报错,那么就需要引入spring-aop的jar包。

Caused by: java.lang.ClassNotFoundException: org.springframework.aop.TargetSource
	at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1309)
	at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1138)
	... 71 more

然后测试,发现控制台输出如下,说明注解生效了,验证完成。

Dao层保存一条user记录

2、注解方式实现属性依赖注入

2.1、@Value注解

(1)简单类型显示注入

    @Value("Jack")
    private String name = "LiLei";    //注入的值会覆盖初始值,name为 Jack
    
    @Value("20")
    private Integer age ;             //name的值为20
    
    @Override
    public void addUser() {
        System.out.println("service层新增一个user");
        System.out.println("name:"+name);  
        System.out.println("age:"+age);    
        userDao.save();
    }

(2)bean类型属性的注入

格式:@Value("#{要注入的bean名称}")
注意:bean名称必须是已经注册进Spring容器(xml方式配置或者注解方式配置都可以)的bean的名字,而且bean的类型必须与被注入属性的类型一致。

    @Value("#{userDao}")
    private UserDao userDao;
    
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

或

    private UserDao userDao;

    @Value("#{userDao}")
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

(3)结合SpEL从配置文件中读取值并注入

新建properties配置文件config.properties,并填写内容。

新增配置文件
将配置文件通过util标签配置在Spring配置文件中。

    <util:properties id="config" location="classpath:config.properties"></util:properties>

注入属性。

    @Value("#{config.name}")
    private String name = "LiLei";

    @Value("#{config.age}")
    private Integer age ;

2.2、@Autowired注解

关于@Autowired注解,需要注意以下几点:

(1)@Autowired默认根据类型来匹配
(2)标注@Autowired的bean,如果在启动时没有找到匹配的bean会报错,可以加required=false来避免
(3)标注@Autowired的bean,如果在启动时找到了多个类型匹配的bean也会报错
(4)如果需要@Autowired注解根据名字匹配,例如存在多个相同类型但是bean名称不同的bean,需要匹配其中一个,那么可以配合@Qualifier注解一起使用。@Qualifier注解必须与@Autowired注解联合使用,单独使用时虽然不会报错,但无法实现注入,得到的属性值将为null。

	@Autowired
	@Qualifier("userDao")
	private UserDao	userDao;

或

    private UserDao userDao;

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

或

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

2.3、@Resource注解

关于@Resource注解,需要注意以下几点:
(1)默认根据名称匹配,然后才根据类型匹配
(2)标注@Resource的bean,如果在启动时没有找到匹配的bean会报错
(3)标注@Resource的bean,如果在启动时找到了多个类型匹配的bean也会报错

	@Resource
	private UserDao	userDao;

或

    private UserDao userDao;

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

2.4、@Inject注解(一般不用)

@Inject注解的使用需要导入javax.inject 的jar包,即可以单独使用,也可以和@Named注解联合使用。注意@Named注解只能和@Inject注解联合使用,不能单独使用。

	@Inject  //默认按照类型注入
	private UserDao userDao;

或

	@Inject  //默认按照类型注入
	@Named("userDao")  //按照名字注入,必须配合@Inject使用
	private UserDao userDao;

3、Bean的其他属性的注解配置方式

3.1、@PostConstruct 注解

标明初始化方法,相当于 init-method 指定初始化方法。

3.2、@PreDestroy 注解

标明销毁方法,相当于 destroy-method 指定对象销毁方法,注意销毁方法是在关闭容器时才会调用。

3.3、@Scope注解

指定Bean的作用域,默认是singleton,即单例。

3.4、@Lazy注解
指定Bean是否要延迟加载,默认value是true,即要延迟加载。

@Component
@Scope
@Lazy
public class UserDaoImpl implements UserDao {
    @Override
    public void save() {
        System.out.println("Dao层保存一条user记录");
    }

    @PostConstruct
    public void init(){
        System.out.println("初始化了userDao");
    }

    @PreDestroy
    public void destroy(){
        System.out.println("即将销毁userDao");
    }
}

三、XML和注解混合使用

xml和注解是可以混合使用的,例如使用xml来配置bean,用注解来注入bean。

四、Spring的Junit测试集成

Spring提供了spring-test这个jar包,可以整合junit。可以简化测试代码(不需要手动创建上下文,即手动创建spring容器)。

1、导入spring-test和junit的jar包

需要注意的是,如果引入的junit包的版本在4.11以上时,由于jar包中不再包含hamcrest,所以需要再引入hamcrest的jar包 hamcrest-core-1.3.jar。
在这里插入图片描述
否则会报错ClassNotFoundException:org/hamcrest/SelfDescribing。

Exception in thread "main" java.lang.NoClassDefFoundError: org/hamcrest/SelfDescribing
	at java.lang.ClassLoader.defineClass1(Native Method)
	at java.lang.ClassLoader.defineClass(ClassLoader.java:763)
	......
Caused by: java.lang.ClassNotFoundException: org.hamcrest.SelfDescribing
	at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:338)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
	... 21 more

2、编写测试类代码

2.1、@RunWith注解

在测试类上使用@RunWith注解,开启了Spring的注解。

2.2、@ContextConfiguratio
在测试类上使用@ContextConfiguration注解加载核心配置文件,自动构建spring容器。这里根据Spring配置文件的位置不同(是否在类路径src下)有不同的写法。

(1)不在类路径src下
spring配置文件不在src文件夹下
(2)在类路径src下
Spring配置文件在src目录下

3、运行测试类方法

这里直接运行测试类中test01()方法,就能开始测试了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值