使用注解配置Spring入门
说在前面
学习基于注解的IoC配置,大家脑海里首先得有一个认知,即注解配置和xml配置要实现的功能都是一样的,都是要降低程序间的耦合。只是配置的形式不一样。
关于实际的开发中到底使用xml还是注解,每家公司有着不同的使用习惯。所以这两种配置方式我们都需要掌握。
基于注解配置的方式也已经逐渐代替xml。所以我们必须要掌握使用注
配置步骤
注意:Eclipse需要先安装了STS插件,或者使用STS开发工具创建项目。
解的方式配置Spring。
第一步:拷贝必备jar包到工程的lib目录。
第二步:在类的根路径下创建一个任意名称的xml文件(不能是中文)
注意:基于注解整合时,Spring配置文件导入约束时需要多导入一个context名称空间下的约束
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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 "> </beans>
第三步. 创建一个服务类
创建一个测试的服务类,并且加入使用@Component注解,声明该类允许注入到Spring容器
用于对象的注解-IOC相关注解
我们将用于被扫描创建对象的注解,统称为组件注解。
组件包括:@Component,@Controller,@Service,@Repository。
组件注解的功能都是标识类为注解的组件类,启动Spring框架的程序时,声明将这些组件类注入到Spring容器里面。意味着,只有加了这四个注解任何一个注解的类,在程序启动的时候,Spring就通过配置文件指定的路径将该路径下的所有带组件注解的类创建对象并且放在容器里面。
功能类似原来配置文件的<bean>标签
问题:明明一个@Component注解就可以满足了扫描的需要,为什么要有四个呢?
答:其实Spring第一版注解的实现(spring 2.5),就是使用一个@Component。从3.0以后,作者认为根据分层的需要,把它拆成了四个。为了可以让开发人员,可见即可得,一看到注解,立即知道类的性质。所以分成了四个
@Controller:用于声明表示层的组件注解
@Service:用于声明服务层的组件注解
@Repository:用于声明持久层的组件注解
@Component:用于声明三层以外的组件注解
问题:那么,这四个注解交换使用会报错吗。如:持久层,我放@Service标记。
答:处理@Controller在SpringMVC里面有强制的要求,SpringMVC的表示层必须使用@Controller组件注解。其他情况,用乱了是不会报错的,不过我们必须不能用乱。不遵守规范,不然别人无法跟你一起开发了。
@Scope用于设置对象的生命周期注解
Xml配置需要配置对象的作用范围
<bean id="someBean" class="..." scope="作用范围"></bean> |
如果使用注解配置bean,那么bean的作用范围也需要使用注解配置
@Scope("作用范围")
singleton |
单例 ,在Spring IoC容器中仅存在一个Bean实例 (默认的scope) |
prototype |
多例 ,每次从容器中调用Bean时,都返回一个新的实例,即每次调用getBean()时 ,相当于执行new XxxBean(): |
request |
用于web开发,将Bean放入request范围 ,request.setAttribute("xxx") , 在同一个request 获得同一个Bean |
session |
用于web开发,将Bean 放入Session范围,在同一个Session 获得同一个Bean |
在开发中主要使用 scope="singleton"、 scope="prototype"
对于MVC中的Action/Controller使用prototype类型,其他使用singleton
package spring4.cn.zi.spring.service; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; /* * @Component * 此注解是spring的组件注解,只要是某一个类上面贴上此注解,Spring框架启动就会自动管理类 * value : 给当前bean设置一个名字,可以省略不写 * 如果value不写,默认bean名称就是当前类首字母小写+后面部分 * @Component * 等价于 <bean id="customService" class="cn.zj.spring.service.CustomService"> * @Component("service") * 等价于 <bean id="service" class="cn.zj.spring.service.CustomService"> * * @Scope 设置被Spring管理对象的生命周期 * 等价于<bean id="someBean" class="..." scope="作用范围"></bean> * @PostConstruct * 初始化方法注解 * @PreDestory * 销毁方法注解 * 等价于 <bean id="" class="" init-method="xxx" destory-method="xxx"> */ @Component("springservice") //@Repository("springservice") Dao/持久层 //@Service("springservice") service/业务层 //@Controller("springservice") web/ public class Service1 { public Service1() { System.out.println(this); } @Scope("prototype") public void say() { System.out.println("你好,Spring"); } @PostConstruct public void init() { System.out.println("初始化方法执行了"); } @PreDestroy public void destroy() { System.out.println("销毁化方法执行了"); } }
第四步在spring的配置文件加入扫描注解
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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="cn.zj.spring"></context:component-scan> </beans>
第五步:测试调用代码
package spring4.cn.zi.spring.test; import org.junit.Test; import org.springframework.context.support.ClassPathXmlApplicationContext; import spring4.cn.zi.spring.service.Service1; public class Test01 { @Test public void testName() throws Exception { ClassPathXmlApplicationContext ct = new ClassPathXmlApplicationContext("applicationContext.xml"); Service1 bean = ct.getBean("springservice", Service1.class); Service1 bean1 = ct.getBean("springservice", Service1.class); bean.say(); bean1.say(); } }
用于依赖注入的注解
回顾:XML配置文件使用<property name=”” ref=””>实现注入的。通过注入注解也可以实现。
Spring提供了两套注解可以解决依对象依赖注入的方案
1,@Autowired +@Qualifier():是Spring定义的标签
2,@Resouce:是J2EE的规范
@Autowired注解
@Autowired注解:用于给引用注入容器的对象。
- @Autowired标签贴在字段或者setter方法上
- 默认情况下@Autowired标签必须要能找到对应的对象,否则报错。不过,可使用required=false来避免该问题:@Autowired(required=false)
- @Autowired找bean的方式:
(1) 首先按照依赖对象的类型找,如果找到则使用setter方法或者字段直接注入;
(2) 如果在Spring上下文中找到多个匹配的类型,再按照名字去找,如果没有匹配则报错;
(3) 可以通过使用@Qualifier("otherBean")标签来规定依赖对象按照bean的id+类型去找
使用@Autowired注入的三种情况
@Qualifier注解
@Qualifier注解:用于指定注入的对象名,使用@Autowired注入对象时,@Autowired没有指定对象名的属性,只能通过@Qualifier字段容器中对象名
属性
value:指定注入Spring容器中对应对象名的对象给引用。
package spring4.cn.zi.spring.web; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Controller; import spring4.cn.zi.spring.service.ServiceC; @Controller("serviceWeb") public class Web01 { /* * * 方式一 : 字段注入,不需要set方法,用的最多 * 依赖注入 * @Autowired 等价于 <propoty name="service" ref="service"> * 默认是按照类型注入,从Spring容器中去找有相同类型的对象自动注入 * 问题一 :如果Spring容器中出现多个相同类型的bean,就会报异常 * org.springframework.beans.factory.NoUniqueBeanDefinitionException * 解决方案: * 使用 @Qualifier * @Qualifier("指定bean的名称") * @Qualifier("service2") * 问题二: 如果Spring容器中相同类型的对象一个都没有,就报异常 * org.springframework.beans.factory.NoSuchBeanDefinitionException * 解决方案 * required属性: 此类型的对象是否是必须有 * true:必须有,默认 * false:不是必须有 * @Autowired(required=false) * */ //@Autowired //@Qualifier("service2") private ServiceC serviceC; /* * 方式二: set方法注入,属性注入 * 是否方法和字段注入一致
使用方法注解必须要有参数 */ //@Autowired //@Qualifier("service2") public void setServiceC(ServiceC serviceC) { System.out.println("hello"); this.serviceC=serviceC; } /* * 方式三 :构造器注入 * 和上面不同 Qualifier位置不同了 */ @Autowired public Web01(@Qualifier("service2") ServiceC serviceC) { System.out.println("hello"); this.serviceC=serviceC; } public void save() { serviceC.save(); } }
@Resource注解
@Resource注解是Spring框架支持Sun官方制定的JSR-250标准注入对象的实现。
JSR-250就是Sun公司制定,对注入的对象的标准。
@Resource 功能等同 @Autowired + @Qualifier ,等同配置文件标签 <proprty name=”...” ref=”...”>
@Resource注解:用于给引用注入容器的对象,可以通过name属性指定对象名
注意事项:@Resource只能使用字段和setter方法,不能注入构造方法
package spring4.cn.zi.spring.web; import javax.annotation.Resource; import org.springframework.stereotype.Controller; import spring4.cn.zi.spring.service.ServiceC; @Controller("serviceWeb") public class Web01 { /* * * * 方式一: 字段注入 * @Resource * JavaEE官方的 DI注解 * 默认按照类型注入 * 问题一:如果Spring容器中有多个相同类型的对象,如何解决 * 解决方案: * 使用 name 属性即可 * @Resource(name="service1") * * 注意: @Resource 注入必须保证Spring容器中有一个注入类型对应的对象 * */ @Resource(name="service1") private ServiceC serviceC; /* * 方式二:set方法注入,注入方式和字段注入一样 */ //@Resource(name="service2") public void setServiceC(ServiceC serviceC) { System.out.println("hello"); this.serviceC=serviceC; } public void save() { serviceC.save(); } }
依赖注入注解所在的包的位置
除了@Resource注解是Java官方的标准,内置在JDK里面以外,Spring内置实现的注解声明放在spring-beans-5.0.5.RELEASE.jar里面。
纯注解配置
问题
我们发现,之所以我们现在离不开xml配置文件,是因为我们有一句很关键的配置:
<!-- 告知spring框架在,读取配置文件,创建容器时,扫描注解,依据注解创建对象,并存入容器中 -->
<context:component-scan base-package="cn.zj.spring"></context:component-scan>
如果他要也能用注解配置,那么我们就可以脱离xml文件了。
通过@Configuration注解和@ComponentScan注解
替换XML配置文件的 @Configuration注解 |
@Configuration配置类注解,在纯注解配置中,类加了该注解,就意味着该类是Spring的配置类。该类的功能就是用于替代原来的XML配置文件。 作用: 用于指定当前类是一个spring配置类,当创建容器时会从该类上加载注解。获取容器时需要使用AnnotationConfigApplicationContext(有@Configuration注解的类.class)。 |
@ComponentScan注解 |
@ComponentScan注解扫描类,作用就是配置扫描Spring组件类的路径。功能等同原来配置文件的 --作用: 用于指定spring在初始化容器时要扫描的包。作用和在spring的xml配置文件中的: <context:component-scan base-package="cn.zj.spring"/>是一样的。 --属性: basePackages:用于指定要扫描的包。和该注解中的value属性作用一样。 |
@PropertySource注解 |
作用: 用于加载.properties文件中的配置。例如我们配置数据源时,可以把连接数据库的信息写到properties配置文件中,就可以使用此注解指定properties配置文件的位置。 属性: value[]:用于指定properties文件位置。如果是在类路径下,需要写上classpath: |
@Bean注解 |
作用: 该注解只能写在方法上,使用此方法创建一个对象,并且放入spring容器。它就相当于我们之前在xml配置中介绍的<bean标签> 属性: name:给当前@Bean注解方法创建的对象指定一个名称(即bean的id)。 |
@Import注解 |
作用: 用于导入其他配置类,在引入其他配置类时,可以不用再写@Configuration注解。当然,写上也没问题。 <import > 属性: value[]:用于指定其他配置类的字节码。 |
@Value注解
<value>
/value只能设置,标量类型=基础数据类型+包装类+String
@Value注解:注入基本数据类型以及它们的包装类和String类型数据的,支持${}注入Properties文件的键值对,等同 <proprty name=”...” value=”${Key}”>。
属性:
value:注入基本数据类型和String类型数据的
jdbc.driverClassName=com.mysql.cj.jdbc.Driver
jdbc.username=root
jdbc.password=gzsxt
jdbc.maxActive=10
package spring4.cn.zi.spring.config; import javax.sql.DataSource; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.context.annotation.PropertySource; import com.alibaba.druid.pool.DruidDataSource; /* * 1. @Configuration * 把当前类作为spring框架配置类,功能等价于 applicationContext.xml * 2. @ComponentScan(basePackages= {"cn.zj.spring"}) * 配置包扫描位置 * 等价于 <context:component-scan basePackage="cn.zj.spring"> * 3. @PropertySource("classpath:db.properties") * 读取 db.properties配置文件到Spring框架中 * 等价于 <context:property-placeholder location="classpath:db.properties"/> * 4. @Import(SpringConfigDao.class) * 引入其他配置文件 * 等价于 * <import resource="classpath:applicationContext-dao.xml"/> */ @Configuration @ComponentScan(basePackages= {"cn.zj.spring"}) @PropertySource("classpath:db.properties") @Import(SpringConfigDao.class) public class SpringConfig { /* * @Value("${properties配置文件的key或者是固定值}") * 可以使用SpringEL 表达式获取 properties配置文件key对应的value */ @Value("${jdbc.driverClassName}") private String driverClassName; @Value("jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT") private String url; @Value("${jdbc.username}") private String username; @Value("${jdbc.password}") private String password; @Value("${jdbc.maxActive}") private Integer maxActive; /* * @Bean(initMethod="init",destroyMethod="close") 返回 bean对象 * @Scope("singleton/prototype") * 等价于 *<bean id="dataSource" class="com.alibaba.druid.pool.Druid`DataSource" * init-method="init" destroy-method="close" scope="singleton/prototype"> */ //druid连接池
@Bean(name="dataSource",initMethod="init",destroyMethod="close")
public DataSource getdataSource () {
DruidDataSource ds = new DruidDataSource(); ds.setDriverClassName(driverClassName); ds.setUrl(url); ds.setUsername(username); ds.setPassword(password); ds.setMaxActive(maxActive); return ds; } }
public void testName() throws Exception { //ClassPathXmlApplicationContext ct = new ClassPathXmlApplicationContext("applicationContext.xml"); //1.创建Spring容器对象--读SpringConfig 配置文件 //ApplicationContext ct = new AnnotationConfigApplicationContext(SpringConfig.class); //2.获取UserController对象 //Web01 bean = ct.getBean("serviceWeb", Web01.class); User user = new User(); user.setName("咋回家才"); bean.insert(user); }
package spring4.cn.zi.spring.test; import javax.sql.DataSource; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import spring4.cn.zi.spring.pojo.User; import spring4.cn.zi.spring.web.Web01; //把Spring的容器提高到JVM级别,单元测放入到Spring容器中 @RunWith(SpringJUnit4ClassRunner.class) //读取配置文件 @ContextConfiguration("classpath:applicationContext.xml") public class Test01 { @Autowired private Web01 bean; @Test public void testName() throws Exception { //ClassPathXmlApplicationContext ct = new ClassPathXmlApplicationContext("applicationContext.xml"); //1.创建Spring容器对象--读SpringConfig 配置文件 //ApplicationContext ct = new AnnotationConfigApplicationContext(SpringConfig.class); //连接数据库对象 // DataSource dataSource = ct.getBean("dataSource", DataSource.class); //2.获取UserController对象 //Web01 bean = ct.getBean("serviceWeb", Web01.class); User user = new User(); user.setName("咋回家才"); bean.insert(user); } }