> 文章列表 > Spring更简单的存取方法

Spring更简单的存取方法

Spring更简单的存取方法

Spring存对象

在Spring的创建和使用篇章里,提到了用bean标签来将对象标识到Spring里面,但是这样的方法过于麻烦,下面我们来介绍使用类注解来存储对象。

五大类注解存对象

Spring里面有五大类注解:

@Controller、@Service、@Repository、@Component、@Configuration

在介绍类注解的使用之前,要先说明一下bean的自动命名规则,为后面使用对象做准备。

五大类注解中bean的命名规则

我们在idea中的搜索里,搜索 AnnotationBeanNameGenerator 这个类,里面有一个 buildDefaultBeanName 方法,如下:

 在这个红框的方法里面描述了一个命名方式,并且这个方法是在jdk里面的,如下:

字符串为 null 和 空字符串 的情况会返回本身;

当字符串超过1个字符时,如果首字符和第二个字符都为大写,此时返回原字符串;

除上述情况之外,将字符串的首字符变为小写,返回字符串。

总结:如果类名的首字符和第二个字符都为大写,bean的名字为类名;

除此之外,bean的名字为,首字母变小写的类名。

我们用个例子展示一下:

    public static void main(String[] args) {String className1 = "UserName";String className2 = "UClass";System.out.println("UserName -> " + decapitalize(className1));System.out.println("UClass -> " + decapitalize(className2));}

此时它的输出为:

 和我们前面的结论相同。

Content标签

Spring为了提高访问类的效率,提供了一个Content标签(Content标签可以有多个),在标签里面,将使用类注解的类,所对应的包名写入其中,如下:

<?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:content="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 https://www.springframework.org/schema/context/spring-context.xsd"><!-- 在 base-package 里填入包名 --><content:component-scan base-package="Demo"></content:component-scan>
</beans>

加入标签后,里面的包及其子包里面的类,只要加了类注解,就都被存储到Spring里面了。

使用类注解存储对象

上述的五个类注解的使用方法相同,此处就展示一个。

//该类属于 Demo 包,和前面的Content标签的包名相对应。
@Controller
public class StudentController {public void sayHi() {System.out.println("do StudentController sayHi");}
}

然后我们使用这个类(此处的使用为复杂版本),bean的名字规范前面已经说过,此处不说明为什么是“studentController”了。

public static void main(String[] args) {ApplicationContext context =new ClassPathXmlApplicationContext("spring-config.xml");StudentController studentController = 
context.getBean("studentController", StudentController.class);studentController.sayHi();}

输出效果:

 输出和我们的预期一样。

不过在使用类注释的时候要注意几个点:

1. 在同一个包下(包括子包),在加类注释时,最好不要有两个类名相同的类,如果一定要有,要在注释后面加上value来区分。

如下:

@Controller(value = "UserController2")
public class UserController {public void sayHi() {System.out.println("Demo.Controller -> do UserController sayHi");}
}

这样在使用该对象时,在id栏填入这个value的值即可。

但是要注意:相同的类在使用时不能导入多个包,要至少有一个是使用包名.类名的方式来获取该类的对象。

