9 - SpringIOC的注解应用

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();
    }*/
}

最后进行测试,同上,结果正常。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值