5.通过注解创建组件
1.@Controller:控制器;我们推荐给控制器层(servlet)的组件加这个注解
2.@Service:业务逻辑;我们推荐业务逻辑层的组件添加这个注解
3.@Repository:给数据库层(持久层,dao层)的组件添加这个注解
4.@Component:给不属于以上几层的组件添加这个注解
示例:@Controller
1.添加标签
@Controller--------------------->这个注解,spring在扫描时会创建该类实例,放入spring容器中,id为(默认类名首字母小写)
public class BookServlet {
}
2.在spring配置文件中添加
<?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:p="http://www.springframework.org/schema/p"
xmlns:util="http://www.springframework.org/schema/util"
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="cn.com.wmd"></context:component-scan>----->添加包扫描
</beans>
测试类:
public class Test {
private static ApplicationContext ioc= new ClassPathXmlApplicationContext("ioc.xml");
public static void main(String[] args) {
Object bean1=ioc.getBean("bookServlet");---------------->此处的id是BookServlet的类名首字母小写
Object bean2=ioc.getBean("bookServlet");
System.out.println(bean1==bean2);---------------------->输出为true,因为默认是单例的
}
}
注意:标签必须添加aop包,要不会报错
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.3.13.RELEASE</version>
</dependency>
1.更改默认的id值
@Controller("book")------->格式为("自定义id值")
public class BookServlet {
}
测试代码:
private static ApplicationContext ioc= new ClassPathXmlApplicationContext("ioc.xml");
public static void main(String[] args) {
Object bean1=ioc.getBean("book");------------->根据book获取容器中的BookServlet组件
Object bean2=ioc.getBean("book");
System.out.println(bean1==bean2);----->输出:true
}
2.更改默认的单实例,改为多实例
@Controller("book")
@Scope(value = "prototype")----->添加scope标签,并赋值prototype(多实例)
public class BookServlet {
}
测试代码:
private static ApplicationContext ioc= new ClassPathXmlApplicationContext("ioc.xml");
public static void main(String[] args) {
Object bean1=ioc.getBean("book");
Object bean2=ioc.getBean("book");
System.out.println(bean1==bean2);------->输出为false
}
注意:
bean标签和注解的用法:
1.标签不能把非自定义的类(例如maven下载的jar包中的摸一个类)加入容器,因为不能点进去到jar包中加注解
2.bean标签可以给所有的类(包括maven下载的依赖包中的类)加入容器
3.注解方便,bean全面,所以两者配合
2.使用exclude-filter和include-filter指定扫描包时组件的创建规则
1.exclude-filter的使用(指定扫描包时不包含的类)
1.1 exclude-filter type="annotation":按照组件进行排除
spring配置文件写法:
<context:component-scan base-package="cn.com.wmd">
<!--
扫描的cn.com.wmd时候可以排除一些不要的组件:
type=annotation:指定排除规则,按照注解进行排除,标注了指定注解的组件不要
expression=“”:注解的全类名
-->
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
实体类的写法:
@Controller("book")
@Scope(value = "prototype")
public class BookServlet {
}
测试类:
private static ApplicationContext ioc= new ClassPathXmlApplicationContext("ioc.xml");
public static void main(String[] args) {
Object bean1=ioc.getBean("book");----------->会获取报错,因为该类跳过了,没有注册在spring容器中
Object bean2=ioc.getBean("book");
System.out.println(bean1==bean2);
}
结论:spring在扫描cn.com.wmd包时会跳过@Controller注解的类
1.2exclude-filter type="assignable":按照具体类进行排除
spring的配置文件写法:
<context:component-scan base-package="cn.com.wmd">
<!--
type="assignable":指定排除某个具体的类,按照类排除
expression="":类的全类名
-->
<context:exclude-filter type="assignable" expression="cn.com.wmd.control.BookServlet"/>
</context:component-scan>
实体类的写法:
@Controller("book")
@Scope(value = "prototype")
public class BookServlet {
}
测试类:
private static ApplicationContext ioc= new ClassPathXmlApplicationContext("ioc.xml");
public static void main(String[] args) {
Object bean1=ioc.getBean("book");----------->会获取报错,因为该类跳过了,没有注册在spring容器中
Object bean2=ioc.getBean("book");
System.out.println(bean1==bean2);
}
结论:spring在扫描cn.com.wmd包时会跳过cn.com.wmd.control.BookServlet的类
以下三种不常用
1.3type="aspectj":后来aspectj表达式
1.4type="custom":自定义一个TypeFilter接口的实现类,通过重写里面的方法决定哪些组件是否注入
1.5type="regex":可以写正则表达式
2.include-filter的使用(指定扫描包时要包含的类)
前提:因为默认是扫描全部的带注解类,若想要只扫描带指定注解的类,需要:use-default-filters="false",更改默认的扫描规则
2.1type="annotation":包含指定注解的类
<context:component-scan base-package="cn.com.wmd" use-default-filters="false">---->必须加上use-default-filters="false",更改默认的扫描规则
<!--
只扫描哪些组件,默认是全部扫描进来
一定要禁用默认的过滤规则才行:use-default-filters="false"
-->
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
3.@Autowired标签的使用
3.1初始用
需求:模拟页面保存数据
1.Servlet类
@Controller("book")
public class BookServlet {
@Autowired------------------------->在需要自动装配的bean上加上@Autowired标签
private BookService bookService;
public void doGet(){
System.out.println("模拟servlet的doGet方法");
bookService.bookServie();
}
}
2.Service业务层代码
@Service
public class BookService {
@Autowired------------------------->在需要自动装配的bean上加上@Autowired标签
private BookDao bookDao;
public void bookServie(){
System.out.println("正在调用BookService的booService方法");
bookDao.saveBook();
}
}
3.持久层代码
@Repository
public class BookDao {
public void saveBook(){
System.out.println("正在调用bookDao的saveBook方法");
}
}
4.测试代码;
public class Test {
private static ApplicationContext ioc= new ClassPathXmlApplicationContext("ioc.xml");
public static void main(String[] args) {
BookServlet bookServlet=ioc.getBean(BookServlet.class);
bookServlet.doGet();
}
}
可以输出:
模拟servlet的doGet方法
正在调用BookService的booService方法
正在调用bookDao的saveBook方法
结论:
1.使用@Autowired标签装配时,spring容器中必须要有这个组件,比如:
@Autowired
private BookService bookService;
若容器中没有BookService这个组件,会自动装配失败,报空指针
2.不像spring配置文件中的Autowire属性,需要get/set方法(spring配置文件中使用Autowire自动注入时,必须要有对应属性的get/set方法,其次容器中必须要有该组件)
3.2@Autowired原理
@Autowired
private BookService bookService;
1.先按照类型去容器中找对应的组件:bookService=ioc.getBean(BookService.class)
1.1找到一个:赋值
1.2没找到:抛出异常 NoSuchBeanDefinitionException
1.3按照类型找到多个
1.3.1按照变量名(bookService)作为id去容器中继续查找
例如:
@Service
public class BookService {
@Autowired
private BookDao bookDao;
public void bookServie(){
System.out.println("正在调用BookService的booService方法");
bookDao.saveBook();
}
}
//BookService的子类
@Service
public class BookServiceExt extends BookService{
@Override
public void bookServie() {
System.out.println("BookServiceExt重写的bookServie方法");
}
}
这时容器通过BookService类型查找时会找到多个,但是在调用时仍掉用了BookServiced的bookServie方法
因为找到多个时,会根据变量名作为id继续去容器中查找组件
若此时:
将
@Autowired
private BookService bookService;
改为:
@Autowired
private BookService bookServiceExt ;
再调用时调用的是子类BookServiceExt的bookServie方法,因为标签创建的组件id默认是类名首字母小写
2.@Autowired加在方法上:
则:
1.这个方法会在bean创建的时候自动运行
2.这个方法的每个参数都会自动注入值
例如:
@Controller("book")
public class BookServlet {
@Autowired---------------------->在方法上加上@Autowired标签
public void doGet(BookService bookService, @Qualifier("bookDao") BookDao bookDao){
System.out.println("模拟servlet的doGet方法");
bookService.bookServie();
}
}
测试代码:只是启动容器,并没有手动调用方法,已经输出方法内容
public class Test {
private static ApplicationContext ioc= new ClassPathXmlApplicationContext("ioc.xml");
public static void main(String[] args) {
}
}
输出:
模拟servlet的doGet方法
正在调用BookService的booService方法
正在调用bookDao的saveBook方法
结论:当容器扫描包创建组件时,会调用带@Autowired的方法,并将参数按类型注入,参数上也可以加上@Qualifier("id值"),注入特定的组件
3.3@Qualifier标签的使用(根据id去找)
@Autowired
@Qualifier("bookService")
private BookService bookService;
public void doGet(){
System.out.println("模拟servlet的doGet方法");
bookService.bookServie();
}
结论:
查找步骤
1.先按照类型在容器中查找该组件
2.查找到多个:
再根据具体的id(bookService)去找到的多个组件中选择,并注入,
若根据类型找到的多个组件中,没有该id的组件,抛出异常 NoSuchBeanDefinitionException
3.找到一个,并且容器中的id和@Qualifier中的id不符时会报错!
3.4@Autowired和@Resource和@Inject都是自动装配的意思
1.@Autowired是spring自己的注解,最为强大
2.@Resource注解是(import javax.annotation.Resource;)j2ee,java自己的注解
@Resource
private BookDao bookDao;
也可以完成自动装配
这两者的区别:
一个是spring自己的注解(@Autowired),一个是java的注解(@Resource),但因为@Resource是java的注解,意味着扩展性更强
当切换到另外一种容器@Resourc仍可以继续使用,@Autowired会失去作用
@Autowired标签的父子类继承关系:
前提:两个成继承关系的service类,并且子类重写了父类的service方法
代码:
父类代码:
public class BookService {
public BookService() {
System.out.println("BookService的无参构造器!");
}
public void service() {
System.out.println("bookService的service方法!");
}
}
子类代码:
@Service------------------>子类加注解,父类并没有,即spring容器中只有子类对象!
public class BookServiceExt extends BookService {
public BookServiceExt() {
System.out.println("BookServiceExt的无参构造器!");
}
@Override
public void service() {
System.out.println("BookServiceExt的service方法!");
}
}
测试代码:
@ContextConfiguration(locations = "classpath:ioc.xml")
@RunWith(SpringJUnit4ClassRunner.class)
public class Test {
@Autowired
BookService bookService;
@org.junit.Test
public void test01() {
bookService.service();
}
}
输出:
BookService的无参构造器!
BookServiceExt的无参构造器!
BookServiceExt的service方法!
结论:虽然父类没有在容器中,但是可以利用父类接收子类的spring容器中的组件,相当于向上转型:
BookService bookService=new BookServiceExt();注入的是spring中子类的组件!
2.当父子类都在容器中时:
具体父子类代码:
@Service
public class BookService {}
@Service
public class BookServiceExt extends BookService {}
父子类均有注解,即父子类都在容器中!
测试类代码:
@ContextConfiguration(locations = "classpath:ioc.xml")
@RunWith(SpringJUnit4ClassRunner.class)
public class Test {
@Autowired
BookService bookService;----->此时的名称为:bookService和父类的id一样
@org.junit.Test
public void test01() {
bookService.service();
}
}
输出:
BookService的无参构造器!
BookService的无参构造器!
BookServiceExt的无参构造器!
bookService的service方法!
结论:发现其调用的是父类的service方法
若测试类的方法为:
@ContextConfiguration(locations = "classpath:ioc.xml")
@RunWith(SpringJUnit4ClassRunner.class)
public class Test {
@Autowired
BookService bookServiceExt;---->此处的类型是BookService,但参数名称为bookServiceExt
@org.junit.Test
public void test01() {
bookServiceExt.service();
}
}
输出:
BookService的无参构造器!
BookService的无参构造器!
BookServiceExt的无参构造器!
BookServiceExt的service方法
结论:发现其调用的是子类的service方法
结论:
子类有两种类型,第一是父类类型,第二是自己的类型,
综上所述,@Autowired先根据类型去找,如果找到一个,就注入,找打多个爱根据具体的id值去容器中查找对应的组件,
找到就注入,找不到就报NoUniqueBeanDefinitionException