public static void main(String[] args) {ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");UserController userController = 
context.getBean("userController", UserController.class);userController.sayHi();//使用 包名.类名 的方式获取重名的类的对象Demo.Controller.UserController userController1 =context.getBean("UserController2", Demo.Controller.UserController.class);userController1.sayHi();}

2. bean标签和Content标签可以同时使用。

3. 没有加类注解的类,即使属于Content的包名下,也不会存储到Spring里。

4. 使用了类注解,但是该类不属于Content标签的包名下,也不会存储到Spring里。

五大类注解的区别和规范

我们来看一下这个五个类注解的源码:
@Controller

 @Service

@Repository

@Configuration

可以看到 @Controller、@Service、@Repository、@Configuration这四个类注解都实现了@Component 注解。

也就说明,这五个注解的功能其实相差不大,但是为什么要分为五个注解呢?

因为需要类注解标识自己对应的类的功能。

@Controller -- 验证用户请求的正确性(安保)

@Service -- 编排和调度具体的执行方法(分配)

@Repository -- 和数据库做交互

@Component -- 组件,工具类

@Configuration -- 配置项,项目中的一些配置

在JavaEE的标准分层中规定,至少要有三层:

1. 控制层、2. 服务层、3. 数据持久层

如下图:

 上述的流程在Java开发手册里面也有说明,和上面基本相同,如下:

 总结:每个类注解都可以将对象存储到Spring里,分出五大类注解的原因是为了让程序员更好区分每个类的功能。

使用@Bean去存储对象

和前面的五大类注解不同,@Bean是一个方法注解,当这个方法返回了一个对象,并且该方法使用了@Bean注解,此时这个返回的对象就存储到了Spring里面。

重点一、只有当这个类使用了五大类注解时,里面的@Bean注解才会生效

如下:

@Controller
public class UserBeans {@Beanpublic User UserById() {User user = new User();user.setUid(1);user.setUsername("张三");user.setPassword("123456");user.setAge(18);return user;}
}

@Bean的命名规则和五大类注解的命名规则不同,它的bean名字默认是方法名,他还可以起别名/起多个别名,并且起了别名之后就无法使用方法名了。

@Controller
public class UserBeans {//多个别名 name/value都可以@Bean(name = {"user1", "u1"})public User UserById() {User user = new User();user.setUid(1);user.setUsername("张三");user.setPassword("123456");user.setAge(18);return user;}//单个别名 name/value都可以@Bean(value = "user")public User UserByName() {User user = new User();user.setUid(2);user.setUsername("李四");user.setPassword("123456");user.setAge(20);return user;}
}

重点二、如果多个类里面有多个方法,他们的返回类型、方法名相同,此时会发生方法覆盖,也就是后存储的类会覆盖掉前一个存储的类,若不想被覆盖就进行@Bean的重命名(起别名)。

如果想控制存储的顺序,可以使用@Order注解

@Order(1)
@Controller
public class UserBeans {@Beanpublic User UserByName() {User user = new User();user.setUid(2);user.setUsername("李四");user.setPassword("123456");user.setAge(20);return user;}
}//另一个类中
@Order(10)
@Controller
public class UserBeans2 {@Beanpublic User UserByName() {User user = new User();user.setUid(100);user.setUsername("abcdefg");user.setPassword("123456");user.setAge(999);return user;}
}

@Order注解中,值越小,存储的优先级越高,也就是说,在上面提到的情况下,Order里面的值越大,该类的方法就可以覆盖掉其他类的方法。

重点三、如果方法里面有参数是无法进行存储的,因此,方法重载也是无法被存储的。

Spring取对象

@Autowired注解

@Autowired用于将对象注入,它有三种注入方式:

属性注入(常用)

使用方法:在属性上面加上进行注入@Autowired注解

被注入的类(后面不再展示):

@Service
public class UserService {public void sayHi() {System.out.println("do UserService sayHi");}
}

使用类:

@Controller
public class UserController {//属性注入@Autowiredprivate UserService userService;public void sayHi() {System.out.println("do UserController sayHi");userService.sayHi();}
}

不过这个注入方式要注意:不能给被final修饰的属性注入。 

如下:

 总结:

属性注入的优点:方便快捷。

属性注入的缺点:

1. 无法给不可变(final修饰)对象注入

2. 兼容性差,只适用于IoC容器

3. 有一定风险,由于使用比较简单,所以容易违背 单一设计原则

Setter注入

使用方法:在Setting方法上加 @Autowired 注解。

@Controller
public class UserController {//setter 注入private UserService userService;@Autowiredpublic void setUserService(UserService userService) {this.userService = userService;}public void sayHi() {System.out.println("do UserController sayHi");userService.sayHi();}
}

同时这个注入方式也是无法给 final 修饰的属性注入的。

 Setter注入的优点:符合单一设计原则。

 Setter注入的缺点:

1. 无法给不可变类(被final修饰)注入

2. 使用 Setter注入 的对象可能被修改成其他对象。因为Setting是一个方法,它可以在任何时候被调用,然后传入其他的对象。

构造方法注入(Spring官方推荐)

使用方法:在构造方法上加 @Autowired 注解

@Controller
public class UserController {//构造方法注入private UserService userService;@Autowiredpublic UserController(UserService userService) {this.userService = userService;}public void sayHi() {System.out.println("do UserController sayHi");userService.sayHi();}
}

如果只需要注入一个对象,并且只有一个构造方法的情况下,可以不加 @Autowired  注解

@Controller
public class UserController {//构造方法注入private UserService userService;public UserController(UserService userService) {this.userService = userService;}public void sayHi() {System.out.println("do UserController sayHi");userService.sayHi();}
}

构造方法注入的优点:

1. 可以对不可变对象(被final修饰)进行注入。

因为被final修饰的对象要么直接赋值,要么在构造方法赋值。

2. 不会在其他地方被修改。(构造方法调用一次就会创建一次对象)

3. 可以保证注入的对象被完全初始化。

4. 通用性更好,其他的框架也可以使用

@Resoutce注解

@Resoutce 注解和 @Autowired注解都可以实现对象的注入,但是他们是有一定区别的。

@Resoutce 和 @Autowired 的区别

1. @Autowired注解属于Spring,@Resoutce注解属于jdk。

2. @Autowired注解支持构造方法注入,@Resoutce注解不支持构造方法注入。

3. @Resoutce注解有更多的功能,比如:name、value...,@Autowired注解功能较少。

4. @Autowired是先查找类,再查找名称;@Resoutce是先查找名称,后查找类。