远涛网站建设百度一下app下载安装
共有两类注解类型可以实现:
1. 类注解:@Controller、@Service、@Repository、@Component、@Configuration.
2. 方法注解:@Bean.
类注解
@Controller(控制器存储)
使⽤@Controller存储bean的代码如下所⽰:
@Controller // 将对象存储到 Spring 中 public class UserController {public void sayHi(){System.out.println("hi,UserController...");}}
如何观察这个对象已经存在Spring容器当中了呢?
接下来我们学习如何从Spring容器中获取对象
@SpringBootApplicationpublic class SpringIocDemoApplication {public static void main(String[] args) {//获取Spring上下⽂对象 ApplicationContext context =SpringApplication.run(SpringIocDemoApplication.class, args);//从Spring上下⽂中获取对象 UserController userController = context.getBean(UserController.class);//使⽤对象 userController.sayHi();}}
观察运⾏结果,发现成功从Spring中获取到Controller对象,并执⾏Controller的sayHi⽅法
如果把@Controller删掉,再观察运⾏结果
没有类型为“com.kdust.exercise”的合格bean。UserController”可用
获取bean对象的其他⽅式
上述代码是根据类型来查找对象,如果Spring容器中,同⼀个类型存在多个bean的话,怎么来获取呢?
ApplicationContext也提供了其他获取bean的⽅式,ApplicationContext获取bean对象的功能,是⽗类BeanFactory提供的功能.
public interface BeanFactory {//以上省略... // 1. 根据bean名称获取bean Object getBean(String var1) throws BeansException;// 2. 根据bean名称和类型获取bean <T> T getBean(String var1, Class<T> var2) throws BeansException;// 3. 按bean名称和构造函数参数动态创建bean,只适⽤于具有原型(prototype)作⽤域的bean Object getBean(String var1, Object... var2) throws BeansException;// 4. 根据类型获取bean <T> T getBean(Class<T> var1) throws BeansException;// 5. 按bean类型和构造函数参数动态创建bean, 只适⽤于具有原型(prototype)作⽤域的bean<T> T getBean(Class<T> var1, Object... var2) throws BeansException;//以下省略...
}
常⽤的是上述1,2,4种,这三种⽅式,获取到的bean是⼀样的
其中1,2种都涉及到根据名称来获取对象.
代码演示:
@SpringBootApplication
public class SpringIocDemoApplication {public static void main(String[] args) {//获取Spring上下⽂对象ApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class, args);//从Spring上下⽂中获取对象//根据bean类型, 从Spring上下⽂中获取对象UserController userController1 = context.getBean(UserController.class);//根据bean名称, 从Spring上下⽂中获取对象UserController userController2 = (UserController)context.getBean("userController");//根据bean类型+名称, 从Spring上下⽂中获取对象UserController userController3 =context.getBean("userController", UserController.class);System.out.println(userController1);System.out.println(userController2);System.out.println(userController3);}
}
地址⼀样,说明对象是⼀个
获取bean对象,是⽗类BeanFactory提供的功能
• 继承关系和功能⽅⾯来说:Spring容器有两个顶级的接:BeanFactory和ApplicationContext。其中BeanFactory提供了基础的访问容器的能⼒,⽽ApplicationContext属于BeanFactory的⼦类,它除了继承了BeanFactory的所有功能之外, 它还拥有独特的特性,还添加了对国际化⽀持、资源访问⽀持、以及事件传播等⽅⾯的⽀持.
• 从性能⽅⾯来说:ApplicationContext是⼀次性加载并初始化所有的Bean对象,⽽BeanFactory是需要那个才去加载那个,因此更加轻量.(空间换时间)
@Service(服务存储)
使⽤@Service存储bean的代码如下所⽰:
@Service
public class UserService {public void sayHi(String name) {System.out.println("Hi," + name);}
}
public static void main(String[] args) {//获取Spring上下⽂对象ApplicationContext context =SpringApplication.run(SpringIocDemoApplication.class, args);//从Spring中获取UserService对象UserService userService = context.getBean(UserService.class);//使⽤对象userService.sayHi("UserService");}
观察运⾏结果,发现成功从Spring中获取到UserService对象,并执⾏UserService的sayHi⽅法
同样的,把注解@Service删掉,再观察运⾏结果
没有类型为“com.kdust.exercise”的合格bean。UserService”可用
@Repository(仓库存储)
使⽤@Repository 存储bean的代码如下所⽰:
@Repository
public class UserRepository {public void sayHi() {System.out.println("Hi, UserRepository~");}
}
public static void main(String[] args) {//获取Spring上下⽂对象ApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class, args);//从Spring上下⽂中获取对象UserRepository userRepository = context.getBean(UserRepository.class);//使⽤对象userRepository.sayHi();}
观察运⾏结果,发现成功从Spring中获取到UserRepository对象,并执⾏UserRepository的sayHi⽅法
同样的,把注解@Repository删掉,再观察运⾏结果
没有类型为“com.kdust.exercise”的合格bean。UserRepository”可用
@Component(组件存储)
使⽤@Component存储bean的代码如下所⽰:
@Component
public class UserComponent {public void sayHi() {System.out.println("Hi, UserComponent~");}
}
public static void main(String[] args) {//获取Spring上下⽂对象ApplicationContext context =SpringApplication.run(SpringIocDemoApplication.class, args);//从Spring上下⽂中获取对象UserComponent userComponent = context.getBean(UserComponent.class);//使⽤对象userComponent.sayHi();}
观察运⾏结果,发现成功从Spring中获取到UserRepository对象,并执⾏UserRepository的sayHi⽅法
同样的,把注解@Component删掉,再观察运⾏结果
没有类型为“com.kdust.exercise”的合格bean。UserComponent”可用
@Configuration(配置存储)
使⽤@Configuration存储bean的代码如下所⽰:
@Configuration
public class UserConfiguration {public void sayHi() {System.out.println("Hi,UserConfiguration~");}
}
public static void main(String[] args) {//获取Spring上下⽂对象ApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class, args);//从Spring上下⽂中获取对象UserConfiguration userConfiguration = context.getBean(UserConfiguration.class);//使⽤对象userConfiguration.sayHi();}
观察运⾏结果,发现成功从Spring中获取到UserConfiguration对象,并执⾏UserConfiguration的 sayHi⽅法
同样的,把注解@Configuration删掉,再观察运⾏结果
没有类型为“com.kdust.exercise”的合格bean。UserConfiguration”可用
为什么要这么多类注解?
这个也是和咱们前⾯讲的应⽤分层是呼应的.让程序员看到类注解之后,就能直接了解当前类的⽤途.
• @Controller:控制层,接收请求,对请求进⾏处理,并进⾏响应.
• @Servie:业务逻辑层,处理具体的业务逻辑.
• @Repository:数据访问层,也称为持久层.负责数据访问操作
• @Configuration:配置层.处理项⽬中的⼀些配置信息.
这和每个省/市都有⾃⼰的⻋牌号是⼀样的.
⻋牌号都是唯⼀的,标识⼀个⻋辆的.但是为什么还需要设置不同的⻋牌开头呢.
⽐如陕西的⻋牌号就是:陕X:XXXXXX,北京的⻋牌号:京X:XXXXXX,甚⾄⼀个省不同的县区也 是不同的,⽐如西安就是,陕A:XXXXX,咸阳:陕B:XXXXXX,宝鸡,陕C:XXXXXX,⼀样.
这样做的好处除了可以节约号码之外,更重要的作⽤是可以直观的标识⼀辆⻋的归属地.
程序的应⽤分层,调⽤流程如下:
类注解之间的关系
查看@Controller /@Service /@Repository /@Configuration 等注解的源码发现:
其实这些注解⾥⾯都有⼀个注解@Component ,说明它们本⾝就是属于@Component 的"⼦类".
@Component 是⼀个元注解,也就是说可以注解其他类注解,如 @Controller ,@Service , @Repository 等.这些注解被称为
@Component 的衍⽣注解. @Controller ,@Service 和@Repository ⽤于更具体的⽤例(分别在控制层,业务逻辑层,持久化层),在开发过程中,如果你要在业务逻辑层使⽤ @Component 或@Service,显然@Service是更 好的选择
⽐如杯⼦有喝⽔杯,刷⽛杯等,但是我们更倾向于在⽇常喝⽔时使⽤⽔杯,洗漱时使⽤刷⽛杯.
方法注解@Bean
类注解是添加到某个类上的,但是存在两个问题:
1. 使⽤外部包⾥的类,没办法添加类注解
2. ⼀个类,需要多个对象,⽐如多个数据源
这种场景,我们就需要使⽤⽅法注解@Bean
我们先来看看⽅法注解如何使⽤:
public class BeanConfig {@Beanpublic User user(){User user = new User();user.setName("zhangsan");user.setAge(18);return user;}
}
然⽽,当我们写完以上代码,尝试获取bean对象中的user时却发现,根本获取不到:
@SpringBootApplication
public class SpringIocDemoApplication {public static void main(String[] args) {//获取Spring上下⽂对象ApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class, args);//从Spring上下⽂中获取对象 User user = context.getBean(User.class);//使⽤对象 System.out.println(user);}
}
以上程序的执⾏结果如下:
这是为什么呢?
方法注解要配合类注解使用
在Spring框架的设计中,⽅法注解 @Bean 要配合类注解才能将对象正常的存储到Spring容器中, 如下代码所⽰:
@Component
public class BeanConfig {@Beanpublic User user(){User user = new User();user.setName("zhangsan");user.setAge(18);return user;}
}
再次执⾏以上代码,运⾏结果如下:
定义多个对象
对于同⼀个类,如何定义多个对象呢?
⽐如多数据源的场景,类是同⼀个,但是配置不同,指向不同的数据源.
我们看下@Bean的使⽤
@Component
public class BeanConfig {@Beanpublic User user1(){User user = new User();user.setName("zhangsan");user.setAge(18);return user;}@Beanpublic User user2(){User user = new User();user.setName("lisi");user.setAge(19);return user;}
}
public static void main(String[] args) {//获取Spring上下⽂对象ApplicationContext context =SpringApplication.run(SpringIocDemoApplication.class, args);//从Spring上下⽂中获取对象SecurityProperties.User user = context.getBean(SecurityProperties.User.class);//使⽤对象System.out.println(user);}
定义了多个对象的话,我们根据类型获取对象,获取的是哪个对象呢?
运⾏结果:
报错信息显⽰:期望只有⼀个匹配,结果发现了两个,user1,user2
从报错信息中,可以看出来,@Bean注解的bean,bean的名称就是它的⽅法名
接下来我们根据名称来获取bean对象
@SpringBootApplicationpublic class SpringIocDemoApplication {public static void main(String[] args) {//获取Spring上下⽂对象 ApplicationContext context =SpringApplication.run(SpringIocDemoApplication.class, args);//根据bean名称, 从Spring上下⽂中获取对象 User user1 = (User) context.getBean("user1");User user2 = (User) context.getBean("user2");System.out.println(user1);System.out.println(user2);}}
运⾏结果:
可以看到,@Bean可以针对同⼀个类,定义多个对象.
重命名Bean
@Bean(name = {"u1","user1"})public User user1(){User user = new User();user.setName("zhangsan");user.setAge(18);return user;}
此时我们使⽤u1就可以获取到User对象了,如下代码所⽰:
@SpringBootApplicationpublic class SpringIocDemoApplication {public static void main(String[] args) {//获取Spring上下⽂对象 ApplicationContext context =SpringApplication.run(SpringIocDemoApplication.class, args);//从Spring上下⽂中获取对象 User u1 = (User) context.getBean("u1");//使⽤对象 System.out.println(u1);}}
name={}可以省略,如下代码所⽰:
@Bean({"u1","user1"})public User user1(){User user = new User();user.setName("zhangsan");user.setAge(18);return user;}
只有⼀个名称时,{}也可以省略,如:
@Bean("u1")public User user1(){User user = new User();user.setName("zhangsan");user.setAge(18);return user;}
扫描路径
Q:使⽤前⾯学习的四个注解声明的bean,⼀定会⽣效吗?
A:不⼀定(原因:bean想要⽣效,还需要被Spring扫描)
下⾯我们通过修改项⽬⼯程的⽬录结构,来测试bean对象是否⽣效:
再运⾏代码:
@SpringBootApplicationpublic class SpringIocDemoApplication {public static void main(String[] args) {//获取Spring上下⽂对象 ApplicationContext context =SpringApplication.run(SpringIocDemoApplication.class, args);//从Spring上下⽂中获取对象 User u1 = (User) context.getBean("u1");//使⽤对象 System.out.println(u1);}}
运⾏结果:
解释:没有bean的名称为u1
为什么没有找到bean对象呢?
使⽤五⼤注解声明的bean,要想⽣效,还需要配置扫描路径,让Spring扫描到这些注解
也就是通过 @ComponentScan 来配置扫描路径.
@ComponentScan({"com.example.demo"})@SpringBootApplicationpublic class SpringIocDemoApplication {public static void main(String[] args) {//获取Spring上下⽂对象 ApplicationContext context =SpringApplication.run(SpringIocDemoApplication.class, args);//从Spring上下⽂中获取对象 User u1 = (User) context.getBean("u1");//使⽤对象 System.out.println(u1);}}
{}⾥可以配置多个包路径
这种做法仅做了解,不做推荐使⽤
那为什么前⾯没有配置@ComponentScan注解也可以呢?
@ComponentScan 注解虽然没有显式配置,但是实际上已经包含在了启动类声明注解
@SpringBootApplication中了
默认扫描的范围是SpringBoot启动类所在包及其⼦包
在配置类上添加@ComponentScan注解,该注解默认会扫描该类所在的包下所有的配置类
推荐做法:
把启动类放在我们希望扫描的包的路径下,这样我们定义的bean就都可以被扫描到