Spring IOC操作 之 基于注解的Bean管理

基于注解实现

基于注解方式实现对象创建和注入属性

什么是注解
  • 注解是代码的特殊标记,本身也是一个类,格式为@注解name(属性name=属性value[,…])。

  • 注解可以使用在类、方法和属性上。

  • 使用注解实现的目的是简化xml配置。

基于注解创建对象

Spring针对Bean管理中创建对象的步骤主要提供了四个注解:
@Component、@Service、@Controller、@Repository

上面的四个注解都可以创建对象(bean注入),但是后三个注解实际是@Component的衍生注解,分别推荐在service层、web层、dao层使用,四个注解功能实际是差不多的。

案例

下面我们通过一个案例来实现基于注解创建对象

引入依赖

使用注解需要额外引入依赖aop

该jar包在Spring目录下的lib文件夹中

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QvvuAqKX-1625410434275)(C:\Users\15998\AppData\Roaming\Typora\typora-user-images\image-20210703152922368.png)]

把这个jar包放到我们手动创建的lib目录中,右键点击Add as Library即可完成导入:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FaiGYqJC-1625410434277)(C:\Users\15998\AppData\Roaming\Typora\typora-user-images\image-20210703155459829.png)]

开启组件扫描

在使用注解时我们还需要开启组件扫描,告诉Spring哪一个包的类需要用到注解

首先在配置文件中引入名称空间:

xmlns:context="http://www.springframework.org/schema/context"

然后在xsi:schemaLocation处添加:

http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd

组件扫描需要用到context:component-scan标签,里面有一个base-package属性,用于指明要扫描的包(这里创建了两个包用于后续代码编写):

<context:component-scan base-package="com.atguigu.spring5.dao, com.atguigu.spring5.service"/>

这里对dao包、service包进行扫描,包名之间用逗号分隔。

当然也可以直接写所有要扫描的包的共同的上层目录,比如dao和service的共同上层目录是com.atguigu.spring5:

<context:component-scan base-package="com.atguigu.spring5"/>

完整的配置文件如下:

<?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="com.atguigu.spring5.dao, com.atguigu.spring5.service"/>

</beans>
创建类并添加创建对象注解

在service包里创建UserService类,如下所示:

@Component(value = "userService")
public class UserService {

    public void add(){
        System.out.println("service add...");
    }
    
}

上面代码在UserService类前添加了@Component注解,表示该类基于注解创建对象

当然,写法也可以变成下面这样的:

@Component
public class UserService {

    public void add(){
        System.out.println("service add...");
    }
    
}

这时注解的value的值默认是类名(当然,值的首字母会变成小写)。

编写测试代码:

@Test
public void testService(){
    ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
    UserService userService = context.getBean("userService", UserService.class);
    System.out.println(userService);
    userService.add();
}

注意,这里的getBean的第一个参数,按照使用xml创建时,该参数写的是bean的id,而使用注解时,该参数写的是注解的value,也就是类名(首字母变小写)。

点击运行,看到如下结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bD0O8FhX-1625410434278)(C:\Users\15998\AppData\Roaming\Typora\typora-user-images\image-20210703161153658.png)]

证明bean已经被创建成功。

这一步主要是名称空间的引入,不能有差错

当然,@Component注解换成其他的注解,达到的效果也是一样的:

@Service
public class UserService {

    public void add(){
        System.out.println("service add...");
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nOkHScJZ-1625410434279)(C:\Users\15998\AppData\Roaming\Typora\typora-user-images\image-20210703161751669.png)]

开启组件扫描的配置的一些细节

Spring的组件扫描默认是某个包下的全部类,但是有些时候我们只需要扫描某些类,又或者说不需要扫描某些类

use-default-filters

用于context:component-scan标签,默认值为true,如果设置其值false,表明由我们自己设置filters,也就是说由我们自己定义扫描范围

该属性结合context:include-filter使用,不结合context:exclude-filter使用。

context:include-filter

该标签表明只扫描的范围,主要属性有type,其值表示扫描的方式、expression,其值表示需要扫描的注解的类

context:exclude-filter

该标签表明不扫描的范围,主要属性有type,其值表示扫描的方式、expression,其值表示不需要扫描的注解的类,此时应该使用默认的filters。

为了方便案例展示,这里新建一个类PersonService,使用@Controller注解:

@Controller
public class PersonService {

    public void person_add(){
        System.out.println("PersonService add...");
    }

}

同时编写测试代码:

@Test
public void testService(){
    ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");

    try {
        PersonService personService = context.getBean("personService", PersonService.class);
        personService.person_add();
    } catch (Throwable e) {
        System.out.println(e);
    }

    try {
        UserService userService = context.getBean("userService", UserService.class);
        userService.add();
    } catch (Throwable e) {
        System.out.println(e);
    }
}
案例:只扫描某些类

下面是配置文件:

<?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="com.atguigu.spring5" use-default-filters="false">
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>
</beans>

上面的标签的意思是只扫描com.atguigu.spring5下的有@Controller注解标识的类,type表明根据注解来扫描,expression表明扫描的注解是@Controller。

运行结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AyMQbcRa-1625410434280)(C:\Users\15998\AppData\Roaming\Typora\typora-user-images\image-20210703190043762.png)]

可以看到注入第二个bean时抛出了错误,这是因为我们只扫描了被@Controller注解的类。

案例:不扫描某些类

注意,如果使用context:exclude-filter标签,那么context:component-scan标签就不能设置use-default-filters=“false”。因为这是扫描全部类的基础上不扫描某些类。

<?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="com.atguigu.spring5">
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>
</beans>

上面配置标识不扫描被@Controller标识的类。

