1、使用注解的方式注册bean到IOC容器中
2、定义扫描包时要包含的类和不要包含的类
3、 使用@AutoWired进行自动注入
使用注解的方式实现自动注入需要使用@AutoWired注解。
、
4、@Autowired可以定义在方法上
当我们查看@Autowired直接的源码的时候,发现,此注解不仅可以使用在成员变量上,也可以使用在方法上。
5、自动装配的注解@Autowired , @Resource
上面1-5的代码示例:
controller层:PersonController类
package com.zhoulz.controller;
import com.zhoulz.dao.PersonDao;
import com.zhoulz.service.PersonService;
import com.zhoulz.service.PersonService2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.ImportResource;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Controller;
//@Controller(value = "personController02") //可以改写自定义的id,然后进行识别
//然后在测试类中要进行对应的更改,不然会报错“No bean named 'personController' available”
@Controller
@Scope(value = "singleton") //—— 单例
//@Scope(value = "prototype") //—— 多例
public class PersonController {
/**
* 通过@Autowired注解能够完成自动注入的功能。
* 是按照什么方式进行自动注入的呢?
* :默认情况下是按照ByType的方式来进行装配的,如果找到直接赋值,找不到报错。
* 如果找到多个类型一样的(怎么一样?继承即可)bean对象,此时会按照id(/名称)来进行查找,默认的id是类名首字母小写,
* 如果找到了直接注入,找不到则报错。
* (继承的时候,即多个类型一样时:见下例;private PersonService personService2; —— 调用的是 PersonService2
* private PersonService personService; —— 调用的是 PersonService (按照id) )
* 不存在 多个类型一样时 : private PersonService2 personService; —— 调用的是 PersonService2
* private PersonService personService; —— 调用的是 PersonService (按类型ByType)
*
* 即:先按 type 查找,type一样的话再按 id !!!
*
* 但是,多个类型一样的情况下,如果想 private PersonService personService2;调用 PersonService(原本是调用的 PersonService2,见上),怎么办?
* 见下:在private PersonService personService2;的上面再加一个注解: @Qualifier("personService")
* (即:如果你想通过名字id进行查找,可以自己规定名称,使用注解@Qualifie)
*
* 当 @Autowired 添加到方法上的时候,此方法在创建对象的时候会默认调用,同时方法中的参数会进行自动装配
* @Qualifier 注解也可以定义在方法的参数列表中,可以指定当前属性的id名称
*
* 使用@Resource可以完成跟 @Autowired 相同的功能,但是要注意他们之间的区别
* 1、@Resource是jdk提供的功能,@Autowired是spring提供的功能;
* 2、@Resource可以在其他框架中使用,而@Autowired只能在spring中使用,
* 换句话说,@Resource扩展性好,而@Autowired 支持的框架比较单一
* 3、@Resource是按照名称进行装配的,如果名字找不到,那么就使用类型;
* 而@Autowired是按照类型进行装配的,如果类型找不到,那么就使用名字进行查找;(第3点,有点绕了)
*/
//因为引入外部对象了,所以加上@Autowired。PersonService类中,同理。
//@Resource //发现没有@Resource ???
@Autowired
@Qualifier("personService")
private PersonService personService2;
//private PersonService2 personService; //这样的话,调用的是哪个(PersonService or PersonService2)?(因为原来说是按照首字母小写)
public void save(){
personService2.save();
}
@Autowired
public void test(@Qualifier("personDao")PersonDao personDao123){ //还可以传入参数,这里传的是 PersonDao 对象(然后调用了PersonDao里面的方法)
System.out.println("test---- 当 @Autowired 添加到方法上的时候,此方法在创建对象的时候会默认调用");
personDao123.update();
}
}
dao层:PersonDao类
package com.zhoulz.dao;
import org.springframework.stereotype.Repository;
@Repository
public class PersonDao {
//PersonController-控制层,PersonService-业务逻辑层,PersonDao-数据层
//首先,因为:PersonController中定义了一个PersonService的对象,然后PersonService中定义了一个PersonDao的对象
//然后:
//现在PersonDao中有一个save()方法,
//然后按顺序,PersonService中也有一个save()方法,并且在这个save()方法中调用了PersonDao中save()方法
//然后,同上,PersonController中也有一个save()方法,并且在这个save()方法中调用了PersonService中save()方法
public void save(){
System.out.println("保存成功!");
}
//当像上面这样一层一层写完之后,每次获取对象的时候,我们应该获取的是PersonController对象
//然后通过PersonController对象就可以完成对save()方法的调用了
//举例,见测试类test02()
public void update(){
System.out.println("更新成功!!!");
}
}
service层:PersonService、 PersonService2
package com.zhoulz.service;
import com.zhoulz.dao.PersonDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class PersonService {
//因为引入外部对象了,所以加上@Autowired
@Autowired
private PersonDao personDao; // 这些对象没有进行new的创建过程
//如果想有这些对象的话,怎么办? —— 需要自动注入的功能,来进行最基本的实现
public void save(){
System.out.println("PersonService-----");
personDao.save();
}
}
package com.zhoulz.service;
import com.zhoulz.dao.PersonDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class PersonService2 extends PersonService{ //继承,则两个类型是一样的
@Autowired
private PersonDao personDao;
@Override
public void save(){
System.out.println("PersonService2-----");
personDao.save();
}
}
resources下的:applicationContext.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
">
<!--在之前的操作中,我们都是使用bean标签的方式向IOC容器中注册bean,下面使用注解的方式:
当使用注解的时候,可以在当前类的上面添加某些注解标识:
@Component:组件,理论上可以在任意的类上进行添加,在扫描的时候都会完成bean的注册
@Controller:放置在控制层,用来接收用户的请求 (所以要在控制层controller下的PersonController类上添加“@Controller” )
@Service:放置在业务逻辑层(同上,在service下的类上面添加上“@Service”)
@Repository:放置在数据访问层(同上,在dao下的类上面添加上“@Repository”)
这四个注解写在类上面的时候都可以完成注册bean的功能,但是这些规定并不是spring识别的标识
在spring程序运行过程中,不会这四个注解做任何区分,看起来都是一样的,都会完成bean的注册
在实际的开发过程中,最好能分清楚,提高代码的可读性。
所以,最偷懒的方式是,在所有需要注册的bean类上添加@Component注解
注意:在使用注解的时候,还需要告诉spring应该从哪个包开始扫描(见下面的) —— 一定不能忘记!!!
(一般在定义的时候都写上相同包的路径)
注意: 需要导入context命名空间
在使用注解的时候,没有定义id和class,那么如何根据id来进行识别?
:默认是把当前类的名称的首字母小写之后作为id来识别的。
那可以换其他的名字吗?举例,见PersonController类中
即:如果需要改变名称,那么需要在对应注解中添加参数值value来完成修改名字的目的
@Scope注解可以声明当前类是单例还是多例
-->
<!--举例:以前的方式-->
<!--<bean id="personController" class="com.zhoulz.controller.PersonController"></bean>-->
<!--告诉spring应该从哪个包开始扫描,见下,com.zhoulz下的包都会被扫描-->
<!--<context:component-scan base-package="com.zhoulz"></context:component-scan>--><!--先注释,下面有-->
<!--如下,则只会扫描controller下的包-->
<!--<context:component-scan base-package="com.zhoulz.controller"></context:component-scan>-->
<!--但是,有些情况下,就想想扫描某些包和不想扫描某些包,该怎么设置?-->
<context:component-scan base-package="com.zhoulz">
<!--下面输入“<”后,会自动弹出两个选项:
当定义好注解的扫描路径之后,可以做更细粒度的控制,可以选择扫描哪个注解,也可以选择不扫描哪个注解
include-filter:表示要包含扫描的注解,一般不用,但是如果引入的第三方包中包含注解,此时就需要使用此标签俩进行标识。
exclude-filter:表示要排除扫描的注解
其中,有如下参数: type:规则的类型
expression:表达式
type下的选项有:
assignable:可以指定对应的类的名称,但是必须是完全限定名!!!
annotation:按照注解来进行排除,但是表达式中必须是注解的完全限定名!!!
regex:使用正则表达式的方式 —— 一般不用
aspectj:使用切面的方式 —— 一般不用
custom:使用自定义的方,可以自己定义自己的筛选规则(但是要有继承关系) —— 一般不用
举例:见下
-->
<!--如,排除controller下的PersonController类:-->
<!--assignable方式:-->
<!--<context:exclude-filter type="assignable" expression="com.zhoulz.controller.PersonController"/>-->
<!--annotation方式:-->
<!--org.springframework.stereotype(注解的完全限定名)在哪,在PersonController类中,找到“@Controller”,点击进入,最上面包package的名称即是-->
<!--<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>-->
<!-- include-filter 中参数的用法同 exclude-filter,但是一般不用,因为上面定义好了扫描包后,包下的就都可以被扫描到了 -->
</context:component-scan>
<!--以前有写过的,autowire参数— 按什么进行注入 -->
<bean id="personController" class="com.zhoulz.controller.PersonController" autowire="default"></bean>
<!--现把该标签/参数 单独抽象出来,换另外一种写的方式:-->
</beans>
测试类:引入了junit单元测试(加上@Test注解)
import com.zhoulz.controller.BaseController;
import com.zhoulz.controller.PersonController;
import com.zhoulz.service.PersonService;
import com.zhoulz.service.StudentService;
import com.zhoulz.service.TeacherService;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* 注意:给测试类起名字的时候,千万不要定义成Test
* 测试的方法不可以有参数,不可以有返回值
*/
public class MyTest {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//这句话可以挪出来,这样下面的每个方法中就不用写这句话了。
@Test
public void test01(){
//ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//以前的方式—>注册bean (bean标签的方式)
PersonController personController = context.getBean("personController", PersonController.class);
//PersonController personController = context.getBean("personController02", PersonController.class);
System.out.println(personController);
//组件默认情况下都是单例的,想要改成多例需要在注解下添加@Scope注解——见PersonController类
PersonController personController2 = context.getBean("personController", PersonController.class);
System.out.println(personController == personController2);
PersonService personService = context.getBean("personService", PersonService.class);
System.out.println(personService);
}
@Test
public void test02(){
//ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//通过PersonController对象就可以完成对save()方法的调用
PersonController personController = context.getBean("personController", PersonController.class);
personController.save(); //会报空指针异常—— NullPointerException
//点击进去,发现是PersonController下的personService 空了
//说明在当前这个类(PersonController)中,没有对它(personService)进行具体的new创建对象的过程
//这个过程该由谁完成?
// —— 由spring完成具体的操作
// —— 换种写法,在PersonController类中:PersonService对象的创建/定义上面加上一行: @Autowired —— 自动装配的注解
//然后同理,在PersonService类中:PersonDao对象的创建/定义上面也加上一行:@Autowired
//此时,再运行就正常了。
}
@Test
public void test03(){
// ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
BaseController baseController = context.getBean("baseController", BaseController.class);
baseController.save();
//baseController.save2();
}
@Test
public void test04(){
//ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
StudentService studentService = context.getBean("studentService", StudentService.class);
studentService.save();
TeacherService teacherService = context.getBean("teacherService", TeacherService.class);
teacherService.save();
}
}
附: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>com.zhoulz</groupId>
<artifactId>spring_annotation_study</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<!--添加的maven依赖-->
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.3.RELEASE</version>
</dependency>
<!--添加junit的依赖-->
<!-- https://mvnrepository.com/artifact/junit/junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
6、泛型依赖注入
先写一个基本的案例:
bean下先有两个普通的类:Teacher、Student
package com.zhoulz.bean;
public class Teacher {
}
package com.zhoulz.bean;
public class Student {
}
然后在dao层,定义一个(抽象)类:BaseDao,里面定义一个方法
package com.zhoulz.dao;
public abstract class BaseDao<T> { //定义了一个泛型
public abstract void save();
}
接着,在dao层再创建两个最基本的(BaseDao类的)子类实现:TeacherDao、StudentDao(即两个实现类)
package com.zhoulz.dao;
import org.springframework.stereotype.Repository;
@Repository
public class TeacherDao extends BaseDao {
@Override
public void save(){
System.out.println("保存老师");
}
}
package com.zhoulz.dao;
import org.springframework.stereotype.Repository;
@Repository
public class StudentDao extends BaseDao {
@Override
public void save() {
System.out.println("保存学生");
}
}
如上,当定义好两个具体的实现类后,下一步就可以进行基本的调用了:
这里不用service层(假设没有),用controller层:
在controller层下创建类:BaseController,然后在里面想调用Teacher和Student:
package com.zhoulz.controller;
import com.zhoulz.dao.BaseDao;
import com.zhoulz.dao.StudentDao;
import com.zhoulz.dao.TeacherDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
@Controller
public class BaseController {
@Autowired
TeacherDao teacherDao;
@Autowired
StudentDao studentDao;
public void save(){
teacherDao.save();
}
public void save2(){
studentDao.save();
}
}
最后进行测试:
@Test
public void test03(){
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
BaseController baseController = context.getBean("baseController", BaseController.class);
baseController.save();
baseController.save2();
}
结果,能够正常输出。
上述代码是我们之前可以完成的功能,但是可以思考:Controller层的代码是否能够改写??
因为,在BaseController里写了两个dao,一个TeacherDao、StudentDao,感觉不太合适。
而这两个dao都归属于BaseDao,那能不能在BaseController中改一种写法(结合泛型):
首先,
注:BaseDao中定义了一个泛型,而TeacherDao、StudentDao是继承自它的。
所以,分别在TeacherDao、StudentDao中的BaseDao后面添加上具体的泛型类型Teacher和Student:
同时,BaseController中进行如下改写
package com.zhoulz.controller;
import com.zhoulz.dao.BaseDao;
import com.zhoulz.dao.StudentDao;
import com.zhoulz.dao.TeacherDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
@Controller
public class BaseController {
/*@Autowired
TeacherDao teacherDao;
@Autowired
StudentDao studentDao;*/
//改一种写法:
@Autowired
BaseDao baseDao;
public void save(){
baseDao.save();
}
/*public void save(){
teacherDao.save();
}
public void save2(){
studentDao.save();
}*/
}
那么有一个问题:BaseController中进行 BaseDao baseDao; 的调用时,怎么确定调用的是哪个dao(TeacherDao??StudentDao??)怎么确定baseDao传入的是啥??
运行,发现报错: (原因:通过类型和名称都找不到)
根据报的异常:“No qualifying bean of type 'com.zhoulz.dao.BaseDao<?>' available: expected single matching bean but found 2: studentDao,teacherDao”
所以还要加一个具体的限制(泛型???),限制具体是哪一个dao的类型,
所以这个泛型应该怎么加?往哪加?
:(在上面的)BaseController中,在 BaseDao baseDao; 这一行加上泛型:
//改一种写法:
@Autowired
//BaseDao<Student> baseDao;
BaseDao<Teacher> baseDao;
BaseController 具体代码:
package com.zhoulz.controller;
import com.zhoulz.bean.Student;
import com.zhoulz.bean.Teacher;
import com.zhoulz.dao.BaseDao;
import com.zhoulz.dao.StudentDao;
import com.zhoulz.dao.TeacherDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
@Controller
public class BaseController {
/*@Autowired
TeacherDao teacherDao;
@Autowired
StudentDao studentDao;*/
//改一种写法:
@Autowired
BaseDao<Student> baseDao; //打印学生
//BaseDao<Teacher> baseDao; //打印老师
public void save(){
baseDao.save();
}
//换种写法,见上面
/*public void save(){
teacherDao.save();
}
public void save2(){
studentDao.save();
}*/
}
即,通过泛型 —— 找到具体的一个对象。
者或,换一种写法(像以前一样):(不用basecontroller了)
首先,
在service层中,分别创建StudentService、TeacherService:
package com.zhoulz.service;
import com.zhoulz.dao.StudentDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class StudentService {
@Autowired
private StudentDao studentDao;
public void save(){
studentDao.save();
}
}
package com.zhoulz.service;
import com.zhoulz.dao.TeacherDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class TeacherService {
@Autowired
private TeacherDao teacherDao;
public void save(){
teacherDao.save();
}
}
然后进行测试:
@Test
public void test04(){
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
StudentService studentService = context.getBean("studentService", StudentService.class);
studentService.save();
TeacherService teacherService = context.getBean("teacherService", TeacherService.class);
teacherService.save();
}
结果正常。
但是,这种每个service都创建一个单独的类如:(如:StudentService、TeacherService),比较麻烦,能不能有一种简单的写法呢?
:在service层创建一个抽象的东西(类): BaseService (带泛型的):
里面定义 BaseDao 的对象(注意,BaseDao是带泛型的),并调用 BaseDao 的save()方法
package com.zhoulz.service;
import com.zhoulz.dao.BaseDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class BaseService<T> {
@Autowired
private BaseDao<T> baseDao;
public void save(){
baseDao.save();
}
}
然后,改写刚才创建的 StudentService、TeacherService:(去继承BaseService)
(注意:记得注销BaseService上的@Service注解,因为其子类已经注入了,父类没必要再注入,并且如果注入了会报错,因为是相同的类型)
package com.zhoulz.service;
import com.zhoulz.bean.Student;
import com.zhoulz.dao.StudentDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class StudentService extends BaseService<Student> {
//改写:上面先加上对BaseService 的继承(带泛型的)
/*@Autowired
private StudentDao studentDao;
public void save(){
studentDao.save();
}*/
}
package com.zhoulz.service;
import com.zhoulz.bean.Teacher;
import com.zhoulz.dao.TeacherDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class TeacherService extends BaseService<Teacher> {
/*@Autowired
private TeacherDao teacherDao;
public void save(){
teacherDao.save();
}*/
}
最后进行测试,同上,结果正常。