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销毁了
网友评论