运行结果如图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fP8BZGe1-1625410434281)(C:\Users\15998\AppData\Roaming\Typora\typora-user-images\image-20210703190834474.png)]

可以看到在注入第一个bean时抛出了错误,因为我们不扫描@Controller。

基于注解注入属性

Spring对于注入属性主要提供了四个注解:@Autowired、@Qualifier、@Value

java扩展包javax提供有注解:@Resource

自动注入其实跟自动装配差不多

@Autowired

根据属性类型自动注入

在dao层创建UserDao接口,内有add方法:

public interface UserDao {
    public void add();
}

UserDao实现类(记得加上@Repository注解):

@Repository
public class UserDaoImpl implements UserDao{

    @Override
    public void add(){
        System.out.println("dao add...");
    }
}

以往我们通过xml,在UserService类拿到UserDaoImpl类时,需要分别创建两个bean,用外部bean的方式去注入,现在我们只需要在UserService类中用@Autowired即可:

@Service
public class UserService {

    @Autowired
    private UserDao userDao;

    public void add(){
        System.out.println("service add...");
        userDao.add();
    }
}

add方法中调用UserDao的add方法(多态)。

注意,这个时候被注入的字段不需要添加set方法

测试方法:

@Test
public void testService(){
    ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
    UserService userService = context.getBean("userService", UserService.class);
    userService.add();
}

配置文件:

<?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="com.atguigu.spring5"/>
</beans>

下面是运行结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xNl6Kc2K-1625410434282)(C:\Users\15998\AppData\Roaming\Typora\typora-user-images\image-20210703200225451.png)]

可以看到已经实现了UserDao的注入。

@Qualifier

根据属性名称自动注入

这个注解要和@Autowired一起使用

在上面使用@Autowired的过程中,加入一个dao接口有多个实现类,那Spring就不知道我们到低需要哪一个了,这个时候就要结合@Qualifier注解来使用了。

比如我新建一个集成UserDao接口的userDaoImpl1类:

@Repository
public class UserDaoImpl1 implements UserDao{

    @Override
    public void add(){
        System.out.println("dao add...");
    }
}

这个时候如果我没加上@Qualifier注解那么就会报错:

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'userService': Unsatisfied dependency expressed through field 'userDao'; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.atguigu.spring5.dao.UserDao' available: expected single matching bean but found 2: userDaoImpl,userDaoImpl1

加上该注解,指定注入的实现类:

@Autowired
@Qualifier(value = "userDaoImpl1")
private UserDao userDao;

这个时候再运行就不会出现错误了:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Mq9Q0lpC-1625410434282)(C:\Users\15998\AppData\Roaming\Typora\typora-user-images\image-20210704213840911.png)]

@Resource

根据属性名称/属性类型自动注入,根据name判定

name属性:不设置时为根据类型注入,设置时根据其值作为名称来注入

根据名称注入

依旧沿用上面创建的项目,下面是根据名称进行注入,把UserDao上面的注解换成@Resource(name = “userDaoImpl1”),表明注入的实现类是名称为userDaoImpl1的类:

@Service
public class UserService {

    @Resource(name = "userDaoImpl1")
    private UserDao userDao;

    public void add(){
        System.out.println("service add...");
        userDao.add();
    }
}

运行结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZXEgHT6F-1625410434283)(C:\Users\15998\AppData\Roaming\Typora\typora-user-images\image-20210704214533122.png)]

没毛病。

根据类型注入

根据类型进行注入,先把UserDaoImpl1实现类删了免得产生混淆,然后修改UserService类:

@Service
public class UserService {

    @Resource
    private UserDao userDao;

    public void add(){
        System.out.println("service add...");
        userDao.add();
    }
}

运行测试代码:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lZ6hSqYs-1625410434284)(C:\Users\15998\AppData\Roaming\Typora\typora-user-images\image-20210704214723152.png)]

没问题。

可以看到根据类型注入属性比较适合懒人,在实际开发中最好是要求一个接口只有一个实现类,如果要求多个实现类那最好对接口进行再分隔,这样就能主动避免冲突问题,也不需要麻烦地去定义一些注解的属性。

@Value

注入普通类型属性,比如String

@Value(value = "abd")
private String name;

上面的代码为name属性赋值“abc”。

纯注解开发

在上面使用注解的案例中我们可以发现,要想实现这些操作的前提还是得有一个xml文件来扫描整个项目,现在我们需要的是去掉xml,之依靠注解来进行开发。

想要去除xml,我们得有一个能替代配置文件的东西——配置类。

配置类主要用到@Configuration:表示该类是配置类、@ComponentScan:表示要扫描的包

在开发目录下创建一个config包(与启动类同级),用于存放配置类,在包下创建SpringConfig类(名字随便起),然后类上添加注解@Configuration和@ComponentScan,其中@ComponentScan需要配置参数,值为扫描的包,下面是完整的配置类:

@Configuration
@ComponentScan(basePackages = {com.atguigu.spring5})
public class SpringConfig {
}

是的你没看错,配置类啥也不需要写,老工具人了

这个时候你就可以删掉xml配置文件了,当然,这个时候运行你会发现项目报错,这是因为测试方法的原因。

此时测试方法还用着xml文件获取bean

重写测试方法:

@Test
public void testService2(){
    ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
    UserService userService = context.getBean("userService", UserService.class);
    userService.add();
}

观察上面的代码我们可以发现,new的对象不在是ClassPathXmlApplicationContext,而是AnnotationConfigApplicationContext,里面的参数为configname.class,也就是SpringConfig.class。

下面运行测试方法:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uzgZMjQg-1625410434284)(C:\Users\15998\AppData\Roaming\Typora\typora-user-images\image-20210704224858388.png)]

不错,完美

发现没,如果你是从Springboot入门的,那么现在已经有Springboot的味道了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值