Bean的定义(注册) – 扫描机制
第一步:引入依赖
pom.xml
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.2.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.6.4</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
导入log4j.properties
log4j.rootLogger=INFO,A1
log4j.logger.org.apache=INFO
log4j.appender.A1.Target=System.err
log4j.appender.A1=org.apache.log4j.ConsoleAppender
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss,SSS} [%t] [%c]-[%p] %m%n
导入applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p"
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">
<!-- 该 BeanPostProcessor 将自动起作用,对标注 @Autowired 的 Bean 进行自动注入 -->
<bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor"/>
<!-- 开启spring的注解功能 :让注解有效了,识别注解-->
<context:annotation-config/>
<!-- 配置注解扫描
context:component-scan:专门扫描含有@Component注解的类,自动将其作为bean
base-package:要扫描包的路径,包含子包,cn.itcast.spring表示子包下的所有类定义注解都有效
注解扫描配置的时候,会自动开启注解功能
-->
<context:component-scan base-package="cn.itcast.spring"/>
</beans>
第二步: 编写Service和DAO
- xml做法 : <bean id=”customerService” class=”…” />,用<bean>的方式创建对象
- 注解做法 : spring2.5引入 @Component 注解 如果放置到类的上面,相当于在spring容器中定义
创建类:CustomerService.java类
/**
* @Component注解放置到类上
* 相当于spring容器中定义:<bean id="customerService" class="cn.itcast.spring.a_ioc.CustomerService">
* 其中id属性默认bean的名字是类名的小写
* ——————————————————————————————————————————————————————
* @Component(value="customerService")//自定义bean的名字
* 相当于spring容器中定义:<bean id="customer" class="cn.itcast.spring.a_ioc.CustomerService">
* ——————————————————————————————————————————————————————
*/
@Component(value="customerService")
public class CustomerService {
//保存业务方法
public void save(){
System.out.println("CustomerService业务层被调用了。。。");
}
}
第三步: 配置注解开启和注解Bean的扫描。配置的示例如下:配置applicationContext.xml
第四步:测试:
package cn.itcast.spring;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringTest2 {
@Test
public void test(){
//spring容器
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
//获取bean
CustomerService customerService=(CustomerService) applicationContext.getBean("customerService");
customerService.save();
}
}
扩展优化:
1.注解扫描配置
在配置包扫描的时候,spring会自动开启注解功能,所以,注解开启功能可以不配置。
即去掉:<context:annotation-config/>
因为<context:componet-scan> 具有 <context:annotation-config> 作用 !
2.衍生注解的问题
实际开发中,使用的是@Component三个衍生注解(“子注解”)
子注解的作用:有分层的意义(分层注解)。
Spring3.0为我们引入了组件自动扫描机制,它可以在类路径底下寻找标注了@Component、@Service、@Controller、@Repository注解的类,并把这些类纳入进spring容器中管理。
除了@Component外,Spring提供了3个功能基本和@Component等效的注解
功能介绍
@Service用于标注业务层组件、(如Service层)
@Controller用于标注控制层组件(如struts中的action层,springMVC中的controller)
@Repository用于标注数据访问组件,(如DAO层组件)。
而@Component泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注。
第一步:
修改CutomerService2.java
package cn.itcast.spring;
import org.springframework.stereotype.Service;
@Service(value = "customerService2")
public class CustomerService2 {
//保存业务方法
public void save(){
System.out.println("CustomerService2业务层被调用了。。。");
}
}
创建CustomerDao.java
package cn.itcast.spring;
import org.springframework.stereotype.Repository;
//持久层
@Repository("customerDao")
public class CustomerDao {
public void save(){
System.out.println("CustomerDao层被调用了");
}
}
问题:如果将Dao注入到Service呢?
回顾:如果使用xml的配置,那么可以使用setter方法进行注入
<bean id=”” class=””>
<property name=”” ref=””></property>
</bean>
CustomerService.java
@Service(value = "customerService2")
public class CustomerService2 {
//保存业务方法
public void save(){
System.out.println("CustomerService2业务层被调用了。。。");
}
public void setCustomerDao(CustomerDao customerDao) {
}
}
<bean id="customerDao" class="cn.itcast.spring.CustomerDao"/>
<bean id="customerService" class="cn.itcast.spring.CustomerService">
<property name="customerDao" ref="customerDao"></property>
</bean>
Bean属性的依赖注入
简单数据类型依赖注入(了解)
Spring3.0后,提供 @Value注解,可以完成简单数据的注入
package cn.itcast.spring;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
@Service(value = "customerService2")
public class CustomerService2 {
//简单类型的成员变量
@Value("Rose")//参数的值简单类型
private String name="Jack";
//保存业务方法
public void save(){
System.out.println("CustomerService业务层被调用了。。。");
System.out.println("name:"+name);
}
}
测试
package cn.itcast.spring;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringTest2 {
@Test
public void test(){
//spring容器
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
//获取bean
CustomerService2 customerService=(CustomerService2) applicationContext.getBean("customerService2");
customerService.save();
}
}
复杂类型数据依赖注入
下面完成,将Dao类的对象注入到Service类进行使用。
注解实现属性依赖注入,将注解加在setXxx方法上 或者 属性定义上
!(任选其一,省代码了)
第一种: 使用@Value 结合SpEL ---- spring3.0 后用
//@Component(value="customer")
@Service(value="customer")
public class CustomerService {
//简单类型的成员变量
@Value("Rose")//参数的值简单类型
private String name="Jack";
//在属性声明上面注入,底层自动还是生成setCustomerDao()
//第一种: 使用@Value 结合SpEL ---- spring3.0 后用
//其中customerDao表示<bean>节点id的属性值
@Value("#{customerDao}")
private CustomerDao customerDao;
//保存业务方法
public void save(){
System.out.println("CustomerService业务层被调用了。。。");
System.out.println("name:"+name);
customerDao.save();
}
}
测试
package cn.itcast.spring;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringTest2 {
@Test
public void test(){
//spring容器
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
//获取bean
CustomerService customerService=(CustomerService) applicationContext.getBean("customerService");
customerService.save();
}
}
在实际开发过程中,第二种方式用的最多(推荐!)
第二种:使用@Autowired 结合 @Qualifier
单独使用@Autowired ,表示按照类型注入,会到spring容器中查找CustomerDao的类型,对应,class的属性值,如果找到,可以匹配。
//第二种:使用spring的@Autowired
@Autowired//默认按照类型注入
private CustomerDao customerDao;
使用@Autowired + @ Qualifier 表示按照名称注入,回到spring容器中查找customerDao的名称,对应,id的属性值,如果找到,可以匹配。
//第二种:使用spring的@Autowired 结合 @Qualifier
@Autowired//默认按照类型注入的
@Qualifier("customerDao")//必须配合@Autowired注解使用,根据名字注入
private CustomerDao customerDao;
第三种: JSR-250标准(基于jdk) 提供注解@Resource
单独使用@Resource注解,表示先按照名称注入,会到spring容器中查找customerDao的名称,对应,id的属性值,如果找到,可以匹配。
如果没有找到,则会按照类型注入,会到spring容器中查找CustomerDao的类型,对应,class的属性值,如果找到,可以匹配,如果没有找到会抛出异常。
//第三种: JSR-250标准(jdk) 提供@Resource
@Resource//默认先按照名称进行匹配,再按照类型进行匹配
private CustomerDao customerDao;
如果@Resource注解上添加name名称
使用@Resource注解,则按照名称注入,会到spring容器中查找customerDao的名称,对应,id的属性值,如果找到,可以匹配。如果没有找到,抛出异常。
//第三种: JSR-250标准(jdk) 提供@Resource
@Resource(name="customerDao")//只能按照customerDao名称进行匹配
private CustomerDao customerDao;
第四种: JSR-330标准(基于jdk) 提供注解@Inject(麻烦,需要额外导包)
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
Bean的初始化和销毁
使用注解定义Bean的初始化和销毁
Spring初始化bean或销毁bean时,有时需要作一些处理工作,因此spring可以在创建和拆卸bean的时候调用bean的两个生命周期方法。
回顾配置文件的写法:<bean id=“foo” class=“…Foo” init-method=“setup”destroy-method=“teardown”/>
注解的写法:
(1)当bean被载入到容器的时候调用setup ,
注解方式如下:
@PostConstruct
初始化
(2)当bean从容器中删除的时候调用teardown(scope= singleton有效)
注解方式如下:
@PreDestroy
销毁
使用 @PostConstruct 注解, 标明初始化方法 —相当于 init-method 指定初始化方法
使用 @PreDestroy 注解, 标明销毁方法 ----相当于 destroy-method 指定对象销毁方法
第一步:创建类:LifeCycleBean.java,定义构造方法、初始化的方法、销毁的方法。
//测试生命周期过程中的初始化和销毁bean
@Component("lifeCycleBean")
public class LifeCycleBean {
public LifeCycleBean() {
System.out.println("LifeCycleBean构造器调用了");
}
//初始化后自动调用方法:方法名随意,但也不能太随便,一会要配置
@PostConstruct//初始化的方法
public void init(){
System.out.println("LifeCycleBean-init初始化时调用");
}
//bean销毁时调用的方法
@PreDestroy
public void destroy(){
System.out.println("LifeCycleBean-destroy销毁时调用");
}
}
第二步:使用SpringTest.java完成测试
package cn.itcast.spring;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.lang.reflect.InvocationTargetException;
public class SpringTest3 {
@Test
public void test() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
/*CustomerService customerService = (CustomerService) ac.getBean("customerService");
customerService.save();*/
//((AbstractApplicationContext) ac).close();
//反射:
ac.getClass().getMethod("close").invoke(ac);
}
}
注意:如果要执行对象的销毁方法
- 条件一: 单例Bean (
在容器close时,单例Bean才会执行销毁方法
) - 条件二: 必须调用容器 close 方法
Bean的作用域
通过@Scope注解,指定Bean的作用域(默认是 singleton 单例)
//测试生命周期过程中的初始化和销毁bean
@Component("lifeCycleBean")
//@Scope(value=ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Scope("prototype")//默认是单例(singleton),更改为多例(prototype)
public class LifeCycleBean {
}
XML和注解混合配置 (重点)
一个项目中XML和注解都有(时代变迁,夹缝中的产物)
- Spring2.0 就有@Autowired注解
- Spring2.5 之后才有@Component注解
使用
XML 完成Bean定义
注解 完成Bean属性注入
第一步:
(1)创建ProductDao类
//产品的数据层
public class ProductDao {
public void save(){
System.out.println("查询保存到数据口--数据层调用了");
}
}
(2)创建ProductService类
//产品的业务层
public class ProductService {
//注入dao
//强调:注入必须是bean注入bean
@Autowired
private ProductDao productDao;
//产品的保存
public void save(){
System.out.println("产品保存了,--业务层");
//调用dao层
productDao.save();
}
}
第二步:使用XML的方式完成Bean的定义
创建applicationContext-mixed.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.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- xml方式定义bean -->
<bean id="productDao" class="cn.itcast.spring.ProductDao"/>
<bean id="productService" class="cn.itcast.spring.ProductService"/>
<!-- 需要单独开启注解功能 -->
<context:annotation-config/>
</beans>
测试
package cn.itcast.spring;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringTest4 {
@Test
public void test(){
//spring容器
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
//获取bean
ProductService productService=(ProductService) applicationContext.getBean("productService");
productService.save();
}
}
备注:
这里配置 <context:annotation-config>
才能使用 @PostConstruct @PreDestroy @Autowired @Resource
<!-- 需要在spring容器中单独开启注解功能 -->
<context:annotation-config/>
提示:
因为采用注解开发时,<context:component-scan> 具有<context:annotation-config>的功能 。
如果没有配置注解扫描,需要单独配置 <context:annotation-config>, 才能使用注解注入!