Spring注解版学习
Spring纯注解开发
Spring纯注解和Spring的xml相比而言,注解版是非常容易的。但是论学习的话,推荐使用xml来进行入门,这样方便进行理解。掌握了spring的xml,那么纯注解就比较简单了。
注意一个小习惯:
在spring中,习惯把注册到SpirngIOC容器中的对象叫做bean组件。
首先参考下xml的格式和注解版的来进行对比:
1、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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean class="com.guang.User" id="user" name="user1,user2,user3" scope="singleton">
<property name="id" value="1"/>
<property name="name" value="guang"/>
</bean>
</beans>
对应的类(上面配置的是使用无参构造方法以及set方法来给user来进行赋值的):
public class User {
private Integer id;
private String name;
public void setId(Integer id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
测试类:
public class MainConfig {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring- beans.xml");
User user = applicationContext.getBean("user", User.class);
System.out.println(user);
}
}
注意下:
applicationContext.getBean(name);// 这里的name可以是id,也可以是别名。但是在注解版的里面放弃了id,直接使用name,也就是别名来进行获取bean
2、注解版
2.1、@Configuration
使用了注解版,那么必然有一个类是用来代替xml配置文件的。使用一个注解@Configuration来进行代替。
@Configuration注解是配置在类上的。@Configuration的底层注解是一个@Component,将标注的所在类注册成一个bean放入到SpringIOC容器中去。所以配置类也是一个bean组件。
@Configuration
public class MainConfig{
}
使用了@Configuration注解所标注的类就相当于是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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
</beans>
在@Configration中,可以查看到spring5中引入了一个特殊的属性proxyBeanMethods,表示代理bean的方法,默认是true
boolean proxyBeanMethods() default true;
表示的是如果配置类上下文中,多个@Bean方法中,如果需要引入到另外一个使用@Bean方法来给当前@Bean所修饰的方法来注入依赖的对象的时候,根据proxyBeanMethods是true还是false来决定是否从容器中进行获取。同时,网上很多博客都说,这也决定了加载bean的速度,如果是false,那么速度会加快;如果是true,加载速度会变慢。
如果是true的时候,会从容器中获取;如果是false,会new一个新的对象来进行属性注入。
如果是true的时候,会使用cglib来实现动态代理,会从容器中检查是否有这个对象,有的获取得到对象给对象赋值上;如果是false的时候,则不会从容器中获取得到对象,而是new一个新的对象来给bean组件进行赋值。
写两个类测试下:
@ToString
@Data
public class Person {
private Integer id;
private String username;
private Wile wile;
}
@ToString
@Data
public class Wile {
private String name;
private Integer id;
public Wile(){
System.out.println("wile被调用了");
}
}
// 将其置为false,每次都会新new一个对象。可以看其构造函数是否执行了,来进行查看
@Configuration(proxyBeanMethods = false)
@ComponentScan(basePackages = {"com.guang.entity"})
public class MainConfig {
@Bean("wife")
public Wile getWile(){
Wile wile = new Wile();
wile.setId(1);
wile.setName("chenyang");
return wile;
}
@Bean("person")
public Person getPerson(){
Person person = new Person();
person.setWile(getWile());
return person;
}
}
在bean容器中进行获取的时候,是false的时候,可以发现每次Wife的无参构造函数会执行两次;是true的时候,只会执行一次。
参考着上面的来进行书写,因为
2.2、@Bean
@Bean注解是使用在方法上的。方法的返回值代表着是bean的类型,方法名字代表的是bean的name。前提是如果没有在@Bean中的value属性中指定,如果在@Bean中的value属性中指定了,那么使用value的值来代替对应的值。
@Configuration
public class MainConfig{
@Bean
public User user(){
return new User();
}
}
这段代码就将user组件注册到SpringIOC容器中去了。
注解测试:
public class AnnotationTest {
@Test
public void testOne(){
// 获取得到容器
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
User user = applicationContext.getBean("user", User.class);
System.out.println(user);
}
}
注意下:
applicationContext.getBean(name);// 这里的name是方法名字,但是我们更多的不适用方法名字,而是给这个bean的name指定下
在这里使用注解@Bean来将bean的name修改下:
@Configuration
public class MainConfig{
@Bean(value="user")
public User getUser(){
return new User();
}
}
测试:
public class AnnotationTest {
@Test
public void testOne(){
// 获取得到容器
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
User user = applicationContext.getBean("user", User.class);
System.out.println(user);
}
}
此时,如果再通过方法名字当做name来进行调用,会出现找到这个
org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'getUser' available
2.3.1、属性注入
在xml中属性注入大概分为set方法注入、构造方法注入、P标签注入以及Spel表达式注入,但是最常用的就是set方法注入和Spel表达式注入。
利用@Value来给类中的属性来注入值
public class Car {
@Value("1")
private Integer id;
@Value("guang")
private String name;
public Integer getId() {
return id;
}
public String getName() {
return name;
}
}
使用@Value注解的同时,必须要将这个类加入到SpringIOC容器中才可以来进行使用这种便捷的方式使用。
@Autowired注解
标注在一个变量上的时候,先从容器中找到这个类型的bean,如果有多个,会存在着错误。因为这个是通过byType的方式来进行注入的;如果存在着相同类型的bean的时候,可以指定@Qulifierd来指定相同类型的name来进行注入。
2.3.2、@DependOn注解
标注了加载bean的优先级顺序,如果一个bean依赖了另外一个bean,那么被依赖的bean将会优先加载,从而使用这种方式来指定bean组件的优先级顺序。当前bean在加载的时候发现依赖了另外的一个bean的时候,先去加载另外一个bean,加载完成之后,再去创建当前的组件。
2.3、XML和注解版的异同
1、都是将bean注入SpringIOC容器中去的,但是又是有区别的。
使用xml方式注入到容器中,我们无法进行更改其创建过程。但是可以使用工厂方法来进行更改。但是使用工厂模式和使用注解相比较,工厂模式显得比较鸡肋,因为代码太多了!况且xml可以做的事情,注解也可以做到,而且注解比较简单,何乐而不为?
2、@Conponent及其衍生注解和@Bean的区别
我们经常在controller、service、dao层中加上注解,这样做的意义是最终将bean定义注册成组件放入到SpringIOC容器中去。
而使用@Bean我们可以自定义其创建过程,将创建好的bean放入到SpringIOC容器中去。
二者最大的区别:对对象的创建过程实现自定义。而且对于第三方类来说,我们无法获取得到第三方类的源代码,也就是说我们要将不是我们写的类注册到SpringIOC容器中来,无法使用@Conponent,但是使用@Bean注解就可以简单的将其注入进来。比如说将Druid连接池注册成一个bean的时候,@Conponent注解无法来注册,此时使用@Bean就可以来解决这个问题。
在主配置类中使用:
@Bean
public DruidDataSource druidDataSource(){
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setDriverClassName(driver);
druidDataSource.setUrl(url);
druidDataSource.setUsername(username);
druidDataSource.setPassword(password);
return druidDataSource;
}
2.4、向容器中导入组件的另外几种方式
2.4.1、@Import注解
可以在主配置类中引入其他类做来做springIOC的组件。如:在主配置类中引入User类,将User类注册成SpringIOC容器中的bean
@Import({User.class})
@Configuration
public class MainConfig {}
User类:
public class User {
private Integer id;
private String username;
}
测试类:
// 获取得到容器
AnnotationConfigApplicationContext applicationapplicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
for (String beanDefinitionName : applicationapplicationContext.getBeanDefinitionNames()) {
System.out.println(beanDefinitionName);
}
// 使用@Import注解,使用类型来获取。要是想使用其name来进行获取,那么可以查看下它在容器中的name:com.guang.beans.User.User
User user1 = applicationapplicationContext.getBean( User.class);
2.4.2、Import注解扩展
@Import除了可以将类注册成springIOC的组件之外,还可以引入另外的配置类。因为对于xml文件来说,如果xml针对于controller、service、dao包都用一个xml来保存对应的信息,那么注解版的也有这样的作用。可以将另外的一个配置类引入到主配置类进行使用。
这个配置类中不需要再加上@Configration注解了。
public class SecondConfig {
@Bean(value="user")
public User getUser(){
User user = new User();
user.setId(2);
user.setUsername("meng");
return user;
}
}
在主配置类中使用:
@Configuration
@Import({SecondConfig.class})
public class MainConfig {}
2.4.3、ImportSelector接口
作用于是批量注册bean组件。需要注意的是:根据类的全限定类名来进行注册的
@Component
public class MyImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
// 可以书写多个
return new String[]{"com.guang.beans.Car"};
}
}
在获取得到bean的时候,只能够根据类型来获取得到bean。
2.4.4、ImportBeanDefinitionRegistrar 接口
@Component
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry, BeanNameGenerator importBeanNameGenerator) {
GenericBeanDefinition genericBeanDefinition = new GenericBeanDefinition();
genericBeanDefinition.setBeanClass(Flower.class);
registry.registerBeanDefinition("flower",genericBeanDefinition);
}
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
ImportBeanDefinitionRegistrar.super.registerBeanDefinitions(importingClassMetadata, registry);
}
}
这里可以来设置对应的bean的id。
3、总结
在spring中将一个类注册成bean的几种方式:
1、通过xml;
2、通过注解:@Conponent及其衍生注解
3、@Import注解;
4、接口:ImportBeanDefinitionRegistrar和ImportSelector