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
posted @ 2022-05-06 21:35  努力的达子  阅读(66)  评论(0编辑  收藏  举报